summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--builtin/gamemgr.lua309
-rw-r--r--builtin/mainmenu.lua1190
-rw-r--r--builtin/mainmenu_helper.lua105
-rw-r--r--builtin/modmgr.lua881
-rw-r--r--builtin/modstore.lua275
-rw-r--r--doc/lua_api.txt58
-rw-r--r--doc/menu_lua_api.txt163
-rw-r--r--minetest.conf.example6
-rw-r--r--src/CMakeLists.txt11
-rw-r--r--src/convert_json.cpp367
-rw-r--r--src/convert_json.h (renamed from src/guiConfirmMenu.h)48
-rw-r--r--src/defaultsettings.cpp5
-rw-r--r--src/filesys.cpp267
-rw-r--r--src/filesys.h33
-rw-r--r--src/game.cpp27
-rw-r--r--src/guiConfigureWorld.cpp693
-rw-r--r--src/guiConfigureWorld.h107
-rw-r--r--src/guiConfirmMenu.cpp204
-rw-r--r--src/guiCreateWorld.cpp280
-rw-r--r--src/guiCreateWorld.h64
-rw-r--r--src/guiEngine.cpp570
-rw-r--r--src/guiEngine.h260
-rw-r--r--src/guiFileSelectMenu.cpp133
-rw-r--r--src/guiFileSelectMenu.h80
-rw-r--r--src/guiFormSpecMenu.cpp1978
-rw-r--r--src/guiFormSpecMenu.h129
-rw-r--r--src/guiLuaApi.cpp1067
-rw-r--r--src/guiLuaApi.h183
-rw-r--r--src/guiMainMenu.cpp1521
-rw-r--r--src/guiMainMenu.h111
-rw-r--r--src/main.cpp461
-rw-r--r--src/map.cpp13
-rw-r--r--src/mods.cpp28
-rw-r--r--src/mods.h68
-rw-r--r--src/serverlist.cpp60
-rw-r--r--src/test.cpp208
-rw-r--r--textures/base/pack/no_screenshot.pngbin0 -> 2446 bytes
37 files changed, 8036 insertions, 3927 deletions
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("<MODPACK>") ~= 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) <celeron55@gmail.com>,"..
+ "Ryan Kwolek (kwolekr) <kwolekr@minetest.net>,"..
+ "PilzAdam <pilzadam@minetest.net>," ..
+ "IIya Zhuravlev (thexyz) <xyz@minetest.net>,"..
+ "Lisa Milne (darkrose) <lisa@ltmnet.com>,"..
+ "Maciej Kasatkin (RealBadAngel) <mk@realbadangel.pl>,"..
+ "proller <proler@gmail.com>,"..
+ "sfan5 <sfan5@live.de>,"..
+ "kahrl <kahrl@gmx.net>,"..
+ ","..
+ "#YLWActive Contributors," ..
+ "sapier,"..
+ "Vanessa Ezekowitz (VanessaE) <vanessaezekowitz@gmail.com>,"..
+ "Jurgen Doser (doserj) <jurgen.doser@gmail.com>,"..
+ "Jeija <jeija@mesecons.net>,"..
+ "MirceaKitsune <mirceakitsune@gmail.com>,"..
+ "ShadowNinja"..
+ "dannydark <the_skeleton_of_a_child@yahoo.co.uk>"..
+ "0gb.us <0gb.us@0gb.us>,"..
+ "," ..
+ "#YLWPrevious Contributors," ..
+ "Guiseppe Bilotta (Oblomov) <guiseppe.bilotta@gmail.com>,"..
+ "Jonathan Neuschafer <j.neuschaefer@gmx.net>,"..
+ "Nils Dagsson Moskopp (erlehmann) <nils@dieweltistgarnichtso.net>,"..
+ "Constantin Wenger (SpeedProg) <constantin.wenger@googlemail.com>,"..
+ "matttpt <matttpt@gmail.com>,"..
+ "JacobF <queatz@gmail.com>,"..
+ ";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 "<circular reference>"
+ 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 "<function>"
+ elseif type(o) == "userdata" then
+ return "<userdata>"
+ 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 .. " <MODPACK>")
+ 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("<MODPACK>") 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("<MODPACK>") == 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("<MODPACK>") ~= 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("<MODPACK>") == 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("<MODPACK>") ~= 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("<MODPACK>") ~= 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 <modstore.current_list.pagecount then
+ modstore.current_list.page = modstore.current_list.page +1
+ end
+ end
+
+ if fields["btn_confirm_mod_successfull"] then
+ return {
+ current_tab = modstore.tabnames[1],
+ is_dialog = true,
+ show_buttons = false
+ }
+ end
+
+ for i=1, modstore.modsperpage, 1 do
+ local installbtn = "btn_install_mod_" .. i
+
+ if fields[installbtn] then
+ local modlistentry =
+ modstore.current_list.page * modstore.modsperpage + i
+
+ local moddetails = modstore.get_details(modstore.current_list.data[modlistentry].id)
+
+ local fullurl = engine.setting_get("modstore_download_url") ..
+ moddetails.download_url
+ local modfilename = os.tempfolder() .. ".zip"
+ print("Downloading mod from: " .. fullurl .. " to ".. modfilename)
+
+ if engine.download_file(fullurl,modfilename) then
+
+ modmgr.installmod(modfilename,moddetails.basename)
+
+ os.remove(modfilename)
+ modstore.lastmodtitle = modstore.current_list.data[modlistentry].title
+
+ return {
+ current_tab = "modstore_mod_installed",
+ is_dialog = true,
+ show_buttons = false
+ }
+ else
+ gamedata.errormessage = "Unable to download " ..
+ moddetails.download_url .. " (internet connection?)"
+ end
+ end
+ end
+end
+
+--------------------------------------------------------------------------------
+function modstore.update_modlist()
+ modstore.modlist_unsorted = {}
+ modstore.modlist_unsorted.data = engine.get_modstore_list()
+
+ if modstore.modlist_unsorted.data ~= nil then
+ modstore.modlist_unsorted.pagecount =
+ math.floor((#modstore.modlist_unsorted.data / modstore.modsperpage))
+ else
+ modstore.modlist_unsorted.data = {}
+ modstore.modlist_unsorted.pagecount = 0
+ end
+ modstore.modlist_unsorted.page = 0
+end
+
+--------------------------------------------------------------------------------
+function modstore.getmodlist(list)
+ local retval = ""
+ retval = retval .. "label[10,-0.4;Page " .. (list.page +1) ..
+ " of " .. (list.pagecount +1) .. "]"
+
+ retval = retval .. "button[11.6,-0.1;0.5,0.5;btn_modstore_page_up;^]"
+ retval = retval .. "box[11.6,0.35;0.28,8.6;BLK]"
+ local scrollbarpos = 0.35 + (8.1/list.pagecount) * list.page
+ retval = retval .. "box[11.6," ..scrollbarpos .. ";0.28,0.5;LIM]"
+ retval = retval .. "button[11.6,9.0;0.5,0.5;btn_modstore_page_down;v]"
+
+
+ if #list.data < (list.page * modstore.modsperpage) then
+ return retval
+ end
+
+ local endmod = (list.page * modstore.modsperpage) + modstore.modsperpage
+
+ if (endmod > #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[<X>,<Y>;<W>,<H>;<texture name>]
^ Position and size units are inventory slots
^ Example for formspec 8x4 in 16x resolution: image shall be sized 8*16px x 4*16px
+pwdfield[<X>,<Y>;<W>,<H>;<name>;<label>]
+^ Textual password style field; will be sent to server when a button is clicked
+^ x and y position the field relative to the top left of the menu
+^ w and h are the size of the field
+^ fields are a set height, but will be vertically centred on h
+^ Position and size units are inventory slots
+^ name is the name of the field as returned in fields to on_receive_fields
+^ label, if not blank, will be text printed on the top left above the field
+
field[<X>,<Y>;<W>,<H>;<name>;<label>;<default>]
^ Textual field; will be sent to server when a button is clicked
^ x and y position the field relative to the top left of the menu
@@ -910,6 +919,12 @@ label[<X>,<Y>;<label>]
^ label is the text on the label
^ Position and size units are inventory slots
+vertlabel[<X>,<Y>;<label>]
+^ Textual label drawn verticaly
+^ x and y work as per field
+^ label is the text on the label
+^ Position and size units are inventory slots
+
button[<X>,<Y>;<W>,<H>;<name>;<label>]
^ Clickable button. When clicked, fields will be sent.
^ x, y and name work as per field
@@ -922,6 +937,13 @@ image_button[<X>,<Y>;<W>,<H>;<texture name>;<name>;<label>]
^ image is the filename of an image
^ Position and size units are inventory slots
+image_button[<X>,<Y>;<W>,<H>;<texture name>;<name>;<label>;<noclip>;<drawborder>]
+^ x, y, w, h, and name work as per button
+^ image is the filename of an image
+^ Position and size units are inventory slots
+^ noclip true meand imagebutton doesn't need to be within specified formsize
+^ drawborder draw button bodrer or not
+
item_image_button[<X>,<Y>;<W>,<H>;<item name>;<name>;<label>]
^ x, y, w, h, name and label work as per button
^ item name is the registered name of an item/node,
@@ -934,6 +956,42 @@ button_exit[<X>,<Y>;<W>,<H>;<name>;<label>]
image_button_exit[<X>,<Y>;<W>,<H>;<texture name>;<name>;<label>]
^ When clicked, fields will be sent and the form will quit.
+textlist[<X>,<Y>;<W>,<H>;<name>;<listelem 1>,<listelem 2>,...,<listelem n>]
+^Scrollabel itemlist showing arbitrary text elements
+^ x and y position the itemlist relative to the top left of the menu
+^ w and h are the size of the itemlist
+^ listelements can be prepended by #colorkey (see colorkeys),
+^ if you want a listelement to start with # write ##
+^ name fieldname sent to server on doubleclick value is current selected element
+
+tabheader[<X>,<Y>;<name>;<caption 1>,<caption 2>;<current_tab>;<transparent>;<draw_border>]
+^ show a tabHEADER at specific position (ignores formsize)
+^ x and y position the itemlist relative to the top left of the menu
+^ name fieldname data is transfered to lua
+^ caption 1... name shown on top of tab
+^ current_tab index of selected tab 1...
+^ transparent (optional) show transparent
+^ draw_border (optional) draw border
+
+box[<X>,<Y>;<W>,<H>;<colorkey>]
+^ simple colored semitransparent box
+^ x and y position the box relative to the top left of the menu
+^ w and h are the size of box
+^ colorkey (see colorkeys)
+
+Available colorkeys:
+- YLW yellow
+- GRN green
+- LIM lime
+- ORN orange
+- RED red
+- BLU blue
+- CYN cyan
+- BLK black
+- BRN brown
+- WHT white
+- GRY grey
+
Inventory location:
- "context": Selected node metadata (deprecated: "current_name")
diff --git a/doc/menu_lua_api.txt b/doc/menu_lua_api.txt
new file mode 100644
index 000000000..337091a93
--- /dev/null
+++ b/doc/menu_lua_api.txt
@@ -0,0 +1,163 @@
+Minetest Lua Mainmenu API Reference 0.4.6
+========================================
+
+Introduction
+-------------
+The main menu is defined as a formspec by Lua in builtin/mainmenu.lua
+Description of formspec language to show your menu is in lua_api.txt
+
+Callbacks
+---------
+engine.buttonhandler(fields): called when a button is pressed.
+^ fields = {name1 = value1, name2 = value2, ...}
+engine.event_handler(event)
+^ event: "MenuQuit", "KeyEnter", "ExitButton" or "EditBoxEnter"
+
+Gamedata
+--------
+The "gamedata" table is read when calling engine.start(). It should contain:
+{
+ playername = <name>,
+ password = <password>,
+ address = <IP/adress>,
+ port = <port>,
+ selected_world = <index>, -- 0 for client mode
+ singleplayer = <true/false>,
+}
+
+Functions
+---------
+engine.start()
+engine.close()
+
+Filesystem:
+engine.get_scriptdir()
+^ returns directory of script
+engine.get_modpath()
+^ returns path to global modpath
+engine.get_modstore_details(modid)
+^ modid numeric id of mod in modstore
+^ returns {
+ id = <numeric id of mod in modstore>,
+ title = <human readable title>,
+ basename = <basename for mod>,
+ description = <description of mod>,
+ author = <author of mod>,
+ download_url= <best match download url>,
+ license = <short description of license>,
+ rating = <float value of current rating>
+}
+engine.get_modstore_list()
+^ returns {
+ [1] = {
+ id = <numeric id of mod in modstore>,
+ title = <human readable title>,
+ basename = <basename for mod>
+ }
+}
+engine.get_gamepath()
+^ returns path to global gamepath
+engine.get_dirlist(path,onlydirs)
+^ path to get subdirs from
+^ onlydirs should result contain only dirs?
+^ returns list of folders within path
+engine.create_dir(absolute_path)
+^ absolute_path to directory to create (needs to be absolute)
+^ returns true/false
+engine.delete_dir(absolute_path)
+^ absolute_path to directory to delete (needs to be absolute)
+^ returns true/false
+engine.copy_dir(source,destination,keep_soure)
+^ source folder
+^ destination folder
+^ keep_source DEFAULT true --> if set to false source is deleted after copying
+^ returns true/false
+engine.extract_zip(zipfile,destination) [unzip within path required]
+^ zipfile to extract
+^ destination folder to extract to
+^ returns true/false
+engine.download_file(url,target)
+^ url to download
+^ target to store to
+^ returns true/false
+engine.get_version()
+^ returns current minetest version
+
+GUI:
+engine.update_formspec(formspec)
+- engine.set_background(type, texturepath)
+^ type: "background", "overlay", "header" or "footer"
+engine.set_clouds(<true/false>)
+engine.set_topleft_text(text)
+
+Games:
+engine.get_game(index)
+^ returns {
+ id = <id>,
+ path = <full path to game>,
+ gamemods_path = <path>,
+ name = <name of game>,
+ menuicon_path = <full path to menuicon>,
+ DEPRECATED:
+ addon_mods_paths = {[1] = <path>,},
+}
+engine.get_games() -> table of all games in upper format
+
+Favorites:
+engine.get_favorites(location) -> list of favorites
+^ location: "local" or "online"
+^ returns {
+ [1] = {
+ clients = <number of clients/nil>,
+ clients_max = <maximum number of clients/nil>,
+ version = <server version/nil>,
+ password = <true/nil>,
+ creative = <true/nil>,
+ damage = <true/nil>,
+ pvp = <true/nil>,
+ description = <server description/nil>,
+ name = <server name/nil>,
+ address = <address of server/nil>,
+ port = <port>
+ },
+}
+engine.delete_favorite(id, location) -> success
+
+Settings:
+engine.setting_set(name, value)
+engine.setting_get(name) -> string or nil
+engine.setting_setbool(name, value)
+engine.setting_getbool(name) -> bool or nil
+
+Worlds:
+engine.get_worlds() -> list of worlds
+^ returns {
+ [1] = {
+ path = <full path to world>,
+ name = <name of world>,
+ gameid = <gameid of world>,
+ },
+}
+engine.create_world(worldname, gameid)
+engine.delete_world(index)
+
+
+UI:
+engine.get_textlist_index(textlistname) -> index
+engine.show_keys_menu()
+engine.file_open_dialog(formname,caption)
+^ shows a file open dialog
+^ formname is base name of dialog response returned in fields
+^ -if dialog was accepted "_accepted"
+^^ will be added to fieldname containing the path
+^ -if dialog was canceled "_cancelled"
+^ will be added to fieldname value is set to formname itself
+^ returns nil or selected file/folder
+
+Helpers:
+dump(obj, dumped={})
+^ Return object serialized as a string
+string:split(separator)
+^ eg. string:split("a,b", ",") == {"a","b"}
+string:trim()
+^ eg. string.trim("\n \t\tfoo bar\t ") == "foo bar" \ No newline at end of file
diff --git a/minetest.conf.example b/minetest.conf.example
index 75e546c2f..d7fa621a0 100644
--- a/minetest.conf.example
+++ b/minetest.conf.example
@@ -373,3 +373,9 @@
# Enable/disable running an IPv6 server. An IPv6 server may be restricted
# to IPv6 clients, depending on system configuration.
#ipv6_server = false
+
+#main_menu_game_mgr = 0
+#main_menu_mod_mgr = 0
+#modstore_download_url = http://forum.minetest.net/media/
+#modstore_listmods_url = http://forum.minetest.net/mmdb/mods/
+#modstore_details_url = http://forum.minetest.net/mmdb/mod/*/
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index bcd265858..2b60ff0d2 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -271,6 +271,7 @@ set(common_SRCS
staticobject.cpp
serverlist.cpp
pathfinder.cpp
+ convert_json.cpp
${SCRIPT_SRCS}
${UTIL_SRCS}
)
@@ -313,7 +314,6 @@ set(minetest_SRCS
clientobject.cpp
chat.cpp
hud.cpp
- guiMainMenu.cpp
guiKeyChangeMenu.cpp
guiMessageMenu.cpp
guiTextInputMenu.cpp
@@ -323,15 +323,16 @@ set(minetest_SRCS
guiVolumeChange.cpp
guiDeathScreen.cpp
guiChatConsole.cpp
- guiCreateWorld.cpp
- guiConfigureWorld.cpp
- guiConfirmMenu.cpp
client.cpp
filecache.cpp
tile.cpp
shader.cpp
game.cpp
main.cpp
+ guiEngine.cpp
+ guiLuaApi.cpp
+ guiFileSelectMenu.cpp
+ convert_json.cpp
)
if(USE_FREETYPE)
@@ -488,7 +489,7 @@ else()
endif()
set(CMAKE_CXX_FLAGS_RELEASE "-DNDEBUG ${RELEASE_WARNING_FLAGS} ${WARNING_FLAGS} ${OTHER_FLAGS} -O3 -ffast-math -Wall -fomit-frame-pointer -pipe -funroll-loops")
- set(CMAKE_CXX_FLAGS_DEBUG "-g -O1 -Wall ${WARNING_FLAGS} ${OTHER_FLAGS}")
+ set(CMAKE_CXX_FLAGS_DEBUG "-g -O0 -Wall ${WARNING_FLAGS} ${OTHER_FLAGS}")
if(USE_GPROF)
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -pg")
diff --git a/src/convert_json.cpp b/src/convert_json.cpp
new file mode 100644
index 000000000..7a69071ef
--- /dev/null
+++ b/src/convert_json.cpp
@@ -0,0 +1,367 @@
+/*
+Minetest
+Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>
+
+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.
+*/
+
+#include <vector>
+#include <iostream>
+#include <sstream>
+
+#include "convert_json.h"
+#include "mods.h"
+#include "config.h"
+#include "log.h"
+
+#if USE_CURL
+#include <curl/curl.h>
+
+static size_t WriteCallback(void *contents, size_t size, size_t nmemb, void *userp)
+{
+ ((std::string*)userp)->append((char*)contents, size * nmemb);
+ return size * nmemb;
+}
+
+#endif
+
+Json::Value fetchJsonValue(const std::string url,
+ struct curl_slist *chunk) {
+#if USE_CURL
+ std::string liststring;
+ CURL *curl;
+
+ curl = curl_easy_init();
+ if (curl)
+ {
+ CURLcode res;
+
+ curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1);
+ curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
+ curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback);
+ curl_easy_setopt(curl, CURLOPT_WRITEDATA, &liststring);
+
+ if (chunk != 0)
+ curl_easy_setopt(curl, CURLOPT_HTTPHEADER, chunk);
+
+
+ res = curl_easy_perform(curl);
+ if (res != CURLE_OK)
+ errorstream<<"Jsonreader: "<< url <<" not found (internet connection?)"<<std::endl;
+ curl_easy_cleanup(curl);
+ }
+
+ Json::Value root;
+ Json::Reader reader;
+ std::istringstream stream(liststring);
+ if (!liststring.size()) {
+ return Json::Value();
+ }
+
+ if (!reader.parse( stream, root ) )
+ {
+ errorstream << "URL: " << url << std::endl;
+ errorstream << "Failed to parse json data " << reader.getFormattedErrorMessages();
+ errorstream << "data: \"" << liststring << "\"" << std::endl;
+ return Json::Value();
+ }
+
+ if (root.isArray()) {
+ return root;
+ }
+ if ((root["list"].isArray())) {
+ return root["list"];
+ }
+ else {
+ return root;
+ }
+#endif
+ return Json::Value();
+}
+
+std::vector<ModStoreMod> readModStoreList(Json::Value& modlist) {
+ std::vector<ModStoreMod> retval;
+
+ if (modlist.isArray()) {
+ for (unsigned int i = 0; i < modlist.size(); i++)
+ {
+ ModStoreMod toadd;
+ toadd.valid = true;
+
+ //id
+ if (modlist[i]["id"].asString().size()) {
+ const char* id_raw = modlist[i]["id"].asString().c_str();
+ char* endptr = 0;
+ int numbervalue = strtol(id_raw,&endptr,10);
+
+ if ((*id_raw != 0) && (*endptr == 0)) {
+ toadd.id = numbervalue;
+ }
+ }
+ else {
+ toadd.valid = false;
+ }
+
+ //title
+ if (modlist[i]["title"].asString().size()) {
+ toadd.title = modlist[i]["title"].asString();
+ }
+ else {
+ toadd.valid = false;
+ }
+
+ //basename
+ if (modlist[i]["basename"].asString().size()) {
+ toadd.basename = modlist[i]["basename"].asString();
+ }
+ else {
+ toadd.valid = false;
+ }
+
+ //author
+
+ //rating
+
+ //version
+
+ if (toadd.valid) {
+ retval.push_back(toadd);
+ }
+ }
+ }
+ return retval;
+}
+
+ModStoreModDetails readModStoreModDetails(Json::Value& details) {
+
+ ModStoreModDetails retval;
+
+ retval.valid = true;
+
+ //version set
+ if (details["version_set"].isArray()) {
+ for (unsigned int i = 0; i < details["version_set"].size(); i++)
+ {
+ ModStoreVersionEntry toadd;
+
+ if (details["version_set"][i]["id"].asString().size()) {
+ const char* id_raw = details["version_set"][i]["id"].asString().c_str();
+ char* endptr = 0;
+ int numbervalue = strtol(id_raw,&endptr,10);
+
+ if ((*id_raw != 0) && (*endptr == 0)) {
+ toadd.id = numbervalue;
+ }
+ }
+ else {
+ retval.valid = false;
+ }
+
+ //date
+ if (details["version_set"][i]["date"].asString().size()) {
+ toadd.date = details["version_set"][i]["date"].asString();
+ }
+
+ //file
+ if (details["version_set"][i]["file"].asString().size()) {
+ toadd.file = details["version_set"][i]["file"].asString();
+ }
+ else {
+ retval.valid = false;
+ }
+
+ //approved
+
+ //mtversion
+
+ if( retval.valid ) {
+ retval.versions.push_back(toadd);
+ }
+ else {
+ break;
+ }
+ }
+ }
+
+ if (retval.versions.size() < 1) {
+ retval.valid = false;
+ }
+
+ //categories
+ if (details["categories"].isObject()) {
+ for (unsigned int i = 0; i < details["categories"].size(); i++) {
+ ModStoreCategoryInfo toadd;
+
+ if (details["categories"][i]["id"].asString().size()) {
+
+ const char* id_raw = details["categories"][i]["id"].asString().c_str();
+ char* endptr = 0;
+ int numbervalue = strtol(id_raw,&endptr,10);
+
+ if ((*id_raw != 0) && (*endptr == 0)) {
+ toadd.id = numbervalue;
+ }
+ }
+ else {
+ retval.valid = false;
+ }
+ if (details["categories"][i]["title"].asString().size()) {
+ toadd.name = details["categories"][i]["title"].asString();
+ }
+ else {
+ retval.valid = false;
+ }
+
+ if( retval.valid ) {
+ retval.categories.push_back(toadd);
+ }
+ else {
+ break;
+ }
+ }
+ }
+
+ //author
+ if (details["author"].isObject()) {
+ if (details["author"]["id"].asString().size()) {
+
+ const char* id_raw = details["author"]["id"].asString().c_str();
+ char* endptr = 0;
+ int numbervalue = strtol(id_raw,&endptr,10);
+
+ if ((*id_raw != 0) && (*endptr == 0)) {
+ retval.author.id = numbervalue;
+ }
+ else {
+ retval.valid = false;
+ }
+ }
+ else {
+ retval.valid = false;
+ }
+
+ if (details["author"]["username"].asString().size()) {
+ retval.author.username = details["author"]["username"].asString();
+ }
+ else {
+ retval.valid = false;
+ }
+ }
+ else {
+ retval.valid = false;
+ }
+
+ //license
+ if (details["license"].isObject()) {
+ if (details["license"]["id"].asString().size()) {
+
+ const char* id_raw = details["license"]["id"].asString().c_str();
+ char* endptr = 0;
+ int numbervalue = strtol(id_raw,&endptr,10);
+
+ if ((*id_raw != 0) && (*endptr == 0)) {
+ retval.license.id = numbervalue;
+ }
+ }
+ else {
+ retval.valid = false;
+ }
+
+ if (details["license"]["short"].asString().size()) {
+ retval.license.shortinfo = details["license"]["short"].asString();
+ }
+ else {
+ retval.valid = false;
+ }
+
+ if (details["license"]["link"].asString().size()) {
+ retval.license.url = details["license"]["link"].asString();
+ }
+
+ }
+
+ //id
+ if (details["id"].asString().size()) {
+
+ const char* id_raw = details["id"].asString().c_str();
+ char* endptr = 0;
+ int numbervalue = strtol(id_raw,&endptr,10);
+
+ if ((*id_raw != 0) && (*endptr == 0)) {
+ retval.id = numbervalue;
+ }
+ }
+ else {
+ retval.valid = false;
+ }
+
+ //title
+ if (details["title"].asString().size()) {
+ retval.title = details["title"].asString();
+ }
+ else {
+ retval.valid = false;
+ }
+
+ //basename
+ if (details["basename"].asString().size()) {
+ retval.basename = details["basename"].asString();
+ }
+ else {
+ retval.valid = false;
+ }
+
+ //description
+ if (details["desc"].asString().size()) {
+ retval.description = details["desc"].asString();
+ }
+
+ //repository
+ if (details["replink"].asString().size()) {
+ retval.repository = details["replink"].asString();
+ }
+
+ //value
+ if (details["rating"].asString().size()) {
+
+ const char* id_raw = details["rating"].asString().c_str();
+ char* endptr = 0;
+ float numbervalue = strtof(id_raw,&endptr);
+
+ if ((*id_raw != 0) && (*endptr == 0)) {
+ retval.rating = numbervalue;
+ }
+ }
+ else {
+ retval.rating = 0.0;
+ }
+
+ //depends
+ if (details["depends"].isArray()) {
+ //TODO
+ }
+
+ //softdepends
+ if (details["softdep"].isArray()) {
+ //TODO
+ }
+
+ //screenshot url
+ if (details["screenshot_url"].asString().size()) {
+ retval.screenshot_url = details["screenshot_url"].asString();
+ }
+
+ return retval;
+}
diff --git a/src/guiConfirmMenu.h b/src/convert_json.h
index 7d217d6a6..3fa9903b1 100644
--- a/src/guiConfirmMenu.h
+++ b/src/convert_json.h
@@ -17,40 +17,18 @@ with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#ifndef GUICONFIRMMENU_HEADER
-#define GUICONFIRMMENU_HEADER
-
-#include "irrlichttypes_extrabloated.h"
-#include "modalMenu.h"
-#include <string>
-
-struct ConfirmDest
-{
- virtual void answer(bool answer) = 0;
- virtual ~ConfirmDest() {};
-};
-
-class GUIConfirmMenu : public GUIModalMenu
-{
-public:
- GUIConfirmMenu(gui::IGUIEnvironment* env,
- gui::IGUIElement* parent, s32 id,
- IMenuManager *menumgr,
- ConfirmDest *dest,
- std::wstring message_text);
- ~GUIConfirmMenu();
-
- void removeChildren();
- // Remove and re-add (or reposition) stuff
- void regenerateGui(v2u32 screensize);
- void drawMenu();
- void acceptInput(bool answer);
- bool OnEvent(const SEvent& event);
-
-private:
- ConfirmDest *m_dest;
- std::wstring m_message_text;
-};
+#ifndef __CONVERT_JSON_H__
+#define __CONVERT_JSON_H__
-#endif
+#include "json/json.h"
+
+struct ModStoreMod;
+struct ModStoreModDetails;
+
+std::vector<ModStoreMod> readModStoreList(Json::Value& modlist);
+ModStoreModDetails readModStoreModDetails(Json::Value& details);
+Json::Value fetchJsonValue(const std::string url,
+ struct curl_slist *chunk);
+
+#endif
diff --git a/src/defaultsettings.cpp b/src/defaultsettings.cpp
index 44f5d1e86..37be3b651 100644
--- a/src/defaultsettings.cpp
+++ b/src/defaultsettings.cpp
@@ -256,6 +256,11 @@ void set_default_settings(Settings *settings)
// IPv6
settings->setDefault("enable_ipv6", "true");
settings->setDefault("ipv6_server", "false");
+
+ settings->setDefault("modstore_download_url", "http://forum.minetest.net/media/");
+ settings->setDefault("modstore_listmods_url", "http://forum.minetest.net/mmdb/mods/");
+ settings->setDefault("modstore_details_url", "http://forum.minetest.net/mmdb/mod/*/");
+
}
void override_default_settings(Settings *settings, Settings *from)
diff --git a/src/filesys.cpp b/src/filesys.cpp
index 256c8f16a..21ff199a8 100644
--- a/src/filesys.cpp
+++ b/src/filesys.cpp
@@ -20,7 +20,9 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "filesys.h"
#include "strfnd.h"
#include <iostream>
+#include <stdio.h>
#include <string.h>
+#include <errno.h>
#include "log.h"
namespace fs
@@ -30,11 +32,9 @@ namespace fs
#define _WIN32_WINNT 0x0501
#include <windows.h>
-#include <stdio.h>
#include <malloc.h>
#include <tchar.h>
#include <wchar.h>
-#include <stdio.h>
#define BUFSIZE MAX_PATH
@@ -145,6 +145,11 @@ bool IsDir(std::string path)
(attr & FILE_ATTRIBUTE_DIRECTORY));
}
+bool IsDirDelimiter(char c)
+{
+ return c == '/' || c == '\\';
+}
+
bool RecursiveDelete(std::string path)
{
infostream<<"Recursively deleting \""<<path<<"\""<<std::endl;
@@ -207,11 +212,26 @@ bool DeleteSingleFileOrEmptyDirectory(std::string path)
}
}
+std::string TempPath()
+{
+ DWORD bufsize = GetTempPath(0, "");
+ if(bufsize == 0){
+ errorstream<<"GetTempPath failed, error = "<<GetLastError()<<std::endl;
+ return "";
+ }
+ std::vector<char> buf(bufsize);
+ DWORD len = GetTempPath(bufsize, &buf[0]);
+ if(len == 0 || len > bufsize){
+ errorstream<<"GetTempPath failed, error = "<<GetLastError()<<std::endl;
+ return "";
+ }
+ return std::string(buf.begin(), buf.begin() + len);
+}
+
#else // POSIX
#include <sys/types.h>
#include <dirent.h>
-#include <errno.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <unistd.h>
@@ -301,6 +321,11 @@ bool IsDir(std::string path)
return ((statbuf.st_mode & S_IFDIR) == S_IFDIR);
}
+bool IsDirDelimiter(char c)
+{
+ return c == '/';
+}
+
bool RecursiveDelete(std::string path)
{
/*
@@ -364,6 +389,20 @@ bool DeleteSingleFileOrEmptyDirectory(std::string path)
}
}
+std::string TempPath()
+{
+ /*
+ Should the environment variables TMPDIR, TMP and TEMP
+ and the macro P_tmpdir (if defined by stdio.h) be checked
+ before falling back on /tmp?
+
+ Probably not, because this function is intended to be
+ compatible with lua's os.tmpname which under the default
+ configuration hardcodes mkstemp("/tmp/lua_XXXXXX").
+ */
+ return std::string(DIR_DELIM) + "tmp";
+}
+
#endif
void GetRecursiveSubPaths(std::string path, std::vector<std::string> &dst)
@@ -414,16 +453,14 @@ bool RecursiveDeleteContent(std::string path)
bool CreateAllDirs(std::string path)
{
- size_t pos;
std::vector<std::string> tocreate;
std::string basepath = path;
while(!PathExists(basepath))
{
tocreate.push_back(basepath);
- pos = basepath.rfind(DIR_DELIM_C);
- if(pos == std::string::npos)
+ basepath = RemoveLastPathComponent(basepath);
+ if(basepath.empty())
break;
- basepath = basepath.substr(0,pos);
}
for(int i=tocreate.size()-1;i>=0;i--)
if(!CreateDir(tocreate[i]))
@@ -431,5 +468,221 @@ bool CreateAllDirs(std::string path)
return true;
}
+bool CopyFileContents(std::string source, std::string target)
+{
+ FILE *sourcefile = fopen(source.c_str(), "rb");
+ if(sourcefile == NULL){
+ errorstream<<source<<": can't open for reading: "
+ <<strerror(errno)<<std::endl;
+ return false;
+ }
+
+ FILE *targetfile = fopen(target.c_str(), "wb");
+ if(targetfile == NULL){
+ errorstream<<target<<": can't open for writing: "
+ <<strerror(errno)<<std::endl;
+ fclose(sourcefile);
+ return false;
+ }
+
+ size_t total = 0;
+ bool retval = true;
+ bool done = false;
+ char readbuffer[BUFSIZ];
+ while(!done){
+ size_t readbytes = fread(readbuffer, 1,
+ sizeof(readbuffer), sourcefile);
+ total += readbytes;
+ if(ferror(sourcefile)){
+ errorstream<<source<<": IO error: "
+ <<strerror(errno)<<std::endl;
+ retval = false;
+ done = true;
+ }
+ if(readbytes > 0){
+ fwrite(readbuffer, 1, readbytes, targetfile);
+ }
+ if(feof(sourcefile) || ferror(sourcefile)){
+ // flush destination file to catch write errors
+ // (e.g. disk full)
+ fflush(targetfile);
+ done = true;
+ }
+ if(ferror(targetfile)){
+ errorstream<<target<<": IO error: "
+ <<strerror(errno)<<std::endl;
+ retval = false;
+ done = true;
+ }
+ }
+ infostream<<"copied "<<total<<" bytes from "
+ <<source<<" to "<<target<<std::endl;
+ fclose(sourcefile);
+ fclose(targetfile);
+ return retval;
+}
+
+bool CopyDir(std::string source, std::string target)
+{
+ if(PathExists(source)){
+ if(!PathExists(target)){
+ fs::CreateAllDirs(target);
+ }
+ bool retval = true;
+ std::vector<DirListNode> content = fs::GetDirListing(source);
+
+ for(unsigned int i=0; i < content.size(); i++){
+ std::string sourcechild = source + DIR_DELIM + content[i].name;
+ std::string targetchild = target + DIR_DELIM + content[i].name;
+ if(content[i].dir){
+ if(!fs::CopyDir(sourcechild, targetchild)){
+ retval = false;
+ }
+ }
+ else {
+ if(!fs::CopyFileContents(sourcechild, targetchild)){
+ retval = false;
+ }
+ }
+ }
+ return retval;
+ }
+ else {
+ return false;
+ }
+}
+
+bool PathStartsWith(std::string path, std::string prefix)
+{
+ size_t pathsize = path.size();
+ size_t pathpos = 0;
+ size_t prefixsize = prefix.size();
+ size_t prefixpos = 0;
+ for(;;){
+ bool delim1 = pathpos == pathsize
+ || IsDirDelimiter(path[pathpos]);
+ bool delim2 = prefixpos == prefixsize
+ || IsDirDelimiter(prefix[prefixpos]);
+
+ if(delim1 != delim2)
+ return false;
+
+ if(delim1){
+ while(pathpos < pathsize &&
+ IsDirDelimiter(path[pathpos]))
+ ++pathpos;
+ while(prefixpos < prefixsize &&
+ IsDirDelimiter(prefix[prefixpos]))
+ ++prefixpos;
+ if(prefixpos == prefixsize)
+ return true;
+ if(pathpos == pathsize)
+ return false;
+ }
+ else{
+ size_t len = 0;
+ do{
+ char pathchar = path[pathpos+len];
+ char prefixchar = prefix[prefixpos+len];
+ if(FILESYS_CASE_INSENSITIVE){
+ pathchar = tolower(pathchar);
+ prefixchar = tolower(prefixchar);
+ }
+ if(pathchar != prefixchar)
+ return false;
+ ++len;
+ } while(pathpos+len < pathsize
+ && !IsDirDelimiter(path[pathpos+len])
+ && prefixpos+len < prefixsize
+ && !IsDirDelimiter(
+ prefix[prefixsize+len]));
+ pathpos += len;
+ prefixpos += len;
+ }
+ }
+}
+
+std::string RemoveLastPathComponent(std::string path,
+ std::string *removed, int count)
+{
+ if(removed)
+ *removed = "";
+
+ size_t remaining = path.size();
+
+ for(int i = 0; i < count; ++i){
+ // strip a dir delimiter
+ while(remaining != 0 && IsDirDelimiter(path[remaining-1]))
+ remaining--;
+ // strip a path component
+ size_t component_end = remaining;
+ while(remaining != 0 && !IsDirDelimiter(path[remaining-1]))
+ remaining--;
+ size_t component_start = remaining;
+ // strip a dir delimiter
+ while(remaining != 0 && IsDirDelimiter(path[remaining-1]))
+ remaining--;
+ if(removed){
+ std::string component = path.substr(component_start,
+ component_end - component_start);
+ if(i)
+ *removed = component + DIR_DELIM + *removed;
+ else
+ *removed = component;
+ }
+ }
+ return path.substr(0, remaining);
+}
+
+std::string RemoveRelativePathComponents(std::string path)
+{
+ size_t pos = path.size();
+ size_t dotdot_count = 0;
+ while(pos != 0){
+ size_t component_with_delim_end = pos;
+ // skip a dir delimiter
+ while(pos != 0 && IsDirDelimiter(path[pos-1]))
+ pos--;
+ // strip a path component
+ size_t component_end = pos;
+ while(pos != 0 && !IsDirDelimiter(path[pos-1]))
+ pos--;
+ size_t component_start = pos;
+
+ std::string component = path.substr(component_start,
+ component_end - component_start);
+ bool remove_this_component = false;
+ if(component == "."){
+ remove_this_component = true;
+ }
+ else if(component == ".."){
+ remove_this_component = true;
+ dotdot_count += 1;
+ }
+ else if(dotdot_count != 0){
+ remove_this_component = true;
+ dotdot_count -= 1;
+ }
+
+ if(remove_this_component){
+ while(pos != 0 && IsDirDelimiter(path[pos-1]))
+ pos--;
+ path = path.substr(0, pos) + DIR_DELIM +
+ path.substr(component_with_delim_end,
+ std::string::npos);
+ pos++;
+ }
+ }
+
+ if(dotdot_count > 0)
+ return "";
+
+ // remove trailing dir delimiters
+ pos = path.size();
+ while(pos != 0 && IsDirDelimiter(path[pos-1]))
+ pos--;
+ return path.substr(0, pos);
+}
+
} // namespace fs
diff --git a/src/filesys.h b/src/filesys.h
index 263eb796f..d0bf400c7 100644
--- a/src/filesys.h
+++ b/src/filesys.h
@@ -26,10 +26,10 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#ifdef _WIN32 // WINDOWS
#define DIR_DELIM "\\"
-#define DIR_DELIM_C '\\'
+#define FILESYS_CASE_INSENSITIVE 1
#else // POSIX
#define DIR_DELIM "/"
-#define DIR_DELIM_C '/'
+#define FILESYS_CASE_INSENSITIVE 0
#endif
namespace fs
@@ -49,12 +49,17 @@ bool PathExists(std::string path);
bool IsDir(std::string path);
+bool IsDirDelimiter(char c);
+
// Only pass full paths to this one. True on success.
// NOTE: The WIN32 version returns always true.
bool RecursiveDelete(std::string path);
bool DeleteSingleFileOrEmptyDirectory(std::string path);
+// Returns path to temp directory, can return "" on error
+std::string TempPath();
+
/* Multiplatform */
// The path itself not included
@@ -69,6 +74,30 @@ bool RecursiveDeleteContent(std::string path);
// Create all directories on the given path that don't already exist.
bool CreateAllDirs(std::string path);
+// Copy a regular file
+bool CopyFileContents(std::string source, std::string target);
+
+// Copy directory and all subdirectories
+// Omits files and subdirectories that start with a period
+bool CopyDir(std::string source, std::string target);
+
+// Check if one path is prefix of another
+// For example, "/tmp" is a prefix of "/tmp" and "/tmp/file" but not "/tmp2"
+// Ignores case differences and '/' vs. '\\' on Windows
+bool PathStartsWith(std::string path, std::string prefix);
+
+// Remove last path component and the dir delimiter before and/or after it,
+// returns "" if there is only one path component.
+// removed: If non-NULL, receives the removed component(s).
+// count: Number of components to remove
+std::string RemoveLastPathComponent(std::string path,
+ std::string *removed = NULL, int count = 1);
+
+// Remove "." and ".." path components and for every ".." removed, remove
+// the last normal path component before it. Unlike AbsolutePath,
+// this does not resolve symlinks and check for existence of directories.
+std::string RemoveRelativePathComponents(std::string path);
+
}//fs
#endif
diff --git a/src/game.cpp b/src/game.cpp
index bcd155a79..44ec9ee37 100644
--- a/src/game.cpp
+++ b/src/game.cpp
@@ -208,33 +208,6 @@ public:
Client *m_client;
};
-class FormspecFormSource: public IFormSource
-{
-public:
- FormspecFormSource(std::string formspec,FormspecFormSource** game_formspec)
- {
- m_formspec = formspec;
- m_game_formspec = game_formspec;
- }
-
- ~FormspecFormSource()
- {
- *m_game_formspec = 0;
- }
-
- void setForm(std::string formspec) {
- m_formspec = formspec;
- }
-
- std::string getForm()
- {
- return m_formspec;
- }
-
- std::string m_formspec;
- FormspecFormSource** m_game_formspec;
-};
-
/*
Check if a node is pointable
*/
diff --git a/src/guiConfigureWorld.cpp b/src/guiConfigureWorld.cpp
deleted file mode 100644
index 8f5ef937d..000000000
--- a/src/guiConfigureWorld.cpp
+++ /dev/null
@@ -1,693 +0,0 @@
-/*
-Minetest
-Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>
-
-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.
-*/
-
-
-#include <iostream>
-#include <string>
-#include <map>
-
-#include "guiConfigureWorld.h"
-#include "guiMessageMenu.h"
-#include <IGUIButton.h>
-#include <IGUICheckBox.h>
-#include <IGUIListBox.h>
-#include <IGUIStaticText.h>
-#include <IGUITreeView.h>
-#include "gettext.h"
-#include "util/string.h"
-#include "settings.h"
-#include "filesys.h"
-
-enum
-{
- GUI_ID_MOD_TREEVIEW = 101,
- GUI_ID_ENABLED_CHECKBOX,
- GUI_ID_ENABLEALL,
- GUI_ID_DISABLEALL,
- GUI_ID_DEPENDS_LISTBOX,
- GUI_ID_RDEPENDS_LISTBOX,
- GUI_ID_CANCEL,
- GUI_ID_SAVE
-};
-
-#define QUESTIONMARK_STR L"?"
-#define CHECKMARK_STR L"\411"
-#define CROSS_STR L"\403"
-
-GUIConfigureWorld::GUIConfigureWorld(gui::IGUIEnvironment* env,
- gui::IGUIElement* parent, s32 id,
- IMenuManager *menumgr, WorldSpec wspec):
- GUIModalMenu(env, parent, id, menumgr),
- m_wspec(wspec),
- m_gspec(findWorldSubgame(m_wspec.path)),
- m_menumgr(menumgr)
-{
- //will be initialized in regenerateGUI()
- m_treeview=NULL;
-
- // game mods
- m_gamemods = flattenModTree(getModsInPath(m_gspec.gamemods_path));
-
- // world mods
- std::string worldmods_path = wspec.path + DIR_DELIM + "worldmods";
- m_worldmods = flattenModTree(getModsInPath(worldmods_path));
-
- // fill m_addontree with add-on mods
- std::set<std::string> paths = m_gspec.addon_mods_paths;
- for(std::set<std::string>::iterator it=paths.begin();
- it != paths.end(); ++it)
- {
- std::map<std::string,ModSpec> mods = getModsInPath(*it);
- m_addontree.insert(mods.begin(), mods.end());
- }
-
- // expand modpacks
- m_addonmods = flattenModTree(m_addontree);
-
- // collect reverse dependencies
- for(std::map<std::string, ModSpec>::iterator it = m_addonmods.begin();
- it != m_addonmods.end(); ++it)
- {
- std::string modname = (*it).first;
- ModSpec mod = (*it).second;
- for(std::set<std::string>::iterator dep_it = mod.depends.begin();
- dep_it != mod.depends.end(); ++dep_it)
- {
- m_reverse_depends.insert(std::make_pair((*dep_it),modname));
- }
- }
-
- m_settings.readConfigFile((m_wspec.path + DIR_DELIM + "world.mt").c_str());
- std::vector<std::string> names = m_settings.getNames();
-
- // mod_names contains the names of mods mentioned in the world.mt file
- std::set<std::string> mod_names;
- for(std::vector<std::string>::iterator it = names.begin();
- it != names.end(); ++it)
- {
- std::string name = *it;
- if (name.compare(0,9,"load_mod_")==0)
- mod_names.insert(name.substr(9));
- }
-
- // find new mods (installed but not mentioned in world.mt)
- for(std::map<std::string, ModSpec>::iterator it = m_addonmods.begin();
- it != m_addonmods.end(); ++it)
- {
- std::string modname = (*it).first;
- ModSpec mod = (*it).second;
- // a mod is new if it is not a modpack, and does not occur in
- // mod_names
- if(!mod.is_modpack &&
- mod_names.count(modname) == 0)
- m_settings.setBool("load_mod_"+modname, false);
- }
- // find missing mods (mentioned in world.mt, but not installed)
- for(std::set<std::string>::iterator it = mod_names.begin();
- it != mod_names.end(); ++it)
- {
- std::string modname = *it;
- if(m_addonmods.count(modname) == 0)
- m_settings.remove("load_mod_"+modname);
- }
- std::string worldmtfile = m_wspec.path+DIR_DELIM+"world.mt";
- m_settings.updateConfigFile(worldmtfile.c_str());
-}
-
-void GUIConfigureWorld::drawMenu()
-{
- gui::IGUISkin* skin = Environment->getSkin();
- if (!skin)
- return;
- video::IVideoDriver* driver = Environment->getVideoDriver();
-
- video::SColor bgcolor(140,0,0,0);
- driver->draw2DRectangle(bgcolor, AbsoluteRect, &AbsoluteClippingRect);
-
- gui::IGUIElement::draw();
-}
-
-
-void GUIConfigureWorld::regenerateGui(v2u32 screensize)
-{
-
- /*
- Remove stuff
- */
- removeChildren();
-
- /*
- Calculate new sizes and positions
- */
- core::rect<s32> rect(
- screensize.X/2 - 580/2,
- screensize.Y/2 - 300/2,
- screensize.X/2 + 580/2,
- screensize.Y/2 + 300/2
- );
-
- DesiredRect = rect;
- recalculateAbsolutePosition(false);
-
- v2s32 topleft = v2s32(10, 10);
-
- /*
- Add stuff
- */
- changeCtype("");
- {
- core::rect<s32> rect(0, 0, 200, 20);
- rect += topleft;
- //proper text is set below, when a mod is selected
- m_modname_text = Environment->addStaticText(L"Mod: N/A", rect, false,
- false, this, -1);
- }
- {
- core::rect<s32> rect(0, 0, 200, 20);
- rect += v2s32(0, 25) + topleft;
- wchar_t* text = wgettext("enabled");
- m_enabled_checkbox =
- Environment->addCheckBox(false, rect, this, GUI_ID_ENABLED_CHECKBOX,
- text);
- delete[] text;
- m_enabled_checkbox->setVisible(false);
- }
- {
- core::rect<s32> rect(0, 0, 85, 30);
- rect = rect + v2s32(0, 25) + topleft;
- wchar_t* text = wgettext("Enable All");
- m_enableall = Environment->addButton(rect, this, GUI_ID_ENABLEALL,
- text);
- delete[] text;
- m_enableall->setVisible(false);
- }
- {
- core::rect<s32> rect(0, 0, 85, 30);
- rect = rect + v2s32(115, 25) + topleft;
- wchar_t* text = wgettext("Disable All");
- m_disableall = Environment->addButton(rect, this, GUI_ID_DISABLEALL, text );
- delete[] text;
- m_disableall->setVisible(false);
- }
- {
- core::rect<s32> rect(0, 0, 200, 20);
- rect += v2s32(0, 60) + topleft;
- wchar_t* text = wgettext("depends on:");
- Environment->addStaticText(text, rect, false, false, this, -1);
- delete[] text;
- }
- {
- core::rect<s32> rect(0, 0, 200, 85);
- rect += v2s32(0, 80) + topleft;
- m_dependencies_listbox =
- Environment->addListBox(rect, this, GUI_ID_DEPENDS_LISTBOX, true);
- }
- {
- core::rect<s32> rect(0, 0, 200, 20);
- rect += v2s32(0, 175) + topleft;
- wchar_t* text = wgettext("is required by:");
- Environment->addStaticText( text, rect, false, false, this, -1);
- delete[] text;
- }
- {
- core::rect<s32> rect(0, 0, 200, 85);
- rect += v2s32(0, 195) + topleft;
- m_rdependencies_listbox =
- Environment->addListBox(rect,this, GUI_ID_RDEPENDS_LISTBOX,true);
- }
- {
- core::rect<s32> rect(0, 0, 340, 250);
- rect += v2s32(220, 0) + topleft;
- m_treeview = Environment->addTreeView(rect, this,
- GUI_ID_MOD_TREEVIEW,true);
- gui::IGUITreeViewNode* node
- = m_treeview->getRoot()->addChildBack(L"Add-Ons");
- buildTreeView(m_addontree, node);
- }
- {
- core::rect<s32> rect(0, 0, 120, 30);
- rect = rect + v2s32(330, 270) - topleft;
- wchar_t* text = wgettext("Cancel");
- Environment->addButton(rect, this, GUI_ID_CANCEL, text);
- delete[] text;
- }
- {
- core::rect<s32> rect(0, 0, 120, 30);
- rect = rect + v2s32(460, 270) - topleft;
- wchar_t* text = wgettext("Save");
- Environment->addButton(rect, this, GUI_ID_SAVE, text);
- delete[] text;
- }
- changeCtype("C");
-
- // at start, none of the treeview nodes is selected, so we select
- // the first element in the treeview of mods manually here.
- if(m_treeview->getRoot()->hasChilds())
- {
- m_treeview->getRoot()->getFirstChild()->setExpanded(true);
- m_treeview->getRoot()->getFirstChild()->setSelected(true);
- // Because a manual ->setSelected() doesn't cause an event, we
- // have to do this here:
- adjustSidebar();
- }
-}
-
-bool GUIConfigureWorld::OnEvent(const SEvent& event)
-{
-
- gui::IGUITreeViewNode* selected_node = NULL;
- if(m_treeview != NULL)
- selected_node = m_treeview->getSelected();
-
- if(event.EventType==EET_KEY_INPUT_EVENT && event.KeyInput.PressedDown)
- {
- switch (event.KeyInput.Key) {
- case KEY_ESCAPE: {
- quitMenu();
- return true;
- }
- // irrlicht's built-in TreeView gui has no keyboard control,
- // so we do it here: up/down to select prev/next node,
- // left/right to collapse/expand nodes, space to toggle
- // enabled/disabled.
- case KEY_DOWN: {
- if(selected_node != NULL)
- {
- gui::IGUITreeViewNode* node = selected_node->getNextVisible();
- if(node != NULL)
- {
- node->setSelected(true);
- adjustSidebar();
- }
- }
- return true;
- }
- case KEY_UP: {
- if(selected_node != NULL)
- {
- gui::IGUITreeViewNode* node = selected_node->getPrevSibling();
- if(node!=NULL)
- {
- node->setSelected(true);
- adjustSidebar();
- }
- else
- {
- gui::IGUITreeViewNode* parent = selected_node->getParent();
- if(selected_node == parent->getFirstChild() &&
- parent != m_treeview->getRoot())
- {
- parent->setSelected(true);
- adjustSidebar();
- }
- }
- }
- return true;
- }
- case KEY_RIGHT: {
- if(selected_node != NULL && selected_node->hasChilds())
- selected_node->setExpanded(true);
- return true;
- }
- case KEY_LEFT: {
- if(selected_node != NULL && selected_node->hasChilds())
- selected_node->setExpanded(false);
- return true;
- }
- case KEY_SPACE: {
- if(selected_node != NULL && !selected_node->hasChilds() &&
- selected_node->getText() != NULL)
- {
- std::string modname = wide_to_narrow(selected_node->getText());
- bool checked = m_enabled_checkbox->isChecked();
- m_enabled_checkbox->setChecked(!checked);
- setEnabled(modname,!checked);
- }
- return true;
- }
- default: {}
- }
- }
- if(event.EventType==EET_GUI_EVENT)
- {
- if(event.GUIEvent.EventType==gui::EGET_ELEMENT_FOCUS_LOST
- && isVisible())
- {
- if(!canTakeFocus(event.GUIEvent.Element))
- {
- dstream<<"GUIConfigureWorld: Not allowing focus change."
- <<std::endl;
- // Returning true disables focus change
- return true;
- }
- }
- if(event.GUIEvent.EventType==gui::EGET_BUTTON_CLICKED){
- switch(event.GUIEvent.Caller->getID()){
- case GUI_ID_CANCEL: {
- quitMenu();
- return true;
- }
- case GUI_ID_SAVE: {
- std::string worldmtfile = m_wspec.path+DIR_DELIM+"world.mt";
- m_settings.updateConfigFile(worldmtfile.c_str());
-
- // The trailing spaces are because there seems to be a
- // bug in the text-size calculation. if the trailing
- // spaces are removed from the message text, the
- // message gets wrapped and parts of it are cut off:
- wchar_t* text = wgettext("Configuration saved. ");
- GUIMessageMenu *menu =
- new GUIMessageMenu(Environment, Parent, -1, m_menumgr,
- text );
- delete[] text;
- menu->drop();
-
- try
- {
- ModConfiguration modconf(m_wspec.path);
- if(!modconf.isConsistent())
- {
- wchar_t* text = wgettext("Warning: Configuration not consistent. ");
- GUIMessageMenu *menu =
- new GUIMessageMenu(Environment, Parent, -1, m_menumgr,
- text );
- delete[] text;
- menu->drop();
- }
- }
- catch(ModError &err)
- {
- errorstream<<err.what()<<std::endl;
- std::wstring text = narrow_to_wide(err.what()) + wgettext("\nCheck debug.txt for details.");
- GUIMessageMenu *menu =
- new GUIMessageMenu(Environment, Parent, -1, m_menumgr,
- text );
- menu->drop();
- }
-
- quitMenu();
- return true;
- }
- case GUI_ID_ENABLEALL: {
- if(selected_node != NULL && selected_node->getParent() == m_treeview->getRoot())
- {
- enableAllMods(m_addonmods,true);
- }
- else if(selected_node != NULL && selected_node->getText() != NULL)
- {
- std::string modname = wide_to_narrow(selected_node->getText());
- std::map<std::string, ModSpec>::iterator mod_it = m_addonmods.find(modname);
- if(mod_it != m_addonmods.end())
- enableAllMods(mod_it->second.modpack_content,true);
- }
- return true;
- }
- case GUI_ID_DISABLEALL: {
- if(selected_node != NULL && selected_node->getParent() == m_treeview->getRoot())
- {
- enableAllMods(m_addonmods,false);
- }
- if(selected_node != NULL && selected_node->getText() != NULL)
- {
- std::string modname = wide_to_narrow(selected_node->getText());
- std::map<std::string, ModSpec>::iterator mod_it = m_addonmods.find(modname);
- if(mod_it != m_addonmods.end())
- enableAllMods(mod_it->second.modpack_content,false);
- }
- return true;
- }
- }
- }
- if(event.GUIEvent.EventType==gui::EGET_CHECKBOX_CHANGED &&
- event.GUIEvent.Caller->getID() == GUI_ID_ENABLED_CHECKBOX)
- {
- if(selected_node != NULL && !selected_node->hasChilds() &&
- selected_node->getText() != NULL)
- {
- std::string modname = wide_to_narrow(selected_node->getText());
- setEnabled(modname, m_enabled_checkbox->isChecked());
- }
- return true;
- }
- if(event.GUIEvent.EventType==gui::EGET_TREEVIEW_NODE_SELECT &&
- event.GUIEvent.Caller->getID() == GUI_ID_MOD_TREEVIEW)
- {
- selecting_dep = -1;
- selecting_rdep = -1;
- adjustSidebar();
- return true;
- }
- if(event.GUIEvent.EventType==gui::EGET_LISTBOX_CHANGED &&
- event.GUIEvent.Caller->getID() == GUI_ID_DEPENDS_LISTBOX)
- {
- selecting_dep = m_dependencies_listbox->getSelected();
- selecting_rdep = -1;
- return true;
- }
- if(event.GUIEvent.EventType==gui::EGET_LISTBOX_CHANGED &&
- event.GUIEvent.Caller->getID() == GUI_ID_RDEPENDS_LISTBOX)
- {
- selecting_dep = -1;
- selecting_rdep = m_rdependencies_listbox->getSelected();
- return true;
- }
-
- //double click in a dependency listbox: find corresponding
- //treeviewnode and select it:
- if(event.GUIEvent.EventType==gui::EGET_LISTBOX_SELECTED_AGAIN)
- {
- gui::IGUIListBox* box = NULL;
- if(event.GUIEvent.Caller->getID() == GUI_ID_DEPENDS_LISTBOX)
- {
- box = m_dependencies_listbox;
- if(box->getSelected() != selecting_dep)
- return true;
- }
- if(event.GUIEvent.Caller->getID() == GUI_ID_RDEPENDS_LISTBOX)
- {
- box = m_rdependencies_listbox;
- if(box->getSelected() != selecting_rdep)
- return true;
- }
- if(box != NULL && box->getSelected() != -1 &&
- box->getListItem(box->getSelected()) != NULL)
- {
- std::string modname =
- wide_to_narrow(box->getListItem(box->getSelected()));
- std::map<std::string, gui::IGUITreeViewNode*>::iterator it =
- m_nodes.find(modname);
- if(it != m_nodes.end())
- {
- // select node and make sure node is visible by
- // expanding all parents
- gui::IGUITreeViewNode* node = (*it).second;
- node->setSelected(true);
- while(!node->isVisible() &&
- node->getParent() != m_treeview->getRoot())
- {
- node = node->getParent();
- node->setExpanded(true);
- }
- adjustSidebar();
- }
- }
- return true;
- }
- }
-
- return Parent ? Parent->OnEvent(event) : false;
-}
-
-void GUIConfigureWorld::buildTreeView(std::map<std::string, ModSpec> mods,
- gui::IGUITreeViewNode* node)
-{
- for(std::map<std::string,ModSpec>::iterator it = mods.begin();
- it != mods.end(); ++it)
- {
- std::string modname = (*it).first;
- ModSpec mod = (*it).second;
- gui::IGUITreeViewNode* new_node =
- node->addChildBack(narrow_to_wide(modname).c_str());
- m_nodes.insert(std::make_pair(modname, new_node));
- if(mod.is_modpack)
- buildTreeView(mod.modpack_content, new_node);
- else
- {
- // set icon for node: x for disabled mods, checkmark for enabled mods
- bool mod_enabled = false;
- if(m_settings.exists("load_mod_"+modname))
- mod_enabled = m_settings.getBool("load_mod_"+modname);
- if(mod_enabled)
- new_node->setIcon(CHECKMARK_STR);
- else
- new_node->setIcon(CROSS_STR);
- }
- }
-}
-
-
-void GUIConfigureWorld::adjustSidebar()
-{
- gui::IGUITreeViewNode* node = m_treeview->getSelected();
- std::wstring modname_w;
- if(node->getText() != NULL)
- modname_w = node->getText();
- else
- modname_w = L"N/A";
- std::string modname = wide_to_narrow(modname_w);
-
- ModSpec mspec;
- std::map<std::string, ModSpec>::iterator it = m_addonmods.find(modname);
- if(it != m_addonmods.end())
- mspec = it->second;
-
- m_dependencies_listbox->clear();
- m_rdependencies_listbox->clear();
-
- // if no mods installed, there is nothing to enable/disable, so we
- // don't show buttons or checkbox on the sidebar
- if(node->getParent() == m_treeview->getRoot() && !node->hasChilds())
- {
- m_disableall->setVisible(false);
- m_enableall->setVisible(false);
- m_enabled_checkbox->setVisible(false);
- return;
- }
-
- // a modpack is not enabled/disabled by itself, only its cotnents
- // are. so we show show enable/disable all buttons, but hide the
- // checkbox
- if(node->getParent() == m_treeview->getRoot() ||
- mspec.is_modpack)
- {
- m_enabled_checkbox->setVisible(false);
- m_disableall->setVisible(true);
- m_enableall->setVisible(true);
- m_modname_text->setText((L"Modpack: "+modname_w).c_str());
- return;
- }
-
- // for a normal mod, we hide the enable/disable all buttons, but show the checkbox.
- m_disableall->setVisible(false);
- m_enableall->setVisible(false);
- m_enabled_checkbox->setVisible(true);
- m_modname_text->setText((L"Mod: "+modname_w).c_str());
-
- // the mod is enabled unless it is disabled in the world.mt settings.
- bool mod_enabled = true;
- if(m_settings.exists("load_mod_"+modname))
- mod_enabled = m_settings.getBool("load_mod_"+modname);
- m_enabled_checkbox->setChecked(mod_enabled);
-
- for(std::set<std::string>::iterator it=mspec.depends.begin();
- it != mspec.depends.end(); ++it)
- {
- // check if it is an add-on mod or a game/world mod. We only
- // want to show add-ons
- std::string dependency = (*it);
- if(m_gamemods.count(dependency) > 0)
- dependency += " (" + m_gspec.id + ")";
- else if(m_worldmods.count(dependency) > 0)
- dependency += " (" + m_wspec.name + ")";
- else if(m_addonmods.count(dependency) == 0)
- dependency += " (missing)";
- m_dependencies_listbox->addItem(narrow_to_wide(dependency).c_str());
- }
-
- // reverse dependencies of this mod:
- std::pair< std::multimap<std::string, std::string>::iterator,
- std::multimap<std::string, std::string>::iterator > rdep =
- m_reverse_depends.equal_range(modname);
- for(std::multimap<std::string,std::string>::iterator it = rdep.first;
- it != rdep.second; ++it)
- {
- // check if it is an add-on mod or a game/world mod. We only
- // want to show add-ons
- std::string rdependency = (*it).second;
- if(m_addonmods.count(rdependency) > 0)
- m_rdependencies_listbox->addItem(narrow_to_wide(rdependency).c_str());
- }
-}
-
-void GUIConfigureWorld::enableAllMods(std::map<std::string, ModSpec> mods,bool enable)
-{
- for(std::map<std::string, ModSpec>::iterator it = mods.begin();
- it != mods.end(); ++it)
- {
- ModSpec mod = (*it).second;
- if(mod.is_modpack)
- // a modpack, recursively enable all mods in it
- enableAllMods(mod.modpack_content,enable);
- else // not a modpack
- setEnabled(mod.name, enable);
-
- }
-}
-
-void GUIConfigureWorld::enableMod(std::string modname)
-{
- std::map<std::string, ModSpec>::iterator mod_it = m_addonmods.find(modname);
- if(mod_it == m_addonmods.end()){
- errorstream << "enableMod() called with invalid mod name \"" << modname << "\"" << std::endl;
- return;
- }
- ModSpec mspec = mod_it->second;
- m_settings.setBool("load_mod_"+modname,true);
- std::map<std::string,gui::IGUITreeViewNode*>::iterator it =
- m_nodes.find(modname);
- if(it != m_nodes.end())
- (*it).second->setIcon(CHECKMARK_STR);
- //also enable all dependencies
- for(std::set<std::string>::iterator it=mspec.depends.begin();
- it != mspec.depends.end(); ++it)
- {
- std::string dependency = *it;
- // only enable it if it is an add-on mod
- if(m_addonmods.count(dependency) > 0)
- enableMod(dependency);
- }
-}
-
-void GUIConfigureWorld::disableMod(std::string modname)
-{
- std::map<std::string, ModSpec>::iterator mod_it = m_addonmods.find(modname);
- if(mod_it == m_addonmods.end()){
- errorstream << "disableMod() called with invalid mod name \"" << modname << "\"" << std::endl;
- return;
- }
-
- m_settings.setBool("load_mod_"+modname,false);
- std::map<std::string,gui::IGUITreeViewNode*>::iterator it =
- m_nodes.find(modname);
- if(it != m_nodes.end())
- (*it).second->setIcon(CROSS_STR);
- //also disable all mods that depend on this one
- std::pair<std::multimap<std::string, std::string>::iterator,
- std::multimap<std::string, std::string>::iterator > rdep =
- m_reverse_depends.equal_range(modname);
- for(std::multimap<std::string,std::string>::iterator it = rdep.first;
- it != rdep.second; ++it)
- {
- std::string rdependency = (*it).second;
- // only disable it if it is an add-on mod
- if(m_addonmods.count(rdependency) > 0)
- disableMod(rdependency);
- }
-}
-
diff --git a/src/guiConfigureWorld.h b/src/guiConfigureWorld.h
deleted file mode 100644
index 23ebac66d..000000000
--- a/src/guiConfigureWorld.h
+++ /dev/null
@@ -1,107 +0,0 @@
-/*
-Minetest
-Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>
-
-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.
-*/
-
-#ifndef GUICONFIGUREWORLD_HEADER
-#define GUICONFIGUREWORLD_HEADER
-
-#include "irrlichttypes_extrabloated.h"
-#include "modalMenu.h"
-#include "mods.h"
-#include "subgame.h"
-#include "settings.h"
-
-
-namespace irr{
- namespace gui{
- class IGUITreeViewNode;
- }
-}
-
-class GUIConfigureWorld : public GUIModalMenu
-{
-public:
- GUIConfigureWorld(gui::IGUIEnvironment* env,
- gui::IGUIElement* parent, s32 id,
- IMenuManager *menumgr, WorldSpec wspec);
-
- void regenerateGui(v2u32 screensize);
-
- void drawMenu();
-
- bool OnEvent(const SEvent& event);
-
-private:
- WorldSpec m_wspec;
- SubgameSpec m_gspec;
-
- // tree of installed add-on mods. key is the mod name, modpacks
- // are not expanded.
- std::map<std::string, ModSpec> m_addontree;
-
- // like m_addontree, but modpacks are expanded.
- std::map<std::string, ModSpec> m_addonmods;
-
- // list of game mods (flattened)
- std::map<std::string, ModSpec> m_gamemods;
-
- // list of world mods (flattened)
- std::map<std::string, ModSpec> m_worldmods;
-
- // for each mod, the set of mods depending on it
- std::multimap<std::string, std::string> m_reverse_depends;
-
- // the settings in the world.mt file
- Settings m_settings;
-
- // maps modnames to nodes in m_treeview
- std::map<std::string,gui::IGUITreeViewNode*> m_nodes;
-
- gui::IGUIStaticText* m_modname_text;
- gui::IGUITreeView* m_treeview;
- gui::IGUIButton* m_enableall;
- gui::IGUIButton* m_disableall;
- gui::IGUICheckBox* m_enabled_checkbox;
- gui::IGUIListBox* m_dependencies_listbox;
- gui::IGUIListBox* m_rdependencies_listbox;
- void buildTreeView(std::map<std::string,ModSpec> mods,
- gui::IGUITreeViewNode* node);
- void adjustSidebar();
- void enableAllMods(std::map<std::string,ModSpec> mods, bool enable);
- void setEnabled(std::string modname, bool enable)
- {
- if(enable)
- enableMod(modname);
- else
- disableMod(modname);
- };
-
- void enableMod(std::string modname);
- void disableMod(std::string modname);
-
- // hack to work around wonky handling of double-click in
- // irrlicht. store selected index of listbox items here so event
- // handling can check whether it was a real double click on the
- // same item. (irrlicht also reports a double click if you rapidly
- // select two different items.)
- int selecting_dep;
- int selecting_rdep;
-
- IMenuManager* m_menumgr;
-};
-#endif
diff --git a/src/guiConfirmMenu.cpp b/src/guiConfirmMenu.cpp
deleted file mode 100644
index 86b230506..000000000
--- a/src/guiConfirmMenu.cpp
+++ /dev/null
@@ -1,204 +0,0 @@
-/*
-Minetest
-Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>
-
-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.
-*/
-
-#include "guiConfirmMenu.h"
-#include "debug.h"
-#include "serialization.h"
-#include <string>
-#include <IGUICheckBox.h>
-#include <IGUIEditBox.h>
-#include <IGUIButton.h>
-#include <IGUIStaticText.h>
-#include <IGUIFont.h>
-
-#include "gettext.h"
-
-enum
-{
- GUI_ID_YES = 101,
- GUI_ID_NO,
-};
-
-GUIConfirmMenu::GUIConfirmMenu(gui::IGUIEnvironment* env,
- gui::IGUIElement* parent, s32 id,
- IMenuManager *menumgr,
- ConfirmDest *dest,
- std::wstring message_text
-):
- GUIModalMenu(env, parent, id, menumgr),
- m_dest(dest),
- m_message_text(message_text)
-{
-}
-
-GUIConfirmMenu::~GUIConfirmMenu()
-{
- removeChildren();
- if(m_dest)
- delete m_dest;
-}
-
-void GUIConfirmMenu::removeChildren()
-{
- const core::list<gui::IGUIElement*> &children = getChildren();
- core::list<gui::IGUIElement*> children_copy;
- for(core::list<gui::IGUIElement*>::ConstIterator
- i = children.begin(); i != children.end(); i++)
- {
- children_copy.push_back(*i);
- }
- for(core::list<gui::IGUIElement*>::Iterator
- i = children_copy.begin();
- i != children_copy.end(); i++)
- {
- (*i)->remove();
- }
-}
-
-void GUIConfirmMenu::regenerateGui(v2u32 screensize)
-{
- /*
- Remove stuff
- */
- removeChildren();
-
- /*
- Calculate new sizes and positions
- */
- core::rect<s32> rect(
- screensize.X/2 - 580/2,
- screensize.Y/2 - 300/2,
- screensize.X/2 + 580/2,
- screensize.Y/2 + 300/2
- );
-
- DesiredRect = rect;
- recalculateAbsolutePosition(false);
-
- v2s32 size = rect.getSize();
-
- gui::IGUISkin *skin = Environment->getSkin();
- gui::IGUIFont *font = skin->getFont();
- s32 msg_h = font->getDimension(m_message_text.c_str()).Height;
- s32 msg_w = font->getDimension(m_message_text.c_str()).Width;
- if(msg_h > 200)
- msg_h = 200;
- if(msg_w > 540)
- msg_w = 540;
-
- /*
- Add stuff
- */
- {
- core::rect<s32> rect(0, 0, msg_w, msg_h);
- rect += v2s32(size.X/2-msg_w/2, size.Y/2-30/2 - msg_h/2);
- Environment->addStaticText(m_message_text.c_str(),
- rect, false, true, this, -1);
- }
- changeCtype("");
- int bw = 100;
- {
- core::rect<s32> rect(0, 0, bw, 30);
- rect = rect + v2s32(size.X/2-bw/2-(bw/2+5), size.Y/2-30/2+5 + msg_h/2);
- wchar_t* text = wgettext("Yes");
- Environment->addButton(rect, this, GUI_ID_YES,
- text);
- delete[] text;
- }
- {
- core::rect<s32> rect(0, 0, bw, 30);
- rect = rect + v2s32(size.X/2-bw/2+(bw/2+5), size.Y/2-30/2+5 + msg_h/2);
- wchar_t* text = wgettext("No");
- Environment->addButton(rect, this, GUI_ID_NO,
- text);
- delete[] text;
- }
- changeCtype("C");
-}
-
-void GUIConfirmMenu::drawMenu()
-{
- gui::IGUISkin* skin = Environment->getSkin();
- if (!skin)
- return;
- video::IVideoDriver* driver = Environment->getVideoDriver();
-
- video::SColor bgcolor(140,0,0,0);
- driver->draw2DRectangle(bgcolor, AbsoluteRect, &AbsoluteClippingRect);
-
- gui::IGUIElement::draw();
-}
-
-void GUIConfirmMenu::acceptInput(bool answer)
-{
- if(m_dest)
- m_dest->answer(answer);
-}
-
-bool GUIConfirmMenu::OnEvent(const SEvent& event)
-{
- if(event.EventType==EET_KEY_INPUT_EVENT)
- {
- if(event.KeyInput.Key==KEY_ESCAPE && event.KeyInput.PressedDown)
- {
- acceptInput(false);
- quitMenu();
- return true;
- }
- if(event.KeyInput.Key==KEY_RETURN && event.KeyInput.PressedDown)
- {
- acceptInput(true);
- quitMenu();
- return true;
- }
- }
- if(event.EventType==EET_GUI_EVENT)
- {
- if(event.GUIEvent.EventType==gui::EGET_ELEMENT_FOCUS_LOST
- && isVisible())
- {
- if(!canTakeFocus(event.GUIEvent.Element))
- {
- dstream<<"GUIConfirmMenu: Not allowing focus change."
- <<std::endl;
- // Returning true disables focus change
- return true;
- }
- }
- if(event.GUIEvent.EventType==gui::EGET_BUTTON_CLICKED)
- {
- switch(event.GUIEvent.Caller->getID())
- {
- case GUI_ID_YES:
- acceptInput(true);
- quitMenu();
- // quitMenu deallocates menu
- return true;
- case GUI_ID_NO:
- acceptInput(false);
- quitMenu();
- // quitMenu deallocates menu
- return true;
- }
- }
- }
-
- return Parent ? Parent->OnEvent(event) : false;
-}
-
diff --git a/src/guiCreateWorld.cpp b/src/guiCreateWorld.cpp
deleted file mode 100644
index f9afe9592..000000000
--- a/src/guiCreateWorld.cpp
+++ /dev/null
@@ -1,280 +0,0 @@
-/*
-Minetest
-Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>
-
-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.
-*/
-
-#include "guiCreateWorld.h"
-#include "debug.h"
-#include "serialization.h"
-#include <string>
-#include <IGUICheckBox.h>
-#include <IGUIEditBox.h>
-#include <IGUIButton.h>
-#include <IGUIStaticText.h>
-#include <IGUIFont.h>
-#include <IGUIListBox.h>
-#include "gettext.h"
-#include "util/string.h"
-
-enum
-{
- GUI_ID_NAME_INPUT = 101,
- GUI_ID_GAME_LISTBOX,
- GUI_ID_CREATE,
- GUI_ID_CANCEL
-};
-
-GUICreateWorld::GUICreateWorld(gui::IGUIEnvironment* env,
- gui::IGUIElement* parent, s32 id,
- IMenuManager *menumgr,
- CreateWorldDest *dest,
- const std::vector<SubgameSpec> &games,
- const std::string &initial_game
-):
- GUIModalMenu(env, parent, id, menumgr),
- m_dest(dest),
- m_games(games),
- m_initial_game_i(0)
-{
- assert(games.size() > 0);
-
- for(size_t i=0; i<games.size(); i++){
- if(games[i].id == initial_game){
- m_initial_game_i = i;
- break;
- }
- }
-}
-
-GUICreateWorld::~GUICreateWorld()
-{
- removeChildren();
- if(m_dest)
- delete m_dest;
-}
-
-void GUICreateWorld::removeChildren()
-{
- const core::list<gui::IGUIElement*> &children = getChildren();
- core::list<gui::IGUIElement*> children_copy;
- for(core::list<gui::IGUIElement*>::ConstIterator
- i = children.begin(); i != children.end(); i++)
- {
- children_copy.push_back(*i);
- }
- for(core::list<gui::IGUIElement*>::Iterator
- i = children_copy.begin();
- i != children_copy.end(); i++)
- {
- (*i)->remove();
- }
-}
-
-void GUICreateWorld::regenerateGui(v2u32 screensize)
-{
- std::wstring name = L"";
-
- {
- gui::IGUIElement *e = getElementFromId(GUI_ID_NAME_INPUT);
- if(e != NULL)
- name = e->getText();
- }
-
- /*
- Remove stuff
- */
- removeChildren();
-
- /*
- Calculate new sizes and positions
- */
- core::rect<s32> rect(
- screensize.X/2 - 580/2,
- screensize.Y/2 - 300/2,
- screensize.X/2 + 580/2,
- screensize.Y/2 + 300/2
- );
-
- DesiredRect = rect;
- recalculateAbsolutePosition(false);
-
- v2s32 topleft = v2s32(10+80, 10+70);
-
- /*
- Add stuff
- */
- {
- core::rect<s32> rect(0, 0, 100, 20);
- rect += v2s32(0, 5) + topleft;
- wchar_t* text = wgettext("World name");
- Environment->addStaticText(text, rect, false, true, this, -1);
- delete[] text;
- }
- {
- core::rect<s32> rect(0, 0, 300, 30);
- rect = rect + v2s32(100, 0) + topleft;
- gui::IGUIElement *e =
- Environment->addEditBox(name.c_str(), rect, true, this, GUI_ID_NAME_INPUT);
- Environment->setFocus(e);
-
- irr::SEvent evt;
- evt.EventType = EET_KEY_INPUT_EVENT;
- evt.KeyInput.Key = KEY_END;
- evt.KeyInput.PressedDown = true;
- evt.KeyInput.Char = 0;
- evt.KeyInput.Control = 0;
- evt.KeyInput.Shift = 0;
- e->OnEvent(evt);
- }
- {
- core::rect<s32> rect(0, 0, 100, 20);
- rect += v2s32(0, 40+5) + topleft;
- wchar_t* text = wgettext("Game");
- Environment->addStaticText(text, rect, false, true, this, -1);
- delete[] text;
- }
- {
- core::rect<s32> rect(0, 0, 300, 80);
- rect += v2s32(100, 40) + topleft;
- gui::IGUIListBox *e = Environment->addListBox(rect, this,
- GUI_ID_GAME_LISTBOX);
- e->setDrawBackground(true);
- for(u32 i=0; i<m_games.size(); i++){
- std::wostringstream os(std::ios::binary);
- os<<narrow_to_wide(m_games[i].name).c_str();
- os<<L" [";
- os<<narrow_to_wide(m_games[i].id).c_str();
- os<<L"]";
- e->addItem(os.str().c_str());
- }
- e->setSelected(m_initial_game_i);
- }
- changeCtype("");
- {
- core::rect<s32> rect(0, 0, 120, 30);
- rect = rect + v2s32(170, 140) + topleft;
- wchar_t* text = wgettext("Create");
- Environment->addButton(rect, this, GUI_ID_CREATE,
- text);
- delete[] text;
- }
- {
- core::rect<s32> rect(0, 0, 120, 30);
- rect = rect + v2s32(300, 140) + topleft;
- wchar_t* text = wgettext("Cancel");
- Environment->addButton(rect, this, GUI_ID_CANCEL,
- text);
- delete [] text;
- }
- changeCtype("C");
-}
-
-void GUICreateWorld::drawMenu()
-{
- gui::IGUISkin* skin = Environment->getSkin();
- if (!skin)
- return;
- video::IVideoDriver* driver = Environment->getVideoDriver();
-
- video::SColor bgcolor(140,0,0,0);
- driver->draw2DRectangle(bgcolor, AbsoluteRect, &AbsoluteClippingRect);
-
- gui::IGUIElement::draw();
-}
-
-void GUICreateWorld::acceptInput()
-{
- if(m_dest)
- {
- int selected = -1;
- {
- gui::IGUIElement *e = getElementFromId(GUI_ID_GAME_LISTBOX);
- if(e != NULL && e->getType() == gui::EGUIET_LIST_BOX)
- selected = ((gui::IGUIListBox*)e)->getSelected();
- }
- std::wstring name;
- {
- gui::IGUIElement *e = getElementFromId(GUI_ID_NAME_INPUT);
- if(e != NULL)
- name = e->getText();
- }
- if(selected != -1 && name != L"")
- m_dest->accepted(name, m_games[selected].id);
- delete m_dest;
- m_dest = NULL;
- }
-}
-
-bool GUICreateWorld::OnEvent(const SEvent& event)
-{
- if(event.EventType==EET_KEY_INPUT_EVENT)
- {
- if(event.KeyInput.Key==KEY_ESCAPE && event.KeyInput.PressedDown)
- {
- quitMenu();
- return true;
- }
- if(event.KeyInput.Key==KEY_RETURN && event.KeyInput.PressedDown)
- {
- acceptInput();
- quitMenu();
- return true;
- }
- }
- if(event.EventType==EET_GUI_EVENT)
- {
- if(event.GUIEvent.EventType==gui::EGET_ELEMENT_FOCUS_LOST
- && isVisible())
- {
- if(!canTakeFocus(event.GUIEvent.Element))
- {
- dstream<<"GUICreateWorld: Not allowing focus change."
- <<std::endl;
- // Returning true disables focus change
- return true;
- }
- }
- bool accept_input = false;
- if(event.GUIEvent.EventType==gui::EGET_BUTTON_CLICKED){
- switch(event.GUIEvent.Caller->getID()){
- case GUI_ID_CANCEL:
- quitMenu();
- return true;
- break;
- case GUI_ID_CREATE:
- accept_input = true;
- break;
- }
- }
- if(event.GUIEvent.EventType==gui::EGET_EDITBOX_ENTER){
- switch(event.GUIEvent.Caller->getID()){
- case GUI_ID_NAME_INPUT:
- accept_input = true;
- break;
- }
- }
- if(accept_input){
- acceptInput();
- quitMenu();
- // quitMenu deallocates menu
- return true;
- }
- }
-
- return Parent ? Parent->OnEvent(event) : false;
-}
-
diff --git a/src/guiCreateWorld.h b/src/guiCreateWorld.h
deleted file mode 100644
index 2765dc2bd..000000000
--- a/src/guiCreateWorld.h
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
-Minetest
-Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>
-
-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.
-*/
-
-#ifndef GUICREATEWORLD_HEADER
-#define GUICREATEWORLD_HEADER
-
-#include "irrlichttypes_extrabloated.h"
-#include "modalMenu.h"
-#include <string>
-#include "subgame.h"
-
-struct CreateWorldDest
-{
- virtual void accepted(std::wstring name, std::string gameid) = 0;
- virtual ~CreateWorldDest() {};
-};
-
-class GUICreateWorld : public GUIModalMenu
-{
-public:
- GUICreateWorld(gui::IGUIEnvironment* env,
- gui::IGUIElement* parent, s32 id,
- IMenuManager *menumgr,
- CreateWorldDest *dest,
- const std::vector<SubgameSpec> &games,
- const std::string &initial_game);
- ~GUICreateWorld();
-
- void removeChildren();
- /*
- Remove and re-add (or reposition) stuff
- */
- void regenerateGui(v2u32 screensize);
-
- void drawMenu();
-
- void acceptInput();
-
- bool OnEvent(const SEvent& event);
-
-private:
- CreateWorldDest *m_dest;
- std::vector<SubgameSpec> m_games;
- int m_initial_game_i;
-};
-
-#endif
-
diff --git a/src/guiEngine.cpp b/src/guiEngine.cpp
new file mode 100644
index 000000000..f04f15820
--- /dev/null
+++ b/src/guiEngine.cpp
@@ -0,0 +1,570 @@
+/*
+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.
+*/
+
+extern "C" {
+#include "lua.h"
+#include "lauxlib.h"
+#include "lualib.h"
+}
+
+#include "irrlicht.h"
+
+#include "porting.h"
+#include "filesys.h"
+#include "main.h"
+#include "settings.h"
+#include "guiMainMenu.h"
+
+#include "guiEngine.h"
+
+#if USE_CURL
+#include <curl/curl.h>
+#endif
+
+/******************************************************************************/
+int menuscript_ErrorHandler(lua_State *L) {
+ lua_getfield(L, LUA_GLOBALSINDEX, "debug");
+ if (!lua_istable(L, -1)) {
+ lua_pop(L, 1);
+ return 1;
+ }
+ lua_getfield(L, -1, "traceback");
+ if (!lua_isfunction(L, -1)) {
+ lua_pop(L, 2);
+ return 1;
+ }
+ lua_pushvalue(L, 1);
+ lua_pushinteger(L, 2);
+ lua_call(L, 2, 1);
+ return 1;
+}
+
+/******************************************************************************/
+TextDestGuiEngine::TextDestGuiEngine(GUIEngine* engine)
+{
+ m_engine = engine;
+}
+
+/******************************************************************************/
+void TextDestGuiEngine::gotText(std::map<std::string, std::string> fields)
+{
+ m_engine->handleButtons(fields);
+}
+
+/******************************************************************************/
+void TextDestGuiEngine::gotText(std::wstring text)
+{
+ m_engine->handleEvent(wide_to_narrow(text));
+}
+
+/******************************************************************************/
+GUIEngine::GUIEngine( irr::IrrlichtDevice* dev,
+ gui::IGUIElement* parent,
+ IMenuManager *menumgr,
+ scene::ISceneManager* smgr,
+ MainMenuData* data) :
+ m_device(dev),
+ m_parent(parent),
+ m_menumanager(menumgr),
+ m_smgr(smgr),
+ m_data(data),
+ m_formspecgui(0),
+ m_buttonhandler(0),
+ m_menu(0),
+ m_startgame(false),
+ m_engineluastack(0),
+ m_luaerrorhandler(-1),
+ m_scriptdir(""),
+ m_irr_toplefttext(0),
+ m_clouds_enabled(true),
+ m_cloud()
+{
+ //initialize texture pointers
+ for (unsigned int i = 0; i < TEX_LAYER_MAX; i++) {
+ m_textures[i] = 0;
+ }
+ // is deleted by guiformspec!
+ m_buttonhandler = new TextDestGuiEngine(this);
+
+ //create luastack
+ m_engineluastack = luaL_newstate();
+
+ //load basic lua modules
+ luaL_openlibs(m_engineluastack);
+
+ //init
+ guiLuaApi::initialize(m_engineluastack,this);
+
+ //push errorstring
+ if (m_data->errormessage != "")
+ {
+ lua_getglobal(m_engineluastack, "gamedata");
+ int gamedata_idx = lua_gettop(m_engineluastack);
+ lua_pushstring(m_engineluastack, "errormessage");
+ lua_pushstring(m_engineluastack,m_data->errormessage.c_str());
+ lua_settable(m_engineluastack, gamedata_idx);
+ m_data->errormessage = "";
+ }
+
+ //create topleft header
+ core::rect<s32> rect(0, 0, 500, 40);
+ rect += v2s32(4, 0);
+ std::string t = "Minetest " VERSION_STRING;
+
+ m_irr_toplefttext =
+ m_device->getGUIEnvironment()->addStaticText(narrow_to_wide(t).c_str(),
+ rect,false,true,0,-1);
+
+ //create formspecsource
+ m_formspecgui = new FormspecFormSource("",&m_formspecgui);
+
+ /* Create menu */
+ m_menu =
+ new GUIFormSpecMenu( m_device,
+ m_parent,
+ -1,
+ m_menumanager,
+ 0 /* &client */,
+ 0 /* gamedef */);
+
+ m_menu->allowClose(false);
+ m_menu->lockSize(true,v2u32(800,600));
+ m_menu->setFormSource(m_formspecgui);
+ m_menu->setTextDest(m_buttonhandler);
+ m_menu->useGettext(true);
+
+ std::string builtin_helpers
+ = porting::path_share + DIR_DELIM + "builtin"
+ + DIR_DELIM + "mainmenu_helper.lua";
+
+ if (!runScript(builtin_helpers)) {
+ errorstream
+ << "GUIEngine::GUIEngine unable to load builtin helper script"
+ << std::endl;
+ return;
+ }
+
+ std::string menuscript = "";
+ if (g_settings->exists("main_menu_script"))
+ menuscript = g_settings->get("main_menu_script");
+ std::string builtin_menuscript =
+ porting::path_share + DIR_DELIM + "builtin"
+ + DIR_DELIM + "mainmenu.lua";
+
+ lua_pushcfunction(m_engineluastack, menuscript_ErrorHandler);
+ m_luaerrorhandler = lua_gettop(m_engineluastack);
+
+ m_scriptdir = menuscript.substr(0,menuscript.find_last_of(DIR_DELIM)-1);
+ if((menuscript == "") || (!runScript(menuscript))) {
+ infostream
+ << "GUIEngine::GUIEngine execution of custom menu failed!"
+ << std::endl
+ << "\tfalling back to builtin menu"
+ << std::endl;
+ m_scriptdir = fs::RemoveRelativePathComponents(porting::path_share + DIR_DELIM + "builtin"+ DIR_DELIM);
+ if(!runScript(builtin_menuscript)) {
+ errorstream
+ << "GUIEngine::GUIEngine unable to load builtin menu"
+ << std::endl;
+ return;
+ }
+ }
+
+ run();
+
+ m_menumanager->deletingMenu(m_menu);
+ m_menu->drop();
+ m_menu = 0;
+}
+
+/******************************************************************************/
+bool GUIEngine::runScript(std::string script) {
+
+ int ret = luaL_loadfile(m_engineluastack, script.c_str()) ||
+ lua_pcall(m_engineluastack, 0, 0, m_luaerrorhandler);
+ if(ret){
+ errorstream<<"========== ERROR FROM LUA WHILE CREATING MAIN MENU ==========="<<std::endl;
+ errorstream<<"Failed to load and run script from "<<std::endl;
+ errorstream<<script<<":"<<std::endl;
+ errorstream<<std::endl;
+ errorstream<<lua_tostring(m_engineluastack, -1)<<std::endl;
+ errorstream<<std::endl;
+ errorstream<<"=================== END OF ERROR FROM LUA ===================="<<std::endl;
+ lua_pop(m_engineluastack, 1); // Pop error message from stack
+ lua_pop(m_engineluastack, 1); // Pop the error handler from stack
+ return false;
+ }
+ return true;
+}
+
+/******************************************************************************/
+void GUIEngine::run()
+{
+
+ // Always create clouds because they may or may not be
+ // needed based on the game selected
+ video::IVideoDriver* driver = m_device->getVideoDriver();
+
+ cloudInit();
+
+ while(m_device->run() && (!m_startgame)) {
+ driver->beginScene(true, true, video::SColor(255,140,186,250));
+
+ if (m_clouds_enabled)
+ {
+ cloudPreProcess();
+ drawOverlay(driver);
+ }
+ else
+ drawBackground(driver);
+
+ drawHeader(driver);
+ drawFooter(driver);
+
+ m_device->getGUIEnvironment()->drawAll();
+
+ driver->endScene();
+
+ if (m_clouds_enabled)
+ cloudPostProcess();
+ else
+ sleep_ms(25);
+ }
+
+ m_menu->quitMenu();
+}
+
+/******************************************************************************/
+void GUIEngine::handleEvent(std::string text)
+{
+ lua_getglobal(m_engineluastack, "engine");
+
+ lua_getfield(m_engineluastack, -1, "event_handler");
+
+ if(lua_isnil(m_engineluastack, -1))
+ return;
+
+ luaL_checktype(m_engineluastack, -1, LUA_TFUNCTION);
+
+ lua_pushstring(m_engineluastack, text.c_str());
+
+ if(lua_pcall(m_engineluastack, 1, 0, m_luaerrorhandler))
+ scriptError("error: %s", lua_tostring(m_engineluastack, -1));
+}
+
+/******************************************************************************/
+void GUIEngine::handleButtons(std::map<std::string, std::string> fields)
+{
+ lua_getglobal(m_engineluastack, "engine");
+
+ lua_getfield(m_engineluastack, -1, "button_handler");
+
+ if(lua_isnil(m_engineluastack, -1))
+ return;
+
+ luaL_checktype(m_engineluastack, -1, LUA_TFUNCTION);
+
+ lua_newtable(m_engineluastack);
+ for(std::map<std::string, std::string>::const_iterator
+ i = fields.begin(); i != fields.end(); i++){
+ const std::string &name = i->first;
+ const std::string &value = i->second;
+ lua_pushstring(m_engineluastack, name.c_str());
+ lua_pushlstring(m_engineluastack, value.c_str(), value.size());
+ lua_settable(m_engineluastack, -3);
+ }
+
+ if(lua_pcall(m_engineluastack, 1, 0, m_luaerrorhandler))
+ scriptError("error: %s", lua_tostring(m_engineluastack, -1));
+}
+
+/******************************************************************************/
+GUIEngine::~GUIEngine()
+{
+ video::IVideoDriver* driver = m_device->getVideoDriver();
+ assert(driver != 0);
+
+ lua_close(m_engineluastack);
+
+ m_irr_toplefttext->setText(L"");
+
+ //initialize texture pointers
+ for (unsigned int i = 0; i < TEX_LAYER_MAX; i++) {
+ if (m_textures[i] != 0)
+ driver->removeTexture(m_textures[i]);
+ }
+
+ m_cloud.clouds->drop();
+}
+
+/******************************************************************************/
+void GUIEngine::cloudInit()
+{
+ m_cloud.clouds = new Clouds(m_smgr->getRootSceneNode(),
+ m_smgr, -1, rand(), 100);
+ m_cloud.clouds->update(v2f(0, 0), video::SColor(255,200,200,255));
+
+ m_cloud.camera = m_smgr->addCameraSceneNode(0,
+ v3f(0,0,0), v3f(0, 60, 100));
+ m_cloud.camera->setFarValue(10000);
+
+ m_cloud.lasttime = m_device->getTimer()->getTime();
+}
+
+/******************************************************************************/
+void GUIEngine::cloudPreProcess()
+{
+ u32 time = m_device->getTimer()->getTime();
+
+ if(time > m_cloud.lasttime)
+ m_cloud.dtime = (time - m_cloud.lasttime) / 1000.0;
+ else
+ m_cloud.dtime = 0;
+
+ m_cloud.lasttime = time;
+
+ m_cloud.clouds->step(m_cloud.dtime*3);
+ m_cloud.clouds->render();
+ m_smgr->drawAll();
+}
+
+/******************************************************************************/
+void GUIEngine::cloudPostProcess()
+{
+ float fps_max = g_settings->getFloat("fps_max");
+ // Time of frame without fps limit
+ float busytime;
+ u32 busytime_u32;
+ // not using getRealTime is necessary for wine
+ u32 time = m_device->getTimer()->getTime();
+ if(time > m_cloud.lasttime)
+ busytime_u32 = time - m_cloud.lasttime;
+ else
+ busytime_u32 = 0;
+ busytime = busytime_u32 / 1000.0;
+
+ // FPS limiter
+ u32 frametime_min = 1000./fps_max;
+
+ if(busytime_u32 < frametime_min) {
+ u32 sleeptime = frametime_min - busytime_u32;
+ m_device->sleep(sleeptime);
+ }
+}
+
+/******************************************************************************/
+void GUIEngine::drawBackground(video::IVideoDriver* driver)
+{
+ v2u32 screensize = driver->getScreenSize();
+
+ video::ITexture* texture = m_textures[TEX_LAYER_BACKGROUND];
+
+ /* If no texture, draw background of solid color */
+ if(!texture){
+ video::SColor color(255,80,58,37);
+ core::rect<s32> rect(0, 0, screensize.X, screensize.Y);
+ driver->draw2DRectangle(color, rect, NULL);
+ return;
+ }
+
+ /* Draw background texture */
+ v2u32 sourcesize = texture->getSize();
+ driver->draw2DImage(texture,
+ core::rect<s32>(0, 0, screensize.X, screensize.Y),
+ core::rect<s32>(0, 0, sourcesize.X, sourcesize.Y),
+ NULL, NULL, true);
+}
+
+/******************************************************************************/
+void GUIEngine::drawOverlay(video::IVideoDriver* driver)
+{
+ v2u32 screensize = driver->getScreenSize();
+
+ video::ITexture* texture = m_textures[TEX_LAYER_OVERLAY];
+
+ /* If no texture, draw background of solid color */
+ if(!texture)
+ return;
+
+ /* Draw background texture */
+ v2u32 sourcesize = texture->getSize();
+ driver->draw2DImage(texture,
+ core::rect<s32>(0, 0, screensize.X, screensize.Y),
+ core::rect<s32>(0, 0, sourcesize.X, sourcesize.Y),
+ NULL, NULL, true);
+}
+
+/******************************************************************************/
+void GUIEngine::drawHeader(video::IVideoDriver* driver)
+{
+ core::dimension2d<u32> screensize = driver->getScreenSize();
+
+ video::ITexture* texture = m_textures[TEX_LAYER_HEADER];
+
+ /* If no texture, draw nothing */
+ if(!texture)
+ return;
+
+ f32 mult = (((f32)screensize.Width / 2)) /
+ ((f32)texture->getOriginalSize().Width);
+
+ v2s32 splashsize(((f32)texture->getOriginalSize().Width) * mult,
+ ((f32)texture->getOriginalSize().Height) * mult);
+
+ // Don't draw the header is there isn't enough room
+ s32 free_space = (((s32)screensize.Height)-320)/2;
+
+ if (free_space > splashsize.Y) {
+ core::rect<s32> splashrect(0, 0, splashsize.X, splashsize.Y);
+ splashrect += v2s32((screensize.Width/2)-(splashsize.X/2),
+ ((free_space/2)-splashsize.Y/2)+10);
+
+ video::SColor bgcolor(255,50,50,50);
+
+ driver->draw2DImage(texture, splashrect,
+ core::rect<s32>(core::position2d<s32>(0,0),
+ core::dimension2di(texture->getSize())),
+ NULL, NULL, true);
+ }
+}
+
+/******************************************************************************/
+void GUIEngine::drawFooter(video::IVideoDriver* driver)
+{
+ core::dimension2d<u32> screensize = driver->getScreenSize();
+
+ video::ITexture* texture = m_textures[TEX_LAYER_FOOTER];
+
+ /* If no texture, draw nothing */
+ if(!texture)
+ return;
+
+ f32 mult = (((f32)screensize.Width)) /
+ ((f32)texture->getOriginalSize().Width);
+
+ v2s32 footersize(((f32)texture->getOriginalSize().Width) * mult,
+ ((f32)texture->getOriginalSize().Height) * mult);
+
+ // Don't draw the footer if there isn't enough room
+ s32 free_space = (((s32)screensize.Height)-320)/2;
+
+ if (free_space > footersize.Y) {
+ core::rect<s32> rect(0,0,footersize.X,footersize.Y);
+ rect += v2s32(screensize.Width/2,screensize.Height-footersize.Y);
+ rect -= v2s32(footersize.X/2, 0);
+
+ driver->draw2DImage(texture, rect,
+ core::rect<s32>(core::position2d<s32>(0,0),
+ core::dimension2di(texture->getSize())),
+ NULL, NULL, true);
+ }
+}
+
+/******************************************************************************/
+bool GUIEngine::setTexture(texture_layer layer,std::string texturepath) {
+
+ video::IVideoDriver* driver = m_device->getVideoDriver();
+ assert(driver != 0);
+
+ if (m_textures[layer] != 0)
+ {
+ driver->removeTexture(m_textures[layer]);
+ m_textures[layer] = 0;
+ }
+
+ if ((texturepath == "") || !fs::PathExists(texturepath))
+ return false;
+
+ m_textures[layer] = driver->getTexture(texturepath.c_str());
+
+ if (m_textures[layer] == 0) return false;
+
+ return true;
+}
+
+/******************************************************************************/
+#if USE_CURL
+static size_t WriteCallback(void *contents, size_t size, size_t nmemb, void *userp)
+{
+ FILE* targetfile = (FILE*) userp;
+ fwrite(contents,size,nmemb,targetfile);
+ return size * nmemb;
+}
+#endif
+bool GUIEngine::downloadFile(std::string url,std::string target) {
+#if USE_CURL
+ //download file via curl
+ CURL *curl;
+
+ curl = curl_easy_init();
+
+ if (curl)
+ {
+ CURLcode res;
+ bool retval = true;
+
+ FILE* targetfile = fopen(target.c_str(),"wb");
+
+ if (targetfile) {
+ curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1);
+ curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
+ curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback);
+ curl_easy_setopt(curl, CURLOPT_WRITEDATA, targetfile);
+
+ res = curl_easy_perform(curl);
+ if (res != CURLE_OK) {
+ errorstream << "File at url \"" << url
+ <<"\" not found (internet connection?)" <<std::endl;
+ retval = false;
+ }
+ fclose(targetfile);
+ }
+ else {
+ retval = false;
+ }
+
+ curl_easy_cleanup(curl);
+ return retval;
+ }
+#endif
+ return false;
+}
+
+/******************************************************************************/
+void GUIEngine::scriptError(const char *fmt, ...)
+{
+ va_list argp;
+ va_start(argp, fmt);
+ char buf[10000];
+ vsnprintf(buf, 10000, fmt, argp);
+ va_end(argp);
+ errorstream<<"MAINMENU ERROR: "<<buf;
+}
+
+/******************************************************************************/
+void GUIEngine::setTopleftText(std::string append) {
+ std::string toset = "Minetest " VERSION_STRING;
+
+ if (append != "") {
+ toset += " / ";
+ toset += append;
+ }
+
+ m_irr_toplefttext->setText(narrow_to_wide(toset).c_str());
+}
diff --git a/src/guiEngine.h b/src/guiEngine.h
new file mode 100644
index 000000000..2445ed847
--- /dev/null
+++ b/src/guiEngine.h
@@ -0,0 +1,260 @@
+/*
+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.
+*/
+
+#ifndef GUI_ENGINE_H_
+#define GUI_ENGINE_H_
+
+/******************************************************************************/
+/* Includes */
+/******************************************************************************/
+#include "irrlichttypes.h"
+#include "modalMenu.h"
+#include "clouds.h"
+#include "guiLuaApi.h"
+#include "guiFormSpecMenu.h"
+
+/******************************************************************************/
+/* Typedefs and macros */
+/******************************************************************************/
+#define MAX_MENUBAR_BTN_COUNT 10
+#define MAX_MENUBAR_BTN_ID 256
+#define MIN_MENUBAR_BTN_ID (MAX_MENUBAR_BTN_ID - MAX_MENUBAR_BTN_COUNT)
+
+/** texture layer ids */
+typedef enum {
+ TEX_LAYER_BACKGROUND = 0,
+ TEX_LAYER_OVERLAY,
+ TEX_LAYER_HEADER,
+ TEX_LAYER_FOOTER,
+ TEX_LAYER_MAX
+} texture_layer;
+
+/******************************************************************************/
+/* forward declarations */
+/******************************************************************************/
+class GUIEngine;
+struct MainMenuData;
+
+/******************************************************************************/
+/* declarations */
+/******************************************************************************/
+
+/** GUIEngine specific implementation of TextDest used within guiFormSpecMenu */
+class TextDestGuiEngine : public TextDest
+{
+public:
+ /**
+ * default constructor
+ * @param engine the engine data is transmitted for further processing
+ */
+ TextDestGuiEngine(GUIEngine* engine);
+ /**
+ * receive fields transmitted by guiFormSpecMenu
+ * @param fields map containing formspec field elements currently active
+ */
+ void gotText(std::map<std::string, std::string> fields);
+
+ /**
+ * receive text/events transmitted by guiFormSpecMenu
+ * @param text textual representation of event
+ */
+ void gotText(std::wstring text);
+private:
+ /** target to transmit data to */
+ GUIEngine* m_engine;
+};
+
+
+/** implementation of main menu based uppon formspecs */
+class GUIEngine {
+public:
+ /** TextDestGuiEngine needs to transfer data to engine */
+ friend class TextDestGuiEngine;
+ /** guiLuaApi is used to initialize contained stack */
+ friend class guiLuaApi;
+
+ /**
+ * default constructor
+ * @param dev device to draw at
+ * @param parent parent gui element
+ * @param menumgr manager to add menus to
+ * @param smgr scene manager to add scene elements to
+ * @param data struct to transfer data to main game handling
+ */
+ GUIEngine( irr::IrrlichtDevice* dev,
+ gui::IGUIElement* parent,
+ IMenuManager *menumgr,
+ scene::ISceneManager* smgr,
+ MainMenuData* data);
+
+ /** default destructor */
+ virtual ~GUIEngine();
+
+protected:
+ /**
+ * process field data recieved from formspec
+ * @param fields data in field format
+ */
+ void handleButtons(std::map<std::string, std::string> fields);
+ /**
+ * process events received from formspec
+ * @param text events in textual form
+ */
+ void handleEvent(std::string text);
+
+ /**
+ * return dir of current menuscript
+ */
+ std::string getScriptDir() {
+ return m_scriptdir;
+ }
+
+private:
+
+ /* run main menu loop */
+ void run();
+
+ /** handler to limit frame rate within main menu */
+ void limitFrameRate();
+
+ /** device to draw at */
+ irr::IrrlichtDevice* m_device;
+ /** parent gui element */
+ gui::IGUIElement* m_parent;
+ /** manager to add menus to */
+ IMenuManager* m_menumanager;
+ /** scene manager to add scene elements to */
+ scene::ISceneManager* m_smgr;
+ /** pointer to data beeing transfered back to main game handling */
+ MainMenuData* m_data;
+
+ /** representation of form source to be used in mainmenu formspec */
+ FormspecFormSource* m_formspecgui;
+ /** formspec input receiver */
+ TextDestGuiEngine* m_buttonhandler;
+ /** the formspec menu */
+ GUIFormSpecMenu* m_menu;
+
+ /** variable used to abort menu and return back to main game handling */
+ bool m_startgame;
+
+ /**
+ * initialize lua stack
+ * @param L stack to initialize
+ */
+ void initalize_api(lua_State * L);
+
+ /**
+ * run a lua script
+ * @param script full path to script to run
+ */
+ bool runScript(std::string script);
+
+ /**
+ * script error handler to process errors within lua
+ */
+ void scriptError(const char *fmt, ...);
+
+ /** lua stack */
+ lua_State* m_engineluastack;
+ /** lua internal stack number of error handler*/
+ int m_luaerrorhandler;
+
+ /** script basefolder */
+ std::string m_scriptdir;
+
+ /**
+ * draw background layer
+ * @param driver to use for drawing
+ */
+ void drawBackground(video::IVideoDriver* driver);
+ /**
+ * draw overlay layer
+ * @param driver to use for drawing
+ */
+ void drawOverlay(video::IVideoDriver* driver);
+ /**
+ * draw header layer
+ * @param driver to use for drawing
+ */
+ void drawHeader(video::IVideoDriver* driver);
+ /**
+ * draw footer layer
+ * @param driver to use for drawing
+ */
+ void drawFooter(video::IVideoDriver* driver);
+
+ /**
+ * load a texture for a specified layer
+ * @param layer draw layer to specify texture
+ * @param texturepath full path of texture to load
+ */
+ bool setTexture(texture_layer layer,std::string texturepath);
+
+ /**
+ * download a file using curl
+ * @param url url to download
+ * @param target file to store to
+ */
+ bool downloadFile(std::string url,std::string target);
+
+ /** array containing pointers to current specified texture layers */
+ video::ITexture* m_textures[TEX_LAYER_MAX];
+
+ /** draw version string in topleft corner */
+ void drawVersion();
+
+ /**
+ * specify text to be appended to version string
+ * @param text to set
+ */
+ void setTopleftText(std::string append);
+
+ /** pointer to gui element shown at topleft corner */
+ irr::gui::IGUIStaticText* m_irr_toplefttext;
+
+ /** initialize cloud subsystem */
+ void cloudInit();
+ /** do preprocessing for cloud subsystem */
+ void cloudPreProcess();
+ /** do postprocessing for cloud subsystem */
+ void cloudPostProcess();
+
+ /** internam data required for drawing clouds */
+ struct clouddata {
+ /** delta time since last cloud processing */
+ f32 dtime;
+ /** absolute time of last cloud processing */
+ u32 lasttime;
+ /** pointer to cloud class */
+ Clouds* clouds;
+ /** camera required for drawing clouds */
+ scene::ICameraSceneNode* camera;
+ };
+
+ /** is drawing of clouds enabled atm */
+ bool m_clouds_enabled;
+ /** data used to draw clouds */
+ clouddata m_cloud;
+
+};
+
+
+
+#endif /* GUI_ENGINE_H_ */
diff --git a/src/guiFileSelectMenu.cpp b/src/guiFileSelectMenu.cpp
new file mode 100644
index 000000000..93d43f786
--- /dev/null
+++ b/src/guiFileSelectMenu.cpp
@@ -0,0 +1,133 @@
+/*
+ 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.
+ */
+
+#include "guiFileSelectMenu.h"
+#include "util/string.h"
+#include <locale.h>
+
+GUIFileSelectMenu::GUIFileSelectMenu(gui::IGUIEnvironment* env,
+ gui::IGUIElement* parent, s32 id, IMenuManager *menumgr,
+ std::string title, std::string formname) :
+GUIModalMenu(env, parent, id, menumgr)
+{
+ m_title = narrow_to_wide(title);
+ m_parent = parent;
+ m_formname = formname;
+ m_text_dst = 0;
+ m_accepted = false;
+ m_previous_locale = setlocale(LC_ALL,0);
+}
+
+GUIFileSelectMenu::~GUIFileSelectMenu()
+{
+ removeChildren();
+ setlocale(LC_ALL,m_previous_locale.c_str());
+}
+
+void GUIFileSelectMenu::removeChildren()
+{
+ const core::list<gui::IGUIElement*> &children = getChildren();
+ core::list<gui::IGUIElement*> children_copy;
+ for (core::list<gui::IGUIElement*>::ConstIterator i = children.begin(); i
+ != children.end(); i++)
+ {
+ children_copy.push_back(*i);
+ }
+ for (core::list<gui::IGUIElement*>::Iterator i = children_copy.begin(); i
+ != children_copy.end(); i++)
+ {
+ (*i)->remove();
+ }
+}
+
+void GUIFileSelectMenu::regenerateGui(v2u32 screensize)
+{
+ removeChildren();
+ m_fileOpenDialog = 0;
+
+ core::dimension2du size(600,400);
+ core::rect < s32 > rect(0,0,screensize.X,screensize.Y);
+
+ DesiredRect = rect;
+ recalculateAbsolutePosition(false);
+
+ m_fileOpenDialog =
+ Environment->addFileOpenDialog(m_title.c_str(),false,this,-1);
+
+ core::position2di pos = core::position2di(screensize.X/2 - size.Width/2,screensize.Y/2 -size.Height/2);
+ m_fileOpenDialog->setRelativePosition(pos);
+ m_fileOpenDialog->setMinSize(size);
+}
+
+void GUIFileSelectMenu::drawMenu()
+{
+ gui::IGUISkin* skin = Environment->getSkin();
+ if (!skin)
+ return;
+
+ gui::IGUIElement::draw();
+}
+
+void GUIFileSelectMenu::acceptInput() {
+ if ((m_text_dst != 0) && (this->m_formname != "")){
+ std::map<std::string, std::string> fields;
+
+ if (m_accepted)
+ fields[m_formname + "_accepted"] = wide_to_narrow(m_fileOpenDialog->getFileName());
+ else
+ fields[m_formname + "_canceled"] = m_formname;
+
+ this->m_text_dst->gotText(fields);
+ }
+}
+
+bool GUIFileSelectMenu::OnEvent(const SEvent& event)
+{
+
+ if (event.EventType == irr::EET_GUI_EVENT) {
+
+ int callerId = event.GUIEvent.Caller->getID();
+ if (callerId >= 0) {
+ std::cout << "CallerId:" << callerId << std::endl;
+ }
+
+ switch (event.GUIEvent.EventType) {
+ case gui::EGET_ELEMENT_CLOSED:
+ case gui::EGET_FILE_CHOOSE_DIALOG_CANCELLED:
+ m_accepted=false;
+ acceptInput();
+ quitMenu();
+ return true;
+ break;
+
+ case gui::EGET_DIRECTORY_SELECTED:
+ case gui::EGET_FILE_SELECTED:
+ m_accepted=true;
+ acceptInput();
+ quitMenu();
+ return true;
+ break;
+
+ default:
+ //ignore this event
+ break;
+ }
+ }
+ return Parent ? Parent->OnEvent(event) : false;
+}
diff --git a/src/guiFileSelectMenu.h b/src/guiFileSelectMenu.h
new file mode 100644
index 000000000..987a9f2ee
--- /dev/null
+++ b/src/guiFileSelectMenu.h
@@ -0,0 +1,80 @@
+/*
+ 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.
+ */
+
+#ifndef GUIFILESELECTMENU_H_
+#define GUIFILESELECTMENU_H_
+
+#include <string>
+
+#include "modalMenu.h"
+#include "IGUIFileOpenDialog.h"
+#include "guiFormSpecMenu.h" //required because of TextDest only !!!
+
+
+class GUIFileSelectMenu: public GUIModalMenu
+{
+public:
+ GUIFileSelectMenu(gui::IGUIEnvironment* env, gui::IGUIElement* parent,
+ s32 id, IMenuManager *menumgr,
+ std::string title,
+ std::string formid);
+ ~GUIFileSelectMenu();
+
+ void removeChildren();
+
+ /*
+ Remove and re-add (or reposition) stuff
+ */
+ void regenerateGui(v2u32 screensize);
+
+ void drawMenu();
+
+ bool OnEvent(const SEvent& event);
+
+ bool isRunning() {
+ return m_running;
+ }
+
+ void setTextDest(TextDest * dest) {
+ m_text_dst = dest;
+ }
+
+private:
+ void acceptInput();
+
+ std::wstring m_title;
+ bool m_accepted;
+ gui::IGUIElement* m_parent;
+
+ std::string m_selectedPath;
+
+ gui::IGUIFileOpenDialog* m_fileOpenDialog;
+
+ std::string m_previous_locale;
+
+ bool m_running;
+
+ TextDest *m_text_dst;
+
+ std::string m_formname;
+};
+
+
+
+#endif /* GUIFILESELECTMENU_H_ */
diff --git a/src/guiFormSpecMenu.cpp b/src/guiFormSpecMenu.cpp
index ee39df8b7..0aa2c2dcd 100644
--- a/src/guiFormSpecMenu.cpp
+++ b/src/guiFormSpecMenu.cpp
@@ -18,6 +18,11 @@ with this program; if not, write to the Free Software Foundation, Inc.,
*/
+#include <cstdlib>
+#include <algorithm>
+#include <iterator>
+#include <sstream>
+#include <limits>
#include "guiFormSpecMenu.h"
#include "constants.h"
#include "gamedef.h"
@@ -28,13 +33,34 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include <IGUIButton.h>
#include <IGUIStaticText.h>
#include <IGUIFont.h>
+#include <IGUIListBox.h>
+#include <IGUITabControl.h>
+#include <IGUIScrollBar.h>
+#include <IGUIComboBox.h>
#include "log.h"
#include "tile.h" // ITextureSource
#include "util/string.h"
#include "util/numeric.h"
+#include "filesys.h"
#include "gettext.h"
+
+#define MY_CHECKPOS(a,b) \
+ if (v_pos.size() != 2) { \
+ errorstream<< "Invalid pos for element " << a << "specified: \"" \
+ << parts[b] << "\"" << std::endl; \
+ return; \
+ }
+
+#define MY_CHECKGEOM(a,b) \
+ if (v_geom.size() != 2) { \
+ errorstream<< "Invalid pos for element " << a << "specified: \"" \
+ << parts[b] << "\"" << std::endl; \
+ return; \
+ }
+
+
void drawItemStack(video::IVideoDriver *driver,
gui::IGUIFont *font,
const ItemStack &item,
@@ -140,7 +166,10 @@ GUIFormSpecMenu::GUIFormSpecMenu(irr::IrrlichtDevice* dev,
m_selected_item(NULL),
m_selected_amount(0),
m_selected_dragging(false),
- m_tooltip_element(NULL)
+ m_tooltip_element(NULL),
+ m_allowclose(true),
+ m_use_gettext(false),
+ m_lock(false)
{
}
@@ -180,436 +209,1337 @@ void GUIFormSpecMenu::removeChildren()
}
}
-void GUIFormSpecMenu::regenerateGui(v2u32 screensize)
-{
- // Remove children
- removeChildren();
-
- v2s32 size(100,100);
- s32 helptext_h = 15;
- core::rect<s32> rect;
+int GUIFormSpecMenu::getListboxIndex(std::string listboxname) {
- // Base position of contents of form
- v2s32 basepos = getBasePos();
- // State of basepos, 0 = not set, 1= set by formspec, 2 = set by size[] element
- // Used to adjust form size automatically if needed
- // A proceed button is added if there is no size[] element
- int bp_set = 0;
-
- /* Convert m_init_draw_spec to m_inventorylists */
-
- m_inventorylists.clear();
- m_images.clear();
- m_backgrounds.clear();
- m_itemimages.clear();
- m_fields.clear();
+ std::wstring wlistboxname = narrow_to_wide(listboxname.c_str());
- Strfnd f(m_formspec_string);
- while(f.atend() == false)
- {
- std::string type = trim(f.next_esc("["));
- if(type == "invsize" || type == "size")
- {
- v2f invsize;
- invsize.X = stof(f.next_esc(","));
- if(type == "size")
- {
- invsize.Y = stof(f.next_esc("]"));
- }
- else{
- invsize.Y = stof(f.next_esc(";"));
- f.next_esc("]");
- }
- infostream<<"Form size ("<<invsize.X<<","<<invsize.Y<<")"<<std::endl;
-
- padding = v2s32(screensize.Y/40, screensize.Y/40);
- spacing = v2s32(screensize.Y/12, screensize.Y/13);
- imgsize = v2s32(screensize.Y/15, screensize.Y/15);
- size = v2s32(
- padding.X*2+spacing.X*(invsize.X-1.0)+imgsize.X,
- padding.Y*2+spacing.Y*(invsize.Y-1.0)+imgsize.Y + (helptext_h-5)
- );
- rect = core::rect<s32>(
- screensize.X/2 - size.X/2,
- screensize.Y/2 - size.Y/2,
- screensize.X/2 + size.X/2,
- screensize.Y/2 + size.Y/2
- );
- DesiredRect = rect;
- recalculateAbsolutePosition(false);
- basepos = getBasePos();
- bp_set = 2;
- }
- else if(type == "list")
- {
- std::string name = f.next_esc(";");
- InventoryLocation loc;
- if(name == "context" || name == "current_name")
- loc = m_current_inventory_location;
- else
- loc.deSerialize(name);
- std::string listname = f.next_esc(";");
- v2s32 pos = basepos;
- pos.X += stof(f.next_esc(",")) * (float)spacing.X;
- pos.Y += stof(f.next_esc(";")) * (float)spacing.Y;
- v2s32 geom;
- geom.X = stoi(f.next_esc(","));
- geom.Y = stoi(f.next_esc(";"));
- infostream<<"list inv="<<name<<", listname="<<listname
- <<", pos=("<<pos.X<<","<<pos.Y<<")"
- <<", geom=("<<geom.X<<","<<geom.Y<<")"
- <<std::endl;
- std::string start_i_s = f.next_esc("]");
- s32 start_i = 0;
- if(start_i_s != "")
- start_i = stoi(start_i_s);
- if(bp_set != 2)
- errorstream<<"WARNING: invalid use of list without a size[] element"<<std::endl;
- m_inventorylists.push_back(ListDrawSpec(loc, listname, pos, geom, start_i));
- }
- else if(type == "image")
- {
- v2s32 pos = basepos;
- pos.X += stof(f.next_esc(",")) * (float)spacing.X;
- pos.Y += stof(f.next_esc(";")) * (float)spacing.Y;
- v2s32 geom;
- geom.X = stof(f.next_esc(",")) * (float)imgsize.X;
- geom.Y = stof(f.next_esc(";")) * (float)imgsize.Y;
- std::string name = f.next_esc("]");
- infostream<<"image name="<<name
- <<", pos=("<<pos.X<<","<<pos.Y<<")"
- <<", geom=("<<geom.X<<","<<geom.Y<<")"
- <<std::endl;
- if(bp_set != 2)
- errorstream<<"WARNING: invalid use of image without a size[] element"<<std::endl;
- m_images.push_back(ImageDrawSpec(name, pos, geom));
- }
- else if(type == "item_image")
- {
- v2s32 pos = basepos;
- pos.X += stof(f.next_esc(",")) * (float)spacing.X;
- pos.Y += stof(f.next_esc(";")) * (float)spacing.Y;
- v2s32 geom;
- geom.X = stof(f.next_esc(",")) * (float)imgsize.X;
- geom.Y = stof(f.next_esc(";")) * (float)imgsize.Y;
- std::string name = f.next_esc("]");
- infostream<<"item name="<<name
- <<", pos=("<<pos.X<<","<<pos.Y<<")"
- <<", geom=("<<geom.X<<","<<geom.Y<<")"
- <<std::endl;
- if(bp_set != 2)
- errorstream<<"WARNING: invalid use of item_image without a size[] element"<<std::endl;
- m_itemimages.push_back(ImageDrawSpec(name, pos, geom));
- }
- else if(type == "background")
- {
- v2s32 pos = basepos;
- pos.X += stof(f.next_esc(",")) * (float)spacing.X - ((float)spacing.X-(float)imgsize.X)/2;
- pos.Y += stof(f.next_esc(";")) * (float)spacing.Y - ((float)spacing.Y-(float)imgsize.Y)/2;
- v2s32 geom;
- geom.X = stof(f.next_esc(",")) * (float)spacing.X;
- geom.Y = stof(f.next_esc(";")) * (float)spacing.Y;
- std::string name = f.next_esc("]");
- infostream<<"image name="<<name
- <<", pos=("<<pos.X<<","<<pos.Y<<")"
- <<", geom=("<<geom.X<<","<<geom.Y<<")"
- <<std::endl;
- if(bp_set != 2)
- errorstream<<"WARNING: invalid use of background without a size[] element"<<std::endl;
- m_backgrounds.push_back(ImageDrawSpec(name, pos, geom));
+ for(unsigned int i=0; i < m_listboxes.size(); i++) {
+
+ std::wstring name(m_listboxes[i].first.fname.c_str());
+ if ( name == wlistboxname) {
+ return m_listboxes[i].second->getSelected();
}
- else if(type == "field" || type == "textarea")
- {
- std::string fname = f.next_esc(";");
- std::string flabel = f.next_esc(";");
+ }
+ return -1;
+}
- if(fname.find(",") == std::string::npos && flabel.find(",") == std::string::npos)
- {
- if (type == "textarea")
- errorstream<<"WARNING: Textarea connot be unpositioned"<<std::endl;
+std::vector<std::string> split(const std::string &s, char delim, bool escape=false) {
+ std::vector<std::string> tokens;
- if(!bp_set)
- {
- rect = core::rect<s32>(
- screensize.X/2 - 580/2,
- screensize.Y/2 - 300/2,
- screensize.X/2 + 580/2,
- screensize.Y/2 + 300/2
- );
- DesiredRect = rect;
- recalculateAbsolutePosition(false);
- basepos = getBasePos();
- bp_set = 1;
+ if (!escape) {
+ int startpos = 0;
+ size_t nextpos = s.find(delim);
+
+ while(nextpos != std::string::npos) {
+ std::string toadd = s.substr(startpos,nextpos-startpos);
+ tokens.push_back(toadd);
+ startpos = nextpos+1;
+ nextpos = s.find(delim,nextpos+1);
+ }
+
+ //push last element
+ tokens.push_back(s.substr(startpos));
+ }
+ else {
+ std::string current = "";
+ current += s.c_str()[0];
+ bool last_was_delim = false;
+ for(unsigned int i=1; i < s.size(); i++) {
+ if (last_was_delim) {
+ if (s.c_str()[i] == delim) {
+ current += s.c_str()[i];
+ last_was_delim = false;
}
- else if(bp_set == 2)
- errorstream<<"WARNING: invalid use of unpositioned "<<type<<" in inventory"<<std::endl;
+ else {
+ tokens.push_back(current);
- v2s32 pos = basepos;
- pos.Y = ((m_fields.size()+2)*60);
- v2s32 size = DesiredRect.getSize();
- rect = core::rect<s32>(size.X/2-150, pos.Y, (size.X/2-150)+300, pos.Y+30);
+ current = "";
+ current += s.c_str()[i];
+ last_was_delim = false;
+ }
}
- else
- {
- v2s32 pos;
- pos.X = stof(fname.substr(0,fname.find(","))) * (float)spacing.X;
- pos.Y = stof(fname.substr(fname.find(",")+1)) * (float)spacing.Y;
- v2s32 geom;
- geom.X = (stof(flabel.substr(0,flabel.find(","))) * (float)spacing.X)-(spacing.X-imgsize.X);
- if (type == "textarea")
- {
- geom.Y = (stof(flabel.substr(flabel.find(",")+1)) * (float)imgsize.Y) - (spacing.Y-imgsize.Y);
- pos.Y += 15;
+ else {
+ if (s.c_str()[i] == delim) {
+ last_was_delim = true;
}
- else
- {
- pos.Y += (stof(flabel.substr(flabel.find(",")+1)) * (float)imgsize.Y)/2;
- pos.Y -= 15;
- geom.Y = 30;
+ else {
+ last_was_delim = false;
+ current += s.c_str()[i];
}
+ }
+ }
+ //push last element
+ tokens.push_back(current);
+ }
+
+ return tokens;
+}
- rect = core::rect<s32>(pos.X, pos.Y, pos.X+geom.X, pos.Y+geom.Y);
+void GUIFormSpecMenu::parseSize(parserData* data,std::string element) {
+ std::vector<std::string> parts = split(element,',');
+ if (parts.size() == 2) {
+ v2f invsize;
-
- fname = f.next_esc(";");
- flabel = f.next_esc(";");
- if(bp_set != 2)
- errorstream<<"WARNING: invalid use of positioned "<<type<<" without a size[] element"<<std::endl;
-
- }
+ if (parts[1].find(';') != std::string::npos)
+ parts[1] = parts[1].substr(0,parts[1].find(';'));
+
+ invsize.X = stof(parts[0]);
+ invsize.Y = stof(parts[1]);
+
+ infostream<<"Form size ("<<invsize.X<<","<<invsize.Y<<")"<<std::endl;
+
+ if (m_lock) {
+ v2u32 current_screensize = m_device->getVideoDriver()->getScreenSize();
+ v2u32 delta = current_screensize - m_lockscreensize;
- std::string odefault = f.next_esc("]");
- std::string fdefault;
+ if (current_screensize.Y > m_lockscreensize.Y)
+ delta.Y /= 2;
+ else
+ delta.Y = 0;
- // fdefault may contain a variable reference, which
- // needs to be resolved from the node metadata
- if(m_form_src)
- fdefault = m_form_src->resolveText(odefault);
+ if (current_screensize.X > m_lockscreensize.X)
+ delta.X /= 2;
else
- fdefault = odefault;
+ delta.X = 0;
+
+ offset = v2s32(delta.X,delta.Y);
+
+ data->screensize = m_lockscreensize;
+ }
+ else {
+ offset = v2s32(0,0);
+ }
- fdefault = unescape_string(fdefault);
- flabel = unescape_string(flabel);
+ padding = v2s32(data->screensize.Y/40, data->screensize.Y/40);
+ spacing = v2s32(data->screensize.Y/12, data->screensize.Y/13);
+ imgsize = v2s32(data->screensize.Y/15, data->screensize.Y/15);
+ data->size = v2s32(
+ padding.X*2+spacing.X*(invsize.X-1.0)+imgsize.X,
+ padding.Y*2+spacing.Y*(invsize.Y-1.0)+imgsize.Y + (data->helptext_h-5)
+ );
+ data->rect = core::rect<s32>(
+ data->screensize.X/2 - data->size.X/2 + offset.X,
+ data->screensize.Y/2 - data->size.Y/2 + offset.Y,
+ data->screensize.X/2 + data->size.X/2 + offset.X,
+ data->screensize.Y/2 + data->size.Y/2 + offset.Y
+ );
+
+ DesiredRect = data->rect;
+ recalculateAbsolutePosition(false);
+ data->basepos = getBasePos();
+ data->bp_set = 2;
+ return;
+ }
+ errorstream<< "Invalid size element (" << parts.size() << "): '" << element << "'" << std::endl;
+}
+
+void GUIFormSpecMenu::parseList(parserData* data,std::string element) {
- FieldSpec spec = FieldSpec(
- narrow_to_wide(fname.c_str()),
- narrow_to_wide(flabel.c_str()),
- narrow_to_wide(fdefault.c_str()),
+ if (m_gamedef == 0) {
+ errorstream<<"WARNING: invalid use of 'list' with m_gamedef==0"<<std::endl;
+ return;
+ }
+
+ std::vector<std::string> parts = split(element,';');
+
+ if ((parts.size() == 4) || (parts.size() == 5)) {
+ std::string location = parts[0];
+ std::string listname = parts[1];
+ std::vector<std::string> v_pos = split(parts[2],',');
+ std::vector<std::string> v_geom = split(parts[3],',');
+ std::string startindex = "";
+ if (parts.size() == 5)
+ startindex = parts[4];
+
+ MY_CHECKPOS("list",2);
+ MY_CHECKGEOM("list",3);
+
+ InventoryLocation loc;
+
+ if(location == "context" || location == "current_name")
+ loc = m_current_inventory_location;
+ else
+ loc.deSerialize(location);
+
+ v2s32 pos = padding + AbsoluteRect.UpperLeftCorner;
+ pos.X += stof(v_pos[0]) * (float)spacing.X;
+ pos.Y += stof(v_pos[1]) * (float)spacing.Y;
+
+ v2s32 geom;
+ geom.X = stoi(v_geom[0]);
+ geom.Y = stoi(v_geom[1]);
+ infostream<<"list inv="<<location<<", listname="<<listname
+ <<", pos=("<<pos.X<<","<<pos.Y<<")"
+ <<", geom=("<<geom.X<<","<<geom.Y<<")"
+ <<std::endl;
+
+ s32 start_i = 0;
+ if(startindex != "")
+ start_i = stoi(startindex);
+ if(data->bp_set != 2)
+ errorstream<<"WARNING: invalid use of list without a size[] element"<<std::endl;
+ m_inventorylists.push_back(ListDrawSpec(loc, listname, pos, geom, start_i));
+ return;
+ }
+ errorstream<< "Invalid list element(" << parts.size() << "): '" << element << "'" << std::endl;
+}
+
+void GUIFormSpecMenu::parseCheckbox(parserData* data,std::string element) {
+ std::vector<std::string> parts = split(element,';');
+
+ if ((parts.size() == 3) || (parts.size() == 4)) {
+ std::vector<std::string> v_pos = split(parts[0],',');
+ std::string name = parts[1];
+ std::string label = parts[2];
+ std::string selected = "";
+
+ if (parts.size() == 4)
+ selected = parts[3];
+
+ MY_CHECKPOS("checkbox",0);
+
+ v2s32 pos = padding;
+ pos.X += stof(v_pos[0]) * (float) spacing.X;
+ pos.Y += stof(v_pos[1]) * (float) spacing.Y;
+
+ core::rect<s32> rect = core::rect<s32>(pos.X, pos.Y+((imgsize.Y/2)-15), pos.X+300, pos.Y+((imgsize.Y/2)+15));
+
+ bool fselected = false;
+
+ if (selected == "true")
+ fselected = true;
+
+ wchar_t* wlabel = 0;
+
+ if (m_use_gettext)
+ wlabel = wgettext(label.c_str());
+ else
+ wlabel = (wchar_t*) narrow_to_wide(label.c_str()).c_str();
+
+ FieldSpec spec = FieldSpec(
+ narrow_to_wide(name.c_str()),
+ narrow_to_wide(""),
+ wlabel,
258+m_fields.size()
);
- // three cases: field name and no label, label and field, label name and no field
- gui::IGUIEditBox *e;
- if (fname == "")
- {
- // spec field id to 0, this stops submit searching for a value that isn't there
- Environment->addStaticText(spec.flabel.c_str(), rect, false, true, this, spec.fid);
- }
- else
- {
- spec.send = true;
- e = Environment->addEditBox(spec.fdefault.c_str(), rect, true, this, spec.fid);
- Environment->setFocus(e);
+ spec.ftype = f_CheckBox;
- if (type == "textarea")
- {
- e->setMultiLine(true);
- e->setTextAlignment(gui::EGUIA_UPPERLEFT, gui::EGUIA_UPPERLEFT);
- } else {
- irr::SEvent evt;
- evt.EventType = EET_KEY_INPUT_EVENT;
- evt.KeyInput.Key = KEY_END;
- evt.KeyInput.PressedDown = true;
- evt.KeyInput.Char = 0;
- evt.KeyInput.Control = 0;
- evt.KeyInput.Shift = 0;
- e->OnEvent(evt);
+ gui::IGUICheckBox* e = Environment->addCheckBox(fselected, rect, this,
+ spec.fid, wlabel);
+
+ m_checkboxes.push_back(std::pair<FieldSpec,gui::IGUICheckBox*>(spec,e));
+ m_fields.push_back(spec);
+ if (m_use_gettext)
+ delete[] wlabel;
+ return;
+ }
+ errorstream<< "Invalid checkbox element(" << parts.size() << "): '" << element << "'" << std::endl;
+}
+
+void GUIFormSpecMenu::parseImage(parserData* data,std::string element) {
+ std::vector<std::string> parts = split(element,';');
+
+ if (parts.size() == 3) {
+ std::vector<std::string> v_pos = split(parts[0],',');
+ std::vector<std::string> v_geom = split(parts[1],',');
+ std::string name = parts[2];
+
+ MY_CHECKPOS("image",0);
+ MY_CHECKGEOM("image",1);
+
+ v2s32 pos = padding + AbsoluteRect.UpperLeftCorner;
+ pos.X += stof(v_pos[0]) * (float) spacing.X;
+ pos.Y += stof(v_pos[1]) * (float) spacing.Y;
+
+ v2s32 geom;
+ geom.X = stoi(v_geom[0]) * (float)imgsize.X;
+ geom.Y = stoi(v_geom[1]) * (float)imgsize.Y;
+
+ infostream<<"image name="<<name
+ <<", pos=("<<pos.X<<","<<pos.Y<<")"
+ <<", geom=("<<geom.X<<","<<geom.Y<<")"
+ <<std::endl;
+ if(data->bp_set != 2)
+ errorstream<<"WARNING: invalid use of image without a size[] element"<<std::endl;
+ m_images.push_back(ImageDrawSpec(name, pos, geom));
+ return;
+ }
+
+ if (parts.size() == 2) {
+ std::vector<std::string> v_pos = split(parts[0],',');
+ std::string name = parts[1];
+
+ MY_CHECKPOS("image",0);
+
+ v2s32 pos = padding + AbsoluteRect.UpperLeftCorner;
+ pos.X += stof(v_pos[0]) * (float) spacing.X;
+ pos.Y += stof(v_pos[1]) * (float) spacing.Y;
+
+ std::cout<<"image name="<<name
+ <<", pos=("<<pos.X<<","<<pos.Y<<")"
+ <<std::endl;
+ if(data->bp_set != 2)
+ errorstream<<"WARNING: invalid use of image without a size[] element"<<std::endl;
+ m_images.push_back(ImageDrawSpec(name, pos));
+ return;
+ }
+ errorstream<< "Invalid image element(" << parts.size() << "): '" << element << "'" << std::endl;
+}
+
+void GUIFormSpecMenu::parseItemImage(parserData* data,std::string element) {
+ std::vector<std::string> parts = split(element,';');
+
+ if (parts.size() == 3) {
+ std::vector<std::string> v_pos = split(parts[0],',');
+ std::vector<std::string> v_geom = split(parts[1],',');
+ std::string name = parts[2];
+
+ MY_CHECKPOS("itemimage",0);
+ MY_CHECKGEOM("itemimage",1);
+
+ v2s32 pos = padding + AbsoluteRect.UpperLeftCorner;
+ pos.X += stof(v_pos[0]) * (float) spacing.X;
+ pos.Y += stof(v_pos[1]) * (float) spacing.Y;
+
+ v2s32 geom;
+ geom.X = stoi(v_geom[0]) * (float)imgsize.X;
+ geom.Y = stoi(v_geom[1]) * (float)imgsize.Y;
+
+ infostream<<"item name="<<name
+ <<", pos=("<<pos.X<<","<<pos.Y<<")"
+ <<", geom=("<<geom.X<<","<<geom.Y<<")"
+ <<std::endl;
+ if(data->bp_set != 2)
+ errorstream<<"WARNING: invalid use of item_image without a size[] element"<<std::endl;
+ m_itemimages.push_back(ImageDrawSpec(name, pos, geom));
+ return;
+ }
+ errorstream<< "Invalid ItemImage element(" << parts.size() << "): '" << element << "'" << std::endl;
+}
+
+void GUIFormSpecMenu::parseButton(parserData* data,std::string element,std::string type) {
+ std::vector<std::string> parts = split(element,';');
+
+ if (parts.size() == 4) {
+ std::vector<std::string> v_pos = split(parts[0],',');
+ std::vector<std::string> v_geom = split(parts[1],',');
+ std::string name = parts[2];
+ std::string label = parts[3];
+
+ MY_CHECKPOS("button",0);
+ MY_CHECKGEOM("button",1);
+
+ v2s32 pos = padding;
+ pos.X += stof(v_pos[0]) * (float)spacing.X;
+ pos.Y += stof(v_pos[1]) * (float)spacing.Y;
+
+ v2s32 geom;
+ geom.X = (stof(v_geom[0]) * (float)spacing.X)-(spacing.X-imgsize.X);
+ pos.Y += (stof(v_geom[1]) * (float)imgsize.Y)/2;
+
+ core::rect<s32> rect = core::rect<s32>(pos.X, pos.Y-15, pos.X+geom.X, pos.Y+15);
+
+ if(data->bp_set != 2)
+ errorstream<<"WARNING: invalid use of button without a size[] element"<<std::endl;
+
+ label = unescape_string(label);
+
+ wchar_t* wlabel = 0;
+
+ if (m_use_gettext)
+ wlabel = wgettext(label.c_str());
+ else
+ wlabel = (wchar_t*) narrow_to_wide(label.c_str()).c_str();
+
+ FieldSpec spec = FieldSpec(
+ narrow_to_wide(name.c_str()),
+ wlabel,
+ narrow_to_wide(""),
+ 258+m_fields.size()
+ );
+ spec.ftype = f_Button;
+ if(type == "button_exit")
+ spec.is_exit = true;
+
+ Environment->addButton(rect, this, spec.fid, spec.flabel.c_str());
+ m_fields.push_back(spec);
+ if (m_use_gettext)
+ delete[] wlabel;
+ return;
+ }
+ errorstream<< "Invalid button element(" << parts.size() << "): '" << element << "'" << std::endl;
+}
+
+void GUIFormSpecMenu::parseBackground(parserData* data,std::string element) {
+ std::vector<std::string> parts = split(element,';');
+
+ if (parts.size() == 3) {
+ std::vector<std::string> v_pos = split(parts[0],',');
+ std::vector<std::string> v_geom = split(parts[1],',');
+ std::string name = parts[2];
+
+ MY_CHECKPOS("background",0);
+ MY_CHECKGEOM("background",1);
+
+ v2s32 pos = padding + AbsoluteRect.UpperLeftCorner;
+ pos.X += stof(v_pos[0]) * (float)spacing.X - ((float)spacing.X-(float)imgsize.X)/2;
+ pos.Y += stof(v_pos[1]) * (float)spacing.Y - ((float)spacing.Y-(float)imgsize.Y)/2;
+
+ v2s32 geom;
+ geom.X = stof(v_geom[0]) * (float)spacing.X;
+ geom.Y = stof(v_geom[1]) * (float)spacing.Y;
+
+ infostream<<"image name="<<name
+ <<", pos=("<<pos.X<<","<<pos.Y<<")"
+ <<", geom=("<<geom.X<<","<<geom.Y<<")"
+ <<std::endl;
+ if(data->bp_set != 2)
+ errorstream<<"WARNING: invalid use of background without a size[] element"<<std::endl;
+ m_backgrounds.push_back(ImageDrawSpec(name, pos, geom));
+ return;
+ }
+ errorstream<< "Invalid background element(" << parts.size() << "): '" << element << "'" << std::endl;
+}
+
+void GUIFormSpecMenu::parseTextList(parserData* data,std::string element) {
+ std::vector<std::string> parts = split(element,';');
+
+ if ((parts.size() == 5) || (parts.size() == 6)) {
+ std::vector<std::string> v_pos = split(parts[0],',');
+ std::vector<std::string> v_geom = split(parts[1],',');
+ std::string name = parts[2];
+ std::vector<std::string> items = split(parts[3],',');
+ std::string str_initial_selection = "";
+ std::string str_transparent = "false";
+
+ if (parts.size() >= 5)
+ str_initial_selection = parts[4];
+
+ if (parts.size() >= 6)
+ str_transparent = parts[5];
+
+ MY_CHECKPOS("textlist",0);
+ MY_CHECKGEOM("textlist",1);
+
+ v2s32 pos = padding;
+ pos.X += stof(v_pos[0]) * (float)spacing.X;
+ pos.Y += stof(v_pos[1]) * (float)spacing.Y;
+
+ v2s32 geom;
+ geom.X = stof(v_geom[0]) * (float)spacing.X;
+ geom.Y = stof(v_geom[1]) * (float)spacing.Y;
+
+
+ core::rect<s32> rect = core::rect<s32>(pos.X, pos.Y, pos.X+geom.X, pos.Y+geom.Y);
+
+ std::wstring fname_w = narrow_to_wide(name.c_str());
+
+ FieldSpec spec = FieldSpec(
+ fname_w,
+ narrow_to_wide(""),
+ narrow_to_wide(""),
+ 258+m_fields.size()
+ );
+
+ spec.ftype = f_ListBox;
+
+ //now really show list
+ gui::IGUIListBox *e = Environment->addListBox(rect, this,spec.fid);
+
+ //don't reset if we already have a user specified selection
+ if (data->listbox_selections.find(fname_w) == data->listbox_selections.end()) {
+ e->setAutoScrollEnabled(false);
+ }
+
+ if (str_transparent == "false")
+ e->setDrawBackground(true);
+
+ for (unsigned int i=0; i < items.size(); i++) {
+ if (items[i].c_str()[0] == '#') {
+ if (items[i].c_str()[1] == '#') {
+ e->addItem(narrow_to_wide(items[i]).c_str() +1);
}
+ else {
+ std::wstring toadd = narrow_to_wide(items[i].c_str() + 4);
+ std::string color = items[i].substr(1,3);
- if (flabel != "")
- {
- rect.UpperLeftCorner.Y -= 15;
- rect.LowerRightCorner.Y = rect.UpperLeftCorner.Y + 15;
- Environment->addStaticText(spec.flabel.c_str(), rect, false, true, this, 0);
+ e->addItem(toadd.c_str());
+
+ bool valid_color = true;
+
+ irr::video::SColor toset = getColor(color,valid_color);
+
+ if (valid_color)
+ e->setItemOverrideColor(i,toset);
}
}
+ else {
+ e->addItem(narrow_to_wide(items[i]).c_str());
+ }
+ }
+
+ if (str_initial_selection != "")
+ e->setSelected(stoi(str_initial_selection.c_str())-1);
- m_fields.push_back(spec);
+ if (data->listbox_selections.find(fname_w) != data->listbox_selections.end()) {
+ e->setSelected(data->listbox_selections[fname_w]);
}
- else if(type == "label")
- {
- v2s32 pos = padding;
- pos.X += stof(f.next_esc(",")) * (float)spacing.X;
- pos.Y += stof(f.next_esc(";")) * (float)spacing.Y;
- rect = core::rect<s32>(pos.X, pos.Y+((imgsize.Y/2)-15), pos.X+300, pos.Y+((imgsize.Y/2)+15));
-
- std::string flabel = f.next_esc("]");
- if(bp_set != 2)
- errorstream<<"WARNING: invalid use of label without a size[] element"<<std::endl;
+ m_listboxes.push_back(std::pair<FieldSpec,gui::IGUIListBox*>(spec,e));
+ m_fields.push_back(spec);
+ return;
+ }
+ errorstream<< "Invalid textlist element(" << parts.size() << "): '" << element << "'" << std::endl;
+}
- flabel = unescape_string(flabel);
- FieldSpec spec = FieldSpec(
- narrow_to_wide(""),
- narrow_to_wide(flabel.c_str()),
- narrow_to_wide(""),
- 258+m_fields.size()
+void GUIFormSpecMenu::parseDropDown(parserData* data,std::string element) {
+ std::vector<std::string> parts = split(element,';');
+
+ if (parts.size() == 5) {
+ std::vector<std::string> v_pos = split(parts[0],',');
+ std::string name = parts[2];
+ std::vector<std::string> items = split(parts[3],',');
+ std::string str_initial_selection = "";
+ str_initial_selection = parts[4];
+
+ MY_CHECKPOS("dropdown",0);
+
+ v2s32 pos = padding;
+ pos.X += stof(v_pos[0]) * (float)spacing.X;
+ pos.Y += stof(v_pos[1]) * (float)spacing.Y;
+
+ s32 width = stof(parts[1]) * (float)spacing.Y;
+
+ core::rect<s32> rect = core::rect<s32>(pos.X, pos.Y, pos.X+width, pos.Y+30);
+
+ std::wstring fname_w = narrow_to_wide(name.c_str());
+
+ FieldSpec spec = FieldSpec(
+ fname_w,
+ narrow_to_wide(""),
+ narrow_to_wide(""),
+ 258+m_fields.size()
+ );
+
+ spec.ftype = f_DropDown;
+ spec.send = true;
+
+ //now really show list
+ gui::IGUIComboBox *e = Environment->addComboBox(rect, this,spec.fid);
+
+ //don't reset if we already have a user specified selection
+ //if (data->combobox_selections.find(fname_w) == data->listbox_selections.end()) {
+ // e->setAutoScrollEnabled(false);
+ //}
+
+ for (unsigned int i=0; i < items.size(); i++) {
+ e->addItem(narrow_to_wide(items[i]).c_str());
+ }
+
+ if (str_initial_selection != "")
+ e->setSelected(stoi(str_initial_selection.c_str())-1);
+
+ //if (data->listbox_selections.find(fname_w) != data->listbox_selections.end()) {
+ // e->setSelected(data->listbox_selections[fname_w]);
+ //}
+
+ //m_listboxes.push_back(std::pair<FieldSpec,gui::IGUIListBox*>(spec,e));
+ m_fields.push_back(spec);
+ return;
+ }
+ errorstream << "Invalid dropdown element(" << parts.size() << "): '"
+ << element << "'" << std::endl;
+}
+
+void GUIFormSpecMenu::parsePwdField(parserData* data,std::string element) {
+ std::vector<std::string> parts = split(element,';');
+
+ if (parts.size() == 4) {
+ std::vector<std::string> v_pos = split(parts[0],',');
+ std::vector<std::string> v_geom = split(parts[1],',');
+ std::string name = parts[2];
+ std::string label = parts[3];
+
+ MY_CHECKPOS("pwdfield",0);
+ MY_CHECKGEOM("pwdfield",1);
+
+ v2s32 pos;
+ pos.X += stof(v_pos[0]) * (float)spacing.X;
+ pos.Y += stof(v_pos[1]) * (float)spacing.Y;
+
+ v2s32 geom;
+ geom.X = (stof(v_geom[0]) * (float)spacing.X)-(spacing.X-imgsize.X);
+
+ pos.Y += (stof(v_geom[1]) * (float)imgsize.Y)/2;
+ pos.Y -= 15;
+ geom.Y = 30;
+
+ core::rect<s32> rect = core::rect<s32>(pos.X, pos.Y, pos.X+geom.X, pos.Y+geom.Y);
+
+ label = unescape_string(label);
+
+ wchar_t* wlabel = 0;
+
+ if (m_use_gettext) {
+ if (label.length() > 1)
+ wlabel = wgettext(label.c_str());
+ else
+ wlabel = (wchar_t*) narrow_to_wide("").c_str();
+ }
+ else
+ wlabel = (wchar_t*) narrow_to_wide(label.c_str()).c_str();
+
+ FieldSpec spec = FieldSpec(
+ narrow_to_wide(name.c_str()),
+ wlabel,
+ narrow_to_wide(""),
+ 258+m_fields.size()
);
- Environment->addStaticText(spec.flabel.c_str(), rect, false, true, this, spec.fid);
- m_fields.push_back(spec);
+
+ spec.send = true;
+ gui::IGUIEditBox * e = Environment->addEditBox(0, rect, true, this, spec.fid);
+ Environment->setFocus(e);
+
+ if (label.length() > 1)
+ {
+ rect.UpperLeftCorner.Y -= 15;
+ rect.LowerRightCorner.Y = rect.UpperLeftCorner.Y + 15;
+ Environment->addStaticText(spec.flabel.c_str(), rect, false, true, this, 0);
}
- else if(type == "button" || type == "button_exit")
+
+ e->setPasswordBox(true,L'*');
+
+ irr::SEvent evt;
+ evt.KeyInput.Key = KEY_END;
+ evt.EventType = EET_KEY_INPUT_EVENT;
+ evt.KeyInput.PressedDown = true;
+ e->OnEvent(evt);
+ m_fields.push_back(spec);
+ if ((m_use_gettext) && (label.length() >1))
+ delete[] wlabel;
+ return;
+ }
+ errorstream<< "Invalid pwdfield element(" << parts.size() << "): '" << element << "'" << std::endl;
+}
+
+void GUIFormSpecMenu::parseSimpleField(parserData* data,std::vector<std::string> &parts) {
+ std::string name = parts[0];
+ std::string label = parts[1];
+ std::string default_val = parts[2];
+
+ core::rect<s32> rect;
+
+ if(!data->bp_set)
+ {
+ rect = core::rect<s32>(
+ data->screensize.X/2 - 580/2,
+ data->screensize.Y/2 - 300/2,
+ data->screensize.X/2 + 580/2,
+ data->screensize.Y/2 + 300/2
+ );
+ DesiredRect = rect;
+ recalculateAbsolutePosition(false);
+ data->basepos = getBasePos();
+ data->bp_set = 1;
+ }
+ else if(data->bp_set == 2)
+ errorstream<<"WARNING: invalid use of unpositioned \"field\" in inventory"<<std::endl;
+
+ v2s32 pos = padding + AbsoluteRect.UpperLeftCorner;
+ pos.Y = ((m_fields.size()+2)*60);
+ v2s32 size = DesiredRect.getSize();
+
+ rect = core::rect<s32>(size.X/2-150, pos.Y, (size.X/2-150)+300, pos.Y+30);
+
+
+ if(m_form_src)
+ default_val = m_form_src->resolveText(default_val);
+
+ default_val = unescape_string(default_val);
+ label = unescape_string(label);
+
+ wchar_t* wlabel = 0;
+
+ if (m_use_gettext) {
+ if (label.length() > 1)
+ wlabel = wgettext(label.c_str());
+ else
+ wlabel = (wchar_t*) narrow_to_wide("").c_str();
+ }
+ else
+ wlabel = (wchar_t*) narrow_to_wide(label.c_str()).c_str();
+
+ FieldSpec spec = FieldSpec(
+ narrow_to_wide(name.c_str()),
+ wlabel,
+ narrow_to_wide(default_val.c_str()),
+ 258+m_fields.size()
+ );
+
+ if (name == "")
+ {
+ // spec field id to 0, this stops submit searching for a value that isn't there
+ Environment->addStaticText(spec.flabel.c_str(), rect, false, true, this, spec.fid);
+ }
+ else
+ {
+ spec.send = true;
+ gui::IGUIEditBox *e = Environment->addEditBox(spec.fdefault.c_str(), rect, true, this, spec.fid);
+ Environment->setFocus(e);
+
+ irr::SEvent evt;
+ evt.KeyInput.Key = KEY_END;
+ evt.EventType = EET_KEY_INPUT_EVENT;
+ evt.KeyInput.PressedDown = true;
+ e->OnEvent(evt);
+
+ if (label.length() > 1)
{
- v2s32 pos = padding;
- pos.X += stof(f.next_esc(",")) * (float)spacing.X;
- pos.Y += stof(f.next_esc(";")) * (float)spacing.Y;
- v2s32 geom;
- geom.X = (stof(f.next_esc(",")) * (float)spacing.X)-(spacing.X-imgsize.X);
- pos.Y += (stof(f.next_esc(";")) * (float)imgsize.Y)/2;
-
- rect = core::rect<s32>(pos.X, pos.Y-15, pos.X+geom.X, pos.Y+15);
-
- std::string fname = f.next_esc(";");
- std::string flabel = f.next_esc("]");
- if(bp_set != 2)
- errorstream<<"WARNING: invalid use of button without a size[] element"<<std::endl;
-
- flabel = unescape_string(flabel);
-
- FieldSpec spec = FieldSpec(
- narrow_to_wide(fname.c_str()),
- narrow_to_wide(flabel.c_str()),
- narrow_to_wide(""),
- 258+m_fields.size()
- );
- spec.is_button = true;
- if(type == "button_exit")
- spec.is_exit = true;
- Environment->addButton(rect, this, spec.fid, spec.flabel.c_str());
- m_fields.push_back(spec);
+ rect.UpperLeftCorner.Y -= 15;
+ rect.LowerRightCorner.Y = rect.UpperLeftCorner.Y + 15;
+ Environment->addStaticText(spec.flabel.c_str(), rect, false, true, this, 0);
}
- else if(type == "image_button" || type == "image_button_exit")
+ }
+ if (m_use_gettext && (label.length() > 1))
+ delete[] wlabel;
+
+ m_fields.push_back(spec);
+}
+
+void GUIFormSpecMenu::parseTextArea(parserData* data,std::vector<std::string>& parts,std::string type) {
+
+ std::vector<std::string> v_pos = split(parts[0],',');
+ std::vector<std::string> v_geom = split(parts[1],',');
+ std::string name = parts[2];
+ std::string label = parts[3];
+ std::string default_val = parts[4];
+
+ MY_CHECKPOS(type,0);
+ MY_CHECKGEOM(type,1);
+
+ v2s32 pos;
+ pos.X = stof(v_pos[0]) * (float) spacing.X;
+ pos.Y = stof(v_pos[1]) * (float) spacing.Y;
+
+ v2s32 geom;
+
+ geom.X = (stof(v_geom[0]) * (float)spacing.X)-(spacing.X-imgsize.X);
+
+ if (type == "textarea")
+ {
+ geom.Y = (stof(v_geom[1]) * (float)imgsize.Y) - (spacing.Y-imgsize.Y);
+ pos.Y += 15;
+ }
+ else
+ {
+ pos.Y += (stof(v_geom[1]) * (float)imgsize.Y)/2;
+ pos.Y -= 15;
+ geom.Y = 30;
+ }
+
+ core::rect<s32> rect = core::rect<s32>(pos.X, pos.Y, pos.X+geom.X, pos.Y+geom.Y);
+
+ if(data->bp_set != 2)
+ errorstream<<"WARNING: invalid use of positioned "<<type<<" without a size[] element"<<std::endl;
+
+ if(m_form_src)
+ default_val = m_form_src->resolveText(default_val);
+
+
+ default_val = unescape_string(default_val);
+ label = unescape_string(label);
+
+ wchar_t* wlabel = 0;
+
+ if (m_use_gettext) {
+ if (label.length() > 1)
+ wlabel = wgettext(label.c_str());
+ else
+ wlabel = (wchar_t*) narrow_to_wide("").c_str();
+ }
+ else
+ wlabel = (wchar_t*) narrow_to_wide(label.c_str()).c_str();
+
+ FieldSpec spec = FieldSpec(
+ narrow_to_wide(name.c_str()),
+ wlabel,
+ narrow_to_wide(default_val.c_str()),
+ 258+m_fields.size()
+ );
+
+ if (name == "")
+ {
+ // spec field id to 0, this stops submit searching for a value that isn't there
+ Environment->addStaticText(spec.flabel.c_str(), rect, false, true, this, spec.fid);
+ }
+ else
+ {
+ spec.send = true;
+ gui::IGUIEditBox *e = Environment->addEditBox(spec.fdefault.c_str(), rect, true, this, spec.fid);
+ Environment->setFocus(e);
+
+ if (type == "textarea")
{
- v2s32 pos = padding;
- pos.X += stof(f.next_esc(",")) * (float)spacing.X;
- pos.Y += stof(f.next_esc(";")) * (float)spacing.Y;
- v2s32 geom;
- geom.X = (stof(f.next_esc(",")) * (float)spacing.X)-(spacing.X-imgsize.X);
- geom.Y = (stof(f.next_esc(";")) * (float)spacing.Y)-(spacing.Y-imgsize.Y);
-
- rect = core::rect<s32>(pos.X, pos.Y, pos.X+geom.X, pos.Y+geom.Y);
-
- std::string fimage = f.next_esc(";");
- std::string fname = f.next_esc(";");
- std::string flabel = f.next_esc("]");
- if(bp_set != 2)
- errorstream<<"WARNING: invalid use of image_button without a size[] element"<<std::endl;
-
- flabel = unescape_string(flabel);
-
- FieldSpec spec = FieldSpec(
- narrow_to_wide(fname.c_str()),
- narrow_to_wide(flabel.c_str()),
- narrow_to_wide(fimage.c_str()),
- 258+m_fields.size()
- );
- spec.is_button = true;
- if(type == "image_button_exit")
- spec.is_exit = true;
-
- video::ITexture *texture = m_gamedef->tsrc()->getTexture(fimage);
- gui::IGUIButton *e = Environment->addButton(rect, this, spec.fid, spec.flabel.c_str());
- e->setUseAlphaChannel(true);
- e->setImage(texture);
- e->setPressedImage(texture);
- e->setScaleImage(true);
-
- m_fields.push_back(spec);
+ e->setMultiLine(true);
+ e->setTextAlignment(gui::EGUIA_UPPERLEFT, gui::EGUIA_UPPERLEFT);
+ } else {
+ irr::SEvent evt;
+ evt.EventType = EET_KEY_INPUT_EVENT;
+ evt.KeyInput.Key = KEY_END;
+ evt.KeyInput.Char = 0;
+ evt.KeyInput.Control = 0;
+ evt.KeyInput.Shift = 0;
+ evt.KeyInput.PressedDown = true;
+ e->OnEvent(evt);
}
- else if(type == "item_image_button")
+
+ if (label.length() > 1)
{
- v2s32 pos = padding;
- pos.X += stof(f.next_esc(",")) * (float)spacing.X;
- pos.Y += stof(f.next_esc(";")) * (float)spacing.Y;
- v2s32 geom;
- geom.X = (stof(f.next_esc(",")) * (float)spacing.X)-(spacing.X-imgsize.X);
- geom.Y = (stof(f.next_esc(";")) * (float)spacing.Y)-(spacing.Y-imgsize.Y);
- rect = core::rect<s32>(pos.X, pos.Y, pos.X+geom.X, pos.Y+geom.Y);
- std::string fimage = f.next_esc(";");
- std::string fname = f.next_esc(";");
- std::string flabel = f.next_esc("]");
- if(bp_set != 2)
- errorstream<<"WARNING: invalid use of item_image_button without a size[] element"<<std::endl;
- IItemDefManager *idef = m_gamedef->idef();
- ItemStack item;
- item.deSerialize(fimage, idef);
- video::ITexture *texture = idef->getInventoryTexture(item.getDefinition(idef).name, m_gamedef);
- std::string tooltip = item.getDefinition(idef).description;
- flabel = unescape_string(flabel);
- FieldSpec spec = FieldSpec(
- narrow_to_wide(fname.c_str()),
- narrow_to_wide(flabel.c_str()),
- narrow_to_wide(fimage.c_str()),
- 258+m_fields.size()
- );
- gui::IGUIButton *e = Environment->addButton(rect, this, spec.fid, spec.flabel.c_str());
- e->setUseAlphaChannel(true);
- e->setImage(texture);
- e->setPressedImage(texture);
- e->setScaleImage(true);
- spec.is_button = true;
- rect+=basepos-padding;
- spec.rect=rect;
- if (tooltip!="")
- spec.tooltip=tooltip;
- m_fields.push_back(spec);
+ rect.UpperLeftCorner.Y -= 15;
+ rect.LowerRightCorner.Y = rect.UpperLeftCorner.Y + 15;
+ Environment->addStaticText(spec.flabel.c_str(), rect, false, true, this, 0);
}
+ }
+ if (m_use_gettext && (label.length() > 1))
+ delete[] wlabel;
+ m_fields.push_back(spec);
+}
+
+void GUIFormSpecMenu::parseField(parserData* data,std::string element,std::string type) {
+ std::vector<std::string> parts = split(element,';');
+
+ if (parts.size() == 3) {
+ parseSimpleField(data,parts);
+ return;
+ }
+
+ if (parts.size() == 5) {
+ parseTextArea(data,parts,type);
+ return;
+ }
+ errorstream<< "Invalid field element(" << parts.size() << "): '" << element << "'" << std::endl;
+}
+
+void GUIFormSpecMenu::parseLabel(parserData* data,std::string element) {
+ std::vector<std::string> parts = split(element,';');
+
+ if (parts.size() == 2) {
+ std::vector<std::string> v_pos = split(parts[0],',');
+ std::string text = parts[1];
+
+ MY_CHECKPOS("label",0);
+
+ v2s32 pos = padding;
+ pos.X += stof(v_pos[0]) * (float)spacing.X;
+ pos.Y += stof(v_pos[1]) * (float)spacing.Y;
+
+ core::rect<s32> rect = core::rect<s32>(pos.X, pos.Y+((imgsize.Y/2)-15), pos.X+300, pos.Y+((imgsize.Y/2)+15));
+
+ if(data->bp_set != 2)
+ errorstream<<"WARNING: invalid use of label without a size[] element"<<std::endl;
+
+ text = unescape_string(text);
+
+ wchar_t* wlabel = 0;
+
+ if (m_use_gettext)
+ wlabel = wgettext(text.c_str());
else
- {
- // Ignore others
- std::string ts = f.next_esc("]");
- infostream<<"Unknown DrawSpec: type="<<type<<", data=\""<<ts<<"\""
- <<std::endl;
+ wlabel = (wchar_t*) narrow_to_wide(text.c_str()).c_str();
+
+ FieldSpec spec = FieldSpec(
+ narrow_to_wide(""),
+ wlabel,
+ narrow_to_wide(""),
+ 258+m_fields.size()
+ );
+ Environment->addStaticText(spec.flabel.c_str(), rect, false, true, this, spec.fid);
+ m_fields.push_back(spec);
+ if (m_use_gettext)
+ delete[] wlabel;
+ return;
+ }
+ errorstream<< "Invalid label element(" << parts.size() << "): '" << element << "'" << std::endl;
+}
+
+void GUIFormSpecMenu::parseVertLabel(parserData* data,std::string element) {
+ std::vector<std::string> parts = split(element,';');
+
+ if (parts.size() == 2) {
+ std::vector<std::string> v_pos = split(parts[0],',');
+ std::string text = parts[1];
+
+ MY_CHECKPOS("vertlabel",1);
+
+ v2s32 pos = padding;
+ pos.X += stof(v_pos[0]) * (float)spacing.X;
+ pos.Y += stof(v_pos[1]) * (float)spacing.Y;
+
+ core::rect<s32> rect = core::rect<s32>(pos.X, pos.Y+((imgsize.Y/2)-15), pos.X+15, pos.Y+300);
+
+ if(data->bp_set != 2)
+ errorstream<<"WARNING: invalid use of label without a size[] element"<<std::endl;
+
+ text = unescape_string(text);
+ std::string label = "";
+
+ if (m_use_gettext) {
+ const char* toset = gettext(text.c_str());
+
+ text = std::string(toset);
+ }
+
+ for (unsigned int i=0; i < text.length(); i++) {
+ label += text.c_str()[i];
+ label += "\n";
+ }
+
+ FieldSpec spec = FieldSpec(
+ narrow_to_wide(""),
+ narrow_to_wide(label.c_str()),
+ narrow_to_wide(""),
+ 258+m_fields.size()
+ );
+ gui::IGUIStaticText *t =
+ Environment->addStaticText(spec.flabel.c_str(), rect, false, true, this, spec.fid);
+ t->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_CENTER);
+ m_fields.push_back(spec);
+ return;
+ }
+ errorstream<< "Invalid vertlabel element(" << parts.size() << "): '" << element << "'" << std::endl;
+}
+
+void GUIFormSpecMenu::parseImageButton(parserData* data,std::string element,std::string type) {
+ std::vector<std::string> parts = split(element,';');
+
+ if ((parts.size() == 5) || (parts.size() == 7)) {
+ std::vector<std::string> v_pos = split(parts[0],',');
+ std::vector<std::string> v_geom = split(parts[1],',');
+ std::string image_name = parts[2];
+ std::string name = parts[3];
+ std::string label = parts[4];
+
+ MY_CHECKPOS("imagebutton",0);
+ MY_CHECKGEOM("imagebutton",1);
+
+ v2s32 pos = padding;
+ pos.X += stof(v_pos[0]) * (float)spacing.X;
+ pos.Y += stof(v_pos[1]) * (float)spacing.Y;
+ v2s32 geom;
+ geom.X = (stof(v_geom[0]) * (float)spacing.X)-(spacing.X-imgsize.X);
+ geom.Y = (stof(v_geom[1]) * (float)spacing.Y)-(spacing.Y-imgsize.Y);
+
+ bool noclip = false;
+ bool drawborder = true;
+
+ if ((parts.size() == 7)) {
+ if (parts[5] == "true")
+ noclip = true;
+
+ if (parts[6] == "false")
+ drawborder = false;
+ }
+
+ core::rect<s32> rect = core::rect<s32>(pos.X, pos.Y, pos.X+geom.X, pos.Y+geom.Y);
+
+ if(data->bp_set != 2)
+ errorstream<<"WARNING: invalid use of item_image_button without a size[] element"<<std::endl;
+
+ label = unescape_string(label);
+
+ FieldSpec spec = FieldSpec(
+ narrow_to_wide(name.c_str()),
+ narrow_to_wide(label.c_str()),
+ narrow_to_wide(image_name.c_str()),
+ 258+m_fields.size()
+ );
+ spec.ftype = f_Button;
+ if(type == "image_button_exit")
+ spec.is_exit = true;
+
+ video::ITexture *texture = 0;
+ //if there's no gamedef specified try to get direct
+ //TODO check for possible texture leak
+ if (m_gamedef != 0)
+ texture = m_gamedef->tsrc()->getTexture(image_name);
+ else {
+ if (fs::PathExists(image_name)) {
+ texture = Environment->getVideoDriver()->getTexture(image_name.c_str());
+ m_Textures.push_back(texture);
+ }
+ }
+
+ gui::IGUIButton *e = Environment->addButton(rect, this, spec.fid, spec.flabel.c_str());
+ e->setUseAlphaChannel(true);
+ e->setImage(texture);
+ e->setPressedImage(texture);
+ e->setScaleImage(true);
+ e->setNotClipped(noclip);
+ e->setDrawBorder(drawborder);
+
+ m_fields.push_back(spec);
+ return;
+ }
+
+ errorstream<< "Invalid imagebutton element(" << parts.size() << "): '" << element << "'" << std::endl;
+}
+
+void GUIFormSpecMenu::parseTabHeader(parserData* data,std::string element) {
+ std::vector<std::string> parts = split(element,';');
+
+ if ((parts.size() == 4) || (parts.size() == 6)) {
+ std::vector<std::string> v_pos = split(parts[0],',');
+ std::string name = parts[1];
+ std::vector<std::string> buttons = split(parts[2],',');
+ std::string str_index = parts[3];
+ bool show_background = true;
+ bool show_border = true;
+ int tab_index = stoi(str_index) -1;
+
+ MY_CHECKPOS("tabheader",0);
+
+ if (parts.size() == 6) {
+ if (parts[4] == "true")
+ show_background = false;
+ if (parts[5] == "false")
+ show_border = false;
+ }
+
+ FieldSpec spec = FieldSpec(
+ narrow_to_wide(name.c_str()),
+ narrow_to_wide(""),
+ narrow_to_wide(""),
+ 258+m_fields.size()
+ );
+
+ spec.ftype = f_TabHeader;
+
+ v2s32 pos = padding;
+ pos.X += stof(v_pos[0]) * (float)spacing.X;
+ pos.Y += stof(v_pos[1]) * (float)spacing.Y;
+ v2s32 geom;
+ geom.X = data->screensize.Y;
+ geom.Y = 30;
+
+ core::rect<s32> rect = core::rect<s32>(pos.X, pos.Y, pos.X+geom.X, pos.Y+geom.Y);
+
+ gui::IGUITabControl *e = Environment->addTabControl(rect,this,show_background,show_border,spec.fid);
+
+ e->setNotClipped(true);
+
+ for (unsigned int i=0; i< buttons.size(); i++) {
+ wchar_t* wbutton = 0;
+
+ if (m_use_gettext)
+ wbutton = wgettext(buttons[i].c_str());
+ else
+ wbutton = (wchar_t*) narrow_to_wide(buttons[i].c_str()).c_str();
+
+ e->addTab(wbutton,-1);
+
+ if (m_use_gettext)
+ delete[] wbutton;
}
+
+ if ((tab_index >= 0) &&
+ (buttons.size() < INT_MAX) &&
+ (tab_index < (int) buttons.size()))
+ e->setActiveTab(tab_index);
+
+ m_fields.push_back(spec);
+ return;
+ }
+ errorstream<< "Invalid TabHeader element(" << parts.size() << "): '" << element << "'" << std::endl;
+}
+
+void GUIFormSpecMenu::parseItemImageButton(parserData* data,std::string element) {
+
+ if (m_gamedef == 0) {
+ errorstream<<"WARNING: invalid use of item_image_button with m_gamedef==0"<<std::endl;
+ return;
+ }
+
+ std::vector<std::string> parts = split(element,';');
+
+ if (parts.size() == 5) {
+ std::vector<std::string> v_pos = split(parts[0],',');
+ std::vector<std::string> v_geom = split(parts[1],',');
+ std::string item_name = parts[2];
+ std::string name = parts[3];
+ std::string label = parts[4];
+
+ MY_CHECKPOS("itemimagebutton",0);
+ MY_CHECKGEOM("itemimagebutton",1);
+
+ v2s32 pos = padding;
+ pos.X += stof(v_pos[0]) * (float)spacing.X;
+ pos.Y += stof(v_pos[1]) * (float)spacing.Y;
+ v2s32 geom;
+ geom.X = (stof(v_geom[0]) * (float)spacing.X)-(spacing.X-imgsize.X);
+ geom.Y = (stof(v_geom[1]) * (float)spacing.Y)-(spacing.Y-imgsize.Y);
+
+ core::rect<s32> rect = core::rect<s32>(pos.X, pos.Y, pos.X+geom.X, pos.Y+geom.Y);
+
+ if(data->bp_set != 2)
+ errorstream<<"WARNING: invalid use of item_image_button without a size[] element"<<std::endl;
+
+ IItemDefManager *idef = m_gamedef->idef();
+ ItemStack item;
+ item.deSerialize(item_name, idef);
+ video::ITexture *texture = idef->getInventoryTexture(item.getDefinition(idef).name, m_gamedef);
+ std::string tooltip = item.getDefinition(idef).description;
+
+ label = unescape_string(label);
+ FieldSpec spec = FieldSpec(
+ narrow_to_wide(name.c_str()),
+ narrow_to_wide(label.c_str()),
+ narrow_to_wide(item_name.c_str()),
+ 258+m_fields.size()
+ );
+
+ gui::IGUIButton *e = Environment->addButton(rect, this, spec.fid, spec.flabel.c_str());
+ e->setUseAlphaChannel(true);
+ e->setImage(texture);
+ e->setPressedImage(texture);
+ e->setScaleImage(true);
+ spec.ftype = f_Button;
+ rect+=data->basepos-padding;
+ spec.rect=rect;
+ if (tooltip!="")
+ spec.tooltip=tooltip;
+ m_fields.push_back(spec);
+ return;
+ }
+ errorstream<< "Invalid ItemImagebutton element(" << parts.size() << "): '" << element << "'" << std::endl;
+}
+
+void GUIFormSpecMenu::parseBox(parserData* data,std::string element) {
+ std::vector<std::string> parts = split(element,';');
+
+ if (parts.size() == 3) {
+ std::vector<std::string> v_pos = split(parts[0],',');
+ std::vector<std::string> v_geom = split(parts[1],',');
+ std::string color_str = parts[2];
+
+ MY_CHECKPOS("box",0);
+ MY_CHECKGEOM("box",1);
+
+ v2s32 pos = padding + AbsoluteRect.UpperLeftCorner;
+ pos.X += stof(v_pos[0]) * (float) spacing.X;
+ pos.Y += stof(v_pos[1]) * (float) spacing.Y;
+
+ v2s32 geom;
+ geom.X = stof(v_geom[0]) * (float)spacing.X;
+ geom.Y = stof(v_geom[1]) * (float)spacing.Y;
+
+ bool valid_color = false;
+
+ irr::video::SColor color = getColor(color_str,valid_color);
+
+ if (valid_color) {
+ BoxDrawSpec spec(pos,geom,color);
+
+ m_boxes.push_back(spec);
+ }
+ else {
+ errorstream<< "Invalid Box element(" << parts.size() << "): '" << element << "' INVALID COLOR" << std::endl;
+ }
+ return;
+ }
+ errorstream<< "Invalid Box element(" << parts.size() << "): '" << element << "'" << std::endl;
+}
+
+void GUIFormSpecMenu::parseElement(parserData* data,std::string element) {
+ //some prechecks
+ if (element == "")
+ return;
+
+ std::vector<std::string> parts = split(element,'[', true);
+
+ if (parts.size() != 2)
+ return;
+
+ std::string type = trim(parts[0]);
+ std::string description = trim(parts[1]);
+
+ if ((type == "size") || (type == "invsize")){
+ parseSize(data,description);
+ return;
+ }
+
+ if (type == "list") {
+ parseList(data,description);
+ return;
+ }
+
+ if (type == "checkbox") {
+ parseCheckbox(data,description);
+ return;
+ }
+
+ if (type == "image") {
+ parseImage(data,description);
+ return;
+ }
+
+ if (type == "item_image") {
+ parseItemImage(data,description);
+ return;
+ }
+
+ if ((type == "button") || (type == "button_exit")) {
+ parseButton(data,description,type);
+ return;
+ }
+
+ if (type == "background") {
+ parseBackground(data,description);
+ return;
+ }
+
+ if (type == "textlist"){
+ parseTextList(data,description);
+ return;
+ }
+
+ if (type == "dropdown"){
+ parseDropDown(data,description);
+ return;
+ }
+
+ if (type == "pwdfield") {
+ parsePwdField(data,description);
+ return;
+ }
+
+ if ((type == "field") || (type == "textarea")){
+ parseField(data,description,type);
+ return;
+ }
+
+ if (type == "label") {
+ parseLabel(data,description);
+ return;
+ }
+
+ if (type == "vertlabel") {
+ parseVertLabel(data,description);
+ return;
+ }
+
+ if (type == "item_image_button") {
+ parseItemImageButton(data,description);
+ return;
+ }
+
+ if ((type == "image_button") || (type == "image_button_exit")) {
+ parseImageButton(data,description,type);
+ return;
+ }
+
+ if (type == "tabheader") {
+ parseTabHeader(data,description);
+ return;
+ }
+
+ if (type == "box") {
+ parseBox(data,description);
+ return;
+ }
+
+ // Ignore others
+ infostream
+ << "Unknown DrawSpec: type="<<type<<", data=\""<<description<<"\""
+ <<std::endl;
+}
+
+
+
+void GUIFormSpecMenu::regenerateGui(v2u32 screensize)
+{
+ parserData mydata;
+
+ //preserve listboxes
+ for (unsigned int i = 0; i < m_listboxes.size(); i++) {
+ int selection = m_listboxes[i].second->getSelected();
+ if (selection != -1) {
+ std::wstring listboxname = m_listboxes[i].first.fname;
+ mydata.listbox_selections[listboxname] = selection;
+ }
+ }
+
+ // Remove children
+ removeChildren();
+
+ mydata.size= v2s32(100,100);
+ mydata.helptext_h = 15;
+ mydata.screensize = screensize;
+
+ // Base position of contents of form
+ mydata.basepos = getBasePos();
+
+ // State of basepos, 0 = not set, 1= set by formspec, 2 = set by size[] element
+ // Used to adjust form size automatically if needed
+ // A proceed button is added if there is no size[] element
+ mydata.bp_set = 0;
+
+
+ /* Convert m_init_draw_spec to m_inventorylists */
+
+ m_inventorylists.clear();
+ m_images.clear();
+ m_backgrounds.clear();
+ m_itemimages.clear();
+ m_listboxes.clear();
+ m_checkboxes.clear();
+ m_fields.clear();
+ m_boxes.clear();
+
+
+ std::vector<std::string> elements = split(m_formspec_string,']',true);
+
+ for (unsigned int i=0;i< elements.size();i++) {
+ parseElement(&mydata,elements[i]);
}
// If there's inventory, put the usage string at the bottom
if (m_inventorylists.size())
{
changeCtype("");
- core::rect<s32> rect(0, 0, size.X-padding.X*2, helptext_h);
- rect = rect + v2s32(size.X/2 - rect.getWidth()/2,
- size.Y-rect.getHeight()-5);
+ core::rect<s32> rect(0, 0, mydata.size.X-padding.X*2, mydata.helptext_h);
+ rect = rect + v2s32((mydata.size.X/2 - mydata.rect.getWidth()/2) +5,
+ mydata.size.Y-5-mydata.helptext_h);
const wchar_t *text = wgettext("Left click: Move all items, Right click: Move single item");
Environment->addStaticText(text, rect, false, true, this, 256);
delete[] text;
changeCtype("C");
}
// If there's fields, add a Proceed button
- if (m_fields.size() && bp_set != 2)
+ if (m_fields.size() && mydata.bp_set != 2)
{
// if the size wasn't set by an invsize[] or size[] adjust it now to fit all the fields
- rect = core::rect<s32>(
- screensize.X/2 - 580/2,
- screensize.Y/2 - 300/2,
- screensize.X/2 + 580/2,
- screensize.Y/2 + 240/2+(m_fields.size()*60)
+ mydata.rect = core::rect<s32>(
+ mydata.screensize.X/2 - 580/2,
+ mydata.screensize.Y/2 - 300/2,
+ mydata.screensize.X/2 + 580/2,
+ mydata.screensize.Y/2 + 240/2+(m_fields.size()*60)
);
- DesiredRect = rect;
+ DesiredRect = mydata.rect;
recalculateAbsolutePosition(false);
- basepos = getBasePos();
+ mydata.basepos = getBasePos();
changeCtype("");
{
- v2s32 pos = basepos;
+ v2s32 pos = mydata.basepos;
pos.Y = ((m_fields.size()+2)*60);
v2s32 size = DesiredRect.getSize();
- rect = core::rect<s32>(size.X/2-70, pos.Y, (size.X/2-70)+140, pos.Y+30);
+ mydata.rect = core::rect<s32>(size.X/2-70, pos.Y, (size.X/2-70)+140, pos.Y+30);
wchar_t* text = wgettext("Proceed");
- Environment->addButton(rect, this, 257, text);
+ Environment->addButton(mydata.rect, this, 257, text);
delete[] text;
}
changeCtype("C");
@@ -684,8 +1614,8 @@ void GUIFormSpecMenu::drawList(const ListDrawSpec &s, int phase)
for(s32 i=0; i<s.geom.X*s.geom.Y; i++)
{
- u32 item_i = i + s.start_item_i;
- if(item_i >= ilist->getSize())
+ s32 item_i = i + s.start_item_i;
+ if(item_i >= (s32) ilist->getSize())
break;
s32 x = (i%s.geom.X) * spacing.X;
s32 y = (i/s.geom.X) * spacing.Y;
@@ -804,38 +1734,88 @@ void GUIFormSpecMenu::drawMenu()
for(u32 i=0; i<m_backgrounds.size(); i++)
{
const ImageDrawSpec &spec = m_backgrounds[i];
- video::ITexture *texture =
- m_gamedef->tsrc()->getTexture(spec.name);
- // Image size on screen
- core::rect<s32> imgrect(0, 0, spec.geom.X, spec.geom.Y);
- // Image rectangle on screen
- core::rect<s32> rect = imgrect + spec.pos;
- const video::SColor color(255,255,255,255);
- const video::SColor colors[] = {color,color,color,color};
- driver->draw2DImage(texture, rect,
- core::rect<s32>(core::position2d<s32>(0,0),
- core::dimension2di(texture->getOriginalSize())),
- NULL/*&AbsoluteClippingRect*/, colors, true);
+ video::ITexture *texture = 0;
+
+ if (m_gamedef != 0)
+ texture = m_gamedef->tsrc()->getTexture(spec.name);
+ else
+ {
+ texture = driver->getTexture(spec.name.c_str());
+ m_Textures.push_back(texture);
+ }
+
+ if (texture != 0) {
+ // Image size on screen
+ core::rect<s32> imgrect(0, 0, spec.geom.X, spec.geom.Y);
+ // Image rectangle on screen
+ core::rect<s32> rect = imgrect + spec.pos;
+ const video::SColor color(255,255,255,255);
+ const video::SColor colors[] = {color,color,color,color};
+ driver->draw2DImage(texture, rect,
+ core::rect<s32>(core::position2d<s32>(0,0),
+ core::dimension2di(texture->getOriginalSize())),
+ NULL/*&AbsoluteClippingRect*/, colors, true);
+ }
+ else {
+ errorstream << "GUIFormSpecMenu::drawMenu() Draw backgrounds unable to load texture:" << std::endl;
+ errorstream << "\t" << spec.name << std::endl;
+ }
}
/*
+ Draw Boxes
+ */
+ for(u32 i=0; i<m_boxes.size(); i++)
+ {
+ const BoxDrawSpec &spec = m_boxes[i];
+
+ irr::video::SColor todraw = spec.color;
+
+ todraw.setAlpha(140);
+
+ core::rect<s32> rect(spec.pos.X,spec.pos.Y,
+ spec.pos.X + spec.geom.X,spec.pos.Y + spec.geom.Y);
+
+ driver->draw2DRectangle(todraw, rect, 0);
+ }
+ /*
Draw images
*/
for(u32 i=0; i<m_images.size(); i++)
{
const ImageDrawSpec &spec = m_images[i];
- video::ITexture *texture =
- m_gamedef->tsrc()->getTexture(spec.name);
- // Image size on screen
- core::rect<s32> imgrect(0, 0, spec.geom.X, spec.geom.Y);
- // Image rectangle on screen
- core::rect<s32> rect = imgrect + spec.pos;
- const video::SColor color(255,255,255,255);
- const video::SColor colors[] = {color,color,color,color};
- driver->draw2DImage(texture, rect,
- core::rect<s32>(core::position2d<s32>(0,0),
- core::dimension2di(texture->getOriginalSize())),
- NULL/*&AbsoluteClippingRect*/, colors, true);
+ video::ITexture *texture = 0;
+
+ if (m_gamedef != 0)
+ texture = m_gamedef->tsrc()->getTexture(spec.name);
+ else
+ {
+ texture = driver->getTexture(spec.name.c_str());
+ m_Textures.push_back(texture);
+ }
+ if (texture != 0) {
+ const core::dimension2d<u32>& img_origsize = texture->getOriginalSize();
+ // Image size on screen
+ core::rect<s32> imgrect;
+
+ if (spec.scale)
+ imgrect = core::rect<s32>(0,0,spec.geom.X, spec.geom.Y);
+ else {
+
+ imgrect = core::rect<s32>(0,0,img_origsize.Width,img_origsize.Height);
+ }
+ // Image rectangle on screen
+ core::rect<s32> rect = imgrect + spec.pos;
+ const video::SColor color(255,255,255,255);
+ const video::SColor colors[] = {color,color,color,color};
+ driver->draw2DImage(texture, rect,
+ core::rect<s32>(core::position2d<s32>(0,0),img_origsize),
+ NULL/*&AbsoluteClippingRect*/, colors, true);
+ }
+ else {
+ errorstream << "GUIFormSpecMenu::drawMenu() Draw images unable to load texture:" << std::endl;
+ errorstream << "\t" << spec.name << std::endl;
+ }
}
/*
@@ -843,6 +1823,9 @@ void GUIFormSpecMenu::drawMenu()
*/
for(u32 i=0; i<m_itemimages.size(); i++)
{
+ if (m_gamedef == 0)
+ break;
+
const ImageDrawSpec &spec = m_itemimages[i];
IItemDefManager *idef = m_gamedef->idef();
ItemStack item;
@@ -1025,24 +2008,77 @@ ItemStack GUIFormSpecMenu::verifySelectedItem()
return ItemStack();
}
-void GUIFormSpecMenu::acceptInput()
+void GUIFormSpecMenu::acceptInput(int eventtype)
{
if(m_text_dst)
{
std::map<std::string, std::string> fields;
- gui::IGUIElement *e;
+
for(u32 i=0; i<m_fields.size(); i++)
{
const FieldSpec &s = m_fields[i];
if(s.send)
{
- if(s.is_button)
+ if(s.ftype == f_Button)
{
fields[wide_to_narrow(s.fname.c_str())] = wide_to_narrow(s.flabel.c_str());
}
+ else if(s.ftype == f_ListBox) {
+ std::stringstream ss;
+ if (eventtype == gui::EGET_LISTBOX_CHANGED) {
+ ss << "CHG:";
+ }
+ else {
+ ss << "DCL:";
+ }
+ ss << (getListboxIndex(wide_to_narrow(s.fname.c_str()))+1);
+ fields[wide_to_narrow(s.fname.c_str())] = ss.str();
+ }
+ else if(s.ftype == f_DropDown) {
+ // no dynamic cast possible due to some distributions shipped
+ // without rtti support in irrlicht
+ IGUIElement * element = getElementFromId(s.fid);
+ gui::IGUIComboBox *e = NULL;
+ if ((element) && (element->getType() == gui::EGUIET_COMBO_BOX)) {
+ e = static_cast<gui::IGUIComboBox*>(element);
+ }
+ fields[wide_to_narrow(s.fname.c_str())] =
+ wide_to_narrow(e->getItem(e->getSelected()));
+ }
+ else if (s.ftype == f_TabHeader) {
+ // no dynamic cast possible due to some distributions shipped
+ // without rtti support in irrlicht
+ IGUIElement * element = getElementFromId(s.fid);
+ gui::IGUITabControl *e = NULL;
+ if ((element) && (element->getType() == gui::EGUIET_TAB_CONTROL)) {
+ e = static_cast<gui::IGUITabControl*>(element);
+ }
+
+ if (e != 0) {
+ std::stringstream ss;
+ ss << (e->getActiveTab() +1);
+ fields[wide_to_narrow(s.fname.c_str())] = ss.str();
+ }
+ }
+ else if (s.ftype == f_CheckBox) {
+ // no dynamic cast possible due to some distributions shipped
+ // without rtti support in irrlicht
+ IGUIElement * element = getElementFromId(s.fid);
+ gui::IGUICheckBox *e = NULL;
+ if ((element) && (element->getType() == gui::EGUIET_CHECK_BOX)) {
+ e = static_cast<gui::IGUICheckBox*>(element);
+ }
+
+ if (e != 0) {
+ if (e->isChecked())
+ fields[wide_to_narrow(s.fname.c_str())] = "true";
+ else
+ fields[wide_to_narrow(s.fname.c_str())] = "false";
+ }
+ }
else
{
- e = getElementFromId(s.fid);
+ IGUIElement* e = getElementFromId(s.fid);
if(e != NULL)
{
fields[wide_to_narrow(s.fname.c_str())] = wide_to_narrow(e->getText());
@@ -1050,6 +2086,7 @@ void GUIFormSpecMenu::acceptInput()
}
}
}
+
m_text_dst->gotText(fields);
}
}
@@ -1062,13 +2099,20 @@ bool GUIFormSpecMenu::OnEvent(const SEvent& event)
if (event.KeyInput.PressedDown && (kp == EscapeKey ||
kp == getKeySetting("keymap_inventory")))
{
- quitMenu();
+ if (m_allowclose)
+ quitMenu();
+ else
+ m_text_dst->gotText(narrow_to_wide("MenuQuit"));
return true;
}
if(event.KeyInput.Key==KEY_RETURN && event.KeyInput.PressedDown)
{
acceptInput();
- quitMenu();
+
+ if (m_allowclose)
+ quitMenu();
+ else
+ m_text_dst->gotText(narrow_to_wide("KeyEnter"));
return true;
}
}
@@ -1360,6 +2404,27 @@ bool GUIFormSpecMenu::OnEvent(const SEvent& event)
}
if(event.EventType==EET_GUI_EVENT)
{
+
+ if(event.GUIEvent.EventType==gui::EGET_TAB_CHANGED
+ && isVisible())
+ {
+ // find the element that was clicked
+ for(u32 i=0; i<m_fields.size(); i++)
+ {
+ FieldSpec &s = m_fields[i];
+ // if its a button, set the send field so
+ // lua knows which button was pressed
+ if ((s.ftype == f_TabHeader) && (s.fid == event.GUIEvent.Caller->getID()))
+ {
+ s.send = true;
+ acceptInput();
+ s.send = false;
+ // Restore focus to the full form
+ Environment->setFocus(this);
+ return true;
+ }
+ }
+ }
if(event.GUIEvent.EventType==gui::EGET_ELEMENT_FOCUS_LOST
&& isVisible())
{
@@ -1371,28 +2436,37 @@ bool GUIFormSpecMenu::OnEvent(const SEvent& event)
return true;
}
}
- if(event.GUIEvent.EventType==gui::EGET_BUTTON_CLICKED)
+ if((event.GUIEvent.EventType==gui::EGET_BUTTON_CLICKED) ||
+ (event.GUIEvent.EventType==gui::EGET_CHECKBOX_CHANGED))
{
- switch(event.GUIEvent.Caller->getID())
- {
- case 257:
+ unsigned int btn_id = event.GUIEvent.Caller->getID();
+
+ if (btn_id == 257) {
acceptInput();
- quitMenu();
+ if (m_allowclose)
+ quitMenu();
+ else
+ m_text_dst->gotText(narrow_to_wide("ExitButton"));
// quitMenu deallocates menu
return true;
}
+
// find the element that was clicked
for(u32 i=0; i<m_fields.size(); i++)
{
FieldSpec &s = m_fields[i];
// if its a button, set the send field so
// lua knows which button was pressed
- if (s.is_button && s.fid == event.GUIEvent.Caller->getID())
+ if (((s.ftype == f_Button) || (s.ftype == f_CheckBox)) &&
+ (s.fid == event.GUIEvent.Caller->getID()))
{
s.send = true;
acceptInput();
if(s.is_exit){
- quitMenu();
+ if (m_allowclose)
+ quitMenu();
+ else
+ m_text_dst->gotText(narrow_to_wide("ExitButton"));
return true;
}else{
s.send = false;
@@ -1408,13 +2482,103 @@ bool GUIFormSpecMenu::OnEvent(const SEvent& event)
if(event.GUIEvent.Caller->getID() > 257)
{
acceptInput();
- quitMenu();
+ if (m_allowclose)
+ quitMenu();
+ else
+ m_text_dst->gotText(narrow_to_wide("EditBoxEnter"));
// quitMenu deallocates menu
return true;
}
}
+
+ if((event.GUIEvent.EventType==gui::EGET_LISTBOX_SELECTED_AGAIN) ||
+ (event.GUIEvent.EventType==gui::EGET_LISTBOX_CHANGED))
+ {
+ int current_id = event.GUIEvent.Caller->getID();
+ if(current_id > 257)
+ {
+ // find the element that was clicked
+ for(u32 i=0; i<m_fields.size(); i++)
+ {
+ FieldSpec &s = m_fields[i];
+ // if its a button, set the send field so
+ // lua knows which button was pressed
+ if ((s.ftype == f_ListBox) && (s.fid == current_id))
+ {
+ s.send = true;
+ acceptInput(event.GUIEvent.EventType);
+ s.send=false;
+ // Restore focus to the full form
+ Environment->setFocus(this);
+ }
+ }
+ return true;
+ }
+ }
}
return Parent ? Parent->OnEvent(event) : false;
}
+irr::video::SColor GUIFormSpecMenu::getColor(std::string color,bool& valid_color) {
+
+ if (color == "YLW") {
+ valid_color = true;
+ return irr::video::SColor(255,255,255,0);
+ }
+
+ if (color == "GRN") {
+ valid_color = true;
+ return irr::video::SColor(255,34,249,34);
+ }
+
+ if (color == "LIM") {
+ valid_color = true;
+ return irr::video::SColor(255,50,205,50);
+ }
+
+ if (color == "RED") {
+ valid_color = true;
+ return irr::video::SColor(255,255,0,0);
+ }
+
+ if (color == "ORN") {
+ valid_color = true;
+ return irr::video::SColor(255,255,140,0);
+ }
+
+ if (color == "BLU") {
+ valid_color = true;
+ return irr::video::SColor(255,0,0,255);
+ }
+
+ if (color == "CYN") {
+ valid_color = true;
+ return irr::video::SColor(255,0,255,255);
+ }
+
+ if (color == "BLK") {
+ valid_color = true;
+ return irr::video::SColor(255,0,0,0);
+ }
+
+ if (color == "BRN") {
+ valid_color = true;
+ return irr::video::SColor(255,139,69,19);
+ }
+
+ if (color == "WHT") {
+ valid_color = true;
+ return irr::video::SColor(255,255,255,255);
+ }
+
+ if (color == "GRY") {
+ valid_color = true;
+ return irr::video::SColor(255,205,201,201);
+ }
+
+ valid_color = false;
+
+ return irr::video::SColor(0,0,0,0);
+}
+
diff --git a/src/guiFormSpecMenu.h b/src/guiFormSpecMenu.h
index ae985adde..f5a273668 100644
--- a/src/guiFormSpecMenu.h
+++ b/src/guiFormSpecMenu.h
@@ -21,6 +21,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#ifndef GUIINVENTORYMENU_HEADER
#define GUIINVENTORYMENU_HEADER
+#include <utility>
+
#include "irrlichttypes_extrabloated.h"
#include "inventory.h"
#include "inventorymanager.h"
@@ -29,6 +31,15 @@ with this program; if not, write to the Free Software Foundation, Inc.,
class IGameDef;
class InventoryManager;
+typedef enum {
+ f_Button,
+ f_ListBox,
+ f_TabHeader,
+ f_CheckBox,
+ f_DropDown,
+ f_Unknown
+} FormspecFieldType;
+
struct TextDest
{
virtual ~TextDest() {};
@@ -113,10 +124,19 @@ class GUIFormSpecMenu : public GUIModalMenu
pos(a_pos),
geom(a_geom)
{
+ scale = true;
+ }
+ ImageDrawSpec(const std::string &a_name,
+ v2s32 a_pos):
+ name(a_name),
+ pos(a_pos)
+ {
+ scale = false;
}
std::string name;
v2s32 pos;
v2s32 geom;
+ bool scale;
};
struct FieldSpec
@@ -131,7 +151,7 @@ class GUIFormSpecMenu : public GUIModalMenu
fid(id)
{
send = false;
- is_button = false;
+ ftype = f_Unknown;
is_exit = false;
tooltip="";
}
@@ -140,12 +160,24 @@ class GUIFormSpecMenu : public GUIModalMenu
std::wstring fdefault;
int fid;
bool send;
- bool is_button;
+ FormspecFieldType ftype;
bool is_exit;
core::rect<s32> rect;
std::string tooltip;
};
+ struct BoxDrawSpec {
+ BoxDrawSpec(v2s32 a_pos, v2s32 a_geom,irr::video::SColor a_color):
+ pos(a_pos),
+ geom(a_geom),
+ color(a_color)
+ {
+ }
+ v2s32 pos;
+ v2s32 geom;
+ irr::video::SColor color;
+ };
+
public:
GUIFormSpecMenu(irr::IrrlichtDevice* dev,
gui::IGUIElement* parent, s32 id,
@@ -153,6 +185,7 @@ public:
InventoryManager *invmgr,
IGameDef *gamedef
);
+
~GUIFormSpecMenu();
void setFormSpec(const std::string &formspec_string,
@@ -175,6 +208,20 @@ public:
m_text_dst = text_dst;
}
+ void allowClose(bool value)
+ {
+ m_allowclose = value;
+ }
+
+ void useGettext(bool value) {
+ m_use_gettext = true;
+ }
+
+ void lockSize(bool lock,v2u32 basescreensize=v2u32(0,0)) {
+ m_lock = lock;
+ m_lockscreensize = basescreensize;
+ }
+
void removeChildren();
/*
Remove and re-add (or reposition) stuff
@@ -188,18 +235,21 @@ public:
void updateSelectedItem();
ItemStack verifySelectedItem();
- void acceptInput();
+ void acceptInput(int evttype=-1);
bool OnEvent(const SEvent& event);
+ int getListboxIndex(std::string listboxname);
+
protected:
v2s32 getBasePos() const
{
- return padding + AbsoluteRect.UpperLeftCorner;
+ return padding + offset + AbsoluteRect.UpperLeftCorner;
}
v2s32 padding;
v2s32 spacing;
v2s32 imgsize;
+ v2s32 offset;
irr::IrrlichtDevice* m_device;
InventoryManager *m_invmgr;
@@ -214,7 +264,10 @@ protected:
std::vector<ImageDrawSpec> m_backgrounds;
std::vector<ImageDrawSpec> m_images;
std::vector<ImageDrawSpec> m_itemimages;
+ std::vector<BoxDrawSpec> m_boxes;
std::vector<FieldSpec> m_fields;
+ std::vector<std::pair<FieldSpec,gui::IGUIListBox*> > m_listboxes;
+ std::vector<std::pair<FieldSpec,gui::IGUICheckBox*> > m_checkboxes;
ItemSpec *m_selected_item;
u32 m_selected_amount;
@@ -228,6 +281,74 @@ protected:
v2s32 m_pointer;
gui::IGUIStaticText *m_tooltip_element;
+
+ bool m_allowclose;
+ bool m_use_gettext;
+ bool m_lock;
+ v2u32 m_lockscreensize;
+private:
+ typedef struct {
+ v2s32 size;
+ s32 helptext_h;
+ core::rect<s32> rect;
+ v2s32 basepos;
+ int bp_set;
+ v2u32 screensize;
+ std::map<std::wstring,int> listbox_selections;
+ } parserData;
+
+ std::vector<video::ITexture *> m_Textures;
+
+ void parseElement(parserData* data,std::string element);
+
+ void parseSize(parserData* data,std::string element);
+ void parseList(parserData* data,std::string element);
+ void parseCheckbox(parserData* data,std::string element);
+ void parseImage(parserData* data,std::string element);
+ void parseItemImage(parserData* data,std::string element);
+ void parseButton(parserData* data,std::string element,std::string typ);
+ void parseBackground(parserData* data,std::string element);
+ void parseTextList(parserData* data,std::string element);
+ void parseDropDown(parserData* data,std::string element);
+ void parsePwdField(parserData* data,std::string element);
+ void parseField(parserData* data,std::string element,std::string type);
+ void parseSimpleField(parserData* data,std::vector<std::string> &parts);
+ void parseTextArea(parserData* data,std::vector<std::string>& parts,std::string type);
+ void parseLabel(parserData* data,std::string element);
+ void parseVertLabel(parserData* data,std::string element);
+ void parseImageButton(parserData* data,std::string element,std::string type);
+ void parseItemImageButton(parserData* data,std::string element);
+ void parseTabHeader(parserData* data,std::string element);
+ void parseBox(parserData* data,std::string element);
+
+ irr::video::SColor getColor(std::string color,bool& valid_color);
+};
+
+class FormspecFormSource: public IFormSource
+{
+public:
+ FormspecFormSource(std::string formspec,FormspecFormSource** game_formspec)
+ {
+ m_formspec = formspec;
+ m_game_formspec = game_formspec;
+ }
+
+ ~FormspecFormSource()
+ {
+ *m_game_formspec = 0;
+ }
+
+ void setForm(std::string formspec) {
+ m_formspec = formspec;
+ }
+
+ std::string getForm()
+ {
+ return m_formspec;
+ }
+
+ std::string m_formspec;
+ FormspecFormSource** m_game_formspec;
};
#endif
diff --git a/src/guiLuaApi.cpp b/src/guiLuaApi.cpp
new file mode 100644
index 000000000..bc02c062d
--- /dev/null
+++ b/src/guiLuaApi.cpp
@@ -0,0 +1,1067 @@
+/*
+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.
+*/
+
+extern "C" {
+#include "lua.h"
+#include "lauxlib.h"
+#include "lualib.h"
+}
+#include "porting.h"
+#include "guiMainMenu.h"
+#include "subgame.h"
+#include "guiKeyChangeMenu.h"
+#include "guiFileSelectMenu.h"
+#include "main.h"
+#include "settings.h"
+#include "filesys.h"
+#include "convert_json.h"
+
+
+#include "IFileArchive.h"
+#include "IFileSystem.h"
+
+#include "guiLuaApi.h"
+#include "guiEngine.h"
+
+#define API_FCT(name) registerFunction(L,#name,l_##name,top)
+
+void guiLuaApi::initialize(lua_State* L,GUIEngine* engine)
+{
+ lua_pushlightuserdata(L, engine);
+ lua_setfield(L, LUA_REGISTRYINDEX, "engine");
+
+ lua_pushstring(L, DIR_DELIM);
+ lua_setglobal(L, "DIR_DELIM");
+
+ lua_newtable(L);
+ lua_setglobal(L, "gamedata");
+
+ lua_newtable(L);
+ lua_setglobal(L, "engine");
+
+ lua_getglobal(L, "engine");
+ int top = lua_gettop(L);
+
+ bool retval = true;
+
+ //add api functions
+ retval &= API_FCT(update_formspec);
+ retval &= API_FCT(set_clouds);
+ retval &= API_FCT(get_textlist_index);
+ retval &= API_FCT(get_worlds);
+ retval &= API_FCT(get_games);
+ retval &= API_FCT(start);
+ retval &= API_FCT(close);
+ retval &= API_FCT(get_favorites);
+ retval &= API_FCT(show_keys_menu);
+ retval &= API_FCT(setting_set);
+ retval &= API_FCT(setting_get);
+ retval &= API_FCT(setting_getbool);
+ retval &= API_FCT(setting_setbool);
+ retval &= API_FCT(create_world);
+ retval &= API_FCT(delete_world);
+ retval &= API_FCT(delete_favorite);
+ retval &= API_FCT(set_background);
+ retval &= API_FCT(set_topleft_text);
+ retval &= API_FCT(get_modpath);
+ retval &= API_FCT(get_gamepath);
+ retval &= API_FCT(get_dirlist);
+ retval &= API_FCT(create_dir);
+ retval &= API_FCT(delete_dir);
+ retval &= API_FCT(copy_dir);
+ retval &= API_FCT(extract_zip);
+ retval &= API_FCT(get_scriptdir);
+ retval &= API_FCT(show_file_open_dialog);
+ retval &= API_FCT(get_version);
+ retval &= API_FCT(download_file);
+ retval &= API_FCT(get_modstore_details);
+ retval &= API_FCT(get_modstore_list);
+
+ if (!retval) {
+ //TODO show error
+ }
+}
+
+/******************************************************************************/
+bool guiLuaApi::registerFunction( lua_State* L,
+ const char* name,
+ lua_CFunction fct,
+ int top
+ )
+{
+ lua_pushstring(L,name);
+ lua_pushcfunction(L,fct);
+ lua_settable(L, top);
+
+ return true;
+}
+
+/******************************************************************************/
+GUIEngine* guiLuaApi::get_engine(lua_State *L)
+{
+ // Get server from registry
+ lua_getfield(L, LUA_REGISTRYINDEX, "engine");
+ GUIEngine* sapi_ptr = (GUIEngine*) lua_touserdata(L, -1);
+ lua_pop(L, 1);
+ return sapi_ptr;
+}
+
+/******************************************************************************/
+std::string guiLuaApi::getTextData(lua_State *L, std::string name)
+{
+ lua_getglobal(L, "gamedata");
+
+ lua_getfield(L, -1, name.c_str());
+
+ if(lua_isnil(L, -1))
+ return "";
+
+ return luaL_checkstring(L, -1);
+}
+
+/******************************************************************************/
+int guiLuaApi::getIntegerData(lua_State *L, std::string name,bool& valid)
+{
+ lua_getglobal(L, "gamedata");
+
+ lua_getfield(L, -1, name.c_str());
+
+ if(lua_isnil(L, -1)) {
+ valid = false;
+ return -1;
+ }
+
+ valid = true;
+ return luaL_checkinteger(L, -1);
+}
+
+/******************************************************************************/
+int guiLuaApi::getBoolData(lua_State *L, std::string name,bool& valid)
+{
+ lua_getglobal(L, "gamedata");
+
+ lua_getfield(L, -1, name.c_str());
+
+ if(lua_isnil(L, -1)) {
+ valid = false;
+ return false;
+ }
+
+ valid = true;
+ return lua_toboolean(L, -1);
+}
+
+/******************************************************************************/
+int guiLuaApi::l_update_formspec(lua_State *L)
+{
+ GUIEngine* engine = get_engine(L);
+ assert(engine != 0);
+
+ if (engine->m_startgame)
+ return 0;
+
+ //read formspec
+ std::string formspec(luaL_checkstring(L, 1));
+
+ if (engine->m_formspecgui != 0) {
+ engine->m_formspecgui->setForm(formspec);
+ }
+
+ return 0;
+}
+
+/******************************************************************************/
+int guiLuaApi::l_start(lua_State *L)
+{
+ GUIEngine* engine = get_engine(L);
+ assert(engine != 0);
+
+ //update c++ gamedata from lua table
+
+ bool valid = false;
+
+
+ engine->m_data->selected_world = getIntegerData(L, "selected_world",valid) -1;
+ engine->m_data->simple_singleplayer_mode = getBoolData(L,"singleplayer",valid);
+ engine->m_data->name = getTextData(L,"playername");
+ engine->m_data->password = getTextData(L,"password");
+ engine->m_data->address = getTextData(L,"address");
+ engine->m_data->port = getTextData(L,"port");
+
+ //close menu next time
+ engine->m_startgame = true;
+ return 0;
+}
+
+/******************************************************************************/
+int guiLuaApi::l_close(lua_State *L)
+{
+ GUIEngine* engine = get_engine(L);
+ assert(engine != 0);
+
+ engine->m_data->kill = true;
+
+ //close menu next time
+ engine->m_startgame = true;
+ engine->m_menu->quitMenu();
+ return 0;
+}
+
+/******************************************************************************/
+int guiLuaApi::l_set_background(lua_State *L)
+{
+ GUIEngine* engine = get_engine(L);
+ assert(engine != 0);
+
+ std::string backgroundlevel(luaL_checkstring(L, 1));
+ std::string texturename(luaL_checkstring(L, 2));
+
+ bool retval = false;
+
+ if (backgroundlevel == "background") {
+ retval |= engine->setTexture(TEX_LAYER_BACKGROUND,texturename);
+ }
+
+ if (backgroundlevel == "overlay") {
+ retval |= engine->setTexture(TEX_LAYER_OVERLAY,texturename);
+ }
+
+ if (backgroundlevel == "header") {
+ retval |= engine->setTexture(TEX_LAYER_HEADER,texturename);
+ }
+
+ if (backgroundlevel == "footer") {
+ retval |= engine->setTexture(TEX_LAYER_FOOTER,texturename);
+ }
+
+ lua_pushboolean(L,retval);
+ return 1;
+}
+
+/******************************************************************************/
+int guiLuaApi::l_set_clouds(lua_State *L)
+{
+ GUIEngine* engine = get_engine(L);
+ assert(engine != 0);
+
+ bool value = lua_toboolean(L,1);
+
+ engine->m_clouds_enabled = value;
+
+ return 0;
+}
+
+/******************************************************************************/
+int guiLuaApi::l_get_textlist_index(lua_State *L)
+{
+ GUIEngine* engine = get_engine(L);
+ assert(engine != 0);
+
+ std::string listboxname(luaL_checkstring(L, 1));
+
+ int selection = engine->m_menu->getListboxIndex(listboxname);
+
+ if (selection >= 0)
+ selection++;
+
+ lua_pushinteger(L, selection);
+ return 1;
+}
+
+/******************************************************************************/
+int guiLuaApi::l_get_worlds(lua_State *L)
+{
+ GUIEngine* engine = get_engine(L);
+ assert(engine != 0);
+
+ std::vector<WorldSpec> worlds = getAvailableWorlds();
+
+ lua_newtable(L);
+ int top = lua_gettop(L);
+ unsigned int index = 1;
+
+ for (unsigned int i = 0; i < worlds.size(); i++)
+ {
+ lua_pushnumber(L,index);
+
+ lua_newtable(L);
+ int top_lvl2 = lua_gettop(L);
+
+ lua_pushstring(L,"path");
+ lua_pushstring(L,worlds[i].path.c_str());
+ lua_settable(L, top_lvl2);
+
+ lua_pushstring(L,"name");
+ lua_pushstring(L,worlds[i].name.c_str());
+ lua_settable(L, top_lvl2);
+
+ lua_pushstring(L,"gameid");
+ lua_pushstring(L,worlds[i].gameid.c_str());
+ lua_settable(L, top_lvl2);
+
+ lua_settable(L, top);
+ index++;
+ }
+ return 1;
+}
+
+/******************************************************************************/
+int guiLuaApi::l_get_games(lua_State *L)
+{
+ GUIEngine* engine = get_engine(L);
+ assert(engine != 0);
+
+ std::vector<SubgameSpec> games = getAvailableGames();
+
+ lua_newtable(L);
+ int top = lua_gettop(L);
+ unsigned int index = 1;
+
+ for (unsigned int i = 0; i < games.size(); i++)
+ {
+ lua_pushnumber(L,index);
+ lua_newtable(L);
+ int top_lvl2 = lua_gettop(L);
+
+ lua_pushstring(L,"id");
+ lua_pushstring(L,games[i].id.c_str());
+ lua_settable(L, top_lvl2);
+
+ lua_pushstring(L,"path");
+ lua_pushstring(L,games[i].path.c_str());
+ lua_settable(L, top_lvl2);
+
+ lua_pushstring(L,"gamemods_path");
+ lua_pushstring(L,games[i].gamemods_path.c_str());
+ lua_settable(L, top_lvl2);
+
+ lua_pushstring(L,"name");
+ lua_pushstring(L,games[i].name.c_str());
+ lua_settable(L, top_lvl2);
+
+ lua_pushstring(L,"menuicon_path");
+ lua_pushstring(L,games[i].menuicon_path.c_str());
+ lua_settable(L, top_lvl2);
+
+ lua_pushstring(L,"addon_mods_paths");
+ lua_newtable(L);
+ int table2 = lua_gettop(L);
+ int internal_index=1;
+ for (std::set<std::string>::iterator iter = games[i].addon_mods_paths.begin();
+ iter != games[i].addon_mods_paths.end(); iter++) {
+ lua_pushnumber(L,internal_index);
+ lua_pushstring(L,(*iter).c_str());
+ lua_settable(L, table2);
+ internal_index++;
+ }
+ lua_settable(L, top_lvl2);
+ lua_settable(L, top);
+ index++;
+ }
+ return 1;
+}
+/******************************************************************************/
+int guiLuaApi::l_get_modstore_details(lua_State *L)
+{
+ const char *modid = luaL_checkstring(L, 1);
+
+ if (modid != 0) {
+ Json::Value details;
+ std::string url = "";
+ try{
+ url = g_settings->get("modstore_details_url");
+ }
+ catch(SettingNotFoundException &e) {
+ lua_pushnil(L);
+ return 1;
+ }
+
+ size_t idpos = url.find("*");
+ url.erase(idpos,1);
+ url.insert(idpos,modid);
+
+ details = getModstoreUrl(url);
+
+ ModStoreModDetails current_mod = readModStoreModDetails(details);
+
+ if ( current_mod.valid) {
+ lua_newtable(L);
+ int top = lua_gettop(L);
+
+ lua_pushstring(L,"id");
+ lua_pushnumber(L,current_mod.id);
+ lua_settable(L, top);
+
+ lua_pushstring(L,"title");
+ lua_pushstring(L,current_mod.title.c_str());
+ lua_settable(L, top);
+
+ lua_pushstring(L,"basename");
+ lua_pushstring(L,current_mod.basename.c_str());
+ lua_settable(L, top);
+
+ lua_pushstring(L,"description");
+ lua_pushstring(L,current_mod.description.c_str());
+ lua_settable(L, top);
+
+ lua_pushstring(L,"author");
+ lua_pushstring(L,current_mod.author.username.c_str());
+ lua_settable(L, top);
+
+ lua_pushstring(L,"download_url");
+ lua_pushstring(L,current_mod.versions[0].file.c_str());
+ lua_settable(L, top);
+
+ lua_pushstring(L,"license");
+ lua_pushstring(L,current_mod.license.shortinfo.c_str());
+ lua_settable(L, top);
+
+ lua_pushstring(L,"rating");
+ lua_pushnumber(L,current_mod.rating);
+ lua_settable(L, top);
+
+ //TODO depends
+
+ //TODO softdepends
+ return 1;
+ }
+ }
+ return 0;
+}
+
+/******************************************************************************/
+int guiLuaApi::l_get_modstore_list(lua_State *L)
+{
+ GUIEngine* engine = get_engine(L);
+ assert(engine != 0);
+
+ std::string listtype = "local";
+
+ if (!lua_isnone(L,1)) {
+ listtype = luaL_checkstring(L,1);
+ }
+ Json::Value mods;
+ std::string url = "";
+ try{
+ url = g_settings->get("modstore_listmods_url");
+ }
+ catch(SettingNotFoundException &e) {
+ lua_pushnil(L);
+ return 1;
+ }
+
+ mods = getModstoreUrl(url);
+
+ std::vector<ModStoreMod> moddata = readModStoreList(mods);
+
+ lua_newtable(L);
+ int top = lua_gettop(L);
+ unsigned int index = 1;
+
+ for (unsigned int i = 0; i < moddata.size(); i++)
+ {
+ if (moddata[i].valid) {
+ lua_pushnumber(L,index);
+ lua_newtable(L);
+
+ int top_lvl2 = lua_gettop(L);
+
+ lua_pushstring(L,"id");
+ lua_pushnumber(L,moddata[i].id);
+ lua_settable(L, top_lvl2);
+
+ lua_pushstring(L,"title");
+ lua_pushstring(L,moddata[i].title.c_str());
+ lua_settable(L, top_lvl2);
+
+ lua_pushstring(L,"basename");
+ lua_pushstring(L,moddata[i].basename.c_str());
+ lua_settable(L, top_lvl2);
+
+ lua_settable(L, top);
+ index++;
+ }
+ }
+ return 1;
+}
+
+/******************************************************************************/
+int guiLuaApi::l_get_favorites(lua_State *L)
+{
+ GUIEngine* engine = get_engine(L);
+ assert(engine != 0);
+
+ std::string listtype = "local";
+
+ if (!lua_isnone(L,1)) {
+ listtype = luaL_checkstring(L,1);
+ }
+
+ std::vector<ServerListSpec> servers;
+#if USE_CURL
+ if(listtype == "online") {
+ servers = ServerList::getOnline();
+ } else {
+ servers = ServerList::getLocal();
+ }
+#else
+ servers = ServerList::getLocal();
+#endif
+
+ lua_newtable(L);
+ int top = lua_gettop(L);
+ unsigned int index = 1;
+
+ for (unsigned int i = 0; i < servers.size(); i++)
+ {
+ lua_pushnumber(L,index);
+
+ lua_newtable(L);
+ int top_lvl2 = lua_gettop(L);
+
+ if (servers[i]["clients"].asString().size()) {
+
+ const char* clients_raw = servers[i]["clients"].asString().c_str();
+ char* endptr = 0;
+ int numbervalue = strtol(clients_raw,&endptr,10);
+
+ if ((*clients_raw != 0) && (*endptr == 0)) {
+ lua_pushstring(L,"clients");
+ lua_pushnumber(L,numbervalue);
+ lua_settable(L, top_lvl2);
+ }
+ }
+
+ if (servers[i]["clients_max"].asString().size()) {
+
+ const char* clients_max_raw = servers[i]["clients"].asString().c_str();
+ char* endptr = 0;
+ int numbervalue = strtol(clients_max_raw,&endptr,10);
+
+ if ((*clients_max_raw != 0) && (*endptr == 0)) {
+ lua_pushstring(L,"clients_max");
+ lua_pushnumber(L,numbervalue);
+ lua_settable(L, top_lvl2);
+ }
+ }
+
+ if (servers[i]["version"].asString().size()) {
+ lua_pushstring(L,"version");
+ lua_pushstring(L,servers[i]["version"].asString().c_str());
+ lua_settable(L, top_lvl2);
+ }
+
+ if (servers[i]["password"].asString().size()) {
+ lua_pushstring(L,"password");
+ lua_pushboolean(L,true);
+ lua_settable(L, top_lvl2);
+ }
+
+ if (servers[i]["creative"].asString().size()) {
+ lua_pushstring(L,"creative");
+ lua_pushboolean(L,true);
+ lua_settable(L, top_lvl2);
+ }
+
+ if (servers[i]["damage"].asString().size()) {
+ lua_pushstring(L,"damage");
+ lua_pushboolean(L,true);
+ lua_settable(L, top_lvl2);
+ }
+
+ if (servers[i]["pvp"].asString().size()) {
+ lua_pushstring(L,"pvp");
+ lua_pushboolean(L,true);
+ lua_settable(L, top_lvl2);
+ }
+
+ if (servers[i]["description"].asString().size()) {
+ lua_pushstring(L,"description");
+ lua_pushstring(L,servers[i]["description"].asString().c_str());
+ lua_settable(L, top_lvl2);
+ }
+
+ if (servers[i]["name"].asString().size()) {
+ lua_pushstring(L,"name");
+ lua_pushstring(L,servers[i]["name"].asString().c_str());
+ lua_settable(L, top_lvl2);
+ }
+
+ if (servers[i]["address"].asString().size()) {
+ lua_pushstring(L,"address");
+ lua_pushstring(L,servers[i]["address"].asString().c_str());
+ lua_settable(L, top_lvl2);
+ }
+
+ if (servers[i]["port"].asString().size()) {
+ lua_pushstring(L,"port");
+ lua_pushstring(L,servers[i]["port"].asString().c_str());
+ lua_settable(L, top_lvl2);
+ }
+
+ lua_settable(L, top);
+ index++;
+ }
+ return 1;
+}
+
+/******************************************************************************/
+int guiLuaApi::l_delete_favorite(lua_State *L)
+{
+ GUIEngine* engine = get_engine(L);
+ assert(engine != 0);
+
+ std::vector<ServerListSpec> servers;
+
+ std::string listtype = "local";
+
+ if (!lua_isnone(L,2)) {
+ listtype = luaL_checkstring(L,2);
+ }
+
+ if ((listtype != "local") &&
+ (listtype != "online"))
+ return 0;
+
+#if USE_CURL
+ if(listtype == "online") {
+ servers = ServerList::getOnline();
+ } else {
+ servers = ServerList::getLocal();
+ }
+#else
+ servers = ServerList::getLocal();
+#endif
+
+ int fav_idx = luaL_checkinteger(L,1) -1;
+
+ if ((fav_idx >= 0) &&
+ (fav_idx < (int) servers.size())) {
+
+ ServerList::deleteEntry(servers[fav_idx]);
+ }
+
+ return 0;
+}
+
+/******************************************************************************/
+int guiLuaApi::l_show_keys_menu(lua_State *L)
+{
+ GUIEngine* engine = get_engine(L);
+ assert(engine != 0);
+
+ GUIKeyChangeMenu *kmenu
+ = new GUIKeyChangeMenu( engine->m_device->getGUIEnvironment(),
+ engine->m_parent,
+ -1,
+ engine->m_menumanager);
+ kmenu->drop();
+ return 0;
+}
+
+/******************************************************************************/
+int guiLuaApi::l_setting_set(lua_State *L)
+{
+ const char *name = luaL_checkstring(L, 1);
+ const char *value = luaL_checkstring(L, 2);
+ g_settings->set(name, value);
+ return 0;
+}
+
+/******************************************************************************/
+int guiLuaApi::l_setting_get(lua_State *L)
+{
+ const char *name = luaL_checkstring(L, 1);
+ try{
+ std::string value = g_settings->get(name);
+ lua_pushstring(L, value.c_str());
+ } catch(SettingNotFoundException &e){
+ lua_pushnil(L);
+ }
+ return 1;
+}
+
+/******************************************************************************/
+int guiLuaApi::l_setting_getbool(lua_State *L)
+{
+ const char *name = luaL_checkstring(L, 1);
+ try{
+ bool value = g_settings->getBool(name);
+ lua_pushboolean(L, value);
+ } catch(SettingNotFoundException &e){
+ lua_pushnil(L);
+ }
+ return 1;
+}
+
+/******************************************************************************/
+int guiLuaApi::l_setting_setbool(lua_State *L)
+{
+ const char *name = luaL_checkstring(L, 1);
+ bool value = lua_toboolean(L,2);
+
+ g_settings->setBool(name,value);
+
+ return 0;
+}
+
+/******************************************************************************/
+int guiLuaApi::l_create_world(lua_State *L)
+{
+ GUIEngine* engine = get_engine(L);
+ assert(engine != 0);
+
+ const char *name = luaL_checkstring(L, 1);
+ int gameidx = luaL_checkinteger(L,2) -1;
+
+ std::string path = porting::path_user + DIR_DELIM
+ "worlds" + DIR_DELIM
+ + name;
+
+ std::vector<SubgameSpec> games = getAvailableGames();
+
+ if ((gameidx >= 0) &&
+ (gameidx < (int) games.size())) {
+
+ // Create world if it doesn't exist
+ if(!initializeWorld(path, games[gameidx].id)){
+ lua_pushstring(L, "Failed to initialize world");
+
+ }
+ else {
+ lua_pushnil(L);
+ }
+ }
+ else {
+ lua_pushstring(L, "Invalid game index");
+ }
+ return 1;
+}
+
+/******************************************************************************/
+int guiLuaApi::l_delete_world(lua_State *L)
+{
+ GUIEngine* engine = get_engine(L);
+ assert(engine != 0);
+
+ int worldidx = luaL_checkinteger(L,1) -1;
+
+ std::vector<WorldSpec> worlds = getAvailableWorlds();
+
+ if ((worldidx >= 0) &&
+ (worldidx < (int) worlds.size())) {
+
+ WorldSpec spec = worlds[worldidx];
+
+ std::vector<std::string> paths;
+ paths.push_back(spec.path);
+ fs::GetRecursiveSubPaths(spec.path, paths);
+
+ // Delete files
+ if (!fs::DeletePaths(paths)) {
+ lua_pushstring(L, "Failed to delete world");
+ }
+ else {
+ lua_pushnil(L);
+ }
+ }
+ else {
+ lua_pushstring(L, "Invalid world index");
+ }
+ return 1;
+}
+
+/******************************************************************************/
+int guiLuaApi::l_set_topleft_text(lua_State *L)
+{
+ GUIEngine* engine = get_engine(L);
+ assert(engine != 0);
+
+ std::string text = "";
+
+ if (!lua_isnone(L,1) && !lua_isnil(L,1))
+ text = luaL_checkstring(L, 1);
+
+ engine->setTopleftText(text);
+ return 0;
+}
+
+/******************************************************************************/
+int guiLuaApi::l_get_modpath(lua_State *L)
+{
+ //TODO this path may be controversial!
+ std::string modpath
+ = fs::RemoveRelativePathComponents(porting::path_share + DIR_DELIM + "mods" + DIR_DELIM);
+ lua_pushstring(L, modpath.c_str());
+ return 1;
+}
+
+/******************************************************************************/
+int guiLuaApi::l_get_gamepath(lua_State *L)
+{
+ std::string gamepath
+ = fs::RemoveRelativePathComponents(porting::path_share + DIR_DELIM + "games" + DIR_DELIM);
+ lua_pushstring(L, gamepath.c_str());
+ return 1;
+}
+
+/******************************************************************************/
+int guiLuaApi::l_get_dirlist(lua_State *L) {
+ const char *path = luaL_checkstring(L, 1);
+ bool dironly = lua_toboolean(L, 2);
+
+ std::vector<fs::DirListNode> dirlist = fs::GetDirListing(path);
+
+ unsigned int index = 1;
+ lua_newtable(L);
+ int table = lua_gettop(L);
+
+ for (unsigned int i=0;i< dirlist.size(); i++) {
+ if ((dirlist[i].dir) || (dironly == false)) {
+ lua_pushnumber(L,index);
+ lua_pushstring(L,dirlist[i].name.c_str());
+ lua_settable(L, table);
+ index++;
+ }
+ }
+
+ return 1;
+}
+
+/******************************************************************************/
+int guiLuaApi::l_create_dir(lua_State *L) {
+ const char *path = luaL_checkstring(L, 1);
+
+ if (guiLuaApi::isMinetestPath(path)) {
+ lua_pushboolean(L,fs::CreateAllDirs(path));
+ return 1;
+ }
+ lua_pushboolean(L,false);
+ return 1;
+}
+
+/******************************************************************************/
+int guiLuaApi::l_delete_dir(lua_State *L) {
+ const char *path = luaL_checkstring(L, 1);
+
+ std::string absolute_path = fs::RemoveRelativePathComponents(path);
+
+ if (guiLuaApi::isMinetestPath(absolute_path)) {
+ lua_pushboolean(L,fs::RecursiveDelete(absolute_path));
+ return 1;
+ }
+ lua_pushboolean(L,false);
+ return 1;
+}
+
+/******************************************************************************/
+int guiLuaApi::l_copy_dir(lua_State *L) {
+ const char *source = luaL_checkstring(L, 1);
+ const char *destination = luaL_checkstring(L, 2);
+
+ bool keep_source = true;
+
+ if ((!lua_isnone(L,3)) &&
+ (!lua_isnil(L,3))) {
+ keep_source = lua_toboolean(L,3);
+ }
+
+ std::string absolute_destination = fs::RemoveRelativePathComponents(destination);
+ std::string absolute_source = fs::RemoveRelativePathComponents(source);
+
+ if ((guiLuaApi::isMinetestPath(absolute_source)) &&
+ (guiLuaApi::isMinetestPath(absolute_destination))) {
+ bool retval = fs::CopyDir(absolute_source,absolute_destination);
+
+ if (retval && (!keep_source)) {
+
+ retval &= fs::RecursiveDelete(absolute_source);
+ }
+ lua_pushboolean(L,retval);
+ return 1;
+ }
+ lua_pushboolean(L,false);
+ return 1;
+}
+
+/******************************************************************************/
+int guiLuaApi::l_extract_zip(lua_State *L) {
+
+ GUIEngine* engine = get_engine(L);
+ assert(engine != 0);
+
+ const char *zipfile = luaL_checkstring(L, 1);
+ const char *destination = luaL_checkstring(L, 2);
+
+ std::string absolute_destination = fs::RemoveRelativePathComponents(destination);
+
+ if (guiLuaApi::isMinetestPath(absolute_destination)) {
+ fs::CreateAllDirs(absolute_destination);
+
+ io::IFileSystem* fs = engine->m_device->getFileSystem();
+
+ fs->addFileArchive(zipfile,true,false,io::EFAT_ZIP);
+
+ assert(fs->getFileArchiveCount() > 0);
+
+ /**********************************************************************/
+ /* WARNING this is not threadsafe!! */
+ /**********************************************************************/
+ io::IFileArchive* opened_zip =
+ fs->getFileArchive(fs->getFileArchiveCount()-1);
+
+ const io::IFileList* files_in_zip = opened_zip->getFileList();
+
+ unsigned int number_of_files = files_in_zip->getFileCount();
+
+ for (unsigned int i=0; i < number_of_files; i++) {
+ std::string fullpath = destination;
+ fullpath += DIR_DELIM;
+ fullpath += files_in_zip->getFullFileName(i).c_str();
+
+ if (files_in_zip->isDirectory(i)) {
+ if (! fs::CreateAllDirs(fullpath) ) {
+ fs->removeFileArchive(fs->getFileArchiveCount()-1);
+ lua_pushboolean(L,false);
+ return 1;
+ }
+ }
+ else {
+ io::IReadFile* toread = opened_zip->createAndOpenFile(i);
+
+ FILE *targetfile = fopen(fullpath.c_str(),"wb");
+
+ if (targetfile == NULL) {
+ fs->removeFileArchive(fs->getFileArchiveCount()-1);
+ lua_pushboolean(L,false);
+ return 1;
+ }
+
+ char read_buffer[1024];
+ unsigned int total_read = 0;
+
+ while (total_read < toread->getSize()) {
+
+ unsigned int bytes_read =
+ toread->read(read_buffer,sizeof(read_buffer));
+ unsigned int bytes_written;
+ if ((bytes_read < 0 ) ||
+ (bytes_written = fwrite(read_buffer, 1, bytes_read, targetfile) != bytes_read))
+ {
+ fclose(targetfile);
+ fs->removeFileArchive(fs->getFileArchiveCount()-1);
+ lua_pushboolean(L,false);
+ return 1;
+ }
+ total_read += bytes_read;
+ }
+
+ fclose(targetfile);
+ }
+
+ }
+
+ fs->removeFileArchive(fs->getFileArchiveCount()-1);
+ lua_pushboolean(L,true);
+ return 1;
+ }
+
+ lua_pushboolean(L,false);
+ return 1;
+}
+
+/******************************************************************************/
+int guiLuaApi::l_get_scriptdir(lua_State *L) {
+ GUIEngine* engine = get_engine(L);
+ assert(engine != 0);
+
+ lua_pushstring(L,engine->getScriptDir().c_str());
+ return 1;
+}
+
+/******************************************************************************/
+bool guiLuaApi::isMinetestPath(std::string path) {
+
+
+ if (fs::PathStartsWith(path,fs::TempPath()))
+ return true;
+
+ /* games */
+ if (fs::PathStartsWith(path,fs::RemoveRelativePathComponents(porting::path_share + DIR_DELIM + "games")))
+ return true;
+
+ /* mods */
+ if (fs::PathStartsWith(path,fs::RemoveRelativePathComponents(porting::path_share + DIR_DELIM + "mods")))
+ return true;
+
+ /* worlds */
+ if (fs::PathStartsWith(path,fs::RemoveRelativePathComponents(porting::path_user + DIR_DELIM + "worlds")))
+ return true;
+
+
+ return false;
+}
+
+/******************************************************************************/
+int guiLuaApi::l_show_file_open_dialog(lua_State *L) {
+
+ GUIEngine* engine = get_engine(L);
+ assert(engine != 0);
+
+ const char *formname= luaL_checkstring(L, 1);
+ const char *title = luaL_checkstring(L, 2);
+
+ GUIFileSelectMenu* fileOpenMenu =
+ new GUIFileSelectMenu(engine->m_device->getGUIEnvironment(),
+ engine->m_parent,
+ -1,
+ engine->m_menumanager,
+ title,
+ formname);
+ fileOpenMenu->setTextDest(engine->m_buttonhandler);
+ fileOpenMenu->drop();
+ return 0;
+}
+
+/******************************************************************************/
+int guiLuaApi::l_get_version(lua_State *L) {
+ lua_pushstring(L,VERSION_STRING);
+ return 1;
+}
+
+/******************************************************************************/
+int guiLuaApi::l_download_file(lua_State *L) {
+ GUIEngine* engine = get_engine(L);
+ assert(engine != 0);
+
+ const char *url = luaL_checkstring(L, 1);
+ const char *target = luaL_checkstring(L, 2);
+
+ //check path
+ std::string absolute_destination = fs::RemoveRelativePathComponents(target);
+
+ if (guiLuaApi::isMinetestPath(absolute_destination)) {
+ if (engine->downloadFile(url,absolute_destination)) {
+ lua_pushboolean(L,true);
+ return 1;
+ }
+ }
+ lua_pushboolean(L,false);
+ return 1;
+}
diff --git a/src/guiLuaApi.h b/src/guiLuaApi.h
new file mode 100644
index 000000000..e0157f4a0
--- /dev/null
+++ b/src/guiLuaApi.h
@@ -0,0 +1,183 @@
+/*
+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.
+*/
+
+#ifndef GUILUAAPI_H_
+#define GUILUAAPI_H_
+
+/******************************************************************************/
+/* Includes */
+/******************************************************************************/
+#include "serverlist.h"
+
+/******************************************************************************/
+/* Typedefs and macros */
+/******************************************************************************/
+typedef int (*lua_CFunction) (lua_State *L);
+
+/******************************************************************************/
+/* forward declarations */
+/******************************************************************************/
+class GUIEngine;
+
+
+/******************************************************************************/
+/* declarations */
+/******************************************************************************/
+
+/** Implementation of lua api support for mainmenu */
+class guiLuaApi {
+
+public:
+ /**
+ * initialize given Lua stack
+ * @param L lua stack to initialize
+ * @param engine pointer to GUIEngine element to use as reference
+ */
+ static void initialize(lua_State* L,GUIEngine* engine);
+
+ /** default destructor */
+ virtual ~guiLuaApi() {}
+
+private:
+ /**
+ * read a text variable from gamedata table within lua stack
+ * @param L stack to read variable from
+ * @param name name of variable to read
+ * @return string value of requested variable
+ */
+ static std::string getTextData(lua_State *L, std::string name);
+
+ /**
+ * read a integer variable from gamedata table within lua stack
+ * @param L stack to read variable from
+ * @param name name of variable to read
+ * @return integer value of requested variable
+ */
+ static int getIntegerData(lua_State *L, std::string name,bool& valid);
+
+ /**
+ * read a bool variable from gamedata table within lua stack
+ * @param L stack to read variable from
+ * @param name name of variable to read
+ * @return bool value of requested variable
+ */
+ static int getBoolData(lua_State *L, std::string name,bool& valid);
+
+ /**
+ * get the corresponding engine pointer from a lua stack
+ * @param L stack to read pointer from
+ * @return pointer to GUIEngine
+ */
+ static GUIEngine* get_engine(lua_State *L);
+
+
+ /**
+ * register a static member function as lua api call at current position of stack
+ * @param L stack to registe fct to
+ * @param name of function within lua
+ * @param fct C-Function to call on lua call of function
+ * @param top current top of stack
+ */
+ static bool registerFunction( lua_State* L,
+ const char* name,
+ lua_CFunction fct,
+ int top
+ );
+
+ /**
+ * check if a path is within some of minetests folders
+ * @param path path to check
+ * @return true/false
+ */
+ static bool isMinetestPath(std::string path);
+
+ //api calls
+
+ static int l_start(lua_State *L);
+
+ static int l_close(lua_State *L);
+
+ static int l_create_world(lua_State *L);
+
+ static int l_delete_world(lua_State *L);
+
+ static int l_get_worlds(lua_State *L);
+
+ static int l_get_games(lua_State *L);
+
+ static int l_get_favorites(lua_State *L);
+
+ static int l_delete_favorite(lua_State *L);
+
+ static int l_get_version(lua_State *L);
+
+ //gui
+
+ static int l_show_keys_menu(lua_State *L);
+
+ static int l_show_file_open_dialog(lua_State *L);
+
+ static int l_set_topleft_text(lua_State *L);
+
+ static int l_set_clouds(lua_State *L);
+
+ static int l_get_textlist_index(lua_State *L);
+
+ static int l_set_background(lua_State *L);
+
+ static int l_update_formspec(lua_State *L);
+
+ //settings
+
+ static int l_setting_set(lua_State *L);
+
+ static int l_setting_get(lua_State *L);
+
+ static int l_setting_getbool(lua_State *L);
+
+ static int l_setting_setbool(lua_State *L);
+
+ //filesystem
+
+ static int l_get_scriptdir(lua_State *L);
+
+ static int l_get_modpath(lua_State *L);
+
+ static int l_get_gamepath(lua_State *L);
+
+ static int l_get_dirlist(lua_State *L);
+
+ static int l_create_dir(lua_State *L);
+
+ static int l_delete_dir(lua_State *L);
+
+ static int l_copy_dir(lua_State *L);
+
+ static int l_extract_zip(lua_State *L);
+
+ static int l_get_modstore_details(lua_State *L);
+
+ static int l_get_modstore_list(lua_State *L);
+
+ static int l_download_file(lua_State *L);
+
+
+};
+
+#endif
diff --git a/src/guiMainMenu.cpp b/src/guiMainMenu.cpp
deleted file mode 100644
index 223bba93c..000000000
--- a/src/guiMainMenu.cpp
+++ /dev/null
@@ -1,1521 +0,0 @@
-/*
-Minetest
-Copyright (C) 2010-2013 celeron55, Perttu Ahola <celeron55@gmail.com>
-
-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.
-*/
-
-#include "guiMainMenu.h"
-#include "guiKeyChangeMenu.h"
-#include "guiCreateWorld.h"
-#include "guiConfigureWorld.h"
-#include "guiMessageMenu.h"
-#include "guiConfirmMenu.h"
-#include "debug.h"
-#include "serialization.h"
-#include <string>
-#include <IGUICheckBox.h>
-#include <IGUIEditBox.h>
-#include <IGUIButton.h>
-#include <IGUIStaticText.h>
-#include <IGUIFont.h>
-#include <IGUIListBox.h>
-#include <IGUITabControl.h>
-#include <IGUIImage.h>
-// For IGameCallback
-#include "guiPauseMenu.h"
-#include "gettext.h"
-#include "tile.h" // getTexturePath
-#include "filesys.h"
-#include "util/string.h"
-#include "subgame.h"
-
-#define ARRAYLEN(x) (sizeof(x) / sizeof((x)[0]))
-#define LSTRING(x) LSTRING_(x)
-#define LSTRING_(x) L##x
-
-const wchar_t *contrib_core_strs[] = {
- L"Perttu Ahola (celeron55) <celeron55@gmail.com>",
- L"Ryan Kwolek (kwolekr) <kwolekr@minetest.net>",
- L"PilzAdam <pilzadam@minetest.net>",
- L"Ilya Zhuravlev (thexyz) <xyz@minetest.net>",
- L"Lisa Milne (darkrose) <lisa@ltmnet.com>",
- L"Maciej Kasatkin (RealBadAngel) <mk@realbadangel.pl>",
- L"proller <proler@gmail.com>",
- L"sfan5 <sfan5@live.de>",
- L"kahrl <kahrl@gmx.net>"
-};
-
-const wchar_t *contrib_active_strs[] = {
- L"sapier <sapier@gmx.net>",
- L"Vanessa Ezekowitz (VanessaE) <vanessaezekowitz@gmail.com>",
- L"Jurgen Doser (doserj) <jurgen.doser@gmail.com>",
- L"Jeija <jeija@mesecons.net>",
- L"MirceaKitsune <mirceakitsune@gmail.com>",
- L"ShadowNinja",
- L"dannydark <the_skeleton_of_a_child@yahoo.co.uk>",
- L"0gb.us <0gb.us@0gb.us>"
-};
-
-const wchar_t *contrib_previous_strs[] = {
- L"Giuseppe Bilotta (Oblomov) <giuseppe.bilotta@gmail.com>",
- L"Jonathan Neuschafer <j.neuschaefer@gmx.net>",
- L"Nils Dagsson Moskopp (erlehmann) <nils@dieweltistgarnichtso.net>",
- L"Constantin Wenger (SpeedProg) <constantin.wenger@googlemail.com>",
- L"matttpt <matttpt@gmail.com>",
- L"JacobF <queatz@gmail.com>"
-};
-
-
-struct CreateWorldDestMainMenu : public CreateWorldDest
-{
- CreateWorldDestMainMenu(GUIMainMenu *menu):
- m_menu(menu)
- {}
- void accepted(std::wstring name, std::string gameid)
- {
- std::string name_narrow = wide_to_narrow(name);
- if(!string_allowed_blacklist(name_narrow, WORLDNAME_BLACKLISTED_CHARS))
- {
- wchar_t* text = wgettext("Cannot create world: Name contains invalid characters");
- m_menu->displayMessageMenu(text);
- delete[] text;
- return;
- }
- std::vector<WorldSpec> worlds = getAvailableWorlds();
- for(std::vector<WorldSpec>::iterator i = worlds.begin();
- i != worlds.end(); i++)
- {
- if((*i).name == name_narrow)
- {
- wchar_t* text = wgettext("Cannot create world: A world by this name already exists");
- m_menu->displayMessageMenu(text);
- delete[] text;
- return;
- }
- }
- m_menu->createNewWorld(name, gameid);
- }
- GUIMainMenu *m_menu;
-};
-
-struct ConfirmDestDeleteWorld : public ConfirmDest
-{
- ConfirmDestDeleteWorld(WorldSpec spec, GUIMainMenu *menu,
- const std::vector<std::string> &paths):
- m_spec(spec),
- m_menu(menu),
- m_paths(paths)
- {}
- void answer(bool answer)
- {
- if(answer == false)
- return;
- m_menu->deleteWorld(m_paths);
- }
- WorldSpec m_spec;
- GUIMainMenu *m_menu;
- std::vector<std::string> m_paths;
-};
-
-enum
-{
- GUI_ID_QUIT_BUTTON = 101,
- GUI_ID_NAME_INPUT,
- GUI_ID_ADDRESS_INPUT,
- GUI_ID_PORT_INPUT,
- GUI_ID_FANCYTREE_CB,
- GUI_ID_SMOOTH_LIGHTING_CB,
- GUI_ID_3D_CLOUDS_CB,
- GUI_ID_OPAQUE_WATER_CB,
- GUI_ID_MIPMAP_CB,
- GUI_ID_ANISOTROPIC_CB,
- GUI_ID_BILINEAR_CB,
- GUI_ID_TRILINEAR_CB,
- GUI_ID_SHADERS_CB,
- GUI_ID_PRELOAD_ITEM_VISUALS_CB,
- GUI_ID_ENABLE_PARTICLES_CB,
- GUI_ID_LIQUID_FINITE_CB,
- GUI_ID_DAMAGE_CB,
- GUI_ID_CREATIVE_CB,
- GUI_ID_PUBLIC_CB,
- GUI_ID_JOIN_GAME_BUTTON,
- GUI_ID_CHANGE_KEYS_BUTTON,
- GUI_ID_DELETE_WORLD_BUTTON,
- GUI_ID_CREATE_WORLD_BUTTON,
- GUI_ID_CONFIGURE_WORLD_BUTTON,
- GUI_ID_WORLD_LISTBOX,
- GUI_ID_TAB_CONTROL,
- GUI_ID_SERVERLIST,
- GUI_ID_SERVERLIST_TOGGLE,
- GUI_ID_SERVERLIST_DELETE,
- GUI_ID_SERVERLIST_TITLE,
- GUI_ID_GAME_BUTTON_FIRST = 130,
- GUI_ID_GAME_BUTTON_MAX = 150,
-};
-
-GUIMainMenu::GUIMainMenu(gui::IGUIEnvironment* env,
- gui::IGUIElement* parent, s32 id,
- IMenuManager *menumgr,
- MainMenuData *data,
- IGameCallback *gamecallback
-):
- GUIModalMenu(env, parent, id, menumgr),
- m_data(data),
- m_accepted(false),
- m_gamecallback(gamecallback),
- m_is_regenerating(false)
-{
- assert(m_data);
- this->env = env;
- this->parent = parent;
- this->id = id;
- this->menumgr = menumgr;
-}
-
-GUIMainMenu::~GUIMainMenu()
-{
- removeChildren();
-}
-
-void GUIMainMenu::removeChildren()
-{
- const core::list<gui::IGUIElement*> &children = getChildren();
- core::list<gui::IGUIElement*> children_copy;
- for(core::list<gui::IGUIElement*>::ConstIterator
- i = children.begin(); i != children.end(); i++)
- {
- children_copy.push_back(*i);
- }
- for(core::list<gui::IGUIElement*>::Iterator
- i = children_copy.begin();
- i != children_copy.end(); i++)
- {
- (*i)->remove();
- }
-}
-
-void GUIMainMenu::regenerateGui(v2u32 screensize)
-{
- m_is_regenerating = true;
- /*
- Read stuff from elements into m_data
- */
- readInput(m_data);
-
- /*
- Remove stuff
- */
- removeChildren();
-
- /*
- Calculate new sizes and positions
- */
-
- v2s32 size(screensize.X, screensize.Y);
-
- core::rect<s32> rect(
- screensize.X/2 - size.X/2,
- screensize.Y/2 - size.Y/2,
- screensize.X/2 + size.X/2,
- screensize.Y/2 + size.Y/2
- );
-
- DesiredRect = rect;
- recalculateAbsolutePosition(false);
-
- //v2s32 size = rect.getSize();
-
- /*
- Add stuff
- */
-
- changeCtype("");
-
- // Version
- {
- core::rect<s32> rect(0, 0, size.X, 40);
- rect += v2s32(4, 0);
- std::string t = "Minetest " VERSION_STRING;
- if(m_data->selected_game_name != "" &&
- m_data->selected_tab == TAB_SINGLEPLAYER){
- t += "/";
- t += m_data->selected_game_name;
- }
- Environment->addStaticText(narrow_to_wide(t).c_str(),
- rect, false, true, this, -1);
- }
-
- //v2s32 center(size.X/2, size.Y/2);
- v2s32 c800(size.X/2-400, size.Y/2-270);
-
- m_topleft_client = c800 + v2s32(90, 70+50+30);
- m_size_client = v2s32(620, 270);
-
- m_size_server = v2s32(620, 140);
-
- if(m_data->selected_tab == TAB_ADVANCED)
- {
- m_topleft_client = c800 + v2s32(90, 70+50+30);
- m_size_client = v2s32(620, 200);
-
- m_size_server = v2s32(620, 140);
- }
-
- m_topleft_server = m_topleft_client + v2s32(0, m_size_client.Y+20);
-
- // Tabs
- {
- core::rect<s32> rect(0, 0, m_size_client.X, 30);
- rect += m_topleft_client + v2s32(0, -30);
- gui::IGUITabControl *e = Environment->addTabControl(
- rect, this, true, true, GUI_ID_TAB_CONTROL);
- wchar_t* text = wgettext("Singleplayer");
- e->addTab(text);
- delete[] text;
- text = wgettext("Multiplayer");
- e->addTab(text);
- delete[] text;
- text = wgettext("Advanced");
- e->addTab(text);
- delete[] text;
- text = wgettext("Settings");
- e->addTab(text);
- delete[] text;
- text = wgettext("Credits");
- e->addTab(text);
- delete[] text;
-
- e->setActiveTab(m_data->selected_tab);
-
- }
-
- if(m_data->selected_tab == TAB_SINGLEPLAYER)
- {
- // HYBRID
- {
- core::rect<s32> rect(0, 0, 10, m_size_client.Y);
- rect += m_topleft_client + v2s32(15, 0);
- //const wchar_t *text = L"H\nY\nB\nR\nI\nD";
- const wchar_t *text = L"S\nI\nN\nG\nL\nE\n \nP\nL\nA\nY\nE\nR\n";
- gui::IGUIStaticText *t =
- Environment->addStaticText(text, rect, false, true, this, -1);
- t->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_CENTER);
- }
- u32 bs = 5;
- // World selection listbox
- u32 world_sel_h = 160;
- u32 world_sel_w = 365;
- //s32 world_sel_x = 50;
- s32 world_sel_x = m_size_client.X-world_sel_w-30;
- s32 world_sel_y = 30;
- u32 world_button_count = 3;
- u32 world_button_w = (world_sel_w)/world_button_count - bs
- + bs/(world_button_count-1);
- {
- core::rect<s32> rect(0, 0, world_sel_w-4, 20);
- rect += m_topleft_client + v2s32(world_sel_x+4, world_sel_y-20);
- wchar_t* text = wgettext("Select World:");
- /*gui::IGUIStaticText *e =*/ Environment->addStaticText(
- text,
- rect, false, true, this, -1);
- delete[] text;
- /*e->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_CENTER);*/
- }
- {
- core::rect<s32> rect(0, 0, world_sel_w, world_sel_h);
- rect += m_topleft_client + v2s32(world_sel_x, world_sel_y);
- gui::IGUIListBox *e = Environment->addListBox(rect, this,
- GUI_ID_WORLD_LISTBOX);
- e->setDrawBackground(true);
- m_world_indices.clear();
- for(size_t wi = 0; wi < m_data->worlds.size(); wi++){
- const WorldSpec &spec = m_data->worlds[wi];
- if(spec.gameid == m_data->selected_game){
- //e->addItem(narrow_to_wide(spec.name+" ["+spec.gameid+"]").c_str());
- e->addItem(narrow_to_wide(spec.name).c_str());
- m_world_indices.push_back(wi);
- if(m_data->selected_world == (int)wi)
- e->setSelected(m_world_indices.size()-1);
- }
- }
- Environment->setFocus(e);
- }
- // Delete world button
- {
- core::rect<s32> rect(0, 0, world_button_w, 30);
- rect += m_topleft_client + v2s32(world_sel_x, world_sel_y+world_sel_h+0);
- wchar_t* text = wgettext("Delete");
- Environment->addButton(rect, this, GUI_ID_DELETE_WORLD_BUTTON,
- text);
- delete[] text;
- }
- // Create world button
- {
- core::rect<s32> rect(0, 0, world_button_w, 30);
- rect += m_topleft_client + v2s32(world_sel_x+world_button_w+bs, world_sel_y+world_sel_h+0);
- wchar_t* text = wgettext("New");
- Environment->addButton(rect, this, GUI_ID_CREATE_WORLD_BUTTON,
- text);
- delete[] text;
- }
- // Configure world button
- {
- core::rect<s32> rect(0, 0, world_button_w, 30);
- rect += m_topleft_client + v2s32(world_sel_x+(world_button_w+bs)*2,
- world_sel_y+world_sel_h+0);
- wchar_t* text = wgettext("Configure");
- Environment->addButton(rect, this, GUI_ID_CONFIGURE_WORLD_BUTTON,
- text);
- delete[] text;
- }
- // Start game button
- {
- /*core::rect<s32> rect(0, 0, world_button_w, 30);
- rect += m_topleft_client + v2s32(world_sel_x+(world_button_w+bs)*3,
- world_sel_y+world_sel_h+0);*/
- u32 bw = 160;
- /*core::rect<s32> rect(0, 0, bw, 30);
- rect += m_topleft_client + v2s32(m_size_client.X-bw-30,
- m_size_client.Y-30-15);*/
- core::rect<s32> rect(0, 0, bw, 30);
- rect += m_topleft_client + v2s32(world_sel_x+world_sel_w-bw,
- world_sel_y+world_sel_h+30+bs);
- wchar_t* text = wgettext("Play");
- Environment->addButton(rect, this,
- GUI_ID_JOIN_GAME_BUTTON, text);
- delete[] text;
- }
- // Options
- s32 option_x = 50;
- //s32 option_x = 50+world_sel_w+20;
- s32 option_y = 30;
- u32 option_w = 150;
- {
- core::rect<s32> rect(0, 0, option_w, 30);
- rect += m_topleft_client + v2s32(option_x, option_y+20*0);
- wchar_t* text = wgettext("Creative Mode");
- Environment->addCheckBox(m_data->creative_mode, rect, this,
- GUI_ID_CREATIVE_CB, text);
- delete[] text;
- }
- {
- core::rect<s32> rect(0, 0, option_w, 30);
- rect += m_topleft_client + v2s32(option_x, option_y+20*1);
- wchar_t* text = wgettext("Enable Damage");
- Environment->addCheckBox(m_data->enable_damage, rect, this,
- GUI_ID_DAMAGE_CB, text);
- delete[] text;
- }
- changeCtype("C");
-
- /* Add game selection buttons */
- video::IVideoDriver* driver = Environment->getVideoDriver();
- for(size_t i=0; i<m_data->games.size(); i++){
- const SubgameSpec *spec = &m_data->games[i];
- v2s32 p(8 + i*(48+8), screensize.Y - (48+8));
- core::rect<s32> rect(0, 0, 48, 48);
- rect += p;
- video::ITexture *bgtexture = NULL;
- if(spec->menuicon_path != "")
- bgtexture = driver->getTexture(spec->menuicon_path.c_str());
- gui::IGUIButton *b = Environment->addButton(rect, this,
- GUI_ID_GAME_BUTTON_FIRST+i, narrow_to_wide(wrap_rows(spec->id, 4)).c_str());
- if(bgtexture){
- b->setImage(bgtexture);
- b->setText(L"");
- b->setDrawBorder(false);
- b->setUseAlphaChannel(true);
- }
- }
- }
- else if(m_data->selected_tab == TAB_MULTIPLAYER)
- {
- changeCtype("");
- // CLIENT
- {
- core::rect<s32> rect(0, 0, 10, m_size_client.Y);
- rect += m_topleft_client + v2s32(15, 0);
- const wchar_t *text = L"C\nL\nI\nE\nN\nT";
- gui::IGUIStaticText *t =
- Environment->addStaticText(text, rect, false, true, this, -1);
- t->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_CENTER);
- }
- // Nickname + password
- {
- core::rect<s32> rect(0, 0, 110, 20);
- wchar_t* text = wgettext("Name/Password");
- rect += m_topleft_client + v2s32(m_size_client.X-60-100, 10+6);
- Environment->addStaticText(text,
- rect, false, true, this, -1);
- delete [] text;
- }
- changeCtype("C");
- {
- core::rect<s32> rect(0, 0, 120, 30);
- rect += m_topleft_client + v2s32(m_size_client.X-60-100, 50);
- gui::IGUIElement *e =
- Environment->addEditBox(m_data->name.c_str(), rect, true, this, GUI_ID_NAME_INPUT);
- if(m_data->name == L"")
- Environment->setFocus(e);
- }
- {
- core::rect<s32> rect(0, 0, 120, 30);
- rect += m_topleft_client + v2s32(m_size_client.X-60-100, 90);
- gui::IGUIEditBox *e =
- Environment->addEditBox(L"", rect, true, this, 264);
- e->setPasswordBox(true);
- if(m_data->name != L"" && m_data->address != L"")
- Environment->setFocus(e);
-
- }
- changeCtype("");
- // Server List
- {
- core::rect<s32> rect(0, 0, 390, 140);
- rect += m_topleft_client + v2s32(50, 30);
- gui::IGUIListBox *e = Environment->addListBox(rect, this,
- GUI_ID_SERVERLIST);
- e->setDrawBackground(true);
-#if USE_CURL
- if(m_data->selected_serverlist == SERVERLIST_FAVORITES) {
- m_data->servers = ServerList::getLocal();
- {
- core::rect<s32> rect(0, 0, 390, 20);
- rect += m_topleft_client + v2s32(50, 10);
- wchar_t* text = wgettext("Favorites:");
- Environment->addStaticText(text,
- rect, false, true, this, GUI_ID_SERVERLIST_TITLE);
- delete[] text;
- }
- } else {
- m_data->servers = ServerList::getOnline();
- {
- core::rect<s32> rect(0, 0, 390, 20);
- rect += m_topleft_client + v2s32(50, 10);
- wchar_t* text = wgettext("Public Server List:");
- Environment->addStaticText(text,
- rect, false, true, this, GUI_ID_SERVERLIST_TITLE);
- delete[] text;
- }
- }
-#else
- m_data->servers = ServerList::getLocal();
- {
- core::rect<s32> rect(0, 0, 390, 20);
- rect += m_topleft_client + v2s32(50, 10);
- wchar_t* text = wgettext("Favorites:");
- Environment->addStaticText(text,
- rect, false, true, this, GUI_ID_SERVERLIST_TITLE);
- delete[] text;
- }
-#endif
- updateGuiServerList();
- e->setSelected(0);
- }
- // Address + port
- {
- core::rect<s32> rect(0, 0, 110, 20);
- rect += m_topleft_client + v2s32(50, m_size_client.Y-50-15+6);
- wchar_t* text = wgettext("Address/Port");
- Environment->addStaticText(text,
- rect, false, true, this, -1);
- delete [] text;
- }
- changeCtype("C");
- {
- core::rect<s32> rect(0, 0, 260, 30);
- rect += m_topleft_client + v2s32(50, m_size_client.Y-25-15);
- gui::IGUIElement *e =
- Environment->addEditBox(m_data->address.c_str(), rect, true,
- this, GUI_ID_ADDRESS_INPUT);
- if(m_data->name != L"" && m_data->address == L"")
- Environment->setFocus(e);
- }
- {
- core::rect<s32> rect(0, 0, 120, 30);
- rect += m_topleft_client + v2s32(50+260+10, m_size_client.Y-25-15);
- Environment->addEditBox(m_data->port.c_str(), rect, true,
- this, GUI_ID_PORT_INPUT);
- }
- changeCtype("");
- #if USE_CURL
- // Toggle Serverlist (Favorites/Online)
- {
- core::rect<s32> rect(0, 0, 260, 30);
- rect += m_topleft_client + v2s32(50,
- 180);
- wchar_t* text = wgettext("Show Public");
- gui::IGUIButton *e = Environment->addButton(rect, this, GUI_ID_SERVERLIST_TOGGLE,
- text);
- delete[] text;
- e->setIsPushButton(true);
- if (m_data->selected_serverlist == SERVERLIST_PUBLIC)
- {
- wchar_t* text = wgettext("Show Favorites");
- e->setText(text);
- e->setPressed();
- delete[] text;
- }
- }
- #endif
- // Delete Local Favorite
- {
- core::rect<s32> rect(0, 0, 120, 30);
- rect += m_topleft_client + v2s32(50+260+10, 180);
- wchar_t* text = wgettext("Delete");
- gui::IGUIButton *e = Environment->addButton(rect, this, GUI_ID_SERVERLIST_DELETE,
- text);
- if (m_data->selected_serverlist == SERVERLIST_PUBLIC) // Hidden when on public list
- e->setVisible(false);
-
- delete [] text;
- }
- // Start game button
- {
- core::rect<s32> rect(0, 0, 120, 30);
- rect += m_topleft_client + v2s32(m_size_client.X-130-30,
- m_size_client.Y-25-15);
- wchar_t* text = wgettext("Connect");
- Environment->addButton(rect, this, GUI_ID_JOIN_GAME_BUTTON,
- text);
- delete[] text;
- }
- changeCtype("C");
- }
- else if(m_data->selected_tab == TAB_ADVANCED)
- {
- changeCtype("");
- // CLIENT
- {
- core::rect<s32> rect(0, 0, 10, m_size_client.Y);
- rect += m_topleft_client + v2s32(15, 0);
- const wchar_t *text = L"C\nL\nI\nE\nN\nT";
- gui::IGUIStaticText *t =
- Environment->addStaticText(text, rect, false, true, this, -1);
- t->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_CENTER);
- }
- // Nickname + password
- {
- core::rect<s32> rect(0, 0, 110, 20);
- rect += m_topleft_client + v2s32(35+30, 35+6);
- wchar_t* text = wgettext("Name/Password");
- Environment->addStaticText(text,
- rect, false, true, this, -1);
- delete [] text;
- }
- changeCtype("C");
- {
- core::rect<s32> rect(0, 0, 230, 30);
- rect += m_topleft_client + v2s32(160+30, 35);
- gui::IGUIElement *e =
- Environment->addEditBox(m_data->name.c_str(), rect, true, this, GUI_ID_NAME_INPUT);
- if(m_data->name == L"")
- Environment->setFocus(e);
- }
- {
- core::rect<s32> rect(0, 0, 120, 30);
- rect += m_topleft_client + v2s32(m_size_client.X-60-100, 35);
- gui::IGUIEditBox *e =
- Environment->addEditBox(L"", rect, true, this, 264);
- e->setPasswordBox(true);
- if(m_data->name != L"" && m_data->address != L"")
- Environment->setFocus(e);
-
- }
- changeCtype("");
- // Address + port
- {
- core::rect<s32> rect(0, 0, 110, 20);
- rect += m_topleft_client + v2s32(35+30, 75+6);
- wchar_t* text = wgettext("Address/Port");
- Environment->addStaticText(text,
- rect, false, true, this, -1);
- delete[] text;
- }
- changeCtype("C");
- {
- core::rect<s32> rect(0, 0, 230, 30);
- rect += m_topleft_client + v2s32(160+30, 75);
- gui::IGUIElement *e =
- Environment->addEditBox(m_data->address.c_str(), rect, true,
- this, GUI_ID_ADDRESS_INPUT);
- if(m_data->name != L"" && m_data->address == L"")
- Environment->setFocus(e);
- }
- {
- core::rect<s32> rect(0, 0, 120, 30);
- rect += m_topleft_client + v2s32(m_size_client.X-60-100, 75);
- Environment->addEditBox(m_data->port.c_str(), rect, true,
- this, GUI_ID_PORT_INPUT);
- }
- changeCtype("");
- {
- core::rect<s32> rect(0, 0, 400, 20);
- rect += m_topleft_client + v2s32(160+30, 75+35);
- wchar_t* text = wgettext("Leave address blank to start a local server.");
- Environment->addStaticText(text,
- rect, false, true, this, -1);
- delete[] text;
- }
- // Start game button
- {
- core::rect<s32> rect(0, 0, 180, 30);
- rect += m_topleft_client + v2s32(m_size_client.X-180-30,
- m_size_client.Y-30-20);
- wchar_t* text = wgettext("Start Game / Connect");
- Environment->addButton(rect, this, GUI_ID_JOIN_GAME_BUTTON,
- text);
- delete[] text;
- }
- /*
- Server section
- */
- // SERVER
- {
- core::rect<s32> rect(0, 0, 10, m_size_server.Y);
- rect += m_topleft_server + v2s32(15, 0);
- const wchar_t *text = L"S\nE\nR\nV\nE\nR";
- gui::IGUIStaticText *t =
- Environment->addStaticText(text, rect, false, true, this, -1);
- t->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_CENTER);
- }
- // Server parameters
- {
- core::rect<s32> rect(0, 0, 250, 30);
- rect += m_topleft_server + v2s32(30+20+250+20, 20);
- wchar_t* text = wgettext("Creative Mode");
- Environment->addCheckBox(m_data->creative_mode, rect, this, GUI_ID_CREATIVE_CB,
- text);
- delete[] text;
- }
- {
- core::rect<s32> rect(0, 0, 250, 30);
- rect += m_topleft_server + v2s32(30+20+250+20, 40);
- wchar_t* text = wgettext("Enable Damage");
- Environment->addCheckBox(m_data->enable_damage, rect, this, GUI_ID_DAMAGE_CB,
- text);
- delete[] text;
- }
- #if USE_CURL
- {
- core::rect<s32> rect(0, 0, 250, 30);
- rect += m_topleft_server + v2s32(30+20+250+20, 60);
- wchar_t* text = wgettext("Public");
- Environment->addCheckBox(m_data->enable_public, rect, this, GUI_ID_PUBLIC_CB,
- text);
- delete[] text;
- }
- #endif
- // Delete world button
- {
- core::rect<s32> rect(0, 0, 130, 30);
- rect += m_topleft_server + v2s32(30+20+250+20, 90);
- wchar_t* text = wgettext("Delete world");
- Environment->addButton(rect, this, GUI_ID_DELETE_WORLD_BUTTON,
- text );
- delete[] text;
- }
- // Create world button
- {
- core::rect<s32> rect(0, 0, 130, 30);
- rect += m_topleft_server + v2s32(30+20+250+20+140, 90);
- wchar_t* text = wgettext("Create world");
- Environment->addButton(rect, this, GUI_ID_CREATE_WORLD_BUTTON,
- text );
- delete[] text;
- }
- // World selection listbox
- {
- core::rect<s32> rect(0, 0, 250, 120);
- rect += m_topleft_server + v2s32(30+20, 10);
- gui::IGUIListBox *e = Environment->addListBox(rect, this,
- GUI_ID_WORLD_LISTBOX);
- e->setDrawBackground(true);
- m_world_indices.clear();
- for(size_t wi = 0; wi < m_data->worlds.size(); wi++){
- const WorldSpec &spec = m_data->worlds[wi];
- e->addItem(narrow_to_wide(spec.name+" ["+spec.gameid+"]").c_str());
- m_world_indices.push_back(wi);
- }
- e->setSelected(m_data->selected_world);
- }
- changeCtype("C");
- }
- else if(m_data->selected_tab == TAB_SETTINGS)
- {
- {
- core::rect<s32> rect(0, 0, 10, m_size_client.Y);
- rect += m_topleft_client + v2s32(15, 0);
- const wchar_t *text = L"S\nE\nT\nT\nI\nN\nG\nS";
- gui::IGUIStaticText *t =
- Environment->addStaticText(text, rect, false, true, this, -1);
- t->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_CENTER);
- }
- s32 option_x = 70;
- s32 option_y = 50;
- u32 option_w = 150;
- {
- core::rect<s32> rect(0, 0, option_w, 30);
- rect += m_topleft_client + v2s32(option_x, option_y);
- wchar_t* text = wgettext("Fancy trees");
- Environment->addCheckBox(m_data->fancy_trees, rect, this,
- GUI_ID_FANCYTREE_CB, text);
- delete[] text;
- }
- {
- core::rect<s32> rect(0, 0, option_w, 30);
- rect += m_topleft_client + v2s32(option_x, option_y+20);
- wchar_t* text = wgettext("Smooth Lighting");
- Environment->addCheckBox(m_data->smooth_lighting, rect, this,
- GUI_ID_SMOOTH_LIGHTING_CB, text);
- delete[] text;
- }
- {
- core::rect<s32> rect(0, 0, option_w, 30);
- rect += m_topleft_client + v2s32(option_x, option_y+20*2);
- wchar_t* text = wgettext("3D Clouds");
- Environment->addCheckBox(m_data->clouds_3d, rect, this,
- GUI_ID_3D_CLOUDS_CB, text);
- delete[] text;
- }
- {
- core::rect<s32> rect(0, 0, option_w, 30);
- rect += m_topleft_client + v2s32(option_x, option_y+20*3);
- wchar_t* text = wgettext("Opaque water");
- Environment->addCheckBox(m_data->opaque_water, rect, this,
- GUI_ID_OPAQUE_WATER_CB, text);
- delete[] text;
- }
-
-
- // Anisotropic/mipmap/bi-/trilinear settings
-
- {
- core::rect<s32> rect(0, 0, option_w+20, 30);
- rect += m_topleft_client + v2s32(option_x+175, option_y);
- wchar_t* text = wgettext("Mip-Mapping");
- Environment->addCheckBox(m_data->mip_map, rect, this,
- GUI_ID_MIPMAP_CB, text);
- delete[] text;
- }
-
- {
- core::rect<s32> rect(0, 0, option_w+20, 30);
- rect += m_topleft_client + v2s32(option_x+175, option_y+20);
- wchar_t* text = wgettext("Anisotropic Filtering");
- Environment->addCheckBox(m_data->anisotropic_filter, rect, this,
- GUI_ID_ANISOTROPIC_CB, text);
- delete[] text;
- }
-
- {
- core::rect<s32> rect(0, 0, option_w+20, 30);
- rect += m_topleft_client + v2s32(option_x+175, option_y+20*2);
- wchar_t* text = wgettext("Bi-Linear Filtering");
- Environment->addCheckBox(m_data->bilinear_filter, rect, this,
- GUI_ID_BILINEAR_CB, text);
- delete[] text;
- }
-
- {
- core::rect<s32> rect(0, 0, option_w+20, 30);
- rect += m_topleft_client + v2s32(option_x+175, option_y+20*3);
- wchar_t* text = wgettext("Tri-Linear Filtering");
- Environment->addCheckBox(m_data->trilinear_filter, rect, this,
- GUI_ID_TRILINEAR_CB, text);
- delete[] text;
- }
-
- // shader/on demand image loading/particles settings
- {
- core::rect<s32> rect(0, 0, option_w+20, 30);
- rect += m_topleft_client + v2s32(option_x+175*2, option_y);
- wchar_t* text = wgettext("Shaders");
- Environment->addCheckBox(m_data->enable_shaders, rect, this,
- GUI_ID_SHADERS_CB, text);
- delete[] text;
- }
-
- {
- core::rect<s32> rect(0, 0, option_w+20+20, 30);
- rect += m_topleft_client + v2s32(option_x+175*2, option_y+20);
- wchar_t* text = wgettext("Preload item visuals");
- Environment->addCheckBox(m_data->preload_item_visuals, rect, this,
- GUI_ID_PRELOAD_ITEM_VISUALS_CB, text);
- delete[] text;
- }
-
- {
- core::rect<s32> rect(0, 0, option_w+20+20, 30);
- rect += m_topleft_client + v2s32(option_x+175*2, option_y+20*2);
- wchar_t* text = wgettext("Enable Particles");
- Environment->addCheckBox(m_data->enable_particles, rect, this,
- GUI_ID_ENABLE_PARTICLES_CB, text);
- delete[] text;
- }
-
- {
- core::rect<s32> rect(0, 0, option_w+20+20, 30);
- rect += m_topleft_client + v2s32(option_x+175*2, option_y+20*3);
- wchar_t* text = wgettext("Finite liquid");
- Environment->addCheckBox(m_data->liquid_finite, rect, this,
- GUI_ID_LIQUID_FINITE_CB, text);
- delete[] text;
- }
-
- // Key change button
- {
- core::rect<s32> rect(0, 0, 120, 30);
- /*rect += m_topleft_client + v2s32(m_size_client.X-120-30,
- m_size_client.Y-30-20);*/
- rect += m_topleft_client + v2s32(option_x, option_y+120);
- wchar_t* text = wgettext("Change keys");
- Environment->addButton(rect, this,
- GUI_ID_CHANGE_KEYS_BUTTON, text);
- delete[] text;
- }
- changeCtype("C");
- }
- else if(m_data->selected_tab == TAB_CREDITS)
- {
- // CREDITS
- {
- core::rect<s32> rect(0, 0, 9, m_size_client.Y);
- rect += m_topleft_client + v2s32(15, 0);
- const wchar_t *text = L"C\nR\nE\nD\nI\nT\nS";
- gui::IGUIStaticText *t =
- Environment->addStaticText(text, rect, false, true, this, -1);
- t->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_CENTER);
- }
- {
- core::rect<s32> rect(0, 0, 130, 70);
- rect += m_topleft_client + v2s32(35, 160);
- Environment->addStaticText(
- L"Minetest " LSTRING(VERSION_STRING) L"\nhttp://minetest.net/",
- rect, false, true, this, -1);
- }
- {
- video::SColor yellow(255, 255, 255, 0);
- core::rect<s32> rect(0, 0, 450, 260);
- rect += m_topleft_client + v2s32(168, 5);
-
- irr::gui::IGUIListBox *list = Environment->addListBox(rect, this);
-
- list->addItem(L"Core Developers");
- list->setItemOverrideColor(list->getItemCount() - 1, yellow);
- for (int i = 0; i != ARRAYLEN(contrib_core_strs); i++)
- list->addItem(contrib_core_strs[i]);
- list->addItem(L"");
- list->addItem(L"Active Contributors");
- list->setItemOverrideColor(list->getItemCount() - 1, yellow);
- for (int i = 0; i != ARRAYLEN(contrib_active_strs); i++)
- list->addItem(contrib_active_strs[i]);
- list->addItem(L"");
- list->addItem(L"Previous Contributors");
- list->setItemOverrideColor(list->getItemCount() - 1, yellow);
- for (int i = 0; i != ARRAYLEN(contrib_previous_strs); i++)
- list->addItem(contrib_previous_strs[i]);
- list->addItem(L"");
- }
- }
-
- m_is_regenerating = false;
-}
-
-void GUIMainMenu::drawMenu()
-{
- gui::IGUISkin* skin = Environment->getSkin();
- if (!skin)
- return;
- video::IVideoDriver* driver = Environment->getVideoDriver();
-
- /* Draw menu background */
-
- /*video::SColor bgcolor(140,0,0,0);
- driver->draw2DRectangle(bgcolor, AbsoluteRect, &AbsoluteClippingRect);*/
-
- video::SColor bgcolor(140,0,0,0);
-
- if(getTab() == TAB_SINGLEPLAYER)
- {
- {
- core::rect<s32> rect(0, 0, m_size_client.X, m_size_client.Y);
- rect += AbsoluteRect.UpperLeftCorner + m_topleft_client;
- driver->draw2DRectangle(bgcolor, rect, &AbsoluteClippingRect);
- }
- }
- else if(getTab() == TAB_MULTIPLAYER)
- {
- {
- core::rect<s32> rect(0, 0, m_size_client.X, m_size_client.Y);
- rect += AbsoluteRect.UpperLeftCorner + m_topleft_client;
- driver->draw2DRectangle(bgcolor, rect, &AbsoluteClippingRect);
- }
- }
- else if(getTab() == TAB_ADVANCED)
- {
- {
- core::rect<s32> rect(0, 0, m_size_client.X, m_size_client.Y);
- rect += AbsoluteRect.UpperLeftCorner + m_topleft_client;
- driver->draw2DRectangle(bgcolor, rect, &AbsoluteClippingRect);
- }
- {
- core::rect<s32> rect(0, 0, m_size_server.X, m_size_server.Y);
- rect += AbsoluteRect.UpperLeftCorner + m_topleft_server;
- driver->draw2DRectangle(bgcolor, rect, &AbsoluteClippingRect);
- }
- }
- else if(getTab() == TAB_SETTINGS)
- {
- {
- core::rect<s32> rect(0, 0, m_size_client.X, m_size_client.Y);
- rect += AbsoluteRect.UpperLeftCorner + m_topleft_client;
- driver->draw2DRectangle(bgcolor, rect, &AbsoluteClippingRect);
- }
- }
- else if(getTab() == TAB_CREDITS)
- {
- {
- core::rect<s32> rect(0, 0, m_size_client.X, m_size_client.Y);
- rect += AbsoluteRect.UpperLeftCorner + m_topleft_client;
- driver->draw2DRectangle(bgcolor, rect, &AbsoluteClippingRect);
- }
- video::ITexture *logotexture =
- driver->getTexture(getTexturePath("logo.png").c_str());
- if(logotexture)
- {
- v2s32 logosize(logotexture->getOriginalSize().Width,
- logotexture->getOriginalSize().Height);
-
- core::rect<s32> rect(0,0,logosize.X,logosize.Y);
- rect += AbsoluteRect.UpperLeftCorner + m_topleft_client;
- rect += v2s32(50, 60);
- driver->draw2DImage(logotexture, rect,
- core::rect<s32>(core::position2d<s32>(0,0),
- core::dimension2di(logotexture->getSize())),
- NULL, NULL, true);
- }
- }
-
- /* Draw UI elements */
-
- gui::IGUIElement::draw();
-}
-
-void GUIMainMenu::readInput(MainMenuData *dst)
-{
- {
- gui::IGUIElement *e = getElementFromId(GUI_ID_TAB_CONTROL);
- if(e != NULL && e->getType() == gui::EGUIET_TAB_CONTROL)
- dst->selected_tab = ((gui::IGUITabControl*)e)->getActiveTab();
- }
- if(dst->selected_tab == TAB_SINGLEPLAYER)
- {
- dst->simple_singleplayer_mode = true;
- }
- else
- {
- dst->simple_singleplayer_mode = false;
- {
- gui::IGUIElement *e = getElementFromId(GUI_ID_NAME_INPUT);
- if(e != NULL)
- dst->name = e->getText();
- }
- {
- gui::IGUIElement *e = getElementFromId(264);
- if(e != NULL)
- dst->password = e->getText();
- }
- {
- gui::IGUIElement *e = getElementFromId(GUI_ID_ADDRESS_INPUT);
- if(e != NULL)
- dst->address = e->getText();
- }
- {
- gui::IGUIElement *e = getElementFromId(GUI_ID_PORT_INPUT);
- if(e != NULL)
- dst->port = e->getText();
- }
- }
- {
- gui::IGUIElement *e = getElementFromId(GUI_ID_CREATIVE_CB);
- if(e != NULL && e->getType() == gui::EGUIET_CHECK_BOX)
- dst->creative_mode = ((gui::IGUICheckBox*)e)->isChecked();
- }
- {
- gui::IGUIElement *e = getElementFromId(GUI_ID_DAMAGE_CB);
- if(e != NULL && e->getType() == gui::EGUIET_CHECK_BOX)
- dst->enable_damage = ((gui::IGUICheckBox*)e)->isChecked();
- }
- {
- gui::IGUIElement *e = getElementFromId(GUI_ID_PUBLIC_CB);
- if(e != NULL && e->getType() == gui::EGUIET_CHECK_BOX)
- dst->enable_public = ((gui::IGUICheckBox*)e)->isChecked();
- }
- {
- gui::IGUIElement *e = getElementFromId(GUI_ID_FANCYTREE_CB);
- if(e != NULL && e->getType() == gui::EGUIET_CHECK_BOX)
- dst->fancy_trees = ((gui::IGUICheckBox*)e)->isChecked();
- }
- {
- gui::IGUIElement *e = getElementFromId(GUI_ID_SMOOTH_LIGHTING_CB);
- if(e != NULL && e->getType() == gui::EGUIET_CHECK_BOX)
- dst->smooth_lighting = ((gui::IGUICheckBox*)e)->isChecked();
- }
- {
- gui::IGUIElement *e = getElementFromId(GUI_ID_3D_CLOUDS_CB);
- if(e != NULL && e->getType() == gui::EGUIET_CHECK_BOX)
- dst->clouds_3d = ((gui::IGUICheckBox*)e)->isChecked();
- }
- {
- gui::IGUIElement *e = getElementFromId(GUI_ID_OPAQUE_WATER_CB);
- if(e != NULL && e->getType() == gui::EGUIET_CHECK_BOX)
- dst->opaque_water = ((gui::IGUICheckBox*)e)->isChecked();
- }
-
- {
- gui::IGUIElement *e = getElementFromId(GUI_ID_MIPMAP_CB);
- if(e != NULL && e->getType() == gui::EGUIET_CHECK_BOX)
- dst->mip_map = ((gui::IGUICheckBox*)e)->isChecked();
- }
-
- {
- gui::IGUIElement *e = getElementFromId(GUI_ID_ANISOTROPIC_CB);
- if(e != NULL && e->getType() == gui::EGUIET_CHECK_BOX)
- dst->anisotropic_filter = ((gui::IGUICheckBox*)e)->isChecked();
- }
-
- {
- gui::IGUIElement *e = getElementFromId(GUI_ID_BILINEAR_CB);
- if(e != NULL && e->getType() == gui::EGUIET_CHECK_BOX)
- dst->bilinear_filter = ((gui::IGUICheckBox*)e)->isChecked();
- }
-
- {
- gui::IGUIElement *e = getElementFromId(GUI_ID_TRILINEAR_CB);
- if(e != NULL && e->getType() == gui::EGUIET_CHECK_BOX)
- dst->trilinear_filter = ((gui::IGUICheckBox*)e)->isChecked();
- }
-
- {
- gui::IGUIElement *e = getElementFromId(GUI_ID_SHADERS_CB);
- if(e != NULL && e->getType() == gui::EGUIET_CHECK_BOX)
- dst->enable_shaders = ((gui::IGUICheckBox*)e)->isChecked() ? 2 : 0;
- }
-
- {
- gui::IGUIElement *e = getElementFromId(GUI_ID_PRELOAD_ITEM_VISUALS_CB);
- if(e != NULL && e->getType() == gui::EGUIET_CHECK_BOX)
- dst->preload_item_visuals = ((gui::IGUICheckBox*)e)->isChecked();
- }
-
- {
- gui::IGUIElement *e = getElementFromId(GUI_ID_ENABLE_PARTICLES_CB);
- if(e != NULL && e->getType() == gui::EGUIET_CHECK_BOX)
- dst->enable_particles = ((gui::IGUICheckBox*)e)->isChecked();
- }
-
- {
- gui::IGUIElement *e = getElementFromId(GUI_ID_LIQUID_FINITE_CB);
- if(e != NULL && e->getType() == gui::EGUIET_CHECK_BOX)
- dst->liquid_finite = ((gui::IGUICheckBox*)e)->isChecked();
- }
-
- {
- gui::IGUIElement *e = getElementFromId(GUI_ID_WORLD_LISTBOX);
- if(e != NULL && e->getType() == gui::EGUIET_LIST_BOX){
- int list_i = ((gui::IGUIListBox*)e)->getSelected();
- if(list_i == -1)
- dst->selected_world = -1;
- else
- dst->selected_world = m_world_indices[list_i];
- }
- }
- {
- ServerListSpec server =
- getServerListSpec(wide_to_narrow(dst->address), wide_to_narrow(dst->port));
- dst->servername = server["name"].asString();
- dst->serverdescription = server["description"].asString();
- }
-}
-
-void GUIMainMenu::acceptInput()
-{
- readInput(m_data);
- m_accepted = true;
-}
-
-bool GUIMainMenu::OnEvent(const SEvent& event)
-{
- if(event.EventType==EET_KEY_INPUT_EVENT)
- {
- if(event.KeyInput.Key==KEY_ESCAPE && event.KeyInput.PressedDown)
- {
- m_gamecallback->exitToOS();
- quitMenu();
- return true;
- }
- if(event.KeyInput.Key==KEY_RETURN && event.KeyInput.PressedDown)
- {
- acceptInput();
- quitMenu();
- return true;
- }
- }
- if(event.EventType==EET_GUI_EVENT)
- {
- if(event.GUIEvent.EventType==gui::EGET_ELEMENT_FOCUS_LOST
- && isVisible())
- {
- if(!canTakeFocus(event.GUIEvent.Element))
- {
- dstream<<"GUIMainMenu: Not allowing focus change."
- <<std::endl;
- // Returning true disables focus change
- return true;
- }
- }
- if(event.GUIEvent.EventType==gui::EGET_TAB_CHANGED)
- {
- if(!m_is_regenerating)
- regenerateGui(m_screensize_old);
- return true;
- }
- if(event.GUIEvent.EventType==gui::EGET_LISTBOX_CHANGED && event.GUIEvent.Caller->getID() == GUI_ID_SERVERLIST)
- {
- serverListOnSelected();
- return true;
- }
- if(event.GUIEvent.EventType==gui::EGET_BUTTON_CLICKED)
- {
- switch(event.GUIEvent.Caller->getID())
- {
- case GUI_ID_JOIN_GAME_BUTTON: {
- MainMenuData cur;
- readInput(&cur);
- if (getTab() == TAB_MULTIPLAYER && cur.address == L"")
- {
- wchar_t* text = wgettext("Address required.");
- (new GUIMessageMenu(env, parent, -1, menumgr,
- text)
- )->drop();
- delete[] text;
- return true;
- }
- acceptInput();
- quitMenu();
- return true;
- }
- case GUI_ID_CHANGE_KEYS_BUTTON: {
- GUIKeyChangeMenu *kmenu = new GUIKeyChangeMenu(env, parent, -1,menumgr);
- kmenu->drop();
- return true;
- }
- case GUI_ID_DELETE_WORLD_BUTTON: {
- MainMenuData cur;
- readInput(&cur);
- if(cur.selected_world == -1){
- wchar_t* text = wgettext("Cannot delete world: Nothing selected");
- (new GUIMessageMenu(env, parent, -1, menumgr,
- text)
- )->drop();
- delete[] text;
- } else {
- WorldSpec spec = m_data->worlds[cur.selected_world];
- // Get files and directories involved
- std::vector<std::string> paths;
- paths.push_back(spec.path);
- fs::GetRecursiveSubPaths(spec.path, paths);
- // Launch confirmation dialog
- ConfirmDestDeleteWorld *dest = new
- ConfirmDestDeleteWorld(spec, this, paths);
- wchar_t* text1 = wgettext("Delete world");
- wchar_t* text2 = wgettext("Files to be deleted");
- std::wstring text = text1;
- text += L" \"";
- text += narrow_to_wide(spec.name);
- text += L"\"?\n\n";
- text += text2;
- text += L":\n";
- delete[] text1;
- delete[] text2;
- for(u32 i=0; i<paths.size(); i++){
- if(i == 3){ text += L"..."; break; }
- text += narrow_to_wide(paths[i]) + L"\n";
- }
- (new GUIConfirmMenu(env, parent, -1, menumgr, dest,
- text.c_str()))->drop();
- }
- return true;
- }
- case GUI_ID_CREATE_WORLD_BUTTON: {
- const std::vector<SubgameSpec> &games = m_data->games;
- if(games.size() == 0){
- wchar_t* text = wgettext("Cannot create world: No games found");
- GUIMessageMenu *menu = new GUIMessageMenu(env, parent,
- -1, menumgr,
- text);
- menu->drop();
- delete[] text;
- } else {
- CreateWorldDest *dest = new CreateWorldDestMainMenu(this);
- GUICreateWorld *menu = new GUICreateWorld(env, parent, -1,
- menumgr, dest, games, m_data->selected_game);
- menu->drop();
- }
- return true;
- }
- case GUI_ID_CONFIGURE_WORLD_BUTTON: {
- MainMenuData cur;
- readInput(&cur);
- if(cur.selected_world == -1)
- {
- wchar_t* text = wgettext("Cannot configure world: Nothing selected");
- (new GUIMessageMenu(env, parent, -1, menumgr,
- text)
- )->drop();
- delete[] text;
- }
- else
- {
- WorldSpec wspec = m_data->worlds[cur.selected_world];
- GUIConfigureWorld *menu = new GUIConfigureWorld(env, parent,
- -1, menumgr, wspec);
- menu->drop();
- }
- return true;
- }
- case GUI_ID_SERVERLIST_DELETE: {
- gui::IGUIListBox *serverlist = (gui::IGUIListBox*)getElementFromId(GUI_ID_SERVERLIST);
- s32 selected = ((gui::IGUIListBox*)serverlist)->getSelected();
- if (selected == -1) return true;
- ServerList::deleteEntry(m_data->servers[selected]);
- m_data->servers = ServerList::getLocal();
- updateGuiServerList();
- if (selected > 0)
- selected -= 1;
- serverlist->setSelected(selected);
- serverListOnSelected();
- return true;
- }
- #if USE_CURL
- case GUI_ID_SERVERLIST_TOGGLE: {
- gui::IGUIElement *togglebutton = getElementFromId(GUI_ID_SERVERLIST_TOGGLE);
- gui::IGUIElement *deletebutton = getElementFromId(GUI_ID_SERVERLIST_DELETE);
- gui::IGUIListBox *serverlist = (gui::IGUIListBox*)getElementFromId(GUI_ID_SERVERLIST);
- gui::IGUIElement *title = getElementFromId(GUI_ID_SERVERLIST_TITLE);
- if (m_data->selected_serverlist == SERVERLIST_PUBLIC) // switch to favorite list
- {
- m_data->servers = ServerList::getLocal();
- wchar_t* text1 = wgettext("Show Public");
- wchar_t* text2 = wgettext("Favorites:");
- togglebutton->setText(text1);
- title->setText(text2);
- delete[] text1;
- delete[] text2;
- deletebutton->setVisible(true);
- updateGuiServerList();
- serverlist->setSelected(0);
- m_data->selected_serverlist = SERVERLIST_FAVORITES;
- }
- else // switch to online list
- {
- m_data->servers = ServerList::getOnline();
- wchar_t* text1 = wgettext("Show Favorites");
- wchar_t* text2 = wgettext("Public Server List:");
- togglebutton->setText(text1);
- title->setText(text2);
- delete[] text1;
- delete[] text2;
- deletebutton->setVisible(false);
- updateGuiServerList();
- serverlist->setSelected(0);
- m_data->selected_serverlist = SERVERLIST_PUBLIC;
- }
- serverListOnSelected();
- }
- #endif
- }
- /* Game buttons */
- int eid = event.GUIEvent.Caller->getID();
- if(eid >= GUI_ID_GAME_BUTTON_FIRST &&
- eid <= GUI_ID_GAME_BUTTON_MAX){
- m_data->selected_game =
- m_data->games[eid - GUI_ID_GAME_BUTTON_FIRST].id;
- m_data->selected_game_name =
- m_data->games[eid - GUI_ID_GAME_BUTTON_FIRST].name;
- regenerateGui(m_screensize_old);
- }
- }
- if(event.GUIEvent.EventType==gui::EGET_EDITBOX_ENTER)
- {
- switch(event.GUIEvent.Caller->getID())
- {
- case GUI_ID_ADDRESS_INPUT: case GUI_ID_PORT_INPUT: case GUI_ID_NAME_INPUT: case 264:
- MainMenuData cur;
- readInput(&cur);
- if (getTab() == TAB_MULTIPLAYER && cur.address == L"")
- {
- wchar_t* text = wgettext("Address required.");
- (new GUIMessageMenu(env, parent, -1, menumgr,
- text)
- )->drop();
- delete[] text;
- return true;
- }
- acceptInput();
- quitMenu();
- return true;
- }
- }
- if(event.GUIEvent.EventType==gui::EGET_LISTBOX_CHANGED)
- {
- readInput(m_data);
- }
- if(event.GUIEvent.EventType==gui::EGET_LISTBOX_SELECTED_AGAIN)
- {
- switch(event.GUIEvent.Caller->getID())
- {
- case GUI_ID_WORLD_LISTBOX:
- acceptInput();
- if(getTab() != TAB_SINGLEPLAYER)
- m_data->address = L""; // Force local game
- quitMenu();
- return true;
- case GUI_ID_SERVERLIST:
- gui::IGUIListBox *serverlist = (gui::IGUIListBox*)getElementFromId(GUI_ID_SERVERLIST);
- if (serverlist->getSelected() > -1)
- {
- acceptInput();
- quitMenu();
- return true;
- }
- }
- }
- }
-
- return Parent ? Parent->OnEvent(event) : false;
-}
-
-void GUIMainMenu::createNewWorld(std::wstring name, std::string gameid)
-{
- if(name == L"")
- return;
- acceptInput();
- m_data->create_world_name = name;
- m_data->create_world_gameid = gameid;
- quitMenu();
-}
-
-void GUIMainMenu::deleteWorld(const std::vector<std::string> &paths)
-{
- // Delete files
- bool did = fs::DeletePaths(paths);
- if(!did){
- wchar_t* text = wgettext("Failed to delete all world files");
- GUIMessageMenu *menu = new GUIMessageMenu(env, parent,
- -1, menumgr, text);
- delete[] text;
- menu->drop();
- }
- // Quit menu to refresh it
- acceptInput();
- m_data->only_refresh = true;
- quitMenu();
-}
-
-int GUIMainMenu::getTab()
-{
- gui::IGUIElement *e = getElementFromId(GUI_ID_TAB_CONTROL);
- if(e != NULL && e->getType() == gui::EGUIET_TAB_CONTROL)
- return ((gui::IGUITabControl*)e)->getActiveTab();
- return TAB_SINGLEPLAYER; // Default
-}
-
-void GUIMainMenu::displayMessageMenu(std::wstring msg)
-{
- (new GUIMessageMenu(env, parent, -1, menumgr, msg))->drop();
-}
-
-void GUIMainMenu::updateGuiServerList()
-{
- gui::IGUIListBox *serverlist = (gui::IGUIListBox *)getElementFromId(GUI_ID_SERVERLIST);
- serverlist->clear();
-
- for(std::vector<ServerListSpec>::iterator i = m_data->servers.begin();
- i != m_data->servers.end(); i++)
- {
- std::string text;
-
- if ((*i)["clients"].asString().size())
- text += (*i)["clients"].asString();
- if ((*i)["clients_max"].asString().size())
- text += "/" + (*i)["clients_max"].asString();
- text += " ";
- if ((*i)["version"].asString().size())
- text += (*i)["version"].asString() + " ";
- if ((*i)["password"].asString().size())
- text += "*";
- if ((*i)["creative"].asString().size())
- text += "C";
- if ((*i)["damage"].asString().size())
- text += "D";
- if ((*i)["pvp"].asString().size())
- text += "P";
- text += " ";
-
- if ((*i)["name"] != "" && (*i)["description"] != "")
- text += (*i)["name"].asString() + " (" + (*i)["description"].asString() + ")";
- else if ((*i)["name"] !="")
- text += (*i)["name"].asString();
- else
- text += (*i)["address"].asString() + ":" + (*i)["port"].asString();
-
- serverlist->addItem(narrow_to_wide(text).c_str());
- }
-}
-
-void GUIMainMenu::serverListOnSelected()
-{
- if (!m_data->servers.empty())
- {
- gui::IGUIListBox *serverlist = (gui::IGUIListBox*)getElementFromId(GUI_ID_SERVERLIST);
- u16 id = serverlist->getSelected();
- //if (id < 0) return; // u16>0!
- ((gui::IGUIEditBox*)getElementFromId(GUI_ID_ADDRESS_INPUT))
- ->setText(narrow_to_wide(m_data->servers[id]["address"].asString()).c_str());
- ((gui::IGUIEditBox*)getElementFromId(GUI_ID_PORT_INPUT))
- ->setText(narrow_to_wide(m_data->servers[id]["port"].asString()).c_str());
- }
-}
-
-ServerListSpec GUIMainMenu::getServerListSpec(std::string address, std::string port)
-{
- ServerListSpec server;
- server["address"] = address;
- server["port"] = port;
- for(std::vector<ServerListSpec>::iterator i = m_data->servers.begin();
- i != m_data->servers.end(); i++)
- {
- if ((*i)["address"] == address && (*i)["port"] == port)
- {
- server["description"] = (*i)["description"];
- server["name"] = (*i)["name"];
- break;
- }
- }
- return server;
-}
diff --git a/src/guiMainMenu.h b/src/guiMainMenu.h
index 8697344c8..5eaca23fa 100644
--- a/src/guiMainMenu.h
+++ b/src/guiMainMenu.h
@@ -24,15 +24,6 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "modalMenu.h"
#include <string>
#include <list>
-#include "subgame.h"
-#include "serverlist.h"
-
-class IGameCallback;
-
-enum {
- SERVERLIST_FAVORITES,
- SERVERLIST_PUBLIC,
-};
enum
{
@@ -45,113 +36,31 @@ enum
struct MainMenuData
{
- // These are in the native format of the gui elements
- // Generic
- int selected_tab;
- std::string selected_game;
- std::string selected_game_name;
// Client options
std::string servername;
std::string serverdescription;
- std::wstring address;
- std::wstring port;
- std::wstring name;
- std::wstring password;
- bool fancy_trees;
- bool smooth_lighting;
- bool clouds_3d;
- bool opaque_water;
- bool mip_map;
- bool anisotropic_filter;
- bool bilinear_filter;
- bool trilinear_filter;
- int enable_shaders;
- bool preload_item_visuals;
- bool enable_particles;
- bool liquid_finite;
+ std::string address;
+ std::string port;
+ std::string name;
+ std::string password;
+
// Server options
- bool creative_mode;
- bool enable_damage;
bool enable_public;
int selected_world;
bool simple_singleplayer_mode;
- // Actions
- std::wstring create_world_name;
- std::string create_world_gameid;
- bool only_refresh;
-
- int selected_serverlist;
- std::vector<WorldSpec> worlds;
- std::vector<SubgameSpec> games;
- std::vector<ServerListSpec> servers;
+ // Actions
+ bool kill;
+ //error handling
+ std::string errormessage;
MainMenuData():
- // Generic
- selected_tab(0),
- selected_game("minetest"),
- selected_game_name("Minetest"),
- // Client opts
- fancy_trees(false),
- smooth_lighting(false),
- // Server opts
- creative_mode(false),
- enable_damage(false),
enable_public(false),
selected_world(0),
simple_singleplayer_mode(false),
- // Actions
- only_refresh(false),
-
- selected_serverlist(SERVERLIST_FAVORITES)
+ errormessage("")
{}
};
-class GUIMainMenu : public GUIModalMenu
-{
-public:
- GUIMainMenu(gui::IGUIEnvironment* env,
- gui::IGUIElement* parent, s32 id,
- IMenuManager *menumgr,
- MainMenuData *data,
- IGameCallback *gamecallback);
- ~GUIMainMenu();
-
- void removeChildren();
- // Remove and re-add (or reposition) stuff
- void regenerateGui(v2u32 screensize);
- void drawMenu();
- void readInput(MainMenuData *dst);
- void acceptInput();
- bool getStatus()
- { return m_accepted; }
- bool OnEvent(const SEvent& event);
- void createNewWorld(std::wstring name, std::string gameid);
- void deleteWorld(const std::vector<std::string> &paths);
- int getTab();
- void displayMessageMenu(std::wstring msg);
-
-private:
- MainMenuData *m_data;
- bool m_accepted;
- IGameCallback *m_gamecallback;
-
- gui::IGUIEnvironment* env;
- gui::IGUIElement* parent;
- s32 id;
- IMenuManager *menumgr;
-
- std::vector<int> m_world_indices;
-
- bool m_is_regenerating;
- v2s32 m_topleft_client;
- v2s32 m_size_client;
- v2s32 m_topleft_server;
- v2s32 m_size_server;
- void updateGuiServerList();
- void serverListOnSelected();
- ServerListSpec getServerListSpec(std::string address, std::string port);
-};
-
#endif
diff --git a/src/main.cpp b/src/main.cpp
index eda992793..02bffa84d 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -78,6 +78,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "serverlist.h"
#include "sound.h"
#include "sound_openal.h"
+#include "guiEngine.h"
/*
Settings.
@@ -660,180 +661,6 @@ private:
bool rightreleased;
};
-struct MenuTextures
-{
- std::string current_gameid;
- bool global_textures;
- video::ITexture *background;
- video::ITexture *overlay;
- video::ITexture *header;
- video::ITexture *footer;
-
- MenuTextures():
- global_textures(false),
- background(NULL),
- overlay(NULL),
- header(NULL),
- footer(NULL)
- {}
-
- static video::ITexture* getMenuTexture(const std::string &tname,
- video::IVideoDriver* driver, const SubgameSpec *spec)
- {
- if(spec){
- std::string path;
- // eg. minetest_menu_background.png (for texture packs)
- std::string pack_tname = spec->id + "_menu_" + tname + ".png";
- path = getTexturePath(pack_tname);
- if(path != "")
- return driver->getTexture(path.c_str());
- // eg. games/minetest_game/menu/background.png
- path = getImagePath(spec->path + DIR_DELIM + "menu" + DIR_DELIM + tname + ".png");
- if(path != "")
- return driver->getTexture(path.c_str());
- } else {
- std::string path;
- // eg. menu_background.png
- std::string pack_tname = "menu_" + tname + ".png";
- path = getTexturePath(pack_tname);
- if(path != "")
- return driver->getTexture(path.c_str());
- }
- return NULL;
- }
-
- void update(video::IVideoDriver* driver, const SubgameSpec *spec, int tab)
- {
- if(tab == TAB_SINGLEPLAYER){
- if(spec->id == current_gameid)
- return;
- current_gameid = spec->id;
- global_textures = false;
- background = getMenuTexture("background", driver, spec);
- overlay = getMenuTexture("overlay", driver, spec);
- header = getMenuTexture("header", driver, spec);
- footer = getMenuTexture("footer", driver, spec);
- } else {
- if(global_textures)
- return;
- current_gameid = "";
- global_textures = true;
- background = getMenuTexture("background", driver, NULL);
- overlay = getMenuTexture("overlay", driver, NULL);
- header = getMenuTexture("header", driver, NULL);
- footer = getMenuTexture("footer", driver, NULL);
- }
- }
-};
-
-void drawMenuBackground(video::IVideoDriver* driver, const MenuTextures &menutextures)
-{
- v2u32 screensize = driver->getScreenSize();
- video::ITexture *texture = menutextures.background;
-
- /* If no texture, draw background of solid color */
- if(!texture){
- video::SColor color(255,80,58,37);
- core::rect<s32> rect(0, 0, screensize.X, screensize.Y);
- driver->draw2DRectangle(color, rect, NULL);
- return;
- }
-
- /* Draw background texture */
- v2u32 sourcesize = texture->getSize();
- driver->draw2DImage(texture,
- core::rect<s32>(0, 0, screensize.X, screensize.Y),
- core::rect<s32>(0, 0, sourcesize.X, sourcesize.Y),
- NULL, NULL, true);
-}
-
-void drawMenuOverlay(video::IVideoDriver* driver, const MenuTextures &menutextures)
-{
- v2u32 screensize = driver->getScreenSize();
- video::ITexture *texture = menutextures.overlay;
-
- /* If no texture, draw nothing */
- if(!texture)
- return;
-
- /* Draw overlay texture */
- v2u32 sourcesize = texture->getSize();
- driver->draw2DImage(texture,
- core::rect<s32>(0, 0, screensize.X, screensize.Y),
- core::rect<s32>(0, 0, sourcesize.X, sourcesize.Y),
- NULL, NULL, true);
-}
-
-void drawMenuHeader(video::IVideoDriver* driver, const MenuTextures &menutextures)
-{
- core::dimension2d<u32> screensize = driver->getScreenSize();
- video::ITexture *texture = menutextures.header;
-
- /* If no texture, draw nothing */
- if(!texture)
- return;
-
- f32 mult = (((f32)screensize.Width / 2)) /
- ((f32)texture->getOriginalSize().Width);
-
- v2s32 splashsize(((f32)texture->getOriginalSize().Width) * mult,
- ((f32)texture->getOriginalSize().Height) * mult);
-
- // Don't draw the header is there isn't enough room
- s32 free_space = (((s32)screensize.Height)-320)/2;
- if (free_space > splashsize.Y) {
- core::rect<s32> splashrect(0, 0, splashsize.X, splashsize.Y);
- splashrect += v2s32((screensize.Width/2)-(splashsize.X/2),
- ((free_space/2)-splashsize.Y/2)+10);
-
- video::SColor bgcolor(255,50,50,50);
-
- driver->draw2DImage(texture, splashrect,
- core::rect<s32>(core::position2d<s32>(0,0),
- core::dimension2di(texture->getSize())),
- NULL, NULL, true);
- }
-}
-
-void drawMenuFooter(video::IVideoDriver* driver, const MenuTextures &menutextures)
-{
- core::dimension2d<u32> screensize = driver->getScreenSize();
- video::ITexture *texture = menutextures.footer;
-
- /* If no texture, draw nothing */
- if(!texture)
- return;
-
- f32 mult = (((f32)screensize.Width)) /
- ((f32)texture->getOriginalSize().Width);
-
- v2s32 footersize(((f32)texture->getOriginalSize().Width) * mult,
- ((f32)texture->getOriginalSize().Height) * mult);
-
- // Don't draw the footer if there isn't enough room
- s32 free_space = (((s32)screensize.Height)-320)/2;
- if (free_space > footersize.Y) {
- core::rect<s32> rect(0,0,footersize.X,footersize.Y);
- rect += v2s32(screensize.Width/2,screensize.Height-footersize.Y);
- rect -= v2s32(footersize.X/2, 0);
-
- driver->draw2DImage(texture, rect,
- core::rect<s32>(core::position2d<s32>(0,0),
- core::dimension2di(texture->getSize())),
- NULL, NULL, true);
- }
-}
-
-static const SubgameSpec* getMenuGame(const MainMenuData &menudata)
-{
- for(size_t i=0; i<menudata.games.size(); i++){
- if(menudata.games[i].id == menudata.selected_game){
- return &menudata.games[i];
- }
- }
- return NULL;
-}
-
#endif // !SERVER
// These are defined global so that they're not optimized too much.
@@ -1730,83 +1557,37 @@ int main(int argc, char *argv[])
// Initialize menu data
MainMenuData menudata;
- if(g_settings->exists("selected_mainmenu_tab"))
- menudata.selected_tab = g_settings->getS32("selected_mainmenu_tab");
- if(g_settings->exists("selected_serverlist"))
- menudata.selected_serverlist = g_settings->getS32("selected_serverlist");
- if(g_settings->exists("selected_mainmenu_game")){
- menudata.selected_game = g_settings->get("selected_mainmenu_game");
- menudata.selected_game_name = findSubgame(menudata.selected_game).name;
- }
- menudata.address = narrow_to_wide(address);
- menudata.name = narrow_to_wide(playername);
- menudata.port = narrow_to_wide(itos(port));
+ menudata.kill = kill;
+ menudata.address = address;
+ menudata.name = playername;
+ menudata.port = itos(port);
+ menudata.errormessage = wide_to_narrow(error_message);
+ error_message = L"";
if(cmd_args.exists("password"))
- menudata.password = narrow_to_wide(cmd_args.get("password"));
- menudata.fancy_trees = g_settings->getBool("new_style_leaves");
- menudata.smooth_lighting = g_settings->getBool("smooth_lighting");
- menudata.clouds_3d = g_settings->getBool("enable_3d_clouds");
- menudata.opaque_water = g_settings->getBool("opaque_water");
- menudata.mip_map = g_settings->getBool("mip_map");
- menudata.anisotropic_filter = g_settings->getBool("anisotropic_filter");
- menudata.bilinear_filter = g_settings->getBool("bilinear_filter");
- menudata.trilinear_filter = g_settings->getBool("trilinear_filter");
- menudata.enable_shaders = g_settings->getS32("enable_shaders");
- menudata.preload_item_visuals = g_settings->getBool("preload_item_visuals");
- menudata.enable_particles = g_settings->getBool("enable_particles");
- menudata.liquid_finite = g_settings->getBool("liquid_finite");
- driver->setTextureCreationFlag(video::ETCF_CREATE_MIP_MAPS, menudata.mip_map);
- menudata.creative_mode = g_settings->getBool("creative_mode");
- menudata.enable_damage = g_settings->getBool("enable_damage");
+ menudata.password = cmd_args.get("password");
+
+ driver->setTextureCreationFlag(video::ETCF_CREATE_MIP_MAPS, g_settings->getBool("mip_map"));
+
menudata.enable_public = g_settings->getBool("server_announce");
- // Default to selecting nothing
- menudata.selected_world = -1;
- // Get world listing for the menu
+
std::vector<WorldSpec> worldspecs = getAvailableWorlds();
- // If there is only one world, select it
- if(worldspecs.size() == 1){
- menudata.selected_world = 0;
- }
- // Otherwise try to select according to selected_world_path
- else if(g_settings->exists("selected_world_path")){
- std::string trypath = g_settings->get("selected_world_path");
- for(u32 i=0; i<worldspecs.size(); i++){
- if(worldspecs[i].path == trypath){
- menudata.selected_world = i;
- break;
- }
- }
- }
+
// If a world was commanded, append and select it
if(commanded_world != ""){
+
std::string gameid = getWorldGameId(commanded_world, true);
std::string name = _("[--world parameter]");
if(gameid == ""){
gameid = g_settings->get("default_game");
name += " [new]";
}
- WorldSpec spec(commanded_world, name, gameid);
- worldspecs.push_back(spec);
- menudata.selected_world = worldspecs.size()-1;
- }
- // Copy worldspecs to menu
- menudata.worlds = worldspecs;
- // Get game listing
- menudata.games = getAvailableGames();
- // If selected game doesn't exist, take first from list
- if(findSubgame(menudata.selected_game).id == "" &&
- !menudata.games.empty()){
- menudata.selected_game = menudata.games[0].id;
+ //TODO find within worldspecs and set config
}
- const SubgameSpec *menugame = getMenuGame(menudata);
-
- MenuTextures menutextures;
- menutextures.update(driver, menugame, menudata.selected_tab);
if(skip_main_menu == false)
{
video::IVideoDriver* driver = device->getVideoDriver();
- float fps_max = g_settings->getFloat("fps_max");
+
infostream<<"Waiting for other menus"<<std::endl;
while(device->run() && kill == false)
{
@@ -1814,7 +1595,6 @@ int main(int argc, char *argv[])
break;
driver->beginScene(true, true,
video::SColor(255,128,128,128));
- drawMenuBackground(driver, menutextures);
guienv->drawAll();
driver->endScene();
// On some computers framerate doesn't seem to be
@@ -1823,170 +1603,40 @@ int main(int argc, char *argv[])
}
infostream<<"Waited for other menus"<<std::endl;
- GUIMainMenu *menu =
- new GUIMainMenu(guienv, guiroot, -1,
- &g_menumgr, &menudata, g_gamecallback);
- menu->allowFocusRemoval(true);
-
- if(error_message != L"")
- {
- verbosestream<<"error_message = "
- <<wide_to_narrow(error_message)<<std::endl;
-
- GUIMessageMenu *menu2 =
- new GUIMessageMenu(guienv, guiroot, -1,
- &g_menumgr, error_message.c_str());
- menu2->drop();
- error_message = L"";
- }
-
- // Time is in milliseconds, for clouds
- u32 lasttime = device->getTimer()->getTime();
+ GUIEngine* temp = new GUIEngine(device, guiroot, &g_menumgr,smgr,&menudata);
+
+ delete temp;
+ //once finished you'll never end up here
+ smgr->clear();
+ kill = menudata.kill;
- MenuMusicFetcher soundfetcher;
- ISoundManager *sound = NULL;
-#if USE_SOUND
- sound = createOpenALSoundManager(&soundfetcher);
-#endif
- if(!sound)
- sound = &dummySoundManager;
- SimpleSoundSpec spec;
- spec.name = "main_menu";
- spec.gain = 1;
- s32 handle = sound->playSound(spec, true);
-
- infostream<<"Created main menu"<<std::endl;
-
- while(device->run() && kill == false)
- {
- if(menu->getStatus() == true)
- break;
-
- // Game can be selected in the menu
- menugame = getMenuGame(menudata);
- menutextures.update(driver, menugame, menu->getTab());
- // Clouds for the main menu
- bool cloud_menu_background = g_settings->getBool("menu_clouds");
- if(menugame){
- // If game has regular background and no overlay, don't use clouds
- if(cloud_menu_background && menutextures.background &&
- !menutextures.overlay){
- cloud_menu_background = false;
- }
- // If game game has overlay and no regular background, always draw clouds
- else if(menutextures.overlay && !menutextures.background){
- cloud_menu_background = true;
- }
- }
-
- // Time calc for the clouds
- f32 dtime=0; // in seconds
- if (cloud_menu_background) {
- u32 time = device->getTimer()->getTime();
- if(time > lasttime)
- dtime = (time - lasttime) / 1000.0;
- else
- dtime = 0;
- lasttime = time;
- }
-
- //driver->beginScene(true, true, video::SColor(255,0,0,0));
- driver->beginScene(true, true, video::SColor(255,140,186,250));
-
- if (cloud_menu_background) {
- // *3 otherwise the clouds would move very slowly
- g_menuclouds->step(dtime*3);
- g_menuclouds->render();
- g_menucloudsmgr->drawAll();
- drawMenuOverlay(driver, menutextures);
- drawMenuHeader(driver, menutextures);
- drawMenuFooter(driver, menutextures);
- } else {
- drawMenuBackground(driver, menutextures);
- drawMenuHeader(driver, menutextures);
- drawMenuFooter(driver, menutextures);
- }
-
- guienv->drawAll();
-
- driver->endScene();
-
- // On some computers framerate doesn't seem to be
- // automatically limited
- if (cloud_menu_background) {
- // Time of frame without fps limit
- float busytime;
- u32 busytime_u32;
- // not using getRealTime is necessary for wine
- u32 time = device->getTimer()->getTime();
- if(time > lasttime)
- busytime_u32 = time - lasttime;
- else
- busytime_u32 = 0;
- busytime = busytime_u32 / 1000.0;
-
- // FPS limiter
- u32 frametime_min = 1000./fps_max;
-
- if(busytime_u32 < frametime_min) {
- u32 sleeptime = frametime_min - busytime_u32;
- device->sleep(sleeptime);
- }
- } else {
- sleep_ms(25);
- }
- }
- sound->stopSound(handle);
- if(sound != &dummySoundManager){
- delete sound;
- sound = NULL;
- }
-
- // Save controls status
- menu->readInput(&menudata);
+ }
- infostream<<"Dropping main menu"<<std::endl;
+ //update worldspecs (necessary as new world may have been created)
+ worldspecs = getAvailableWorlds();
- menu->drop();
- }
+ if (menudata.name == "")
+ menudata.name = std::string("Guest") + itos(myrand_range(1000,9999));
+ else
+ playername = menudata.name;
- playername = wide_to_narrow(menudata.name);
- if (playername == "")
- playername = std::string("Guest") + itos(myrand_range(1000,9999));
- password = translatePassword(playername, menudata.password);
+ password = translatePassword(playername, narrow_to_wide(menudata.password));
//infostream<<"Main: password hash: '"<<password<<"'"<<std::endl;
- address = wide_to_narrow(menudata.address);
- int newport = stoi(wide_to_narrow(menudata.port));
+ address = menudata.address;
+ int newport = stoi(menudata.port);
if(newport != 0)
port = newport;
+
simple_singleplayer_mode = menudata.simple_singleplayer_mode;
+
// Save settings
- g_settings->setS32("selected_mainmenu_tab", menudata.selected_tab);
- g_settings->setS32("selected_serverlist", menudata.selected_serverlist);
- g_settings->set("selected_mainmenu_game", menudata.selected_game);
- g_settings->set("new_style_leaves", itos(menudata.fancy_trees));
- g_settings->set("smooth_lighting", itos(menudata.smooth_lighting));
- g_settings->set("enable_3d_clouds", itos(menudata.clouds_3d));
- g_settings->set("opaque_water", itos(menudata.opaque_water));
-
- g_settings->set("mip_map", itos(menudata.mip_map));
- g_settings->set("anisotropic_filter", itos(menudata.anisotropic_filter));
- g_settings->set("bilinear_filter", itos(menudata.bilinear_filter));
- g_settings->set("trilinear_filter", itos(menudata.trilinear_filter));
-
- g_settings->setS32("enable_shaders", menudata.enable_shaders);
- g_settings->set("preload_item_visuals", itos(menudata.preload_item_visuals));
- g_settings->set("enable_particles", itos(menudata.enable_particles));
- g_settings->set("liquid_finite", itos(menudata.liquid_finite));
-
- g_settings->set("creative_mode", itos(menudata.creative_mode));
- g_settings->set("enable_damage", itos(menudata.enable_damage));
- g_settings->set("server_announce", itos(menudata.enable_public));
g_settings->set("name", playername);
g_settings->set("address", address);
g_settings->set("port", itos(port));
- if(menudata.selected_world != -1)
+
+ if((menudata.selected_world >= 0) &&
+ (menudata.selected_world < worldspecs.size()))
g_settings->set("selected_world_path",
worldspecs[menudata.selected_world].path);
@@ -2010,8 +1660,8 @@ int main(int argc, char *argv[])
{
ServerListSpec server;
server["name"] = menudata.servername;
- server["address"] = wide_to_narrow(menudata.address);
- server["port"] = wide_to_narrow(menudata.port);
+ server["address"] = menudata.address;
+ server["port"] = menudata.port;
server["description"] = menudata.serverdescription;
ServerList::insert(server);
}
@@ -2022,30 +1672,7 @@ int main(int argc, char *argv[])
infostream<<"Selected world: "<<worldspec.name
<<" ["<<worldspec.path<<"]"<<std::endl;
}
-
- // Only refresh if so requested
- if(menudata.only_refresh){
- infostream<<"Refreshing menu"<<std::endl;
- continue;
- }
- // Create new world if requested
- if(menudata.create_world_name != L"")
- {
- std::string path = porting::path_user + DIR_DELIM
- "worlds" + DIR_DELIM
- + wide_to_narrow(menudata.create_world_name);
- // Create world if it doesn't exist
- if(!initializeWorld(path, menudata.create_world_gameid)){
- error_message = wgettext("Failed to initialize world");
- errorstream<<wide_to_narrow(error_message)<<std::endl;
- continue;
- }
- g_settings->set("selected_world_path", path);
- g_settings->set("selected_mainmenu_game", menudata.create_world_gameid);
- continue;
- }
-
// If local game
if(current_address == "")
{
@@ -2085,8 +1712,10 @@ int main(int argc, char *argv[])
}
// Break out of menu-game loop to shut down cleanly
- if(device->run() == false || kill == true)
+ if(device->run() == false || kill == true) {
+ g_settings->updateConfigFile(configpath.c_str());
break;
+ }
/*
Run game
@@ -2138,11 +1767,11 @@ int main(int argc, char *argv[])
break;
}
} // Menu-game loop
-
-
+
+
g_menuclouds->drop();
g_menucloudsmgr->drop();
-
+
delete input;
/*
diff --git a/src/map.cpp b/src/map.cpp
index cf7dd6f9f..272bc9322 100644
--- a/src/map.cpp
+++ b/src/map.cpp
@@ -3237,17 +3237,18 @@ v2s16 ServerMap::getSectorPos(std::string dirname)
{
unsigned int x, y;
int r;
- size_t spos = dirname.rfind(DIR_DELIM_C) + 1;
- assert(spos != std::string::npos);
- if(dirname.size() - spos == 8)
+ std::string component;
+ fs::RemoveLastPathComponent(dirname, &component, 1);
+ if(component.size() == 8)
{
// Old layout
- r = sscanf(dirname.substr(spos).c_str(), "%4x%4x", &x, &y);
+ r = sscanf(component.c_str(), "%4x%4x", &x, &y);
}
- else if(dirname.size() - spos == 3)
+ else if(component.size() == 3)
{
// New layout
- r = sscanf(dirname.substr(spos-4).c_str(), "%3x" DIR_DELIM "%3x", &x, &y);
+ fs::RemoveLastPathComponent(dirname, &component, 2);
+ r = sscanf(component.c_str(), "%3x" DIR_DELIM "%3x", &x, &y);
// Sign-extend the 12 bit values up to 16 bits...
if(x&0x800) x|=0xF000;
if(y&0x800) y|=0xF000;
diff --git a/src/mods.cpp b/src/mods.cpp
index 9bcf73aa7..75c2dd89c 100644
--- a/src/mods.cpp
+++ b/src/mods.cpp
@@ -18,6 +18,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
*/
#include "mods.h"
+#include "main.h"
#include "filesys.h"
#include "strfnd.h"
#include "log.h"
@@ -25,6 +26,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "settings.h"
#include "strfnd.h"
#include <cctype>
+#include "convert_json.h"
static bool parseDependsLine(std::istream &is,
std::string &dep, std::set<char> &symbols)
@@ -389,3 +391,29 @@ void ModConfiguration::resolveDependencies()
// Step 4: write back list of unsatisfied mods
m_unsatisfied_mods.assign(unsatisfied.begin(), unsatisfied.end());
}
+
+#if USE_CURL
+Json::Value getModstoreUrl(std::string url)
+{
+ struct curl_slist *chunk = NULL;
+
+ bool special_http_header = true;
+
+ try{
+ special_http_header = g_settings->getBool("modstore_disable_special_http_header");
+ }
+ catch(SettingNotFoundException &e) {
+ }
+
+ if (special_http_header)
+ chunk = curl_slist_append(chunk, "Accept: application/vnd.minetest.mmdb-v1+json");
+
+ Json::Value retval = fetchJsonValue(url,chunk);
+
+ if (chunk != NULL)
+ curl_slist_free_all(chunk);
+
+ return retval;
+}
+
+#endif
diff --git a/src/mods.h b/src/mods.h
index a8100fcfd..eb453bf6a 100644
--- a/src/mods.h
+++ b/src/mods.h
@@ -29,6 +29,12 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include <map>
#include <exception>
#include <list>
+#include "json/json.h"
+#include "config.h"
+
+#if USE_CURL
+#include <curl/curl.h>
+#endif
#define MODNAME_ALLOWED_CHARS "abcdefghijklmnopqrstuvwxyz0123456789_"
@@ -154,4 +160,66 @@ private:
};
+#if USE_CURL
+Json::Value getModstoreUrl(std::string url);
+#else
+inline Json::Value getModstoreUrl(std::string url) {
+ return Json::Value();
+}
+#endif
+
+struct ModLicenseInfo {
+ int id;
+ std::string shortinfo;
+ std::string url;
+};
+
+struct ModAuthorInfo {
+ int id;
+ std::string username;
+};
+
+struct ModStoreMod {
+ int id;
+ std::string title;
+ std::string basename;
+ ModAuthorInfo author;
+ float rating;
+ bool valid;
+};
+
+struct ModStoreCategoryInfo {
+ int id;
+ std::string name;
+};
+
+struct ModStoreVersionEntry {
+ int id;
+ std::string date;
+ std::string file;
+ bool approved;
+ //ugly version number
+ int mtversion;
+};
+
+struct ModStoreModDetails {
+ /* version_set?? */
+ std::vector<ModStoreCategoryInfo> categories;
+ ModAuthorInfo author;
+ ModLicenseInfo license;
+ int id;
+ std::string title;
+ std::string basename;
+ std::string description;
+ std::string repository;
+ float rating;
+ std::vector<std::string> depends;
+ std::vector<std::string> softdeps;
+
+ std::string download_url;
+ std::string screenshot_url;
+ std::vector<ModStoreVersionEntry> versions;
+ bool valid;
+};
+
#endif
diff --git a/src/serverlist.cpp b/src/serverlist.cpp
index ea5a616c2..6a68e0eac 100644
--- a/src/serverlist.cpp
+++ b/src/serverlist.cpp
@@ -28,6 +28,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "porting.h"
#include "log.h"
#include "json/json.h"
+#include "convert_json.h"
#if USE_CURL
#include <curl/curl.h>
#endif
@@ -68,35 +69,22 @@ std::vector<ServerListSpec> getLocal()
#if USE_CURL
-
-static size_t WriteCallback(void *contents, size_t size, size_t nmemb, void *userp)
-{
- ((std::string*)userp)->append((char*)contents, size * nmemb);
- return size * nmemb;
-}
-
-
std::vector<ServerListSpec> getOnline()
{
- std::string liststring;
- CURL *curl;
+ Json::Value root = fetchJsonValue((g_settings->get("serverlist_url")+"/list").c_str(),0);
- curl = curl_easy_init();
- if (curl)
- {
- CURLcode res;
-
- curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1);
- curl_easy_setopt(curl, CURLOPT_URL, (g_settings->get("serverlist_url")+"/list").c_str());
- curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, ServerList::WriteCallback);
- curl_easy_setopt(curl, CURLOPT_WRITEDATA, &liststring);
+ std::vector<ServerListSpec> serverlist;
- res = curl_easy_perform(curl);
- if (res != CURLE_OK)
- errorstream<<"Serverlist at url "<<g_settings->get("serverlist_url")<<" not found (internet connection?)"<<std::endl;
- curl_easy_cleanup(curl);
+ if (root.isArray()) {
+ for (unsigned int i = 0; i < root.size(); i++)
+ {
+ if (root[i].isObject()) {
+ serverlist.push_back(root[i]);
+ }
+ }
}
- return ServerList::deSerializeJson(liststring);
+
+ return serverlist;
}
#endif
@@ -189,30 +177,6 @@ std::string serialize(std::vector<ServerListSpec> serverlist)
return liststring;
}
-std::vector<ServerListSpec> deSerializeJson(std::string liststring)
-{
- std::vector<ServerListSpec> serverlist;
- Json::Value root;
- Json::Reader reader;
- std::istringstream stream(liststring);
- if (!liststring.size()) {
- return serverlist;
- }
- if (!reader.parse( stream, root ) )
- {
- errorstream << "Failed to parse server list " << reader.getFormattedErrorMessages();
- return serverlist;
- }
- if (root["list"].isArray())
- for (unsigned int i = 0; i < root["list"].size(); i++)
- {
- if (root["list"][i].isObject()) {
- serverlist.push_back(root["list"][i]);
- }
- }
- return serverlist;
-}
-
std::string serializeJson(std::vector<ServerListSpec> serverlist)
{
Json::Value root;
diff --git a/src/test.cpp b/src/test.cpp
index b66f65daf..e609fe26f 100644
--- a/src/test.cpp
+++ b/src/test.cpp
@@ -36,6 +36,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "settings.h"
#include "log.h"
#include "util/string.h"
+#include "filesys.h"
#include "voxelalgorithms.h"
#include "inventory.h"
#include "util/numeric.h"
@@ -171,6 +172,212 @@ struct TestUtilities: public TestBase
}
};
+struct TestPath: public TestBase
+{
+ // adjusts a POSIX path to system-specific conventions
+ // -> changes '/' to DIR_DELIM
+ // -> absolute paths start with "C:\\" on windows
+ std::string p(std::string path)
+ {
+ for(size_t i = 0; i < path.size(); ++i){
+ if(path[i] == '/'){
+ path.replace(i, 1, DIR_DELIM);
+ i += std::string(DIR_DELIM).size() - 1; // generally a no-op
+ }
+ }
+
+ #ifdef _WIN32
+ if(path[0] == '\\')
+ path = "C:" + path;
+ #endif
+
+ return path;
+ }
+
+ void Run()
+ {
+ std::string path, result, removed;
+
+ /*
+ Test fs::IsDirDelimiter
+ */
+ UASSERT(fs::IsDirDelimiter('/') == true);
+ UASSERT(fs::IsDirDelimiter('A') == false);
+ UASSERT(fs::IsDirDelimiter(0) == false);
+ #ifdef _WIN32
+ UASSERT(fs::IsDirDelimiter('\\') == true);
+ #else
+ UASSERT(fs::IsDirDelimiter('\\') == false);
+ #endif
+
+ /*
+ Test fs::PathStartsWith
+ */
+ {
+ const int numpaths = 12;
+ std::string paths[numpaths] = {
+ "",
+ p("/"),
+ p("/home/user/minetest"),
+ p("/home/user/minetest/bin"),
+ p("/home/user/.minetest"),
+ p("/tmp/dir/file"),
+ p("/tmp/file/"),
+ p("/tmP/file"),
+ p("/tmp"),
+ p("/tmp/dir"),
+ p("/home/user2/minetest/worlds"),
+ p("/home/user2/minetest/world"),
+ };
+ /*
+ expected fs::PathStartsWith results
+ 0 = returns false
+ 1 = returns true
+ 2 = returns false on windows, false elsewhere
+ 3 = returns true on windows, true elsewhere
+ 4 = returns true if and only if
+ FILESYS_CASE_INSENSITIVE is true
+ */
+ int expected_results[numpaths][numpaths] = {
+ {1,2,0,0,0,0,0,0,0,0,0,0},
+ {1,1,0,0,0,0,0,0,0,0,0,0},
+ {1,1,1,0,0,0,0,0,0,0,0,0},
+ {1,1,1,1,0,0,0,0,0,0,0,0},
+ {1,1,0,0,1,0,0,0,0,0,0,0},
+ {1,1,0,0,0,1,0,0,1,1,0,0},
+ {1,1,0,0,0,0,1,4,1,0,0,0},
+ {1,1,0,0,0,0,4,1,4,0,0,0},
+ {1,1,0,0,0,0,0,0,1,0,0,0},
+ {1,1,0,0,0,0,0,0,1,1,0,0},
+ {1,1,0,0,0,0,0,0,0,0,1,0},
+ {1,1,0,0,0,0,0,0,0,0,0,1},
+ };
+
+ for (int i = 0; i < numpaths; i++)
+ for (int j = 0; j < numpaths; j++){
+ /*verbosestream<<"testing fs::PathStartsWith(\""
+ <<paths[i]<<"\", \""
+ <<paths[j]<<"\")"<<std::endl;*/
+ bool starts = fs::PathStartsWith(paths[i], paths[j]);
+ int expected = expected_results[i][j];
+ if(expected == 0){
+ UASSERT(starts == false);
+ }
+ else if(expected == 1){
+ UASSERT(starts == true);
+ }
+ #ifdef _WIN32
+ else if(expected == 2){
+ UASSERT(starts == false);
+ }
+ else if(expected == 3){
+ UASSERT(starts == true);
+ }
+ #else
+ else if(expected == 2){
+ UASSERT(starts == true);
+ }
+ else if(expected == 3){
+ UASSERT(starts == false);
+ }
+ #endif
+ else if(expected == 4){
+ UASSERT(starts == (bool)FILESYS_CASE_INSENSITIVE);
+ }
+ }
+ }
+
+ /*
+ Test fs::RemoveLastPathComponent
+ */
+ UASSERT(fs::RemoveLastPathComponent("") == "");
+ path = p("/home/user/minetest/bin/..//worlds/world1");
+ result = fs::RemoveLastPathComponent(path, &removed, 0);
+ UASSERT(result == path);
+ UASSERT(removed == "");
+ result = fs::RemoveLastPathComponent(path, &removed, 1);
+ UASSERT(result == p("/home/user/minetest/bin/..//worlds"));
+ UASSERT(removed == p("world1"));
+ result = fs::RemoveLastPathComponent(path, &removed, 2);
+ UASSERT(result == p("/home/user/minetest/bin/.."));
+ UASSERT(removed == p("worlds/world1"));
+ result = fs::RemoveLastPathComponent(path, &removed, 3);
+ UASSERT(result == p("/home/user/minetest/bin"));
+ UASSERT(removed == p("../worlds/world1"));
+ result = fs::RemoveLastPathComponent(path, &removed, 4);
+ UASSERT(result == p("/home/user/minetest"));
+ UASSERT(removed == p("bin/../worlds/world1"));
+ result = fs::RemoveLastPathComponent(path, &removed, 5);
+ UASSERT(result == p("/home/user"));
+ UASSERT(removed == p("minetest/bin/../worlds/world1"));
+ result = fs::RemoveLastPathComponent(path, &removed, 6);
+ UASSERT(result == p("/home"));
+ UASSERT(removed == p("user/minetest/bin/../worlds/world1"));
+ result = fs::RemoveLastPathComponent(path, &removed, 7);
+ #ifdef _WIN32
+ UASSERT(result == "C:");
+ #else
+ UASSERT(result == "");
+ #endif
+ UASSERT(removed == p("home/user/minetest/bin/../worlds/world1"));
+
+ /*
+ Now repeat the test with a trailing delimiter
+ */
+ path = p("/home/user/minetest/bin/..//worlds/world1/");
+ result = fs::RemoveLastPathComponent(path, &removed, 0);
+ UASSERT(result == path);
+ UASSERT(removed == "");
+ result = fs::RemoveLastPathComponent(path, &removed, 1);
+ UASSERT(result == p("/home/user/minetest/bin/..//worlds"));
+ UASSERT(removed == p("world1"));
+ result = fs::RemoveLastPathComponent(path, &removed, 2);
+ UASSERT(result == p("/home/user/minetest/bin/.."));
+ UASSERT(removed == p("worlds/world1"));
+ result = fs::RemoveLastPathComponent(path, &removed, 3);
+ UASSERT(result == p("/home/user/minetest/bin"));
+ UASSERT(removed == p("../worlds/world1"));
+ result = fs::RemoveLastPathComponent(path, &removed, 4);
+ UASSERT(result == p("/home/user/minetest"));
+ UASSERT(removed == p("bin/../worlds/world1"));
+ result = fs::RemoveLastPathComponent(path, &removed, 5);
+ UASSERT(result == p("/home/user"));
+ UASSERT(removed == p("minetest/bin/../worlds/world1"));
+ result = fs::RemoveLastPathComponent(path, &removed, 6);
+ UASSERT(result == p("/home"));
+ UASSERT(removed == p("user/minetest/bin/../worlds/world1"));
+ result = fs::RemoveLastPathComponent(path, &removed, 7);
+ #ifdef _WIN32
+ UASSERT(result == "C:");
+ #else
+ UASSERT(result == "");
+ #endif
+ UASSERT(removed == p("home/user/minetest/bin/../worlds/world1"));
+
+ /*
+ Test fs::RemoveRelativePathComponent
+ */
+ path = p("/home/user/minetest/bin");
+ result = fs::RemoveRelativePathComponents(path);
+ UASSERT(result == path);
+ path = p("/home/user/minetest/bin/../worlds/world1");
+ result = fs::RemoveRelativePathComponents(path);
+ UASSERT(result == p("/home/user/minetest/worlds/world1"));
+ path = p("/home/user/minetest/bin/../worlds/world1/");
+ result = fs::RemoveRelativePathComponents(path);
+ UASSERT(result == p("/home/user/minetest/worlds/world1"));
+ path = p(".");
+ result = fs::RemoveRelativePathComponents(path);
+ UASSERT(result == "");
+ path = p("./subdir/../..");
+ result = fs::RemoveRelativePathComponents(path);
+ UASSERT(result == "");
+ path = p("/a/b/c/.././../d/../e/f/g/../h/i/j/../../../..");
+ result = fs::RemoveRelativePathComponents(path);
+ UASSERT(result == p("/a/e"));
+ }
+};
+
struct TestSettings: public TestBase
{
void Run()
@@ -1785,6 +1992,7 @@ void run_tests()
infostream<<"run_tests() started"<<std::endl;
TEST(TestUtilities);
+ TEST(TestPath);
TEST(TestSettings);
TEST(TestCompress);
TEST(TestSerialization);
diff --git a/textures/base/pack/no_screenshot.png b/textures/base/pack/no_screenshot.png
new file mode 100644
index 000000000..e309a3eed
--- /dev/null
+++ b/textures/base/pack/no_screenshot.png
Binary files differ