]> git.lizzy.rs Git - dragonfireclient.git/blob - builtin/mainmenu/dlg_create_world.lua
Reorganize some builtin functions in preparation for async env
[dragonfireclient.git] / builtin / mainmenu / dlg_create_world.lua
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
18 -- cf. tab_local, the gamebar already provides game selection so we hide the list from here
19 local hide_gamelist = PLATFORM ~= "Android"
20
21 local function table_to_flags(ftable)
22         -- Convert e.g. { jungles = true, caves = false } to "jungles,nocaves"
23         local str = {}
24         for flag, is_set in pairs(ftable) do
25                 str[#str + 1] = is_set and flag or ("no" .. flag)
26         end
27         return table.concat(str, ",")
28 end
29
30 -- Same as check_flag but returns a string
31 local function strflag(flags, flag)
32         return (flags[flag] == true) and "true" or "false"
33 end
34
35 local cb_caverns = { "caverns", fgettext("Caverns"),
36         fgettext("Very large caverns deep in the underground") }
37
38 local flag_checkboxes = {
39         v5 = {
40                 cb_caverns,
41         },
42         v7 = {
43                 cb_caverns,
44                 { "ridges", fgettext("Rivers"), fgettext("Sea level rivers") },
45                 { "mountains", fgettext("Mountains") },
46                 { "floatlands", fgettext("Floatlands (experimental)"),
47                 fgettext("Floating landmasses in the sky") },
48         },
49         carpathian = {
50                 cb_caverns,
51                 { "rivers", fgettext("Rivers"), fgettext("Sea level rivers") },
52         },
53         valleys = {
54                 { "altitude_chill", fgettext("Altitude chill"),
55                 fgettext("Reduces heat with altitude") },
56                 { "altitude_dry", fgettext("Altitude dry"),
57                 fgettext("Reduces humidity with altitude") },
58                 { "humid_rivers", fgettext("Humid rivers"),
59                 fgettext("Increases humidity around rivers") },
60                 { "vary_river_depth", fgettext("Vary river depth"),
61                 fgettext("Low humidity and high heat causes shallow or dry rivers") },
62         },
63         flat = {
64                 cb_caverns,
65                 { "hills", fgettext("Hills") },
66                 { "lakes", fgettext("Lakes") },
67         },
68         fractal = {
69                 { "terrain", fgettext("Additional terrain"),
70                 fgettext("Generate non-fractal terrain: Oceans and underground") },
71         },
72         v6 = {
73                 { "trees", fgettext("Trees and jungle grass") },
74                 { "flat", fgettext("Flat terrain") },
75                 { "mudflow", fgettext("Mud flow"), fgettext("Terrain surface erosion") },
76                 -- Biome settings are in mgv6_biomes below
77         },
78 }
79
80 local mgv6_biomes = {
81         {
82                 fgettext("Temperate, Desert, Jungle, Tundra, Taiga"),
83                 {jungles = true, snowbiomes = true}
84         },
85         {
86                 fgettext("Temperate, Desert, Jungle"),
87                 {jungles = true, snowbiomes = false}
88         },
89         {
90                 fgettext("Temperate, Desert"),
91                 {jungles = false, snowbiomes = false}
92         },
93 }
94
95 local function create_world_formspec(dialogdata)
96
97         -- Error out when no games found
98         if #pkgmgr.games == 0 then
99                 return "size[12.25,3,true]" ..
100                         "box[0,0;12,2;" .. mt_color_orange .. "]" ..
101                         "textarea[0.3,0;11.7,2;;;"..
102                         fgettext("You have no games installed.") .. "\n" ..
103                         fgettext("Download one from minetest.net") .. "]" ..
104                         "button[4.75,2.5;3,0.5;world_create_cancel;" .. fgettext("Cancel") .. "]"
105         end
106
107         local current_mg = dialogdata.mg
108         local mapgens = core.get_mapgen_names()
109
110         local gameid = core.settings:get("menu_last_game")
111
112         local flags = dialogdata.flags
113
114         local game, gameidx = pkgmgr.find_by_gameid(gameid)
115         if game == nil and hide_gamelist then
116                 -- should never happen but just pick the first game
117                 game = pkgmgr.get_game(1)
118                 gameidx = 1
119                 core.settings:set("menu_last_game", game.id)
120         elseif game == nil then
121                 gameidx = 0
122         end
123
124         local disallowed_mapgen_settings = {}
125         if game ~= nil then
126                 local gameconfig = Settings(game.path.."/game.conf")
127
128                 local allowed_mapgens = (gameconfig:get("allowed_mapgens") or ""):split()
129                 for key, value in pairs(allowed_mapgens) do
130                         allowed_mapgens[key] = value:trim()
131                 end
132
133                 local disallowed_mapgens = (gameconfig:get("disallowed_mapgens") or ""):split()
134                 for key, value in pairs(disallowed_mapgens) do
135                         disallowed_mapgens[key] = value:trim()
136                 end
137
138                 if #allowed_mapgens > 0 then
139                         for i = #mapgens, 1, -1 do
140                                 if table.indexof(allowed_mapgens, mapgens[i]) == -1 then
141                                         table.remove(mapgens, i)
142                                 end
143                         end
144                 end
145
146                 if #disallowed_mapgens > 0 then
147                         for i = #mapgens, 1, -1 do
148                                 if table.indexof(disallowed_mapgens, mapgens[i]) > 0 then
149                                         table.remove(mapgens, i)
150                                 end
151                         end
152                 end
153
154                 local ds = (gameconfig:get("disallowed_mapgen_settings") or ""):split()
155                 for _, value in pairs(ds) do
156                         disallowed_mapgen_settings[value:trim()] = true
157                 end
158         end
159
160         local mglist = ""
161         local selindex
162         do -- build the list of mapgens
163                 local i = 1
164                 local first_mg
165                 for k, v in pairs(mapgens) do
166                         if not first_mg then
167                                 first_mg = v
168                         end
169                         if current_mg == v then
170                                 selindex = i
171                         end
172                         i = i + 1
173                         mglist = mglist .. core.formspec_escape(v) .. ","
174                 end
175                 if not selindex then
176                         selindex = 1
177                         current_mg = first_mg
178                 end
179                 mglist = mglist:sub(1, -2)
180         end
181
182         -- The logic of the flag element IDs is as follows:
183         -- "flag_main_foo-bar-baz" controls dialogdata.flags["main"]["foo_bar_baz"]
184         -- see the buttonhandler for the implementation of this
185
186         local mg_main_flags = function(mapgen, y)
187                 if mapgen == "singlenode" then
188                         return "", y
189                 end
190                 if disallowed_mapgen_settings["mg_flags"] then
191                         return "", y
192                 end
193
194                 local form = "checkbox[0," .. y .. ";flag_main_caves;" ..
195                         fgettext("Caves") .. ";"..strflag(flags.main, "caves").."]"
196                 y = y + 0.5
197
198                 form = form .. "checkbox[0,"..y..";flag_main_dungeons;" ..
199                         fgettext("Dungeons") .. ";"..strflag(flags.main, "dungeons").."]"
200                 y = y + 0.5
201
202                 local d_name = fgettext("Decorations")
203                 local d_tt
204                 if mapgen == "v6" then
205                         d_tt = fgettext("Structures appearing on the terrain (no effect on trees and jungle grass created by v6)")
206                 else
207                         d_tt = fgettext("Structures appearing on the terrain, typically trees and plants")
208                 end
209                 form = form .. "checkbox[0,"..y..";flag_main_decorations;" ..
210                         d_name .. ";" ..
211                         strflag(flags.main, "decorations").."]" ..
212                         "tooltip[flag_mg_decorations;" ..
213                         d_tt ..
214                         "]"
215                 y = y + 0.5
216
217                 form = form .. "tooltip[flag_main_caves;" ..
218                 fgettext("Network of tunnels and caves")
219                 .. "]"
220                 return form, y
221         end
222
223         local mg_specific_flags = function(mapgen, y)
224                 if not flag_checkboxes[mapgen] then
225                         return "", y
226                 end
227                 if disallowed_mapgen_settings["mg"..mapgen.."_spflags"] then
228                         return "", y
229                 end
230                 local form = ""
231                 for _, tab in pairs(flag_checkboxes[mapgen]) do
232                         local id = "flag_"..mapgen.."_"..tab[1]:gsub("_", "-")
233                         form = form .. ("checkbox[0,%f;%s;%s;%s]"):
234                                 format(y, id, tab[2], strflag(flags[mapgen], tab[1]))
235
236                         if tab[3] then
237                                 form = form .. "tooltip["..id..";"..tab[3].."]"
238                         end
239                         y = y + 0.5
240                 end
241
242                 if mapgen ~= "v6" then
243                         -- No special treatment
244                         return form, y
245                 end
246                 -- Special treatment for v6 (add biome widgets)
247
248                 -- Biome type (jungles, snowbiomes)
249                 local biometype
250                 if flags.v6.snowbiomes == true then
251                         biometype = 1
252                 elseif flags.v6.jungles == true  then
253                         biometype = 2
254                 else
255                         biometype = 3
256                 end
257                 y = y + 0.3
258
259                 form = form .. "label[0,"..(y+0.1)..";" .. fgettext("Biomes") .. "]"
260                 y = y + 0.6
261
262                 form = form .. "dropdown[0,"..y..";6.3;mgv6_biomes;"
263                 for b=1, #mgv6_biomes do
264                         form = form .. mgv6_biomes[b][1]
265                         if b < #mgv6_biomes then
266                                 form = form .. ","
267                         end
268                 end
269                 form = form .. ";" .. biometype.. "]"
270
271                 -- biomeblend
272                 y = y + 0.55
273                 form = form .. "checkbox[0,"..y..";flag_v6_biomeblend;" ..
274                         fgettext("Biome blending") .. ";"..strflag(flags.v6, "biomeblend").."]" ..
275                         "tooltip[flag_v6_biomeblend;" ..
276                         fgettext("Smooth transition between biomes") .. "]"
277
278                 return form, y
279         end
280
281         local y_start = 0.0
282         local y = y_start
283         local str_flags, str_spflags
284         local label_flags, label_spflags = "", ""
285         y = y + 0.3
286         str_flags, y = mg_main_flags(current_mg, y)
287         if str_flags ~= "" then
288                 label_flags = "label[0,"..y_start..";" .. fgettext("Mapgen flags") .. "]"
289                 y_start = y + 0.4
290         else
291                 y_start = 0.0
292         end
293         y = y_start + 0.3
294         str_spflags = mg_specific_flags(current_mg, y)
295         if str_spflags ~= "" then
296                 label_spflags = "label[0,"..y_start..";" .. fgettext("Mapgen-specific flags") .. "]"
297         end
298
299         -- Warning if only devtest is installed
300         local devtest_only = ""
301         local gamelist_height = 2.3
302         if #pkgmgr.games == 1 and pkgmgr.games[1].id == "devtest" then
303                 devtest_only = "box[0,0;5.8,1.7;#ff8800]" ..
304                                 "textarea[0.3,0;6,1.8;;;"..
305                                 fgettext("Warning: The Development Test is meant for developers.") .. "\n" ..
306                                 fgettext("Download a game, such as Minetest Game, from minetest.net") .. "]"
307                 gamelist_height = 0.5
308         end
309
310         local retval =
311                 "size[12.25,7,true]" ..
312
313                 -- Left side
314                 "container[0,0]"..
315                 "field[0.3,0.6;6,0.5;te_world_name;" ..
316                 fgettext("World name") ..
317                 ";" .. core.formspec_escape(dialogdata.worldname) .. "]" ..
318                 "set_focus[te_world_name;false]"
319
320         if not disallowed_mapgen_settings["seed"] then
321
322                 retval = retval .. "field[0.3,1.7;6,0.5;te_seed;" ..
323                                 fgettext("Seed") ..
324                                 ";".. core.formspec_escape(dialogdata.seed) .. "]"
325
326         end
327
328         retval = retval ..
329                 "label[0,2;" .. fgettext("Mapgen") .. "]"..
330                 "dropdown[0,2.5;6.3;dd_mapgen;" .. mglist .. ";" .. selindex .. "]"
331
332         if not hide_gamelist or devtest_only ~= "" then
333                 retval = retval ..
334                         "label[0,3.35;" .. fgettext("Game") .. "]"..
335                         "textlist[0,3.85;5.8,"..gamelist_height..";games;" ..
336                         pkgmgr.gamelist() .. ";" .. gameidx .. ";false]" ..
337                         "container[0,4.5]" ..
338                         devtest_only ..
339                         "container_end[]"
340         end
341
342         retval = retval ..
343                 "container_end[]" ..
344
345                 -- Right side
346                 "container[6.2,0]"..
347                 label_flags .. str_flags ..
348                 label_spflags .. str_spflags ..
349                 "container_end[]"..
350
351                 -- Menu buttons
352                 "button[3.25,6.5;3,0.5;world_create_confirm;" .. fgettext("Create") .. "]" ..
353                 "button[6.25,6.5;3,0.5;world_create_cancel;" .. fgettext("Cancel") .. "]"
354
355         return retval
356
357 end
358
359 local function create_world_buttonhandler(this, fields)
360
361         if fields["world_create_confirm"] or
362                 fields["key_enter"] then
363
364                 local worldname = fields["te_world_name"]
365                 local game, gameindex
366                 if hide_gamelist then
367                         game, gameindex = pkgmgr.find_by_gameid(core.settings:get("menu_last_game"))
368                 else
369                         gameindex = core.get_textlist_index("games")
370                         game = pkgmgr.get_game(gameindex)
371                 end
372
373                 local message
374                 if game == nil then
375                         message = fgettext("No game selected")
376                 end
377
378                 if message == nil then
379                         -- For unnamed worlds use the generated name 'world<number>',
380                         -- where the number increments: it is set to 1 larger than the largest
381                         -- generated name number found.
382                         if worldname == "" then
383                                 local worldnum_max = 0
384                                 for _, world in ipairs(menudata.worldlist:get_list()) do
385                                         if world.name:match("^world%d+$") then
386                                                 local worldnum = tonumber(world.name:sub(6))
387                                                 worldnum_max = math.max(worldnum_max, worldnum)
388                                         end
389                                 end
390                                 worldname = "world" .. worldnum_max + 1
391                         end
392
393                         if menudata.worldlist:uid_exists_raw(worldname) then
394                                 message = fgettext("A world named \"$1\" already exists", worldname)
395                         end
396                 end
397
398                 if message == nil then
399                         this.data.seed = fields["te_seed"] or ""
400                         this.data.mg = fields["dd_mapgen"]
401
402                         -- actual names as used by engine
403                         local settings = {
404                                 fixed_map_seed = this.data.seed,
405                                 mg_name = this.data.mg,
406                                 mg_flags = table_to_flags(this.data.flags.main),
407                                 mgv5_spflags = table_to_flags(this.data.flags.v5),
408                                 mgv6_spflags = table_to_flags(this.data.flags.v6),
409                                 mgv7_spflags = table_to_flags(this.data.flags.v7),
410                                 mgfractal_spflags = table_to_flags(this.data.flags.fractal),
411                                 mgcarpathian_spflags = table_to_flags(this.data.flags.carpathian),
412                                 mgvalleys_spflags = table_to_flags(this.data.flags.valleys),
413                                 mgflat_spflags = table_to_flags(this.data.flags.flat),
414                         }
415                         message = core.create_world(worldname, gameindex, settings)
416                 end
417
418                 if message == nil then
419                         core.settings:set("menu_last_game", game.id)
420                         if this.data.update_worldlist_filter then
421                                 menudata.worldlist:set_filtercriteria(game.id)
422                         end
423                         menudata.worldlist:refresh()
424                         core.settings:set("mainmenu_last_selected_world",
425                                         menudata.worldlist:raw_index_by_uid(worldname))
426                 end
427
428                 gamedata.errormessage = message
429                 this:delete()
430                 return true
431         end
432
433         this.data.worldname = fields["te_world_name"]
434         this.data.seed = fields["te_seed"] or ""
435
436         if fields["games"] then
437                 local gameindex = core.get_textlist_index("games")
438                 core.settings:set("menu_last_game", pkgmgr.games[gameindex].id)
439                 return true
440         end
441
442         for k,v in pairs(fields) do
443                 local split = string.split(k, "_", nil, 3)
444                 if split and split[1] == "flag" then
445                         -- We replaced the underscore of flag names with a dash.
446                         local flag = string.gsub(split[3], "-", "_")
447                         local ftable = this.data.flags[split[2]]
448                         assert(ftable)
449                         ftable[flag] = v == "true"
450                         return true
451                 end
452         end
453
454         if fields["world_create_cancel"] then
455                 this:delete()
456                 return true
457         end
458
459         if fields["mgv6_biomes"] then
460                 local entry = core.formspec_escape(fields["mgv6_biomes"])
461                 for b=1, #mgv6_biomes do
462                         if entry == mgv6_biomes[b][1] then
463                                 local ftable = this.data.flags.v6
464                                 ftable.jungles = mgv6_biomes[b][2].jungles
465                                 ftable.snowbiomes = mgv6_biomes[b][2].snowbiomes
466                                 return true
467                         end
468                 end
469         end
470
471         if fields["dd_mapgen"] then
472                 this.data.mg = fields["dd_mapgen"]
473                 return true
474         end
475
476         return false
477 end
478
479
480 function create_create_world_dlg(update_worldlistfilter)
481         local retval = dialog_create("sp_create_world",
482                                         create_world_formspec,
483                                         create_world_buttonhandler,
484                                         nil)
485         retval.data = {
486                 update_worldlist_filter = update_worldlistfilter,
487                 worldname = "",
488                 -- settings the world is created with:
489                 seed = core.settings:get("fixed_map_seed") or "",
490                 mg = core.settings:get("mg_name"),
491                 flags = {
492                         main = core.settings:get_flags("mg_flags"),
493                         v5 = core.settings:get_flags("mgv5_spflags"),
494                         v6 = core.settings:get_flags("mgv6_spflags"),
495                         v7 = core.settings:get_flags("mgv7_spflags"),
496                         fractal = core.settings:get_flags("mgfractal_spflags"),
497                         carpathian = core.settings:get_flags("mgcarpathian_spflags"),
498                         valleys = core.settings:get_flags("mgvalleys_spflags"),
499                         flat = core.settings:get_flags("mgflat_spflags"),
500                 }
501         }
502
503         return retval
504 end