dlg_create_world.lua 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487
  1. --Luanti
  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. { "temples", fgettext("Desert temples"),
  70. fgettext("Different dungeon variant generated in desert biomes (only if dungeons enabled)") },
  71. -- Biome settings are in mgv6_biomes below
  72. },
  73. }
  74. local mgv6_biomes = {
  75. {
  76. fgettext("Temperate, Desert, Jungle, Tundra, Taiga"),
  77. {jungles = true, snowbiomes = true}
  78. },
  79. {
  80. fgettext("Temperate, Desert, Jungle"),
  81. {jungles = true, snowbiomes = false}
  82. },
  83. {
  84. fgettext("Temperate, Desert"),
  85. {jungles = false, snowbiomes = false}
  86. },
  87. }
  88. local function create_world_formspec(dialogdata)
  89. local current_mg = dialogdata.mg
  90. local mapgens = core.get_mapgen_names()
  91. local flags = dialogdata.flags
  92. local game = pkgmgr.find_by_gameid(core.settings:get("menu_last_game"))
  93. if game == nil then
  94. -- should never happen but just pick the first game
  95. game = pkgmgr.games[1]
  96. core.settings:set("menu_last_game", game.id)
  97. end
  98. local disallowed_mapgen_settings = {}
  99. if game ~= nil then
  100. local gameconfig = Settings(game.path.."/game.conf")
  101. local allowed_mapgens = (gameconfig:get("allowed_mapgens") or ""):split()
  102. for key, value in pairs(allowed_mapgens) do
  103. allowed_mapgens[key] = value:trim()
  104. end
  105. local disallowed_mapgens = (gameconfig:get("disallowed_mapgens") or ""):split()
  106. for key, value in pairs(disallowed_mapgens) do
  107. disallowed_mapgens[key] = value:trim()
  108. end
  109. if #allowed_mapgens > 0 then
  110. for i = #mapgens, 1, -1 do
  111. if table.indexof(allowed_mapgens, mapgens[i]) == -1 then
  112. table.remove(mapgens, i)
  113. end
  114. end
  115. end
  116. if #disallowed_mapgens > 0 then
  117. for i = #mapgens, 1, -1 do
  118. if table.indexof(disallowed_mapgens, mapgens[i]) > 0 then
  119. table.remove(mapgens, i)
  120. end
  121. end
  122. end
  123. local ds = (gameconfig:get("disallowed_mapgen_settings") or ""):split()
  124. for _, value in pairs(ds) do
  125. disallowed_mapgen_settings[value:trim()] = true
  126. end
  127. end
  128. local mglist = ""
  129. local selindex
  130. do -- build the list of mapgens
  131. local i = 1
  132. local first_mg
  133. for k, v in pairs(mapgens) do
  134. if not first_mg then
  135. first_mg = v
  136. end
  137. if current_mg == v then
  138. selindex = i
  139. end
  140. i = i + 1
  141. mglist = mglist .. core.formspec_escape(v) .. ","
  142. end
  143. if not selindex then
  144. selindex = 1
  145. current_mg = first_mg
  146. end
  147. mglist = mglist:sub(1, -2)
  148. end
  149. -- The logic of the flag element IDs is as follows:
  150. -- "flag_main_foo-bar-baz" controls dialogdata.flags["main"]["foo_bar_baz"]
  151. -- see the buttonhandler for the implementation of this
  152. local mg_main_flags = function(mapgen, y)
  153. if mapgen == "singlenode" then
  154. return "", y
  155. end
  156. if disallowed_mapgen_settings["mg_flags"] then
  157. return "", y
  158. end
  159. local form = "checkbox[0," .. y .. ";flag_main_caves;" ..
  160. fgettext("Caves") .. ";"..strflag(flags.main, "caves").."]"
  161. y = y + 0.5
  162. form = form .. "checkbox[0,"..y..";flag_main_dungeons;" ..
  163. fgettext("Dungeons") .. ";"..strflag(flags.main, "dungeons").."]"
  164. y = y + 0.5
  165. local d_name = fgettext("Decorations")
  166. local d_tt
  167. if mapgen == "v6" then
  168. d_tt = fgettext("Structures appearing on the terrain (no effect on trees and jungle grass created by v6)")
  169. else
  170. d_tt = fgettext("Structures appearing on the terrain, typically trees and plants")
  171. end
  172. form = form .. "checkbox[0,"..y..";flag_main_decorations;" ..
  173. d_name .. ";" ..
  174. strflag(flags.main, "decorations").."]" ..
  175. "tooltip[flag_mg_decorations;" ..
  176. d_tt ..
  177. "]"
  178. y = y + 0.5
  179. form = form .. "tooltip[flag_main_caves;" ..
  180. fgettext("Network of tunnels and caves")
  181. .. "]"
  182. return form, y
  183. end
  184. local mg_specific_flags = function(mapgen, y)
  185. if not flag_checkboxes[mapgen] then
  186. return "", y
  187. end
  188. if disallowed_mapgen_settings["mg"..mapgen.."_spflags"] then
  189. return "", y
  190. end
  191. local form = ""
  192. for _, tab in pairs(flag_checkboxes[mapgen]) do
  193. local id = "flag_"..mapgen.."_"..tab[1]:gsub("_", "-")
  194. form = form .. ("checkbox[0,%f;%s;%s;%s]"):
  195. format(y, id, tab[2], strflag(flags[mapgen], tab[1]))
  196. if tab[3] then
  197. form = form .. "tooltip["..id..";"..tab[3].."]"
  198. end
  199. y = y + 0.5
  200. end
  201. if mapgen ~= "v6" then
  202. -- No special treatment
  203. return form, y
  204. end
  205. -- Special treatment for v6 (add biome widgets)
  206. -- Biome type (jungles, snowbiomes)
  207. local biometype
  208. if flags.v6.snowbiomes == true then
  209. biometype = 1
  210. elseif flags.v6.jungles == true then
  211. biometype = 2
  212. else
  213. biometype = 3
  214. end
  215. y = y + 0.3
  216. form = form .. "label[0,"..(y+0.1)..";" .. fgettext("Biomes") .. "]"
  217. y = y + 0.6
  218. form = form .. "dropdown[0,"..y..";6.3;mgv6_biomes;"
  219. for b=1, #mgv6_biomes do
  220. form = form .. mgv6_biomes[b][1]
  221. if b < #mgv6_biomes then
  222. form = form .. ","
  223. end
  224. end
  225. form = form .. ";" .. biometype.. "]"
  226. -- biomeblend
  227. y = y + 0.55
  228. form = form .. "checkbox[0,"..y..";flag_v6_biomeblend;" ..
  229. fgettext("Biome blending") .. ";"..strflag(flags.v6, "biomeblend").."]" ..
  230. "tooltip[flag_v6_biomeblend;" ..
  231. fgettext("Smooth transition between biomes") .. "]"
  232. return form, y
  233. end
  234. local y_start = 0.0
  235. local y = y_start
  236. local str_flags, str_spflags
  237. local label_flags, label_spflags = "", ""
  238. y = y + 0.3
  239. str_flags, y = mg_main_flags(current_mg, y)
  240. if str_flags ~= "" then
  241. label_flags = "label[0,"..y_start..";" .. fgettext("Mapgen flags") .. "]"
  242. y_start = y + 0.4
  243. else
  244. y_start = 0.0
  245. end
  246. y = y_start + 0.3
  247. str_spflags = mg_specific_flags(current_mg, y)
  248. if str_spflags ~= "" then
  249. label_spflags = "label[0,"..y_start..";" .. fgettext("Mapgen-specific flags") .. "]"
  250. end
  251. local retval =
  252. "size[12.25,7.4,true]" ..
  253. -- Left side
  254. "container[0,0]"..
  255. "field[0.3,0.6;6,0.5;te_world_name;" ..
  256. fgettext("World name") ..
  257. ";" .. core.formspec_escape(dialogdata.worldname) .. "]" ..
  258. "set_focus[te_world_name;false]"
  259. if not disallowed_mapgen_settings["seed"] then
  260. retval = retval .. "field[0.3,1.7;6,0.5;te_seed;" ..
  261. fgettext("Seed") ..
  262. ";".. core.formspec_escape(dialogdata.seed) .. "]"
  263. end
  264. retval = retval ..
  265. "label[0,2;" .. fgettext("Mapgen") .. "]"..
  266. "dropdown[0,2.5;6.3;dd_mapgen;" .. mglist .. ";" .. selindex .. "]"
  267. -- Warning when making a devtest world
  268. if game.id == "devtest" then
  269. retval = retval ..
  270. "container[0,3.5]" ..
  271. "box[0,0;5.8,1.7;#ff8800]" ..
  272. "textarea[0.4,0.1;6,1.8;;;"..
  273. fgettext("Development Test is meant for developers.") .. "]" ..
  274. "button[1,1;4,0.5;world_create_open_cdb;" .. fgettext("Install another game") .. "]" ..
  275. "container_end[]"
  276. end
  277. retval = retval ..
  278. "container_end[]" ..
  279. -- Right side
  280. "container[6.2,0]"..
  281. label_flags .. str_flags ..
  282. label_spflags .. str_spflags ..
  283. "container_end[]"..
  284. -- Menu buttons
  285. "container[0,6.9]"..
  286. "button[3.25,0;3,0.5;world_create_confirm;" .. fgettext("Create") .. "]" ..
  287. "button[6.25,0;3,0.5;world_create_cancel;" .. fgettext("Cancel") .. "]" ..
  288. "container_end[]"
  289. return retval
  290. end
  291. local function create_world_buttonhandler(this, fields)
  292. if fields["world_create_open_cdb"] then
  293. local dlg = create_contentdb_dlg("game")
  294. dlg:set_parent(this.parent)
  295. this:delete()
  296. this.parent:hide()
  297. dlg:show()
  298. return true
  299. end
  300. if fields["world_create_confirm"] or
  301. fields["key_enter"] then
  302. if fields["key_enter"] then
  303. -- HACK: This timestamp prevents double-triggering when pressing Enter on an input box
  304. -- and releasing it on a button[] or textlist[] due to instant formspec updates.
  305. this.parent.dlg_create_world_closed_at = core.get_us_time()
  306. end
  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_ne("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_ne("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