dlg_create_world.lua 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486
  1. --Minetest
  2. --Copyright (C) 2014 sapier
  3. --
  4. --This program is free software; you can redistribute it and/or modify
  5. --it under the terms of the GNU Lesser General Public License as published by
  6. --the Free Software Foundation; either version 2.1 of the License, or
  7. --(at your option) any later version.
  8. --
  9. --This program is distributed in the hope that it will be useful,
  10. --but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. --MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. --GNU Lesser General Public License for more details.
  13. --
  14. --You should have received a copy of the GNU Lesser General Public License along
  15. --with this program; if not, write to the Free Software Foundation, Inc.,
  16. --51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  17. local function table_to_flags(ftable)
  18. -- Convert e.g. { jungles = true, caves = false } to "jungles,nocaves"
  19. local str = {}
  20. for flag, is_set in pairs(ftable) do
  21. str[#str + 1] = is_set and flag or ("no" .. flag)
  22. end
  23. return table.concat(str, ",")
  24. end
  25. -- Same as check_flag but returns a string
  26. local function strflag(flags, flag)
  27. return (flags[flag] == true) and "true" or "false"
  28. end
  29. local cb_caverns = { "caverns", fgettext("Caverns"),
  30. fgettext("Very large caverns deep in the underground") }
  31. local flag_checkboxes = {
  32. v5 = {
  33. cb_caverns,
  34. },
  35. v7 = {
  36. cb_caverns,
  37. { "ridges", fgettext("Rivers"), fgettext("Sea level rivers") },
  38. { "mountains", fgettext("Mountains") },
  39. { "floatlands", fgettext("Floatlands (experimental)"),
  40. fgettext("Floating landmasses in the sky") },
  41. },
  42. carpathian = {
  43. cb_caverns,
  44. { "rivers", fgettext("Rivers"), fgettext("Sea level rivers") },
  45. },
  46. valleys = {
  47. { "altitude_chill", fgettext("Altitude chill"),
  48. fgettext("Reduces heat with altitude") },
  49. { "altitude_dry", fgettext("Altitude dry"),
  50. fgettext("Reduces humidity with altitude") },
  51. { "humid_rivers", fgettext("Humid rivers"),
  52. fgettext("Increases humidity around rivers") },
  53. { "vary_river_depth", fgettext("Vary river depth"),
  54. fgettext("Low humidity and high heat causes shallow or dry rivers") },
  55. },
  56. flat = {
  57. cb_caverns,
  58. { "hills", fgettext("Hills") },
  59. { "lakes", fgettext("Lakes") },
  60. },
  61. fractal = {
  62. { "terrain", fgettext("Additional terrain"),
  63. fgettext("Generate non-fractal terrain: Oceans and underground") },
  64. },
  65. v6 = {
  66. { "trees", fgettext("Trees and jungle grass") },
  67. { "flat", fgettext("Flat terrain") },
  68. { "mudflow", fgettext("Mud flow"), fgettext("Terrain surface erosion") },
  69. -- Biome settings are in mgv6_biomes below
  70. },
  71. }
  72. local mgv6_biomes = {
  73. {
  74. fgettext("Temperate, Desert, Jungle, Tundra, Taiga"),
  75. {jungles = true, snowbiomes = true}
  76. },
  77. {
  78. fgettext("Temperate, Desert, Jungle"),
  79. {jungles = true, snowbiomes = false}
  80. },
  81. {
  82. fgettext("Temperate, Desert"),
  83. {jungles = false, snowbiomes = false}
  84. },
  85. }
  86. local function create_world_formspec(dialogdata)
  87. -- Point the player to ContentDB when no games are found
  88. if #pkgmgr.games == 0 then
  89. return "size[8,2.5,true]" ..
  90. "style[label_button;border=false]" ..
  91. "button[0.5,0.5;7,0.5;label_button;" ..
  92. fgettext("You have no games installed.") .. "]" ..
  93. "button[0.5,1.5;2.5,0.5;world_create_open_cdb;" .. fgettext("Install a game") .. "]" ..
  94. "button[5.0,1.5;2.5,0.5;world_create_cancel;" .. fgettext("Cancel") .. "]"
  95. end
  96. local current_mg = dialogdata.mg
  97. local mapgens = core.get_mapgen_names()
  98. local flags = dialogdata.flags
  99. local game = pkgmgr.find_by_gameid(core.settings:get("menu_last_game"))
  100. if game == nil then
  101. -- should never happen but just pick the first game
  102. game = pkgmgr.games[1]
  103. core.settings:set("menu_last_game", game.id)
  104. end
  105. local disallowed_mapgen_settings = {}
  106. if game ~= nil then
  107. local gameconfig = Settings(game.path.."/game.conf")
  108. local allowed_mapgens = (gameconfig:get("allowed_mapgens") or ""):split()
  109. for key, value in pairs(allowed_mapgens) do
  110. allowed_mapgens[key] = value:trim()
  111. end
  112. local disallowed_mapgens = (gameconfig:get("disallowed_mapgens") or ""):split()
  113. for key, value in pairs(disallowed_mapgens) do
  114. disallowed_mapgens[key] = value:trim()
  115. end
  116. if #allowed_mapgens > 0 then
  117. for i = #mapgens, 1, -1 do
  118. if table.indexof(allowed_mapgens, mapgens[i]) == -1 then
  119. table.remove(mapgens, i)
  120. end
  121. end
  122. end
  123. if #disallowed_mapgens > 0 then
  124. for i = #mapgens, 1, -1 do
  125. if table.indexof(disallowed_mapgens, mapgens[i]) > 0 then
  126. table.remove(mapgens, i)
  127. end
  128. end
  129. end
  130. local ds = (gameconfig:get("disallowed_mapgen_settings") or ""):split()
  131. for _, value in pairs(ds) do
  132. disallowed_mapgen_settings[value:trim()] = true
  133. end
  134. end
  135. local mglist = ""
  136. local selindex
  137. do -- build the list of mapgens
  138. local i = 1
  139. local first_mg
  140. for k, v in pairs(mapgens) do
  141. if not first_mg then
  142. first_mg = v
  143. end
  144. if current_mg == v then
  145. selindex = i
  146. end
  147. i = i + 1
  148. mglist = mglist .. core.formspec_escape(v) .. ","
  149. end
  150. if not selindex then
  151. selindex = 1
  152. current_mg = first_mg
  153. end
  154. mglist = mglist:sub(1, -2)
  155. end
  156. -- The logic of the flag element IDs is as follows:
  157. -- "flag_main_foo-bar-baz" controls dialogdata.flags["main"]["foo_bar_baz"]
  158. -- see the buttonhandler for the implementation of this
  159. local mg_main_flags = function(mapgen, y)
  160. if mapgen == "singlenode" then
  161. return "", y
  162. end
  163. if disallowed_mapgen_settings["mg_flags"] then
  164. return "", y
  165. end
  166. local form = "checkbox[0," .. y .. ";flag_main_caves;" ..
  167. fgettext("Caves") .. ";"..strflag(flags.main, "caves").."]"
  168. y = y + 0.5
  169. form = form .. "checkbox[0,"..y..";flag_main_dungeons;" ..
  170. fgettext("Dungeons") .. ";"..strflag(flags.main, "dungeons").."]"
  171. y = y + 0.5
  172. local d_name = fgettext("Decorations")
  173. local d_tt
  174. if mapgen == "v6" then
  175. d_tt = fgettext("Structures appearing on the terrain (no effect on trees and jungle grass created by v6)")
  176. else
  177. d_tt = fgettext("Structures appearing on the terrain, typically trees and plants")
  178. end
  179. form = form .. "checkbox[0,"..y..";flag_main_decorations;" ..
  180. d_name .. ";" ..
  181. strflag(flags.main, "decorations").."]" ..
  182. "tooltip[flag_mg_decorations;" ..
  183. d_tt ..
  184. "]"
  185. y = y + 0.5
  186. form = form .. "tooltip[flag_main_caves;" ..
  187. fgettext("Network of tunnels and caves")
  188. .. "]"
  189. return form, y
  190. end
  191. local mg_specific_flags = function(mapgen, y)
  192. if not flag_checkboxes[mapgen] then
  193. return "", y
  194. end
  195. if disallowed_mapgen_settings["mg"..mapgen.."_spflags"] then
  196. return "", y
  197. end
  198. local form = ""
  199. for _, tab in pairs(flag_checkboxes[mapgen]) do
  200. local id = "flag_"..mapgen.."_"..tab[1]:gsub("_", "-")
  201. form = form .. ("checkbox[0,%f;%s;%s;%s]"):
  202. format(y, id, tab[2], strflag(flags[mapgen], tab[1]))
  203. if tab[3] then
  204. form = form .. "tooltip["..id..";"..tab[3].."]"
  205. end
  206. y = y + 0.5
  207. end
  208. if mapgen ~= "v6" then
  209. -- No special treatment
  210. return form, y
  211. end
  212. -- Special treatment for v6 (add biome widgets)
  213. -- Biome type (jungles, snowbiomes)
  214. local biometype
  215. if flags.v6.snowbiomes == true then
  216. biometype = 1
  217. elseif flags.v6.jungles == true then
  218. biometype = 2
  219. else
  220. biometype = 3
  221. end
  222. y = y + 0.3
  223. form = form .. "label[0,"..(y+0.1)..";" .. fgettext("Biomes") .. "]"
  224. y = y + 0.6
  225. form = form .. "dropdown[0,"..y..";6.3;mgv6_biomes;"
  226. for b=1, #mgv6_biomes do
  227. form = form .. mgv6_biomes[b][1]
  228. if b < #mgv6_biomes then
  229. form = form .. ","
  230. end
  231. end
  232. form = form .. ";" .. biometype.. "]"
  233. -- biomeblend
  234. y = y + 0.55
  235. form = form .. "checkbox[0,"..y..";flag_v6_biomeblend;" ..
  236. fgettext("Biome blending") .. ";"..strflag(flags.v6, "biomeblend").."]" ..
  237. "tooltip[flag_v6_biomeblend;" ..
  238. fgettext("Smooth transition between biomes") .. "]"
  239. return form, y
  240. end
  241. local y_start = 0.0
  242. local y = y_start
  243. local str_flags, str_spflags
  244. local label_flags, label_spflags = "", ""
  245. y = y + 0.3
  246. str_flags, y = mg_main_flags(current_mg, y)
  247. if str_flags ~= "" then
  248. label_flags = "label[0,"..y_start..";" .. fgettext("Mapgen flags") .. "]"
  249. y_start = y + 0.4
  250. else
  251. y_start = 0.0
  252. end
  253. y = y_start + 0.3
  254. str_spflags = mg_specific_flags(current_mg, y)
  255. if str_spflags ~= "" then
  256. label_spflags = "label[0,"..y_start..";" .. fgettext("Mapgen-specific flags") .. "]"
  257. end
  258. local retval =
  259. "size[12.25,7,true]" ..
  260. -- Left side
  261. "container[0,0]"..
  262. "field[0.3,0.6;6,0.5;te_world_name;" ..
  263. fgettext("World name") ..
  264. ";" .. core.formspec_escape(dialogdata.worldname) .. "]" ..
  265. "set_focus[te_world_name;false]"
  266. if not disallowed_mapgen_settings["seed"] then
  267. retval = retval .. "field[0.3,1.7;6,0.5;te_seed;" ..
  268. fgettext("Seed") ..
  269. ";".. core.formspec_escape(dialogdata.seed) .. "]"
  270. end
  271. retval = retval ..
  272. "label[0,2;" .. fgettext("Mapgen") .. "]"..
  273. "dropdown[0,2.5;6.3;dd_mapgen;" .. mglist .. ";" .. selindex .. "]"
  274. -- Warning if only devtest is installed
  275. if #pkgmgr.games == 1 and pkgmgr.games[1].id == "devtest" then
  276. retval = retval ..
  277. "container[0,3.5]" ..
  278. "box[0,0;5.8,1.7;#ff8800]" ..
  279. "textarea[0.4,0.1;6,1.8;;;"..
  280. fgettext("Development Test is meant for developers.") .. "]" ..
  281. "button[1,1;4,0.5;world_create_open_cdb;" .. fgettext("Install another game") .. "]" ..
  282. "container_end[]"
  283. end
  284. retval = retval ..
  285. "container_end[]" ..
  286. -- Right side
  287. "container[6.2,0]"..
  288. label_flags .. str_flags ..
  289. label_spflags .. str_spflags ..
  290. "container_end[]"..
  291. -- Menu buttons
  292. "button[3.25,6.5;3,0.5;world_create_confirm;" .. fgettext("Create") .. "]" ..
  293. "button[6.25,6.5;3,0.5;world_create_cancel;" .. fgettext("Cancel") .. "]"
  294. return retval
  295. end
  296. local function create_world_buttonhandler(this, fields)
  297. if fields["world_create_open_cdb"] then
  298. local dlg = create_store_dlg("game")
  299. dlg:set_parent(this.parent)
  300. this:delete()
  301. this.parent:hide()
  302. dlg:show()
  303. return true
  304. end
  305. if fields["world_create_confirm"] or
  306. fields["key_enter"] then
  307. local worldname = fields["te_world_name"]
  308. local game, _ = pkgmgr.find_by_gameid(core.settings:get("menu_last_game"))
  309. local message
  310. if game == nil then
  311. message = fgettext("No game selected")
  312. end
  313. if message == nil then
  314. -- For unnamed worlds use the generated name 'world<number>',
  315. -- where the number increments: it is set to 1 larger than the largest
  316. -- generated name number found.
  317. if worldname == "" then
  318. local worldnum_max = 0
  319. for _, world in ipairs(menudata.worldlist:get_list()) do
  320. if world.name:match("^world%d+$") then
  321. local worldnum = tonumber(world.name:sub(6))
  322. worldnum_max = math.max(worldnum_max, worldnum)
  323. end
  324. end
  325. worldname = "world" .. worldnum_max + 1
  326. end
  327. if menudata.worldlist:uid_exists_raw(worldname) then
  328. message = fgettext("A world named \"$1\" already exists", worldname)
  329. end
  330. end
  331. if message == nil then
  332. this.data.seed = fields["te_seed"] or ""
  333. this.data.mg = fields["dd_mapgen"]
  334. -- actual names as used by engine
  335. local settings = {
  336. fixed_map_seed = this.data.seed,
  337. mg_name = this.data.mg,
  338. mg_flags = table_to_flags(this.data.flags.main),
  339. mgv5_spflags = table_to_flags(this.data.flags.v5),
  340. mgv6_spflags = table_to_flags(this.data.flags.v6),
  341. mgv7_spflags = table_to_flags(this.data.flags.v7),
  342. mgfractal_spflags = table_to_flags(this.data.flags.fractal),
  343. mgcarpathian_spflags = table_to_flags(this.data.flags.carpathian),
  344. mgvalleys_spflags = table_to_flags(this.data.flags.valleys),
  345. mgflat_spflags = table_to_flags(this.data.flags.flat),
  346. }
  347. message = core.create_world(worldname, game.id, settings)
  348. end
  349. if message == nil then
  350. core.settings:set("menu_last_game", game.id)
  351. menudata.worldlist:set_filtercriteria(game.id)
  352. menudata.worldlist:refresh()
  353. core.settings:set("mainmenu_last_selected_world",
  354. menudata.worldlist:raw_index_by_uid(worldname))
  355. end
  356. gamedata.errormessage = message
  357. this:delete()
  358. return true
  359. end
  360. this.data.worldname = fields["te_world_name"]
  361. this.data.seed = fields["te_seed"] or ""
  362. if fields["games"] then
  363. local gameindex = core.get_textlist_index("games")
  364. core.settings:set("menu_last_game", pkgmgr.games[gameindex].id)
  365. return true
  366. end
  367. for k,v in pairs(fields) do
  368. local split = string.split(k, "_", nil, 3)
  369. if split and split[1] == "flag" then
  370. -- We replaced the underscore of flag names with a dash.
  371. local flag = string.gsub(split[3], "-", "_")
  372. local ftable = this.data.flags[split[2]]
  373. assert(ftable)
  374. ftable[flag] = v == "true"
  375. return true
  376. end
  377. end
  378. if fields["world_create_cancel"] then
  379. this:delete()
  380. return true
  381. end
  382. if fields["mgv6_biomes"] then
  383. local entry = core.formspec_escape(fields["mgv6_biomes"])
  384. for b=1, #mgv6_biomes do
  385. if entry == mgv6_biomes[b][1] then
  386. local ftable = this.data.flags.v6
  387. ftable.jungles = mgv6_biomes[b][2].jungles
  388. ftable.snowbiomes = mgv6_biomes[b][2].snowbiomes
  389. return true
  390. end
  391. end
  392. end
  393. if fields["dd_mapgen"] then
  394. this.data.mg = fields["dd_mapgen"]
  395. return true
  396. end
  397. return false
  398. end
  399. function create_create_world_dlg()
  400. local retval = dialog_create("sp_create_world",
  401. create_world_formspec,
  402. create_world_buttonhandler,
  403. nil)
  404. retval.data = {
  405. worldname = "",
  406. -- settings the world is created with:
  407. seed = core.settings:get("fixed_map_seed") or "",
  408. mg = core.settings:get("mg_name"),
  409. flags = {
  410. main = core.settings:get_flags("mg_flags"),
  411. v5 = core.settings:get_flags("mgv5_spflags"),
  412. v6 = core.settings:get_flags("mgv6_spflags"),
  413. v7 = core.settings:get_flags("mgv7_spflags"),
  414. fractal = core.settings:get_flags("mgfractal_spflags"),
  415. carpathian = core.settings:get_flags("mgcarpathian_spflags"),
  416. valleys = core.settings:get_flags("mgvalleys_spflags"),
  417. flat = core.settings:get_flags("mgflat_spflags"),
  418. }
  419. }
  420. return retval
  421. end