tab_local.lua 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468
  1. -- Luanti
  2. -- Copyright (C) 2014 sapier
  3. -- SPDX-License-Identifier: LGPL-2.1-or-later
  4. local current_game, singleplayer_refresh_gamebar
  5. local valid_disabled_settings = {
  6. ["enable_damage"]=true,
  7. ["creative_mode"]=true,
  8. ["enable_server"]=true,
  9. }
  10. -- Name and port stored to persist when updating the formspec
  11. local current_name = core.settings:get("name")
  12. local current_port = core.settings:get("port")
  13. -- Currently chosen game in gamebar for theming and filtering
  14. function current_game()
  15. local gameid = core.settings:get("menu_last_game")
  16. local game = gameid and pkgmgr.find_by_gameid(gameid)
  17. -- Fall back to first game installed if one exists.
  18. if not game and #pkgmgr.games > 0 then
  19. -- If devtest is the first game in the list and there is another
  20. -- game available, pick the other game instead.
  21. local picked_game
  22. if pkgmgr.games[1].id == "devtest" and #pkgmgr.games > 1 then
  23. picked_game = 2
  24. else
  25. picked_game = 1
  26. end
  27. game = pkgmgr.games[picked_game]
  28. gameid = game.id
  29. core.settings:set("menu_last_game", gameid)
  30. end
  31. return game
  32. end
  33. -- Apply menu changes from given game
  34. function apply_game(game)
  35. core.settings:set("menu_last_game", game.id)
  36. menudata.worldlist:set_filtercriteria(game.id)
  37. mm_game_theme.set_game(game)
  38. local index = filterlist.get_current_index(menudata.worldlist,
  39. tonumber(core.settings:get("mainmenu_last_selected_world")))
  40. if not index or index < 1 then
  41. local selected = core.get_textlist_index("sp_worlds")
  42. if selected ~= nil and selected < #menudata.worldlist:get_list() then
  43. index = selected
  44. else
  45. index = #menudata.worldlist:get_list()
  46. end
  47. end
  48. menu_worldmt_legacy(index)
  49. end
  50. function singleplayer_refresh_gamebar()
  51. local old_bar = ui.find_by_name("game_button_bar")
  52. if old_bar ~= nil then
  53. old_bar:delete()
  54. end
  55. -- Hide gamebar if no games are installed
  56. if #pkgmgr.games == 0 then
  57. return false
  58. end
  59. local function game_buttonbar_button_handler(fields)
  60. for _, game in ipairs(pkgmgr.games) do
  61. if fields["game_btnbar_" .. game.id] then
  62. apply_game(game)
  63. return true
  64. end
  65. end
  66. end
  67. local TOUCH_GUI = core.settings:get_bool("touch_gui")
  68. local gamebar_pos_y = MAIN_TAB_H
  69. + TABHEADER_H -- tabheader included in formspec size
  70. + (TOUCH_GUI and GAMEBAR_OFFSET_TOUCH or GAMEBAR_OFFSET_DESKTOP)
  71. local btnbar = buttonbar_create(
  72. "game_button_bar",
  73. {x = 0, y = gamebar_pos_y},
  74. {x = MAIN_TAB_W, y = GAMEBAR_H},
  75. "#000000",
  76. game_buttonbar_button_handler)
  77. for _, game in ipairs(pkgmgr.games) do
  78. local btn_name = "game_btnbar_" .. game.id
  79. local image = nil
  80. local text = nil
  81. local tooltip = core.formspec_escape(game.title)
  82. if (game.menuicon_path or "") ~= "" then
  83. image = core.formspec_escape(game.menuicon_path)
  84. else
  85. local part1 = game.id:sub(1,5)
  86. local part2 = game.id:sub(6,10)
  87. local part3 = game.id:sub(11)
  88. text = part1 .. "\n" .. part2
  89. if part3 ~= "" then
  90. text = text .. "\n" .. part3
  91. end
  92. end
  93. btnbar:add_button(btn_name, text, image, tooltip)
  94. end
  95. local plus_image = core.formspec_escape(defaulttexturedir .. "plus.png")
  96. btnbar:add_button("game_open_cdb", "", plus_image, fgettext("Install games from ContentDB"))
  97. return true
  98. end
  99. local function get_disabled_settings(game)
  100. if not game then
  101. return {}
  102. end
  103. local gameconfig = Settings(game.path .. "/game.conf")
  104. local disabled_settings = {}
  105. if gameconfig then
  106. local disabled_settings_str = (gameconfig:get("disabled_settings") or ""):split()
  107. for _, value in pairs(disabled_settings_str) do
  108. local state = false
  109. value = value:trim()
  110. if string.sub(value, 1, 1) == "!" then
  111. state = true
  112. value = string.sub(value, 2)
  113. end
  114. if valid_disabled_settings[value] then
  115. disabled_settings[value] = state
  116. else
  117. core.log("error", "Invalid disabled setting in game.conf: "..tostring(value))
  118. end
  119. end
  120. end
  121. return disabled_settings
  122. end
  123. local function get_formspec(tabview, name, tabdata)
  124. -- Point the player to ContentDB when no games are found
  125. if #pkgmgr.games == 0 then
  126. local W = tabview.width
  127. local H = tabview.height
  128. local hypertext = "<global valign=middle halign=center size=18>" ..
  129. fgettext_ne("Luanti is a game-creation platform that allows you to play many different games.") .. "\n" ..
  130. fgettext_ne("Luanti doesn't come with a game by default.") .. " " ..
  131. fgettext_ne("You need to install a game before you can create a world.")
  132. local button_y = H * 2/3 - 0.6
  133. return table.concat({
  134. "hypertext[0.375,0;", W - 2*0.375, ",", button_y, ";ht;", core.formspec_escape(hypertext), "]",
  135. "button[5.25,", button_y, ";5,1.2;game_open_cdb;", fgettext("Install a game"), "]"})
  136. end
  137. local retval = ""
  138. local index = filterlist.get_current_index(menudata.worldlist,
  139. tonumber(core.settings:get("mainmenu_last_selected_world")))
  140. local list = menudata.worldlist:get_list()
  141. local world = list and index and list[index]
  142. local game
  143. if world then
  144. game = pkgmgr.find_by_gameid(world.gameid)
  145. else
  146. game = current_game()
  147. end
  148. local disabled_settings = get_disabled_settings(game)
  149. local creative, damage, host = "", "", ""
  150. -- Y offsets for game settings checkboxes
  151. local y = 0.2
  152. local yo = 0.5625
  153. if disabled_settings["creative_mode"] == nil then
  154. creative = "checkbox[0,"..y..";cb_creative_mode;".. fgettext("Creative Mode") .. ";" ..
  155. dump(core.settings:get_bool("creative_mode")) .. "]"
  156. y = y + yo
  157. end
  158. if disabled_settings["enable_damage"] == nil then
  159. damage = "checkbox[0,"..y..";cb_enable_damage;".. fgettext("Enable Damage") .. ";" ..
  160. dump(core.settings:get_bool("enable_damage")) .. "]"
  161. y = y + yo
  162. end
  163. if disabled_settings["enable_server"] == nil then
  164. host = "checkbox[0,"..y..";cb_server;".. fgettext("Host Server") ..";" ..
  165. dump(core.settings:get_bool("enable_server")) .. "]"
  166. y = y + yo
  167. end
  168. retval = retval ..
  169. "container[5.25,4.875]" ..
  170. "button[0,0;3.225,0.8;world_delete;".. fgettext("Delete") .. "]" ..
  171. "button[3.325,0;3.225,0.8;world_configure;".. fgettext("Select Mods") .. "]" ..
  172. "button[6.65,0;3.225,0.8;world_create;".. fgettext("New") .. "]" ..
  173. "container_end[]" ..
  174. "container[0.375,0.375]" ..
  175. creative ..
  176. damage ..
  177. host ..
  178. "container_end[]" ..
  179. "container[5.25,0.375]" ..
  180. "label[0,0.2;".. fgettext("Select World:") .. "]"..
  181. "textlist[0,0.5;9.875,3.9;sp_worlds;" ..
  182. menu_render_worldlist() ..
  183. ";" .. index .. "]" ..
  184. "container_end[]"
  185. if core.settings:get_bool("enable_server") and disabled_settings["enable_server"] == nil then
  186. retval = retval ..
  187. "button[10.1875,5.925;4.9375,0.8;play;".. fgettext("Host Game") .. "]" ..
  188. "container[0.375,0.375]" ..
  189. "checkbox[0,"..y..";cb_server_announce;" .. fgettext("Announce Server") .. ";" ..
  190. dump(core.settings:get_bool("server_announce")) .. "]"
  191. -- Reset y so that the text fields always start at the same position,
  192. -- regardless of whether some of the checkboxes are hidden.
  193. y = 0.2 + 4 * yo + 0.35
  194. retval = retval .. "field[0," .. y .. ";4.5,0.75;te_playername;" .. fgettext("Name") .. ";" ..
  195. core.formspec_escape(current_name) .. "]"
  196. y = y + 1.15 + 0.25
  197. retval = retval .. "pwdfield[0," .. y .. ";4.5,0.75;te_passwd;" .. fgettext("Password") .. "]"
  198. y = y + 1.15 + 0.25
  199. local bind_addr = core.settings:get("bind_address")
  200. if bind_addr ~= nil and bind_addr ~= "" then
  201. retval = retval ..
  202. "field[0," .. y .. ";3,0.75;te_serveraddr;" .. fgettext("Bind Address") .. ";" ..
  203. core.formspec_escape(core.settings:get("bind_address")) .. "]" ..
  204. "field[3.25," .. y .. ";1.25,0.75;te_serverport;" .. fgettext("Port") .. ";" ..
  205. core.formspec_escape(current_port) .. "]"
  206. else
  207. retval = retval ..
  208. "field[0," .. y .. ";4.5,0.75;te_serverport;" .. fgettext("Server Port") .. ";" ..
  209. core.formspec_escape(current_port) .. "]"
  210. end
  211. retval = retval .. "container_end[]"
  212. else
  213. retval = retval ..
  214. "button[10.1875,5.925;4.9375,0.8;play;" .. fgettext("Play Game") .. "]"
  215. end
  216. return retval
  217. end
  218. local function main_button_handler(this, fields, name, tabdata)
  219. assert(name == "local")
  220. if fields.game_open_cdb then
  221. local maintab = ui.find_by_name("maintab")
  222. local dlg = create_contentdb_dlg("game")
  223. dlg:set_parent(maintab)
  224. maintab:hide()
  225. dlg:show()
  226. return true
  227. end
  228. if this.dlg_create_world_closed_at == nil then
  229. this.dlg_create_world_closed_at = 0
  230. end
  231. local world_doubleclick = false
  232. if fields["te_playername"] then
  233. current_name = fields["te_playername"]
  234. end
  235. if fields["te_serverport"] then
  236. current_port = fields["te_serverport"]
  237. end
  238. if fields["sp_worlds"] ~= nil then
  239. local event = core.explode_textlist_event(fields["sp_worlds"])
  240. local selected = core.get_textlist_index("sp_worlds")
  241. menu_worldmt_legacy(selected)
  242. if event.type == "DCL" then
  243. world_doubleclick = true
  244. end
  245. if event.type == "CHG" and selected ~= nil then
  246. core.settings:set("mainmenu_last_selected_world",
  247. menudata.worldlist:get_raw_index(selected))
  248. return true
  249. end
  250. end
  251. if menu_handle_key_up_down(fields,"sp_worlds","mainmenu_last_selected_world") then
  252. return true
  253. end
  254. if fields["cb_creative_mode"] then
  255. core.settings:set("creative_mode", fields["cb_creative_mode"])
  256. local selected = core.get_textlist_index("sp_worlds")
  257. menu_worldmt(selected, "creative_mode", fields["cb_creative_mode"])
  258. return true
  259. end
  260. if fields["cb_enable_damage"] then
  261. core.settings:set("enable_damage", fields["cb_enable_damage"])
  262. local selected = core.get_textlist_index("sp_worlds")
  263. menu_worldmt(selected, "enable_damage", fields["cb_enable_damage"])
  264. return true
  265. end
  266. if fields["cb_server"] then
  267. core.settings:set("enable_server", fields["cb_server"])
  268. return true
  269. end
  270. if fields["cb_server_announce"] then
  271. core.settings:set("server_announce", fields["cb_server_announce"])
  272. local selected = core.get_textlist_index("srv_worlds")
  273. menu_worldmt(selected, "server_announce", fields["cb_server_announce"])
  274. return true
  275. end
  276. if fields["play"] ~= nil or world_doubleclick or fields["key_enter"] then
  277. local enter_key_duration = core.get_us_time() - this.dlg_create_world_closed_at
  278. if world_doubleclick and enter_key_duration <= 200000 then -- 200 ms
  279. this.dlg_create_world_closed_at = 0
  280. return true
  281. end
  282. local selected = core.get_textlist_index("sp_worlds")
  283. gamedata.selected_world = menudata.worldlist:get_raw_index(selected)
  284. if selected == nil or gamedata.selected_world == 0 then
  285. gamedata.errormessage =
  286. fgettext_ne("No world created or selected!")
  287. return true
  288. end
  289. -- Update last game
  290. local world = menudata.worldlist:get_raw_element(gamedata.selected_world)
  291. local game_obj
  292. if world then
  293. game_obj = pkgmgr.find_by_gameid(world.gameid)
  294. core.settings:set("menu_last_game", game_obj.id)
  295. end
  296. local disabled_settings = get_disabled_settings(game_obj)
  297. for k, _ in pairs(valid_disabled_settings) do
  298. local v = disabled_settings[k]
  299. if v ~= nil then
  300. if k == "enable_server" and v == true then
  301. error("Setting 'enable_server' cannot be force-enabled! The game.conf needs to be fixed.")
  302. end
  303. core.settings:set_bool(k, disabled_settings[k])
  304. end
  305. end
  306. if core.settings:get_bool("enable_server") then
  307. gamedata.playername = fields["te_playername"]
  308. gamedata.password = fields["te_passwd"]
  309. gamedata.port = fields["te_serverport"]
  310. gamedata.address = ""
  311. core.settings:set("port",gamedata.port)
  312. if fields["te_serveraddr"] ~= nil then
  313. core.settings:set("bind_address",fields["te_serveraddr"])
  314. end
  315. else
  316. gamedata.singleplayer = true
  317. end
  318. core.start()
  319. return true
  320. end
  321. if fields["world_create"] ~= nil then
  322. this.dlg_create_world_closed_at = 0
  323. local create_world_dlg = create_create_world_dlg()
  324. create_world_dlg:set_parent(this)
  325. this:hide()
  326. create_world_dlg:show()
  327. return true
  328. end
  329. if fields["world_delete"] ~= nil then
  330. local selected = core.get_textlist_index("sp_worlds")
  331. if selected ~= nil and
  332. selected <= menudata.worldlist:size() then
  333. local world = menudata.worldlist:get_list()[selected]
  334. if world ~= nil and
  335. world.name ~= nil and
  336. world.name ~= "" then
  337. local index = menudata.worldlist:get_raw_index(selected)
  338. local delete_world_dlg = create_delete_world_dlg(world.name,index)
  339. delete_world_dlg:set_parent(this)
  340. this:hide()
  341. delete_world_dlg:show()
  342. end
  343. end
  344. return true
  345. end
  346. if fields["world_configure"] ~= nil then
  347. local selected = core.get_textlist_index("sp_worlds")
  348. if selected ~= nil then
  349. local configdialog =
  350. create_configure_world_dlg(
  351. menudata.worldlist:get_raw_index(selected))
  352. if (configdialog ~= nil) then
  353. configdialog:set_parent(this)
  354. this:hide()
  355. configdialog:show()
  356. end
  357. end
  358. return true
  359. end
  360. end
  361. local function on_change(type)
  362. if type == "ENTER" then
  363. local game = current_game()
  364. if game then
  365. apply_game(game)
  366. else
  367. mm_game_theme.set_engine()
  368. end
  369. if singleplayer_refresh_gamebar() then
  370. ui.find_by_name("game_button_bar"):show()
  371. end
  372. elseif type == "LEAVE" then
  373. menudata.worldlist:set_filtercriteria(nil)
  374. local gamebar = ui.find_by_name("game_button_bar")
  375. if gamebar then
  376. gamebar:hide()
  377. end
  378. end
  379. end
  380. --------------------------------------------------------------------------------
  381. return {
  382. name = "local",
  383. caption = fgettext("Start Game"),
  384. cbf_formspec = get_formspec,
  385. cbf_button_handler = main_button_handler,
  386. on_change = on_change
  387. }