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