diff --git a/.gitignore b/.gitignore index db131c29f..2e1584b1d 100644 --- a/.gitignore +++ b/.gitignore @@ -31,6 +31,7 @@ CMakeFiles/* src/CMakeFiles/* src/Makefile src/cmake_config.h +src/cmake_config_githash.h src/cmake_install.cmake src/script/CMakeFiles/* src/script/common/CMakeFiles/* diff --git a/CMakeLists.txt b/CMakeLists.txt index 2db4e4a76..153e1c190 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -12,9 +12,12 @@ set(VERSION_EXTRA "" CACHE STRING "Stuff to append to version string") # Also remember to set PROTOCOL_VERSION in clientserver.h when releasing set(VERSION_MAJOR 0) set(VERSION_MINOR 4) -set(VERSION_PATCH 7) +set(VERSION_PATCH 8) if(VERSION_EXTRA) set(VERSION_PATCH ${VERSION_PATCH}-${VERSION_EXTRA}) +else() + # Comment the following line during release + set(VERSION_PATCH ${VERSION_PATCH}-dev) endif() set(VERSION_STRING "${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_PATCH}") diff --git a/builtin/async_env.lua b/builtin/async_env.lua new file mode 100644 index 000000000..afc69219c --- /dev/null +++ b/builtin/async_env.lua @@ -0,0 +1,19 @@ +engine.log("info","Initializing Asynchronous environment") + +dofile(SCRIPTDIR .. DIR_DELIM .. "misc_helpers.lua") + +function engine.job_processor(serialized_function, serialized_data) + + local fct = marshal.decode(serialized_function) + local params = marshal.decode(serialized_data) + local retval = marshal.encode(nil) + + if fct ~= nil and type(fct) == "function" then + local result = fct(params) + retval = marshal.encode(result) + else + engine.log("error","ASYNC WORKER: unable to deserialize function") + end + + return retval,retval:len() +end diff --git a/builtin/async_event.lua b/builtin/async_event.lua new file mode 100644 index 000000000..f4c7d2449 --- /dev/null +++ b/builtin/async_event.lua @@ -0,0 +1,59 @@ +local tbl = engine or minetest + +tbl.async_jobs = {} + +if engine ~= nil then + function tbl.async_event_handler(jobid, serialized_retval) + local retval = nil + if serialized_retval ~= "ERROR" then + retval= marshal.decode(serialized_retval) + else + tbl.log("error","Error fetching async result") + end + + assert(type(tbl.async_jobs[jobid]) == "function") + tbl.async_jobs[jobid](retval) + tbl.async_jobs[jobid] = nil + end +else + + minetest.register_globalstep( + function(dtime) + local list = tbl.get_finished_jobs() + + for i=1,#list,1 do + local retval = marshal.decode(list[i].retval) + + assert(type(tbl.async_jobs[jobid]) == "function") + tbl.async_jobs[list[i].jobid](retval) + tbl.async_jobs[list[i].jobid] = nil + end + end) +end + +function tbl.handle_async(fct, parameters, callback) + + --serialize fct + local serialized_fct = marshal.encode(fct) + + assert(marshal.decode(serialized_fct) ~= nil) + + --serialize parameters + local serialized_params = marshal.encode(parameters) + + if serialized_fct == nil or + serialized_params == nil or + serialized_fct:len() == 0 or + serialized_params:len() == 0 then + return false + end + + local jobid = tbl.do_async_callback( serialized_fct, + serialized_fct:len(), + serialized_params, + serialized_params:len()) + + tbl.async_jobs[jobid] = callback + + return true +end diff --git a/builtin/chatcommands.lua b/builtin/chatcommands.lua index 7d1c2b62a..b9661a888 100644 --- a/builtin/chatcommands.lua +++ b/builtin/chatcommands.lua @@ -471,10 +471,10 @@ minetest.register_chatcommand("spawnentity", { minetest.chat_send_player(name, "entityname required") return end - print('/spawnentity invoked, entityname="'..entityname..'"') + minetest.log("action", '/spawnentity invoked, entityname="'..entityname..'"') local player = minetest.get_player_by_name(name) if player == nil then - print("Unable to spawn entity, player is nil") + minetest.log("error", "Unable to spawn entity, player is nil") return true -- Handled chat message end local p = player:getpos() @@ -491,7 +491,7 @@ minetest.register_chatcommand("pulverize", { func = function(name, param) local player = minetest.get_player_by_name(name) if player == nil then - print("Unable to pulverize, player is nil") + minetest.log("error", "Unable to pulverize, player is nil") return true -- Handled chat message end if player:get_wielded_item():is_empty() then @@ -515,34 +515,45 @@ minetest.register_on_punchnode(function(pos, node, puncher) end) minetest.register_chatcommand("rollback_check", { - params = "[] []", + params = "[] [] [limit]", description = "check who has last touched a node or near it, ".. - "max. ago (default range=0, seconds=86400=24h)", + "max. ago (default range=0, seconds=86400=24h, limit=5)", privs = {rollback=true}, func = function(name, param) - local range, seconds = string.match(param, "(%d+) *(%d*)") + local range, seconds, limit = + param:match("(%d+) *(%d*) *(%d*)") range = tonumber(range) or 0 seconds = tonumber(seconds) or 86400 - minetest.chat_send_player(name, "Punch a node (limits set: range=".. - dump(range).." seconds="..dump(seconds).."s)") + limit = tonumber(limit) or 5 + if limit > 100 then + minetest.chat_send_player(name, "That limit is too high!") + return + end + minetest.chat_send_player(name, "Punch a node (range=".. + range..", seconds="..seconds.."s, limit="..limit..")") + minetest.rollback_punch_callbacks[name] = function(pos, node, puncher) local name = puncher:get_player_name() - minetest.chat_send_player(name, "Checking...") - local actor, act_p, act_seconds = - minetest.rollback_get_last_node_actor(pos, range, seconds) - if actor == "" then + minetest.chat_send_player(name, "Checking "..minetest.pos_to_string(pos).."...") + local actions = minetest.rollback_get_node_actions(pos, range, seconds, limit) + local num_actions = #actions + if num_actions == 0 then minetest.chat_send_player(name, "Nobody has touched the ".. - "specified location in "..dump(seconds).." seconds") + "specified location in "..seconds.." seconds") return end - local nodedesc = "this node" - if act_p.x ~= pos.x or act_p.y ~= pos.y or act_p.z ~= pos.z then - nodedesc = minetest.pos_to_string(act_p) + local time = os.time() + for i = num_actions, 1, -1 do + local action = actions[i] + minetest.chat_send_player(name, + ("%s %s %s -> %s %d seconds ago.") + :format( + minetest.pos_to_string(action.pos), + action.actor, + action.oldnode.name, + action.newnode.name, + time - action.time)) end - local nodename = minetest.get_node(act_p).name - minetest.chat_send_player(name, "Last actor on "..nodedesc.. - " was "..actor..", "..dump(act_seconds).. - "s ago (node is now "..nodename..")") end end, }) @@ -554,7 +565,7 @@ minetest.register_chatcommand("rollback", { func = function(name, param) local target_name, seconds = string.match(param, ":([^ ]+) *(%d*)") if not target_name then - local player_name = nil; + local player_name = nil player_name, seconds = string.match(param, "([^ ]+) *(%d*)") if not player_name then minetest.chat_send_player(name, "Invalid parameters. See /help rollback and /help rollback_check") @@ -564,13 +575,13 @@ minetest.register_chatcommand("rollback", { end seconds = tonumber(seconds) or 60 minetest.chat_send_player(name, "Reverting actions of ".. - dump(target_name).." since "..dump(seconds).." seconds.") + target_name.." since "..seconds.." seconds.") local success, log = minetest.rollback_revert_actions_by( target_name, seconds) - if #log > 10 then + if #log > 100 then minetest.chat_send_player(name, "(log is too long to show)") else - for _,line in ipairs(log) do + for _, line in pairs(log) do minetest.chat_send_player(name, line) end end diff --git a/builtin/deprecated.lua b/builtin/deprecated.lua index 333f64cdc..d8b578d48 100644 --- a/builtin/deprecated.lua +++ b/builtin/deprecated.lua @@ -46,3 +46,8 @@ setmetatable(minetest.env, { return rawget(table, key) end }) + +function minetest.rollback_get_last_node_actor(pos, range, seconds) + return minetest.rollback_get_node_actions(pos, range, seconds, 1)[1] +end + diff --git a/builtin/falling.lua b/builtin/falling.lua index 605252b7b..c8f3bc556 100644 --- a/builtin/falling.lua +++ b/builtin/falling.lua @@ -54,19 +54,25 @@ minetest.register_entity("__builtin:falling_node", { local pos = self.object:getpos() local bcp = {x=pos.x, y=pos.y-0.7, z=pos.z} -- Position of bottom center point local bcn = minetest.get_node(bcp) + local bcd = minetest.registered_nodes[bcn.name] -- Note: walkable is in the node definition, not in item groups - if minetest.registered_nodes[bcn.name] and - minetest.registered_nodes[bcn.name].walkable or - (minetest.get_node_group(self.node.name, "float") ~= 0 and minetest.registered_nodes[bcn.name].liquidtype ~= "none") - then - if minetest.registered_nodes[bcn.name].leveled and bcn.name == self.node.name then + if not bcd or + (bcd.walkable or + (minetest.get_node_group(self.node.name, "float") ~= 0 and + bcd.liquidtype ~= "none")) then + if bcd and bcd.leveled and + bcn.name == self.node.name then local addlevel = self.node.level - if addlevel == nil or addlevel <= 0 then addlevel = minetest.registered_nodes[bcn.name].leveled end - if minetest.env:add_node_level(bcp, addlevel) == 0 then + if addlevel == nil or addlevel <= 0 then + addlevel = bcd.leveled + end + if minetest.add_node_level(bcp, addlevel) == 0 then self.object:remove() return end - elseif minetest.registered_nodes[bcn.name].buildable_to and (minetest.get_node_group(self.node.name, "float") == 0 or minetest.registered_nodes[bcn.name].liquidtype == "none") then + elseif bcd and bcd.buildable_to and + (minetest.get_node_group(self.node.name, "float") == 0 or + bcd.liquidtype == "none") then minetest.remove_node(bcp) return end diff --git a/builtin/features.lua b/builtin/features.lua index 9d00cfd99..f3de3ba21 100644 --- a/builtin/features.lua +++ b/builtin/features.lua @@ -6,6 +6,7 @@ minetest.features = { chat_send_player_param3 = true, get_all_craft_recipes_works = true, use_texture_alpha = true, + no_legacy_abms = true, } function minetest.has_feature(arg) diff --git a/builtin/filterlist.lua b/builtin/filterlist.lua index 1c2ceb031..379a5cea9 100644 --- a/builtin/filterlist.lua +++ b/builtin/filterlist.lua @@ -17,6 +17,20 @@ -------------------------------------------------------------------------------- -- Generic implementation of a filter/sortable list -- +-- Usage: -- +-- Filterlist needs to be initialized on creation. To achieve this you need to -- +-- pass following functions: -- +-- raw_fct() (mandatory): -- +-- function returning a table containing the elements to be filtered -- +-- compare_fct(element1,element2) (mandatory): -- +-- function returning true/false if element1 is same element as element2 -- +-- uid_match_fct(element1,uid) (optional) -- +-- function telling if uid is attached to element1 -- +-- filter_fct(element,filtercriteria) (optional) -- +-- function returning true/false if filtercriteria met to element -- +-- fetch_param (optional) -- +-- parameter passed to raw_fct to aquire correct raw data -- +-- -- -------------------------------------------------------------------------------- filterlist = {} @@ -157,7 +171,7 @@ function filterlist.process(this) this.m_processed_list = {} for k,v in pairs(this.m_raw_list) do - if this.m_filtercriteria == nil or + if this.m_filtercriteria == nil or this.m_filter_fct(v,this.m_filtercriteria) then table.insert(this.m_processed_list,v) end @@ -167,7 +181,7 @@ function filterlist.process(this) return end - if this.m_sort_list[this.m_sortmode] ~= nil and + if this.m_sort_list[this.m_sortmode] ~= nil and type(this.m_sort_list[this.m_sortmode]) == "function" then this.m_sort_list[this.m_sortmode](this) @@ -237,7 +251,7 @@ function compare_worlds(world1,world2) end -------------------------------------------------------------------------------- -function sort_worlds_alphabetic(this) +function sort_worlds_alphabetic(this) table.sort(this.m_processed_list, function(a, b) --fixes issue #857 (crash due to sorting nil in worldlist) diff --git a/builtin/item.lua b/builtin/item.lua index 6a01f37f6..2f2422c81 100644 --- a/builtin/item.lua +++ b/builtin/item.lua @@ -98,7 +98,7 @@ function minetest.facedir_to_dir(facedir) --indexed into by a table of correlating facedirs [({[0]=1, 2, 3, 4, - 5, 2, 6, 4, + 5, 4, 6, 2, 6, 2, 5, 4, 1, 5, 3, 6, 1, 6, 3, 5, @@ -179,11 +179,11 @@ function minetest.get_node_drops(nodename, toolname) return got_items end -function minetest.item_place_node(itemstack, placer, pointed_thing) +function minetest.item_place_node(itemstack, placer, pointed_thing, param2) local item = itemstack:peek_item() local def = itemstack:get_definition() if def.type ~= "node" or pointed_thing.type ~= "node" then - return itemstack + return itemstack, false end local under = pointed_thing.under @@ -194,7 +194,7 @@ function minetest.item_place_node(itemstack, placer, pointed_thing) if not oldnode_under or not oldnode_above then minetest.log("info", placer:get_player_name() .. " tried to place" .. " node in unloaded position " .. minetest.pos_to_string(above)) - return itemstack + return itemstack, false end local olddef_under = ItemStack({name=oldnode_under.name}):get_definition() @@ -206,7 +206,7 @@ function minetest.item_place_node(itemstack, placer, pointed_thing) minetest.log("info", placer:get_player_name() .. " tried to place" .. " node in invalid position " .. minetest.pos_to_string(above) .. ", replacing " .. oldnode_above.name) - return itemstack + return itemstack, false end -- Place above pointed node @@ -218,14 +218,23 @@ function minetest.item_place_node(itemstack, placer, pointed_thing) place_to = {x = under.x, y = under.y, z = under.z} end + if minetest.is_protected(place_to, placer:get_player_name()) then + minetest.log("action", placer:get_player_name() + .. " tried to place " .. def.name + .. " at protected position " + .. minetest.pos_to_string(place_to)) + minetest.record_protection_violation(place_to, placer:get_player_name()) + return itemstack + end + minetest.log("action", placer:get_player_name() .. " places node " .. def.name .. " at " .. minetest.pos_to_string(place_to)) local oldnode = minetest.get_node(place_to) - local newnode = {name = def.name, param1 = 0, param2 = 0} + local newnode = {name = def.name, param1 = 0, param2 = param2} -- Calculate direction for wall mounted stuff like torches and signs - if def.paramtype2 == 'wallmounted' then + if def.paramtype2 == 'wallmounted' and not param2 then local dir = { x = under.x - above.x, y = under.y - above.y, @@ -233,7 +242,7 @@ function minetest.item_place_node(itemstack, placer, pointed_thing) } newnode.param2 = minetest.dir_to_wallmounted(dir) -- Calculate the direction for furnaces and chests and stuff - elseif def.paramtype2 == 'facedir' then + elseif def.paramtype2 == 'facedir' and not param2 then local placer_pos = placer:getpos() if placer_pos then local dir = { @@ -251,7 +260,7 @@ function minetest.item_place_node(itemstack, placer, pointed_thing) not check_attached_node(place_to, newnode) then minetest.log("action", "attached node " .. def.name .. " can not be placed at " .. minetest.pos_to_string(place_to)) - return itemstack + return itemstack, false end -- Add node and update @@ -283,7 +292,7 @@ function minetest.item_place_node(itemstack, placer, pointed_thing) if take_item then itemstack:take_item() end - return itemstack + return itemstack, true end function minetest.item_place_object(itemstack, placer, pointed_thing) @@ -295,19 +304,19 @@ function minetest.item_place_object(itemstack, placer, pointed_thing) return itemstack end -function minetest.item_place(itemstack, placer, pointed_thing) +function minetest.item_place(itemstack, placer, pointed_thing, param2) -- Call on_rightclick if the pointed node defines it if pointed_thing.type == "node" and placer and not placer:get_player_control().sneak then local n = minetest.get_node(pointed_thing.under) local nn = n.name if minetest.registered_nodes[nn] and minetest.registered_nodes[nn].on_rightclick then - return minetest.registered_nodes[nn].on_rightclick(pointed_thing.under, n, placer, itemstack) or itemstack + return minetest.registered_nodes[nn].on_rightclick(pointed_thing.under, n, placer, itemstack) or itemstack, false end end if itemstack:get_definition().type == "node" then - return minetest.item_place_node(itemstack, placer, pointed_thing) + return minetest.item_place_node(itemstack, placer, pointed_thing, param2) end return itemstack end @@ -370,27 +379,40 @@ end function minetest.node_dig(pos, node, digger) local def = ItemStack({name=node.name}):get_definition() - -- Check if def ~= 0 because we always want to be able to remove unknown nodes - if #def ~= 0 and not def.diggable or (def.can_dig and not def.can_dig(pos,digger)) then + if not def.diggable or (def.can_dig and not def.can_dig(pos,digger)) then minetest.log("info", digger:get_player_name() .. " tried to dig " .. node.name .. " which is not diggable " .. minetest.pos_to_string(pos)) return end + if minetest.is_protected(pos, digger:get_player_name()) then + minetest.log("action", digger:get_player_name() + .. " tried to dig " .. node.name + .. " at protected position " + .. minetest.pos_to_string(pos)) + minetest.record_protection_violation(pos, digger:get_player_name()) + return + end + minetest.log('action', digger:get_player_name() .. " digs " .. node.name .. " at " .. minetest.pos_to_string(pos)) local wielded = digger:get_wielded_item() local drops = minetest.get_node_drops(node.name, wielded:get_name()) - - -- Wear out tool - if not minetest.setting_getbool("creative_mode") then - local tp = wielded:get_tool_capabilities() - local dp = minetest.get_dig_params(def.groups, tp) - wielded:add_wear(dp.wear) - digger:set_wielded_item(wielded) + + local wdef = wielded:get_definition() + local tp = wielded:get_tool_capabilities() + local dp = minetest.get_dig_params(def.groups, tp) + if wdef and wdef.after_use then + wielded = wdef.after_use(wielded, digger, node, dp) or wielded + else + -- Wear out tool + if not minetest.setting_getbool("creative_mode") then + wielded:add_wear(dp.wear) + end end + digger:set_wielded_item(wielded) -- Handle drops minetest.handle_node_drops(pos, drops, digger) @@ -479,7 +501,7 @@ minetest.nodedef_default = { post_effect_color = {a=0, r=0, g=0, b=0}, paramtype = "none", paramtype2 = "none", - is_ground_content = false, + is_ground_content = true, sunlight_propagates = false, walkable = true, pointable = true, diff --git a/builtin/mainmenu.lua b/builtin/mainmenu.lua index 5a1b6e929..f8556a388 100644 --- a/builtin/mainmenu.lua +++ b/builtin/mainmenu.lua @@ -24,6 +24,7 @@ dofile(scriptpath .. DIR_DELIM .. "modstore.lua") dofile(scriptpath .. DIR_DELIM .. "gamemgr.lua") dofile(scriptpath .. DIR_DELIM .. "mm_textures.lua") dofile(scriptpath .. DIR_DELIM .. "mm_menubar.lua") +dofile(scriptpath .. DIR_DELIM .. "async_event.lua") menu = {} local tabbuilder = {} @@ -43,10 +44,10 @@ end -------------------------------------------------------------------------------- function menu.render_favorite(spec,render_details) local text = "" - + if spec.name ~= nil then text = text .. engine.formspec_escape(spec.name:trim()) - + -- if spec.description ~= nil and -- engine.formspec_escape(spec.description):trim() ~= "" then -- text = text .. " (" .. engine.formspec_escape(spec.description) .. ")" @@ -54,51 +55,51 @@ function menu.render_favorite(spec,render_details) else if spec.address ~= nil then text = text .. spec.address:trim() - + if spec.port ~= nil then text = text .. ":" .. spec.port end end end - + if not render_details then return text end - + local details = "" if spec.password == true then details = details .. "*" else details = details .. "_" end - + if spec.creative then details = details .. "C" else details = details .. "_" end - + if spec.damage then details = details .. "D" else details = details .. "_" end - + if spec.pvp then details = details .. "P" else details = details .. "_" end details = details .. " " - + local playercount = "" - + if spec.clients ~= nil and spec.clients_max ~= nil then playercount = string.format("%03d",spec.clients) .. "/" .. string.format("%03d",spec.clients_max) .. " " end - + return playercount .. engine.formspec_escape(details) .. text end @@ -106,7 +107,7 @@ end os.tempfolder = function() local filetocheck = os.tmpname() os.remove(filetocheck) - + local randname = "MTTempModFolder_" .. math.random(0,10000) if DIR_DELIM == "\\" then local tempfolder = os.getenv("TEMP") @@ -118,11 +119,35 @@ os.tempfolder = function() end +-------------------------------------------------------------------------------- +function text2textlist(xpos,ypos,width,height,tl_name,textlen,text,transparency) + local textlines = engine.splittext(text,textlen) + + local retval = "textlist[" .. xpos .. "," .. ypos .. ";" + .. width .. "," .. height .. ";" + .. tl_name .. ";" + + for i=1, #textlines, 1 do + textlines[i] = textlines[i]:gsub("\r","") + retval = retval .. engine.formspec_escape(textlines[i]) .. "," + end + + retval = retval .. ";0;" + + if transparency then + retval = retval .. "true" + end + + retval = retval .. "]" + + return retval +end + -------------------------------------------------------------------------------- function init_globals() --init gamedata gamedata.worldindex = 0 - + worldlist = filterlist.create( engine.get_worlds, compare_worlds, @@ -139,26 +164,25 @@ function init_globals() return false end --filter fct ) - + filterlist.add_sort_mechanism(worldlist,"alphabetic",sort_worlds_alphabetic) filterlist.set_sortmode(worldlist,"alphabetic") - end -------------------------------------------------------------------------------- function update_menu() - local formspec = "size[12,5.2]" - + local formspec + -- handle errors if gamedata.errormessage ~= nil then - formspec = formspec .. + formspec = "size[12,5.2]" .. "field[1,2;10,2;;ERROR: " .. - gamedata.errormessage .. + gamedata.errormessage .. ";]".. "button[4.5,4.2;3,0.5;btn_error_confirm;" .. fgettext("Ok") .. "]" else - formspec = formspec .. tabbuilder.gettab() + formspec = tabbuilder.gettab() end engine.update_formspec(formspec) @@ -167,14 +191,14 @@ end -------------------------------------------------------------------------------- function menu.render_world_list() local retval = "" - + local current_worldlist = filterlist.get_list(worldlist) - + for i,v in ipairs(current_worldlist) do if retval ~= "" then retval = retval .."," end - + retval = retval .. engine.formspec_escape(v.name) .. " \\[" .. engine.formspec_escape(v.gameid) .. "\\]" end @@ -197,24 +221,39 @@ function menu.render_texture_pack_list(list) return retval end +-------------------------------------------------------------------------------- +function menu.asyncOnlineFavourites() + menu.favorites = {} + engine.handle_async( + function(param) + return engine.get_favorites("online") + end, + nil, + function(result) + menu.favorites = result + engine.event_handler("Refresh") + end + ) +end + -------------------------------------------------------------------------------- function menu.init() --init menu data gamemgr.update_gamelist() - + menu.last_game = tonumber(engine.setting_get("main_menu_last_game_idx")) - + if type(menu.last_game) ~= "number" then menu.last_game = 1 end if engine.setting_getbool("public_serverlist") then - menu.favorites = engine.get_favorites("online") + menu.asyncOnlineFavourites() else menu.favorites = engine.get_favorites("local") end - - menu.defaulttexturedir = engine.get_texturepath() .. DIR_DELIM .. "base" .. + + menu.defaulttexturedir = engine.get_texturepath() .. DIR_DELIM .. "base" .. DIR_DELIM .. "pack" .. DIR_DELIM end @@ -223,12 +262,12 @@ function menu.lastgame() if menu.last_game > 0 and menu.last_game <= #gamemgr.games then return gamemgr.games[menu.last_game] end - + if #gamemgr.games >= 1 then menu.last_game = 1 return gamemgr.games[menu.last_game] end - + --error case!! return nil end @@ -239,11 +278,11 @@ function menu.update_last_game() local current_world = filterlist.get_raw_element(worldlist, engine.setting_get("mainmenu_last_selected_world") ) - + if current_world == nil then return end - + local gamespec, i = gamemgr.find_by_gameid(current_world.gameid) if i ~= nil then menu.last_game = i @@ -256,17 +295,17 @@ function menu.handle_key_up_down(fields,textlist,settingname) if fields["key_up"] then local oldidx = engine.get_textlist_index(textlist) - + if oldidx > 1 then local newidx = oldidx -1 engine.setting_set(settingname, filterlist.get_raw_index(worldlist,newidx)) end end - + if fields["key_down"] then local oldidx = engine.get_textlist_index(textlist) - + if oldidx < filterlist.size(worldlist) then local newidx = oldidx + 1 engine.setting_set(settingname, @@ -279,7 +318,8 @@ end function tabbuilder.dialog_create_world() local mapgens = {"v6", "v7", "indev", "singlenode", "math"} - local current_mg = engine.setting_get("mg_name") + local current_seed = engine.setting_get("fixed_map_seed") or "" + local current_mg = engine.setting_get("mg_name") local mglist = "" local selindex = 1 @@ -293,17 +333,22 @@ function tabbuilder.dialog_create_world() end mglist = mglist:sub(1, -2) - local retval = + local retval = "label[2,0;" .. fgettext("World name") .. "]".. - "label[2,1;" .. fgettext("Mapgen") .. "]".. "field[4.5,0.4;6,0.5;te_world_name;;]" .. - "label[2,2;" .. fgettext("Game") .. "]".. - "button[5,4.5;2.6,0.5;world_create_confirm;" .. fgettext("Create") .. "]" .. - "button[7.5,4.5;2.8,0.5;world_create_cancel;" .. fgettext("Cancel") .. "]" .. - "dropdown[4.2,1;6.3;dd_mapgen;" .. mglist .. ";" .. selindex .. "]" .. - "textlist[4.2,1.9;5.8,2.3;games;" .. - gamemgr.gamelist() .. - ";" .. menu.last_game .. ";true]" + + "label[2,1;" .. fgettext("Seed") .. "]".. + "field[4.5,1.4;6,0.5;te_seed;;".. current_seed .. "]" .. + + "label[2,2;" .. fgettext("Mapgen") .. "]".. + "dropdown[4.2,2;6.3;dd_mapgen;" .. mglist .. ";" .. selindex .. "]" .. + + "label[2,3;" .. fgettext("Game") .. "]".. + "textlist[4.2,3;5.8,2.3;games;" .. gamemgr.gamelist() .. + ";" .. menu.last_game .. ";true]" .. + + "button[5,5.5;2.6,0.5;world_create_confirm;" .. fgettext("Create") .. "]" .. + "button[7.5,5.5;2.8,0.5;world_create_cancel;" .. fgettext("Cancel") .. "]" return retval end @@ -317,45 +362,20 @@ function tabbuilder.dialog_delete_world() end -------------------------------------------------------------------------------- + function tabbuilder.gettab() - local retval = "" - + local tsize = tabbuilder.tabsizes[tabbuilder.current_tab] or {width=12, height=5.2} + local retval = "size[" .. tsize.width .. "," .. tsize.height .. "]" + if tabbuilder.show_buttons then retval = retval .. tabbuilder.tab_header() end - if tabbuilder.current_tab == "singleplayer" then - retval = retval .. tabbuilder.tab_singleplayer() - end - - if tabbuilder.current_tab == "multiplayer" then - retval = retval .. tabbuilder.tab_multiplayer() + local buildfunc = tabbuilder.tabfuncs[tabbuilder.current_tab] + if buildfunc ~= nil then + retval = retval .. buildfunc() end - if tabbuilder.current_tab == "server" then - retval = retval .. tabbuilder.tab_server() - end - - if tabbuilder.current_tab == "settings" then - retval = retval .. tabbuilder.tab_settings() - end - - if tabbuilder.current_tab == "texture_packs" then - retval = retval .. tabbuilder.tab_texture_packs() - end - - if tabbuilder.current_tab == "credits" then - retval = retval .. tabbuilder.tab_credits() - end - - if tabbuilder.current_tab == "dialog_create_world" then - retval = retval .. tabbuilder.dialog_create_world() - end - - if tabbuilder.current_tab == "dialog_delete_world" then - retval = retval .. tabbuilder.dialog_delete_world() - end - retval = retval .. modmgr.gettab(tabbuilder.current_tab) retval = retval .. gamemgr.gettab(tabbuilder.current_tab) retval = retval .. modstore.gettab(tabbuilder.current_tab) @@ -365,46 +385,48 @@ end -------------------------------------------------------------------------------- function tabbuilder.handle_create_world_buttons(fields) - + if fields["world_create_confirm"] or fields["key_enter"] then - + local worldname = fields["te_world_name"] local gameindex = engine.get_textlist_index("games") - + if gameindex > 0 and worldname ~= "" then - + local message = nil - + if not filterlist.uid_exists_raw(worldlist,worldname) then engine.setting_set("mg_name",fields["dd_mapgen"]) message = engine.create_world(worldname,gameindex) else message = fgettext("A world named \"$1\" already exists", worldname) end - + + engine.setting_set("fixed_map_seed", fields["te_seed"]) + if message ~= nil then gamedata.errormessage = message else menu.last_game = gameindex engine.setting_set("main_menu_last_game_idx",gameindex) - + filterlist.refresh(worldlist) engine.setting_set("mainmenu_last_selected_world", filterlist.raw_index_by_uid(worldlist,worldname)) end else - gamedata.errormessage = + gamedata.errormessage = fgettext("No worldname given or no game selected") end end - + if fields["games"] then tabbuilder.skipformupdate = true return end - + --close dialog tabbuilder.is_dialog = false tabbuilder.show_buttons = true @@ -413,16 +435,16 @@ end -------------------------------------------------------------------------------- function tabbuilder.handle_delete_world_buttons(fields) - + if fields["world_delete_confirm"] then - if menu.world_to_del > 0 and + if menu.world_to_del > 0 and menu.world_to_del <= #filterlist.get_raw_list(worldlist) then engine.delete_world(menu.world_to_del) menu.world_to_del = 0 filterlist.refresh(worldlist) end end - + tabbuilder.is_dialog = false tabbuilder.show_buttons = true tabbuilder.current_tab = engine.setting_get("main_menu_tab") @@ -430,79 +452,84 @@ end -------------------------------------------------------------------------------- function tabbuilder.handle_multiplayer_buttons(fields) - + if fields["te_name"] ~= nil then gamedata.playername = fields["te_name"] engine.setting_set("name", fields["te_name"]) end - + if fields["favourites"] ~= nil then local event = explode_textlist_event(fields["favourites"]) if event.typ == "DCL" then - gamedata.address = menu.favorites[event.index].address - gamedata.port = menu.favorites[event.index].port - gamedata.playername = fields["te_name"] - if fields["te_pwd"] ~= nil then - gamedata.password = fields["te_pwd"] - end - gamedata.selected_world = 0 - - if menu.favorites ~= nil then - gamedata.servername = menu.favorites[event.index].name - gamedata.serverdescription = menu.favorites[event.index].description - end - - if gamedata.address ~= nil and - gamedata.port ~= nil then - - engine.start() + if event.index <= #menu.favorites then + gamedata.address = menu.favorites[event.index].address + gamedata.port = menu.favorites[event.index].port + gamedata.playername = fields["te_name"] + if fields["te_pwd"] ~= nil then + gamedata.password = fields["te_pwd"] + end + gamedata.selected_world = 0 + + if menu.favorites ~= nil then + gamedata.servername = menu.favorites[event.index].name + gamedata.serverdescription = menu.favorites[event.index].description + end + + if gamedata.address ~= nil and + gamedata.port ~= nil then + engine.setting_set("address",gamedata.address) + engine.setting_set("remote_port",gamedata.port) + engine.start() + end end end - + if event.typ == "CHG" then - local address = menu.favorites[event.index].address - local port = menu.favorites[event.index].port - - if address ~= nil and - port ~= nil then - engine.setting_set("address",address) - engine.setting_set("port",port) + if event.index <= #menu.favorites then + local address = menu.favorites[event.index].address + local port = menu.favorites[event.index].port + + if address ~= nil and + port ~= nil then + engine.setting_set("address",address) + engine.setting_set("remote_port",port) + end + + menu.fav_selected = event.index end - - menu.fav_selected = event.index end return end - + if fields["key_up"] ~= nil or fields["key_down"] ~= nil then - + local fav_idx = engine.get_textlist_index("favourites") - + if fields["key_up"] ~= nil and fav_idx > 1 then fav_idx = fav_idx -1 else if fields["key_down"] and fav_idx < #menu.favorites then fav_idx = fav_idx +1 end end - + local address = menu.favorites[fav_idx].address local port = menu.favorites[fav_idx].port - + if address ~= nil and port ~= nil then engine.setting_set("address",address) - engine.setting_set("port",port) + engine.setting_set("remote_port",port) end - + menu.fav_selected = fav_idx return end - + if fields["cb_public_serverlist"] ~= nil then engine.setting_set("public_serverlist", fields["cb_public_serverlist"]) - + if engine.setting_getbool("public_serverlist") then - menu.favorites = engine.get_favorites("online") + menu.asyncOnlineFavourites() else menu.favorites = engine.get_favorites("local") end @@ -515,36 +542,39 @@ function tabbuilder.handle_multiplayer_buttons(fields) engine.delete_favorite(current_favourite) menu.favorites = engine.get_favorites() menu.fav_selected = nil - + engine.setting_set("address","") - engine.setting_get("port","") - + engine.setting_set("remote_port","30000") + return end if fields["btn_mp_connect"] ~= nil or - fields["key_enter"] then - + fields["key_enter"] ~= nil then + gamedata.playername = fields["te_name"] gamedata.password = fields["te_pwd"] gamedata.address = fields["te_address"] gamedata.port = fields["te_port"] - + local fav_idx = engine.get_textlist_index("favourites") - + if fav_idx > 0 and fav_idx <= #menu.favorites and menu.favorites[fav_idx].address == fields["te_address"] and - menu.favorites[fav_idx].port == fields["te_port"] then - + menu.favorites[fav_idx].port == fields["te_port"] then + gamedata.servername = menu.favorites[fav_idx].name gamedata.serverdescription = menu.favorites[fav_idx].description else - gamedata.servername = "" - gamedata.serverdescription = "" + gamedata.servername = "" + gamedata.serverdescription = "" end gamedata.selected_world = 0 - + + engine.setting_set("address",fields["te_address"]) + engine.setting_set("remote_port",fields["te_port"]) + engine.start() return end @@ -557,7 +587,7 @@ function tabbuilder.handle_server_buttons(fields) if fields["srv_worlds"] ~= nil then local event = explode_textlist_event(fields["srv_worlds"]) - + if event.typ == "DCL" then world_doubleclick = true end @@ -566,13 +596,13 @@ function tabbuilder.handle_server_buttons(fields) filterlist.get_raw_index(worldlist,engine.get_textlist_index("srv_worlds"))) end end - + menu.handle_key_up_down(fields,"srv_worlds","mainmenu_last_selected_world") - + if fields["cb_creative_mode"] then engine.setting_set("creative_mode", fields["cb_creative_mode"]) end - + if fields["cb_enable_damage"] then engine.setting_set("enable_damage", fields["cb_enable_damage"]) end @@ -580,7 +610,7 @@ function tabbuilder.handle_server_buttons(fields) if fields["cb_server_announce"] then engine.setting_set("server_announce", fields["cb_server_announce"]) end - + if fields["start_server"] ~= nil or world_doubleclick or fields["key_enter"] then @@ -591,18 +621,20 @@ function tabbuilder.handle_server_buttons(fields) gamedata.port = fields["te_serverport"] gamedata.address = "" gamedata.selected_world = filterlist.get_raw_index(worldlist,selected) - + + engine.setting_set("port",gamedata.port) + menu.update_last_game(gamedata.selected_world) engine.start() end end - + if fields["world_create"] ~= nil then tabbuilder.current_tab = "dialog_create_world" tabbuilder.is_dialog = true tabbuilder.show_buttons = false end - + if fields["world_delete"] ~= nil then local selected = engine.get_textlist_index("srv_worlds") if selected > 0 and @@ -620,7 +652,7 @@ function tabbuilder.handle_server_buttons(fields) end end end - + if fields["world_configure"] ~= nil then selected = engine.get_textlist_index("srv_worlds") if selected > 0 then @@ -648,7 +680,7 @@ function tabbuilder.handle_settings_buttons(fields) if fields["cb_opaque_water"] then engine.setting_set("opaque_water", fields["cb_opaque_water"]) end - + if fields["cb_mipmapping"] then engine.setting_set("mip_map", fields["cb_mipmapping"]) end @@ -661,9 +693,14 @@ function tabbuilder.handle_settings_buttons(fields) if fields["cb_trilinear"] then engine.setting_set("trilinear_filter", fields["cb_trilinear"]) end - + if fields["cb_shaders"] then - engine.setting_set("enable_shaders", fields["cb_shaders"]) + if (engine.setting_get("video_driver") == "direct3d8" or engine.setting_get("video_driver") == "direct3d9") then + engine.setting_set("enable_shaders", "false") + gamedata.errormessage = fgettext("To enable shaders the OpenGL driver needs to be used.") + else + engine.setting_set("enable_shaders", fields["cb_shaders"]) + end end if fields["cb_pre_ivis"] then engine.setting_set("preload_item_visuals", fields["cb_pre_ivis"]) @@ -674,7 +711,21 @@ function tabbuilder.handle_settings_buttons(fields) if fields["cb_finite_liquid"] then engine.setting_set("liquid_finite", fields["cb_finite_liquid"]) end - + if fields["cb_bumpmapping"] then + engine.setting_set("enable_bumpmapping", fields["cb_bumpmapping"]) + end + if fields["cb_parallax"] then + engine.setting_set("enable_parallax_occlusion", fields["cb_parallax"]) + end + if fields["cb_waving_water"] then + engine.setting_set("enable_waving_water", fields["cb_waving_water"]) + end + if fields["cb_waving_leaves"] then + engine.setting_set("enable_waving_leaves", fields["cb_waving_leaves"]) + end + if fields["cb_waving_plants"] then + engine.setting_set("enable_waving_plants", fields["cb_waving_plants"]) + end if fields["btn_change_keys"] ~= nil then engine.show_keys_menu() end @@ -687,23 +738,23 @@ function tabbuilder.handle_singleplayer_buttons(fields) if fields["sp_worlds"] ~= nil then local event = explode_textlist_event(fields["sp_worlds"]) - + if event.typ == "DCL" then world_doubleclick = true end - + if event.typ == "CHG" then engine.setting_set("mainmenu_last_selected_world", filterlist.get_raw_index(worldlist,engine.get_textlist_index("sp_worlds"))) end end - + menu.handle_key_up_down(fields,"sp_worlds","mainmenu_last_selected_world") - + if fields["cb_creative_mode"] then engine.setting_set("creative_mode", fields["cb_creative_mode"]) end - + if fields["cb_enable_damage"] then engine.setting_set("enable_damage", fields["cb_enable_damage"]) end @@ -715,19 +766,19 @@ function tabbuilder.handle_singleplayer_buttons(fields) if selected > 0 then gamedata.selected_world = filterlist.get_raw_index(worldlist,selected) gamedata.singleplayer = true - + menu.update_last_game(gamedata.selected_world) - + engine.start() end end - + if fields["world_create"] ~= nil then tabbuilder.current_tab = "dialog_create_world" tabbuilder.is_dialog = true tabbuilder.show_buttons = false end - + if fields["world_delete"] ~= nil then local selected = engine.get_textlist_index("sp_worlds") if selected > 0 and @@ -745,7 +796,7 @@ function tabbuilder.handle_singleplayer_buttons(fields) end end end - + if fields["world_configure"] ~= nil then selected = engine.get_textlist_index("sp_worlds") if selected > 0 then @@ -772,7 +823,7 @@ function tabbuilder.handle_texture_pack_buttons(fields) if #list >= current_index then local new_path = engine.get_texturepath()..DIR_DELIM..list[current_index] if list[current_index] == "None" then new_path = "" end - + engine.setting_set("texture_path", new_path) end end @@ -785,15 +836,15 @@ function tabbuilder.tab_header() if tabbuilder.last_tab_index == nil then tabbuilder.last_tab_index = 1 end - + local toadd = "" - + for i=1,#tabbuilder.current_buttons,1 do - + if toadd ~= "" then toadd = toadd .. "," end - + toadd = toadd .. tabbuilder.current_buttons[i].caption end return "tabheader[-0.3,-0.99;main_tab;" .. toadd ..";" .. tabbuilder.last_tab_index .. ";true;false]" @@ -806,66 +857,22 @@ function tabbuilder.handle_tab_buttons(fields) local index = tonumber(fields["main_tab"]) tabbuilder.last_tab_index = index tabbuilder.current_tab = tabbuilder.current_buttons[index].name - + engine.setting_set("main_menu_tab",tabbuilder.current_tab) end - + --handle tab changes if tabbuilder.current_tab ~= tabbuilder.old_tab then - if tabbuilder.current_tab ~= "singleplayer" then + if tabbuilder.current_tab ~= "singleplayer" and not tabbuilder.is_dialog then menu.update_gametype(true) end end - + if tabbuilder.current_tab == "singleplayer" then menu.update_gametype() end - - tabbuilder.old_tab = tabbuilder.current_tab -end --------------------------------------------------------------------------------- -function tabbuilder.init() - tabbuilder.current_tab = engine.setting_get("main_menu_tab") - - if tabbuilder.current_tab == nil or - tabbuilder.current_tab == "" then - tabbuilder.current_tab = "singleplayer" - engine.setting_set("main_menu_tab",tabbuilder.current_tab) - end - - --initialize tab buttons - tabbuilder.last_tab = nil - tabbuilder.show_buttons = true - - tabbuilder.current_buttons = {} - table.insert(tabbuilder.current_buttons,{name="singleplayer", caption=fgettext("Singleplayer")}) - table.insert(tabbuilder.current_buttons,{name="multiplayer", caption=fgettext("Client")}) - table.insert(tabbuilder.current_buttons,{name="server", caption=fgettext("Server")}) - table.insert(tabbuilder.current_buttons,{name="settings", caption=fgettext("Settings")}) - table.insert(tabbuilder.current_buttons,{name="texture_packs", caption=fgettext("Texture Packs")}) - - if engine.setting_getbool("main_menu_game_mgr") then - table.insert(tabbuilder.current_buttons,{name="game_mgr", caption=fgettext("Games")}) - end - - if engine.setting_getbool("main_menu_mod_mgr") then - table.insert(tabbuilder.current_buttons,{name="mod_mgr", caption=fgettext("Mods")}) - end - table.insert(tabbuilder.current_buttons,{name="credits", caption=fgettext("Credits")}) - - - for i=1,#tabbuilder.current_buttons,1 do - if tabbuilder.current_buttons[i].name == tabbuilder.current_tab then - tabbuilder.last_tab_index = i - end - end - - if tabbuilder.current_tab ~= "singleplayer" then - menu.update_gametype(true) - else - menu.update_gametype() - end + tabbuilder.old_tab = tabbuilder.current_tab end -------------------------------------------------------------------------------- @@ -877,27 +884,27 @@ function tabbuilder.tab_multiplayer() "label[1,4.25;".. fgettext("Address/Port") .. "]".. "label[9,2.75;".. fgettext("Name/Password") .. "]" .. "field[1.25,5.25;5.5,0.5;te_address;;" ..engine.setting_get("address") .."]" .. - "field[6.75,5.25;2.25,0.5;te_port;;" ..engine.setting_get("port") .."]" .. + "field[6.75,5.25;2.25,0.5;te_port;;" ..engine.setting_get("remote_port") .."]" .. "checkbox[1,3.6;cb_public_serverlist;".. fgettext("Public Serverlist") .. ";" .. dump(engine.setting_getbool("public_serverlist")) .. "]" - + if not engine.setting_getbool("public_serverlist") then - retval = retval .. + retval = retval .. "button[6.45,3.95;2.25,0.5;btn_delete_favorite;".. fgettext("Delete") .. "]" end - + retval = retval .. "button[9,4.95;2.5,0.5;btn_mp_connect;".. fgettext("Connect") .. "]" .. "field[9.3,3.75;2.5,0.5;te_name;;" ..engine.setting_get("name") .."]" .. "pwdfield[9.3,4.5;2.5,0.5;te_pwd;]" .. "textarea[9.3,0.25;2.5,2.75;;" - if menu.fav_selected ~= nil and + if menu.fav_selected ~= nil and menu.favorites[menu.fav_selected].description ~= nil then - retval = retval .. + retval = retval .. engine.formspec_escape(menu.favorites[menu.fav_selected].description,true) end - - retval = retval .. + + retval = retval .. ";]" .. "textlist[1,0.35;7.5,3.35;favourites;" @@ -905,7 +912,7 @@ function tabbuilder.tab_multiplayer() if #menu.favorites > 0 then retval = retval .. menu.render_favorite(menu.favorites[1],render_details) - + for i=2,#menu.favorites,1 do retval = retval .. "," .. menu.render_favorite(menu.favorites[i],render_details) end @@ -926,8 +933,8 @@ function tabbuilder.tab_server() local index = filterlist.get_current_index(worldlist, tonumber(engine.setting_get("mainmenu_last_selected_world")) ) - - local retval = + + local retval = "button[4,4.15;2.6,0.5;world_delete;".. fgettext("Delete") .. "]" .. "button[6.5,4.15;2.8,0.5;world_create;".. fgettext("New") .. "]" .. "button[9.2,4.15;2.55,0.5;world_configure;".. fgettext("Configure") .. "]" .. @@ -943,50 +950,73 @@ function tabbuilder.tab_server() "field[0.8,3.2;3,0.5;te_playername;".. fgettext("Name") .. ";" .. engine.setting_get("name") .. "]" .. "pwdfield[0.8,4.2;3,0.5;te_passwd;".. fgettext("Password") .. "]" .. - "field[0.8,5.2;3,0.5;te_serverport;".. fgettext("Server Port") .. ";30000]" .. + "field[0.8,5.2;3,0.5;te_serverport;".. fgettext("Server Port") .. ";" .. + engine.setting_get("port") .."]" .. "textlist[4,0.25;7.5,3.7;srv_worlds;" .. menu.render_world_list() .. ";" .. index .. "]" - + return retval end -------------------------------------------------------------------------------- function tabbuilder.tab_settings() - return "vertlabel[0,0;" .. fgettext("SETTINGS") .. "]" .. - "checkbox[1,0.75;cb_fancy_trees;".. fgettext("Fancy trees") .. ";" + tab_string = + "vertlabel[0,0;" .. fgettext("SETTINGS") .. "]" .. + "checkbox[1,0;cb_fancy_trees;".. fgettext("Fancy Trees") .. ";" .. dump(engine.setting_getbool("new_style_leaves")) .. "]".. - "checkbox[1,1.25;cb_smooth_lighting;".. fgettext("Smooth Lighting") + "checkbox[1,0.5;cb_smooth_lighting;".. fgettext("Smooth Lighting") .. ";".. dump(engine.setting_getbool("smooth_lighting")) .. "]".. - "checkbox[1,1.75;cb_3d_clouds;".. fgettext("3D Clouds") .. ";" + "checkbox[1,1;cb_3d_clouds;".. fgettext("3D Clouds") .. ";" .. dump(engine.setting_getbool("enable_3d_clouds")) .. "]".. - "checkbox[1,2.25;cb_opaque_water;".. fgettext("Opaque Water") .. ";" + "checkbox[1,1.5;cb_opaque_water;".. fgettext("Opaque Water") .. ";" .. dump(engine.setting_getbool("opaque_water")) .. "]".. - - "checkbox[4,0.75;cb_mipmapping;".. fgettext("Mip-Mapping") .. ";" - .. dump(engine.setting_getbool("mip_map")) .. "]".. - "checkbox[4,1.25;cb_anisotrophic;".. fgettext("Anisotropic Filtering") .. ";" - .. dump(engine.setting_getbool("anisotropic_filter")) .. "]".. - "checkbox[4,1.75;cb_bilinear;".. fgettext("Bi-Linear Filtering") .. ";" - .. dump(engine.setting_getbool("bilinear_filter")) .. "]".. - "checkbox[4,2.25;cb_trilinear;".. fgettext("Tri-Linear Filtering") .. ";" - .. dump(engine.setting_getbool("trilinear_filter")) .. "]".. - - "checkbox[7.5,0.75;cb_shaders;".. fgettext("Shaders") .. ";" - .. dump(engine.setting_getbool("enable_shaders")) .. "]".. - "checkbox[7.5,1.25;cb_pre_ivis;".. fgettext("Preload item visuals") .. ";" + "checkbox[1,2.0;cb_pre_ivis;".. fgettext("Preload item visuals") .. ";" .. dump(engine.setting_getbool("preload_item_visuals")) .. "]".. - "checkbox[7.5,1.75;cb_particles;".. fgettext("Enable Particles") .. ";" + "checkbox[1,2.5;cb_particles;".. fgettext("Enable Particles") .. ";" .. dump(engine.setting_getbool("enable_particles")) .. "]".. - "checkbox[7.5,2.25;cb_finite_liquid;".. fgettext("Finite Liquid") .. ";" + "checkbox[1,3.0;cb_finite_liquid;".. fgettext("Finite Liquid") .. ";" .. dump(engine.setting_getbool("liquid_finite")) .. "]".. - - "button[1,4.25;2.25,0.5;btn_change_keys;".. fgettext("Change keys") .. "]" + + "checkbox[4.5,0;cb_mipmapping;".. fgettext("Mip-Mapping") .. ";" + .. dump(engine.setting_getbool("mip_map")) .. "]".. + "checkbox[4.5,0.5;cb_anisotrophic;".. fgettext("Anisotropic Filtering") .. ";" + .. dump(engine.setting_getbool("anisotropic_filter")) .. "]".. + "checkbox[4.5,1.0;cb_bilinear;".. fgettext("Bi-Linear Filtering") .. ";" + .. dump(engine.setting_getbool("bilinear_filter")) .. "]".. + "checkbox[4.5,1.5;cb_trilinear;".. fgettext("Tri-Linear Filtering") .. ";" + .. dump(engine.setting_getbool("trilinear_filter")) .. "]".. + + "checkbox[8,0;cb_shaders;".. fgettext("Shaders") .. ";" + .. dump(engine.setting_getbool("enable_shaders")) .. "]".. + "button[1,4.5;2.25,0.5;btn_change_keys;".. fgettext("Change keys") .. "]" + +if engine.setting_getbool("enable_shaders") then + tab_string = tab_string .. + "checkbox[8,0.5;cb_bumpmapping;".. fgettext("Bumpmapping") .. ";" + .. dump(engine.setting_getbool("enable_bumpmapping")) .. "]".. + "checkbox[8,1.0;cb_parallax;".. fgettext("Parallax Occlusion") .. ";" + .. dump(engine.setting_getbool("enable_parallax_occlusion")) .. "]".. + "checkbox[8,1.5;cb_waving_water;".. fgettext("Waving Water") .. ";" + .. dump(engine.setting_getbool("enable_waving_water")) .. "]".. + "checkbox[8,2.0;cb_waving_leaves;".. fgettext("Waving Leaves") .. ";" + .. dump(engine.setting_getbool("enable_waving_leaves")) .. "]".. + "checkbox[8,2.5;cb_waving_plants;".. fgettext("Waving Plants") .. ";" + .. dump(engine.setting_getbool("enable_waving_plants")) .. "]" +else + tab_string = tab_string .. + "textlist[8.33,0.7;4,1;;#888888" .. fgettext("Bumpmapping") .. ";0;true]" .. + "textlist[8.33,1.2;4,1;;#888888" .. fgettext("Parallax Occlusion") .. ";0;true]" .. + "textlist[8.33,1.7;4,1;;#888888" .. fgettext("Waving Water") .. ";0;true]" .. + "textlist[8.33,2.2;4,1;;#888888" .. fgettext("Waving Leaves") .. ";0;true]" .. + "textlist[8.33,2.7;4,1;;#888888" .. fgettext("Waving Plants") .. ";0;true]" + end +return tab_string end -------------------------------------------------------------------------------- function tabbuilder.tab_singleplayer() - + local index = filterlist.get_current_index(worldlist, tonumber(engine.setting_get("mainmenu_last_selected_world")) ) @@ -1013,19 +1043,19 @@ function tabbuilder.tab_texture_packs() "vertlabel[0,-0.25;".. fgettext("TEXTURE PACKS") .. "]" .. "textlist[4,0.25;7.5,5.0;TPs;" - local current_texture_path = engine.setting_get("texture_path") - local list = filter_texture_pack_list(engine.get_dirlist(engine.get_texturepath(), true)) + local current_texture_path = engine.setting_get("texture_path") + local list = filter_texture_pack_list(engine.get_dirlist(engine.get_texturepath(), true)) local index = tonumber(engine.setting_get("mainmenu_last_selected_TP")) - + if index == nil then index = 1 end - + if current_texture_path == "" then retval = retval .. menu.render_texture_pack_list(list) .. ";" .. index .. "]" return retval end - + local infofile = current_texture_path ..DIR_DELIM.."info.txt" local infotext = "" local f = io.open(infofile, "r") @@ -1035,7 +1065,7 @@ function tabbuilder.tab_texture_packs() infotext = f:read("*all") f:close() end - + local screenfile = current_texture_path..DIR_DELIM.."screenshot.png" local no_screenshot = nil if not file_exists(screenfile) then @@ -1056,7 +1086,7 @@ function tabbuilder.tab_credits() local logofile = menu.defaulttexturedir .. "logo.png" return "vertlabel[0,-0.5;CREDITS]" .. "label[0.5,3;Minetest " .. engine.get_version() .. "]" .. - "label[0.5,3.3;http://minetest.net]" .. + "label[0.5,3.3;http://minetest.net]" .. "image[0.5,1;" .. engine.formspec_escape(logofile) .. "]" .. "textlist[3.5,-0.25;8.5,5.8;list_credits;" .. "#FFFF00" .. fgettext("Core Developers") .."," .. @@ -1069,14 +1099,16 @@ function tabbuilder.tab_credits() "proller ,".. "sfan5 ,".. "kahrl ,".. + "sapier,".. + "ShadowNinja ,".. + "Nathanael Courant (Nore/Novatux) ,".. + "BlockMen,".. ",".. "#FFFF00" .. fgettext("Active Contributors") .. "," .. - "sapier,".. "Vanessa Ezekowitz (VanessaE) ,".. "Jurgen Doser (doserj) ,".. "Jeija ,".. "MirceaKitsune ,".. - "ShadowNinja,".. "dannydark ,".. "0gb.us <0gb.us@0gb.us>,".. "," .. @@ -1090,6 +1122,66 @@ function tabbuilder.tab_credits() ";0;true]" end +-------------------------------------------------------------------------------- +function tabbuilder.init() + tabbuilder.tabfuncs = { + singleplayer = tabbuilder.tab_singleplayer, + multiplayer = tabbuilder.tab_multiplayer, + server = tabbuilder.tab_server, + settings = tabbuilder.tab_settings, + texture_packs = tabbuilder.tab_texture_packs, + credits = tabbuilder.tab_credits, + dialog_create_world = tabbuilder.dialog_create_world, + dialog_delete_world = tabbuilder.dialog_delete_world + } + + tabbuilder.tabsizes = { + dialog_create_world = {width=12, height=7}, + dialog_delete_world = {width=12, height=5.2} + } + + tabbuilder.current_tab = engine.setting_get("main_menu_tab") + + if tabbuilder.current_tab == nil or + tabbuilder.current_tab == "" then + tabbuilder.current_tab = "singleplayer" + engine.setting_set("main_menu_tab",tabbuilder.current_tab) + end + + --initialize tab buttons + tabbuilder.last_tab = nil + tabbuilder.show_buttons = true + + tabbuilder.current_buttons = {} + table.insert(tabbuilder.current_buttons,{name="singleplayer", caption=fgettext("Singleplayer")}) + table.insert(tabbuilder.current_buttons,{name="multiplayer", caption=fgettext("Client")}) + table.insert(tabbuilder.current_buttons,{name="server", caption=fgettext("Server")}) + table.insert(tabbuilder.current_buttons,{name="settings", caption=fgettext("Settings")}) + table.insert(tabbuilder.current_buttons,{name="texture_packs", caption=fgettext("Texture Packs")}) + + if engine.setting_getbool("main_menu_game_mgr") then + table.insert(tabbuilder.current_buttons,{name="game_mgr", caption=fgettext("Games")}) + end + + if engine.setting_getbool("main_menu_mod_mgr") then + table.insert(tabbuilder.current_buttons,{name="mod_mgr", caption=fgettext("Mods")}) + end + table.insert(tabbuilder.current_buttons,{name="credits", caption=fgettext("Credits")}) + + + for i=1,#tabbuilder.current_buttons,1 do + if tabbuilder.current_buttons[i].name == tabbuilder.current_tab then + tabbuilder.last_tab_index = i + end + end + + if tabbuilder.current_tab ~= "singleplayer" then + menu.update_gametype(true) + else + menu.update_gametype() + end +end + -------------------------------------------------------------------------------- function tabbuilder.checkretval(retval) @@ -1097,18 +1189,24 @@ function tabbuilder.checkretval(retval) if retval.current_tab ~= nil then tabbuilder.current_tab = retval.current_tab end - + if retval.is_dialog ~= nil then tabbuilder.is_dialog = retval.is_dialog end - + if retval.show_buttons ~= nil then tabbuilder.show_buttons = retval.show_buttons end - + if retval.skipformupdate ~= nil then tabbuilder.skipformupdate = retval.skipformupdate end + + if retval.ignore_menu_quit == true then + tabbuilder.ignore_menu_quit = true + else + tabbuilder.ignore_menu_quit = false + end end end @@ -1119,54 +1217,54 @@ end -------------------------------------------------------------------------------- engine.button_handler = function(fields) --print("Buttonhandler: tab: " .. tabbuilder.current_tab .. " fields: " .. dump(fields)) - + if fields["btn_error_confirm"] then gamedata.errormessage = nil end - + local retval = modmgr.handle_buttons(tabbuilder.current_tab,fields) tabbuilder.checkretval(retval) - + retval = gamemgr.handle_buttons(tabbuilder.current_tab,fields) tabbuilder.checkretval(retval) - + retval = modstore.handle_buttons(tabbuilder.current_tab,fields) tabbuilder.checkretval(retval) - + if tabbuilder.current_tab == "dialog_create_world" then tabbuilder.handle_create_world_buttons(fields) end - + if tabbuilder.current_tab == "dialog_delete_world" then tabbuilder.handle_delete_world_buttons(fields) end - + if tabbuilder.current_tab == "singleplayer" then tabbuilder.handle_singleplayer_buttons(fields) end - + if tabbuilder.current_tab == "texture_packs" then tabbuilder.handle_texture_pack_buttons(fields) end - + if tabbuilder.current_tab == "multiplayer" then tabbuilder.handle_multiplayer_buttons(fields) end - + if tabbuilder.current_tab == "settings" then tabbuilder.handle_settings_buttons(fields) end - + if tabbuilder.current_tab == "server" then tabbuilder.handle_server_buttons(fields) end - + --tab buttons tabbuilder.handle_tab_buttons(fields) - + --menubar buttons menubar.handle_buttons(fields) - + if not tabbuilder.skipformupdate then --update menu update_menu() @@ -1179,6 +1277,10 @@ end engine.event_handler = function(event) if event == "MenuQuit" then if tabbuilder.is_dialog then + if tabbuilder.ignore_menu_quit then + return + end + tabbuilder.is_dialog = false tabbuilder.show_buttons = true tabbuilder.current_tab = engine.setting_get("main_menu_tab") @@ -1188,16 +1290,21 @@ engine.event_handler = function(event) engine.close() end end + + if event == "Refresh" then + update_menu() + end end -------------------------------------------------------------------------------- function menu.update_gametype(reset) - if reset then + local game = menu.lastgame() + + if reset or game == nil then mm_texture.reset() engine.set_topleft_text("") filterlist.set_filtercriteria(worldlist,nil) else - local game = menu.lastgame() mm_texture.update(tabbuilder.current_tab,game) engine.set_topleft_text(game.name) filterlist.set_filtercriteria(worldlist,game.id) diff --git a/builtin/misc.lua b/builtin/misc.lua index 82a0ba298..fd80aacf6 100644 --- a/builtin/misc.lua +++ b/builtin/misc.lua @@ -40,15 +40,24 @@ function minetest.check_player_privs(name, privs) return true, "" end +local player_list = {} + +minetest.register_on_joinplayer(function(player) + player_list[player:get_player_name()] = player +end) + +minetest.register_on_leaveplayer(function(player) + player_list[player:get_player_name()] = nil +end) + function minetest.get_connected_players() - -- This could be optimized a bit, but leave that for later - local list = {} - for _, obj in pairs(minetest.get_objects_inside_radius({x=0,y=0,z=0}, 1000000)) do - if obj:is_player() then - table.insert(list, obj) + local temp_table = {} + for index, value in pairs(player_list) do + if value:is_player_connected() then + table.insert(temp_table, value) end end - return list + return temp_table end function minetest.hash_node_position(pos) @@ -99,3 +108,14 @@ function minetest.setting_get_pos(name) return minetest.string_to_pos(value) end +-- To be overriden by protection mods +function minetest.is_protected(pos, name) + return false +end + +function minetest.record_protection_violation(pos, name) + for _, func in pairs(minetest.registered_on_protection_violation) do + func(pos, name) + end +end + diff --git a/builtin/misc_helpers.lua b/builtin/misc_helpers.lua index 3a325e0d3..55c5798d7 100644 --- a/builtin/misc_helpers.lua +++ b/builtin/misc_helpers.lua @@ -205,6 +205,155 @@ function tbl.formspec_escape(text) return text end + +function tbl.splittext(text,charlimit) + local retval = {} + + local current_idx = 1 + + local start,stop = string.find(text," ",current_idx) + local nl_start,nl_stop = string.find(text,"\n",current_idx) + local gotnewline = false + if nl_start ~= nil and (start == nil or nl_start < start) then + start = nl_start + stop = nl_stop + gotnewline = true + end + local last_line = "" + while start ~= nil do + if string.len(last_line) + (stop-start) > charlimit then + table.insert(retval,last_line) + last_line = "" + end + + if last_line ~= "" then + last_line = last_line .. " " + end + + last_line = last_line .. string.sub(text,current_idx,stop -1) + + if gotnewline then + table.insert(retval,last_line) + last_line = "" + gotnewline = false + end + current_idx = stop+1 + + start,stop = string.find(text," ",current_idx) + nl_start,nl_stop = string.find(text,"\n",current_idx) + + if nl_start ~= nil and (start == nil or nl_start < start) then + start = nl_start + stop = nl_stop + gotnewline = true + end + end + + --add last part of text + if string.len(last_line) + (string.len(text) - current_idx) > charlimit then + table.insert(retval,last_line) + table.insert(retval,string.sub(text,current_idx)) + else + last_line = last_line .. " " .. string.sub(text,current_idx) + table.insert(retval,last_line) + end + + return retval +end + +-------------------------------------------------------------------------------- + +if minetest then + local dirs1 = { 9, 18, 7, 12 } + local dirs2 = { 20, 23, 22, 21 } + + function minetest.rotate_and_place(itemstack, placer, pointed_thing, infinitestacks, orient_flags) + orient_flags = orient_flags or {} + + local node = minetest.get_node(pointed_thing.under) + if not minetest.registered_nodes[node.name] + or not minetest.registered_nodes[node.name].on_rightclick then + + local above = pointed_thing.above + local under = pointed_thing.under + local pitch = placer:get_look_pitch() + local pname = minetest.get_node(under).name + local node = minetest.get_node(above) + local fdir = minetest.dir_to_facedir(placer:get_look_dir()) + local wield_name = itemstack:get_name() + local reg_node = minetest.registered_nodes[pname] + + if not reg_node or not reg_node.on_rightclick then + + local iswall = (above.x ~= under.x) or (above.z ~= under.z) + local isceiling = (above.x == under.x) and (above.z == under.z) + and (pitch > 0) + local pos1 = above + + if reg_node and reg_node.buildable_to then + pos1 = under + iswall = false + end + + reg_node = minetest.registered_nodes[minetest.get_node(pos1).name] + if not reg_node or not reg_node.buildable_to then + return + end + + if orient_flags.force_floor then + iswall = false + isceiling = false + elseif orient_flags.force_ceiling then + iswall = false + isceiling = true + elseif orient_flags.force_wall then + iswall = true + isceiling = false + elseif orient_flags.invert_wall then + iswall = not iswall + end + + if iswall then + minetest.add_node(pos1, {name = wield_name, param2 = dirs1[fdir+1] }) + elseif isceiling then + if orient_flags.force_facedir then + minetest.add_node(pos1, {name = wield_name, param2 = 20 }) + else + minetest.add_node(pos1, {name = wield_name, param2 = dirs2[fdir+1] }) + end + else -- place right side up + if orient_flags.force_facedir then + minetest.add_node(pos1, {name = wield_name, param2 = 0 }) + else + minetest.add_node(pos1, {name = wield_name, param2 = fdir }) + end + end + + if not infinitestacks then + itemstack:take_item() + return itemstack + end + end + else + minetest.registered_nodes[node.name].on_rightclick(pointed_thing.under, node, placer, itemstack) + end + end + + +-------------------------------------------------------------------------------- +--Wrapper for rotate_and_place() to check for sneak and assume Creative mode +--implies infinite stacks when performing a 6d rotation. +-------------------------------------------------------------------------------- + + + minetest.rotate_node = function(itemstack, placer, pointed_thing) + minetest.rotate_and_place(itemstack, placer, pointed_thing, + minetest.setting_getbool("creative_mode"), + {invert_wall = placer:get_player_control().sneak}) + return itemstack + end +end + -------------------------------------------------------------------------------- -- mainmenu only functions -------------------------------------------------------------------------------- diff --git a/builtin/misc_register.lua b/builtin/misc_register.lua index f1d816124..6c50ff02e 100644 --- a/builtin/misc_register.lua +++ b/builtin/misc_register.lua @@ -24,17 +24,15 @@ minetest.registered_aliases = {} -- For tables that are indexed by item name: -- If table[X] does not exist, default to table[minetest.registered_aliases[X]] -local function set_alias_metatable(table) - setmetatable(table, { - __index = function(name) - return rawget(table, minetest.registered_aliases[name]) - end - }) -end -set_alias_metatable(minetest.registered_items) -set_alias_metatable(minetest.registered_nodes) -set_alias_metatable(minetest.registered_craftitems) -set_alias_metatable(minetest.registered_tools) +local alias_metatable = { + __index = function(t, name) + return rawget(t, minetest.registered_aliases[name]) + end +} +setmetatable(minetest.registered_items, alias_metatable) +setmetatable(minetest.registered_nodes, alias_metatable) +setmetatable(minetest.registered_craftitems, alias_metatable) +setmetatable(minetest.registered_tools, alias_metatable) -- These item names may not be used because they would interfere -- with legacy itemstrings @@ -106,6 +104,11 @@ function minetest.register_item(name, itemdef) -- Use the nodebox as selection box if it's not set manually if itemdef.drawtype == "nodebox" and not itemdef.selection_box then itemdef.selection_box = itemdef.node_box + elseif itemdef.drawtype == "fencelike" and not itemdef.selection_box then + itemdef.selection_box = { + type = "fixed", + fixed = {-1/8, -1/2, -1/8, 1/8, 1/2, 1/8}, + } end setmetatable(itemdef, {__index = minetest.nodedef_default}) minetest.registered_nodes[itemdef.name] = itemdef @@ -230,6 +233,20 @@ function minetest.register_biome(biome) register_biome_raw(biome) end +function minetest.on_craft(itemstack, player, old_craft_list, craft_inv) + for _, func in ipairs(minetest.registered_on_crafts) do + itemstack = func(itemstack, player, old_craft_list, craft_inv) or itemstack + end + return itemstack +end + +function minetest.craft_predict(itemstack, player, old_craft_list, craft_inv) + for _, func in ipairs(minetest.registered_craft_predicts) do + itemstack = func(itemstack, player, old_craft_list, craft_inv) or itemstack + end + return itemstack +end + -- Alias the forbidden item names to "" so they can't be -- created via itemstrings (e.g. /give) local name @@ -256,6 +273,7 @@ minetest.register_item(":unknown", { on_place = minetest.item_place, on_drop = minetest.item_drop, groups = {not_in_creative_inventory=1}, + diggable = true, }) minetest.register_node(":air", { @@ -296,6 +314,45 @@ minetest.register_item(":", { groups = {not_in_creative_inventory=1}, }) + +function minetest.run_callbacks(callbacks, mode, ...) + assert(type(callbacks) == "table") + local cb_len = #callbacks + if cb_len == 0 then + if mode == 2 or mode == 3 then + return true + elseif mode == 4 or mode == 5 then + return false + end + end + local ret = nil + for i = 1, cb_len do + local cb_ret = callbacks[i](...) + + if mode == 0 and i == 1 then + ret = cb_ret + elseif mode == 1 and i == cb_len then + ret = cb_ret + elseif mode == 2 then + if not cb_ret or i == 1 then + ret = cb_ret + end + elseif mode == 3 then + if cb_ret then + return cb_ret + end + ret = cb_ret + elseif mode == 4 then + if (cb_ret and not ret) or i == 1 then + ret = cb_ret + end + elseif mode == 5 and cb_ret then + return cb_ret + end + end + return ret +end + -- -- Callback registration -- @@ -323,8 +380,12 @@ minetest.registered_on_generateds, minetest.register_on_generated = make_registr minetest.registered_on_newplayers, minetest.register_on_newplayer = make_registration() minetest.registered_on_dieplayers, minetest.register_on_dieplayer = make_registration() minetest.registered_on_respawnplayers, minetest.register_on_respawnplayer = make_registration() +minetest.registered_on_prejoinplayers, minetest.register_on_prejoinplayer = make_registration() minetest.registered_on_joinplayers, minetest.register_on_joinplayer = make_registration() minetest.registered_on_leaveplayers, minetest.register_on_leaveplayer = make_registration() minetest.registered_on_player_receive_fields, minetest.register_on_player_receive_fields = make_registration_reverse() minetest.registered_on_cheats, minetest.register_on_cheat = make_registration() +minetest.registered_on_crafts, minetest.register_on_craft = make_registration() +minetest.registered_craft_predicts, minetest.register_craft_predict = make_registration() +minetest.registered_on_protection_violation, minetest.register_on_protection_violation = make_registration() diff --git a/builtin/mm_menubar.lua b/builtin/mm_menubar.lua index c3ddbb289..2e4d5f8b8 100644 --- a/builtin/mm_menubar.lua +++ b/builtin/mm_menubar.lua @@ -30,27 +30,28 @@ end -------------------------------------------------------------------------------- function menubar.refresh() - menubar.formspec = "box[-0.3,5.625;12.4,1.3;000000]" .. - "box[-0.3,5.6;12.4,0.05;FFFFFF]" + + menubar.formspec = "box[-0.3,5.625;12.4,1.2;#000000]" .. + "box[-0.3,5.6;12.4,0.05;#FFFFFF]" menubar.buttons = {} - local button_base = -0.25 + local button_base = -0.08 local maxbuttons = #gamemgr.games - if maxbuttons > 10 then - maxbuttons = 10 + if maxbuttons > 11 then + maxbuttons = 11 end for i=1,maxbuttons,1 do local btn_name = "menubar_btn_" .. gamemgr.games[i].id - local buttonpos = button_base + (i-1) * 1.245 + local buttonpos = button_base + (i-1) * 1.1 if gamemgr.games[i].menuicon_path ~= nil and gamemgr.games[i].menuicon_path ~= "" then menubar.formspec = menubar.formspec .. - "image_button[" .. buttonpos .. ",5.7;1.3,1.3;" .. + "image_button[" .. buttonpos .. ",5.72;1.165,1.175;" .. engine.formspec_escape(gamemgr.games[i].menuicon_path) .. ";" .. btn_name .. ";;true;false]" else @@ -65,7 +66,7 @@ function menubar.refresh() text = text .. "\n" .. part3 end menubar.formspec = menubar.formspec .. - "image_button[" .. buttonpos .. ",5.7;1.3,1.3;;" ..btn_name .. + "image_button[" .. buttonpos .. ",5.72;1.165,1.175;;" ..btn_name .. ";" .. text .. ";true;true]" end diff --git a/builtin/modmgr.lua b/builtin/modmgr.lua index d9579c652..13f81c6e0 100644 --- a/builtin/modmgr.lua +++ b/builtin/modmgr.lua @@ -22,7 +22,7 @@ function get_mods(path,retval,modpack) for i=1,#mods,1 do local toadd = {} local modpackfile = nil - + toadd.name = mods[i] toadd.path = path .. DIR_DELIM .. mods[i] .. DIR_DELIM if modpack ~= nil and @@ -33,7 +33,7 @@ function get_mods(path,retval,modpack) local error = nil modpackfile,error = io.open(filename,"r") end - + if modpackfile ~= nil then modpackfile:close() toadd.is_modpack = true @@ -52,14 +52,16 @@ modmgr = {} function modmgr.extract(modfile) if modfile.type == "zip" then local tempfolder = os.tempfolder() - + if tempfolder ~= nil and - tempfodler ~= "" then + tempfolder ~= "" then engine.create_dir(tempfolder) - engine.extract_zip(modfile.name,tempfolder) - return tempfolder + if engine.extract_zip(modfile.name,tempfolder) then + return tempfolder + end end end + return nil end ------------------------------------------------------------------------------- @@ -80,7 +82,7 @@ function modmgr.getbasefolder(temppath) path=temppath } end - + testfile = io.open(temppath .. DIR_DELIM .. "modpack.txt","r") if testfile ~= nil then testfile:close() @@ -89,9 +91,9 @@ function modmgr.getbasefolder(temppath) path=temppath } end - + local subdirs = engine.get_dirlist(temppath,true) - + --only single mod or modpack allowed if #subdirs ~= 1 then return { @@ -100,7 +102,7 @@ function modmgr.getbasefolder(temppath) } end - testfile = + testfile = io.open(temppath .. DIR_DELIM .. subdirs[1] ..DIR_DELIM .."init.lua","r") if testfile ~= nil then testfile:close() @@ -109,8 +111,8 @@ function modmgr.getbasefolder(temppath) path= temppath .. DIR_DELIM .. subdirs[1] } end - - testfile = + + testfile = io.open(temppath .. DIR_DELIM .. subdirs[1] ..DIR_DELIM .."modpack.txt","r") if testfile ~= nil then testfile:close() @@ -131,7 +133,7 @@ function modmgr.isValidModname(modpath) if modpath:find("-") ~= nil then return false end - + return true end @@ -142,20 +144,20 @@ function modmgr.parse_register_line(line) if pos1 ~= nil then pos2 = line:find("\"",pos1+1) end - + if pos1 ~= nil and pos2 ~= nil then local item = line:sub(pos1+1,pos2-1) - + if item ~= nil and item ~= "" then local pos3 = item:find(":") - + if pos3 ~= nil then local retval = item:sub(1,pos3-1) if retval ~= nil and retval ~= "" then return retval - end + end end end end @@ -169,10 +171,10 @@ function modmgr.parse_dofile_line(modpath,line) if pos1 ~= nil then pos2 = line:find("\"",pos1+1) end - + if pos1 ~= nil and pos2 ~= nil then local filename = line:sub(pos1+1,pos2-1) - + if filename ~= nil and filename ~= "" and filename:find(".lua") then @@ -187,37 +189,37 @@ function modmgr.identify_modname(modpath,filename) local testfile = io.open(modpath .. DIR_DELIM .. filename,"r") if testfile ~= nil then local line = testfile:read() - + while line~= nil do local modname = nil - + if line:find("minetest.register_tool") then modname = modmgr.parse_register_line(line) end - + if line:find("minetest.register_craftitem") then modname = modmgr.parse_register_line(line) end - - + + if line:find("minetest.register_node") then modname = modmgr.parse_register_line(line) end - + if line:find("dofile") then modname = modmgr.parse_dofile_line(modpath,line) end - + if modname ~= nil then testfile:close() return modname end - + line = testfile:read() end testfile:close() end - + return nil end @@ -231,44 +233,87 @@ function modmgr.tab() if modmgr.selected_mod == nil then modmgr.selected_mod = 1 end - - local retval = + + local retval = "vertlabel[0,-0.25;".. fgettext("MODS") .. "]" .. "label[0.8,-0.25;".. fgettext("Installed Mods:") .. "]" .. - "textlist[0.75,0.25;4.5,4.3;modlist;" .. - modmgr.render_modlist(modmgr.global_mods) .. + "textlist[0.75,0.25;4.5,4;modlist;" .. + modmgr.render_modlist(modmgr.global_mods) .. ";" .. modmgr.selected_mod .. "]" retval = retval .. - "button[1,4.85;2,0.5;btn_mod_mgr_install_local;".. fgettext("Install") .. "]" .. - "button[3,4.85;2,0.5;btn_mod_mgr_download;".. fgettext("Download") .. "]" - + "label[0.8,4.2;" .. fgettext("Add mod:") .. "]" .. +-- TODO Disabled due to upcoming release 0.4.8 and irrlicht messing up localization +-- "button[0.75,4.85;1.8,0.5;btn_mod_mgr_install_local;".. fgettext("Local install") .. "]" .. + "button[2.45,4.85;3.05,0.5;btn_mod_mgr_download;".. fgettext("Online mod repository") .. "]" + local selected_mod = nil - + if filterlist.size(modmgr.global_mods) >= modmgr.selected_mod then selected_mod = filterlist.get_list(modmgr.global_mods)[modmgr.selected_mod] end - + if selected_mod ~= nil then - if selected_mod.is_modpack then - retval = retval - .. "button[10,4.85;2,0.5;btn_mod_mgr_rename_modpack;" .. - fgettext("Rename") .. "]" - else - --show dependencies - retval = retval .. - "label[6,1.9;".. fgettext("Depends:") .. "]" .. - "textlist[6,2.4;5.7,2;deplist;" - - toadd = modmgr.get_dependencies(selected_mod.path) - - retval = retval .. toadd .. ";0;true,false]" - - --TODO read modinfo + local modscreenshot = nil + + --check for screenshot beeing available + local screenshotfilename = selected_mod.path .. DIR_DELIM .. "screenshot.png" + local error = nil + screenshotfile,error = io.open(screenshotfilename,"r") + if error == nil then + screenshotfile:close() + modscreenshot = screenshotfilename + end + + if modscreenshot == nil then + modscreenshot = modstore.basetexturedir .. "no_screenshot.png" + end + + retval = retval + .. "image[5.5,0;3,2;" .. engine.formspec_escape(modscreenshot) .. "]" + .. "label[8.25,0.6;" .. selected_mod.name .. "]" + + local descriptionlines = nil + error = nil + local descriptionfilename = selected_mod.path .. "description.txt" + descriptionfile,error = io.open(descriptionfilename,"r") + if error == nil then + descriptiontext = descriptionfile:read("*all") + + descriptionlines = engine.splittext(descriptiontext,42) + descriptionfile:close() + else + descriptionlines = {} + table.insert(descriptionlines,fgettext("No mod description available")) + end + + retval = retval .. + "label[5.5,1.7;".. fgettext("Mod information:") .. "]" .. + "textlist[5.5,2.2;6.2,2.4;description;" + + for i=1,#descriptionlines,1 do + retval = retval .. engine.formspec_escape(descriptionlines[i]) .. "," + end + + + if selected_mod.is_modpack then + retval = retval .. ";0]" .. + "button[10,4.85;2,0.5;btn_mod_mgr_rename_modpack;" .. + fgettext("Rename") .. "]" + retval = retval .. "button[5.5,4.85;4.5,0.5;btn_mod_mgr_delete_mod;" + .. fgettext("Uninstall selected modpack") .. "]" + else + --show dependencies + + retval = retval .. ",Depends:," + + toadd = modmgr.get_dependencies(selected_mod.path) + + retval = retval .. toadd .. ";0]" + + retval = retval .. "button[5.5,4.85;4.5,0.5;btn_mod_mgr_delete_mod;" + .. fgettext("Uninstall selected mod") .. "]" end - --show delete button - retval = retval .. "button[8,4.85;2,0.5;btn_mod_mgr_delete_mod;" - .. fgettext("Delete") .. "]" end return retval end @@ -276,16 +321,16 @@ end -------------------------------------------------------------------------------- function modmgr.dialog_rename_modpack() - local mod = filterlist.get_list(modmgr.modlist)[modmgr.selected_mod] - - local retval = + local mod = filterlist.get_list(modmgr.global_mods)[modmgr.selected_mod] + + local retval = "label[1.75,1;".. fgettext("Rename Modpack:") .. "]".. "field[4.5,1.4;6,0.5;te_modpack_name;;" .. mod.name .. "]" .. - "button[5,4.2;2.6,0.5;dlg_rename_modpack_confirm;".. + "button[5,4.2;2.6,0.5;dlg_rename_modpack_confirm;".. fgettext("Accept") .. "]" .. - "button[7.5,4.2;2.8,0.5;dlg_rename_modpack_cancel;".. + "button[7.5,4.2;2.8,0.5;dlg_rename_modpack_cancel;".. fgettext("Cancel") .. "]" return retval @@ -297,15 +342,15 @@ function modmgr.precheck() if modmgr.world_config_selected_world == nil then modmgr.world_config_selected_world = 1 end - + if modmgr.world_config_selected_mod == nil then modmgr.world_config_selected_mod = 1 end - + if modmgr.hide_gamemods == nil then modmgr.hide_gamemods = true end - + if modmgr.hide_modpackcontents == nil then modmgr.hide_modpackcontents = true end @@ -314,27 +359,27 @@ end -------------------------------------------------------------------------------- function modmgr.render_modlist(render_list) local retval = "" - + if render_list == nil then if modmgr.global_mods == nil then modmgr.refresh_globals() end render_list = modmgr.global_mods end - + local list = filterlist.get_list(render_list) local last_modpack = nil - + for i,v in ipairs(list) do if retval ~= "" then retval = retval .."," end local color = "" - + if v.is_modpack then local rawlist = filterlist.get_raw_list(render_list) - + local all_enabled = true for j=1,#rawlist,1 do if rawlist[j].modpack == list[i].name and @@ -343,14 +388,14 @@ function modmgr.render_modlist(render_list) break end end - + if all_enabled == false then color = mt_color_grey else color = mt_color_dark_green end end - + if v.typ == "game_mod" then color = mt_color_blue else @@ -365,34 +410,34 @@ function modmgr.render_modlist(render_list) end retval = retval .. v.name end - + return retval end -------------------------------------------------------------------------------- function modmgr.dialog_configure_world() modmgr.precheck() - + local worldspec = engine.get_worlds()[modmgr.world_config_selected_world] local mod = filterlist.get_list(modmgr.modlist)[modmgr.world_config_selected_mod] - + local retval = "size[11,6.5]" .. "label[0.5,-0.25;" .. fgettext("World:") .. "]" .. "label[1.75,-0.25;" .. worldspec.name .. "]" - + if modmgr.hide_gamemods then retval = retval .. "checkbox[0,5.75;cb_hide_gamemods;" .. fgettext("Hide Game") .. ";true]" else retval = retval .. "checkbox[0,5.75;cb_hide_gamemods;" .. fgettext("Hide Game") .. ";false]" end - + if modmgr.hide_modpackcontents then retval = retval .. "checkbox[2,5.75;cb_hide_mpcontent;" .. fgettext("Hide mp content") .. ";true]" else retval = retval .. "checkbox[2,5.75;cb_hide_mpcontent;" .. fgettext("Hide mp content") .. ";false]" end - + if mod == nil then mod = {name=""} end @@ -404,11 +449,11 @@ function modmgr.dialog_configure_world() modmgr.get_dependencies(mod.path) .. ";0]" .. "button[9.25,6.35;2,0.5;btn_config_world_save;" .. fgettext("Save") .. "]" .. "button[7.4,6.35;2,0.5;btn_config_world_cancel;" .. fgettext("Cancel") .. "]" - + if mod ~= nil and mod.name ~= "" and mod.typ ~= "game_mod" then if mod.is_modpack then local rawlist = filterlist.get_raw_list(modmgr.modlist) - + local all_enabled = true for j=1,#rawlist,1 do if rawlist[j].modpack == mod.name and @@ -417,7 +462,7 @@ function modmgr.dialog_configure_world() break end end - + if all_enabled == false then retval = retval .. "button[5.5,-0.125;2,0.5;btn_mp_enable;" .. fgettext("Enable MP") .. "]" else @@ -431,15 +476,15 @@ function modmgr.dialog_configure_world() end end end - + retval = retval .. "button[8.5,-0.125;2.5,0.5;btn_all_mods;" .. fgettext("Enable all") .. "]" .. "textlist[5.5,0.5;5.5,5.75;world_config_modlist;" - + retval = retval .. modmgr.render_modlist(modmgr.modlist) - + retval = retval .. ";" .. modmgr.world_config_selected_mod .."]" - + return retval end @@ -447,23 +492,23 @@ end function modmgr.handle_buttons(tab,fields) local retval = nil - + if tab == "mod_mgr" then retval = modmgr.handle_modmgr_buttons(fields) end - + if tab == "dialog_rename_modpack" then retval = modmgr.handle_rename_modpack_buttons(fields) end - + if tab == "dialog_delete_mod" then retval = modmgr.handle_delete_mod_buttons(fields) end - + if tab == "dialog_configure_world" then retval = modmgr.handle_configure_world_buttons(fields) end - + return retval end @@ -473,13 +518,13 @@ function modmgr.get_dependencies(modfolder) if modfolder ~= nil then local filename = modfolder .. DIR_DELIM .. "depends.txt" - + local dependencyfile = io.open(filename,"r") - + if dependencyfile then local dependency = dependencyfile:read("*l") while dependency do - if toadd ~= "" then + if toadd ~= "" then toadd = toadd .. "," end toadd = toadd .. dependency @@ -499,11 +544,11 @@ function modmgr.get_worldconfig(worldpath) DIR_DELIM .. "world.mt" local worldfile = Settings(filename) - + local worldconfig = {} worldconfig.global_mods = {} worldconfig.game_mods = {} - + for key,value in pairs(worldfile:to_table()) do if key == "gameid" then worldconfig.id = value @@ -511,7 +556,7 @@ function modmgr.get_worldconfig(worldpath) worldconfig.global_mods[key] = engine.is_yes(value) end end - + --read gamemods local gamespec = gamemgr.find_by_gameid(worldconfig.id) gamemgr.get_game_mods(gamespec, worldconfig.game_mods) @@ -530,11 +575,11 @@ function modmgr.handle_modmgr_buttons(fields) local event = explode_textlist_event(fields["modlist"]) modmgr.selected_mod = event.index end - + if fields["btn_mod_mgr_install_local"] ~= nil then engine.show_file_open_dialog("mod_mgt_open_dlg",fgettext("Select Mod File:")) end - + if fields["btn_mod_mgr_download"] ~= nil then modstore.update_modlist() retval.current_tab = "dialog_modstore_unsorted" @@ -542,26 +587,26 @@ function modmgr.handle_modmgr_buttons(fields) retval.show_buttons = false return retval end - + if fields["btn_mod_mgr_rename_modpack"] ~= nil then retval.current_tab = "dialog_rename_modpack" retval.is_dialog = true retval.show_buttons = false return retval end - + if fields["btn_mod_mgr_delete_mod"] ~= nil then retval.current_tab = "dialog_delete_mod" retval.is_dialog = true retval.show_buttons = false return retval end - + if fields["mod_mgt_open_dlg_accepted"] ~= nil and fields["mod_mgt_open_dlg_accepted"] ~= "" then modmgr.installmod(fields["mod_mgt_open_dlg_accepted"],nil) end - + return nil; end @@ -569,27 +614,27 @@ end function modmgr.installmod(modfilename,basename) local modfile = modmgr.identify_filetype(modfilename) local modpath = modmgr.extract(modfile) - + if modpath == nil then gamedata.errormessage = fgettext("Install Mod: file: \"$1\"", modfile.name) .. - fgettext("\nInstall Mod: unsupported filetype \"$1\"", modfile.type) + fgettext("\nInstall Mod: unsupported filetype \"$1\" or broken archive", modfile.type) return end - - + + local basefolder = modmgr.getbasefolder(modpath) - + if basefolder.type == "modpack" then local clean_path = nil - + if basename ~= nil then clean_path = "mp_" .. basename end - + if clean_path == nil then clean_path = get_last_folder(cleanup_path(basefolder.path)) end - + if clean_path ~= nil then local targetpath = engine.get_modpath() .. DIR_DELIM .. clean_path if not engine.copy_dir(basefolder.path,targetpath) then @@ -599,19 +644,19 @@ function modmgr.installmod(modfilename,basename) gamedata.errormessage = fgettext("Install Mod: unable to find suitable foldername for modpack $1", modfilename) end end - + if basefolder.type == "mod" then local targetfolder = basename - + if targetfolder == nil then targetfolder = modmgr.identify_modname(basefolder.path,"init.lua") end - + --if heuristic failed try to use current foldername if targetfolder == nil then targetfolder = get_last_folder(basefolder.path) - end - + end + if targetfolder ~= nil and modmgr.isValidModname(targetfolder) then local targetpath = engine.get_modpath() .. DIR_DELIM .. targetfolder engine.copy_dir(basefolder.path,targetpath) @@ -619,7 +664,7 @@ function modmgr.installmod(modfilename,basename) gamedata.errormessage = fgettext("Install Mod: unable to find real modname for: $1", modfilename) end end - + engine.delete_dir(modpath) modmgr.refresh_globals() @@ -628,14 +673,17 @@ end -------------------------------------------------------------------------------- function modmgr.handle_rename_modpack_buttons(fields) - + if fields["dlg_rename_modpack_confirm"] ~= nil then - local mod = filterlist.get_list(modmgr.modlist)[modmgr.selected_mod] + local mod = filterlist.get_list(modmgr.global_mods)[modmgr.selected_mod] local oldpath = engine.get_modpath() .. DIR_DELIM .. mod.name local targetpath = engine.get_modpath() .. DIR_DELIM .. fields["te_modpack_name"] engine.copy_dir(oldpath,targetpath,false) + modmgr.refresh_globals() + modmgr.selected_mod = filterlist.get_current_index(modmgr.global_mods, + filterlist.raw_index_by_uid(modmgr.global_mods, fields["te_modpack_name"])) end - + return { is_dialog = false, show_buttons = true, @@ -652,25 +700,25 @@ function modmgr.handle_configure_world_buttons(fields) modmgr.world_config_enable_mod(nil) end end - + if fields["key_enter"] ~= nil then modmgr.world_config_enable_mod(nil) end - + if fields["cb_mod_enable"] ~= nil then local toset = engine.is_yes(fields["cb_mod_enable"]) modmgr.world_config_enable_mod(toset) end - + if fields["btn_mp_enable"] ~= nil or fields["btn_mp_disable"] then local toset = (fields["btn_mp_enable"] ~= nil) modmgr.world_config_enable_mod(toset) end - + if fields["cb_hide_gamemods"] ~= nil then local current = filterlist.get_filtercriteria(modmgr.modlist) - + if current == nil then current = {} end @@ -682,13 +730,13 @@ function modmgr.handle_configure_world_buttons(fields) current.hide_game = false modmgr.hide_gamemods = false end - + filterlist.set_filtercriteria(modmgr.modlist,current) end - + if fields["cb_hide_mpcontent"] ~= nil then local current = filterlist.get_filtercriteria(modmgr.modlist) - + if current == nil then current = {} end @@ -700,21 +748,21 @@ function modmgr.handle_configure_world_buttons(fields) current.hide_modpackcontents = false modmgr.hide_modpackcontents = false end - + filterlist.set_filtercriteria(modmgr.modlist,current) end - + if fields["btn_config_world_save"] then local worldspec = engine.get_worlds()[modmgr.world_config_selected_world] - + local filename = worldspec.path .. DIR_DELIM .. "world.mt" - + local worldfile = Settings(filename) local mods = worldfile:to_table() - + local rawlist = filterlist.get_raw_list(modmgr.modlist) - + local i,mod for i,mod in ipairs(rawlist) do if not mod.is_modpack and @@ -727,42 +775,42 @@ function modmgr.handle_configure_world_buttons(fields) mods["load_mod_"..mod.name] = nil end end - + -- Remove mods that are not present anymore for key,value in pairs(mods) do if key:sub(1,9) == "load_mod_" then worldfile:remove(key) end end - + if not worldfile:write() then - print("failed to write world config file") + engine.log("error", "Failed to write world config file") end - + modmgr.modlist = nil modmgr.worldconfig = nil - + return { is_dialog = false, show_buttons = true, current_tab = engine.setting_get("main_menu_tab") } end - + if fields["btn_config_world_cancel"] then - + modmgr.worldconfig = nil - + return { is_dialog = false, show_buttons = true, current_tab = engine.setting_get("main_menu_tab") } end - + if fields["btn_all_mods"] then local list = filterlist.get_raw_list(modmgr.modlist) - + for i=1,#list,1 do if list[i].typ ~= "game_mod" and not list[i].is_modpack then @@ -770,9 +818,9 @@ function modmgr.handle_configure_world_buttons(fields) end end end - - + + return nil end -------------------------------------------------------------------------------- @@ -803,9 +851,9 @@ end -------------------------------------------------------------------------------- function modmgr.handle_delete_mod_buttons(fields) local mod = filterlist.get_list(modmgr.global_mods)[modmgr.selected_mod] - + if fields["dlg_delete_mod_confirm"] ~= nil then - + if mod.path ~= nil and mod.path ~= "" and mod.path ~= engine.get_modpath() then @@ -817,7 +865,7 @@ function modmgr.handle_delete_mod_buttons(fields) gamedata.errormessage = fgettext("Modmgr: invalid modpath \"$1\"", mod.path) end end - + return { is_dialog = false, show_buttons = true, @@ -829,8 +877,8 @@ end function modmgr.dialog_delete_mod() local mod = filterlist.get_list(modmgr.global_mods)[modmgr.selected_mod] - - local retval = + + local retval = "field[1.75,1;10,3;;" .. fgettext("Are you sure you want to delete \"$1\"?", mod.name) .. ";]".. "button[4,4.2;1,0.5;dlg_delete_mod_confirm;" .. fgettext("Yes") .. "]" .. "button[6.5,4.2;3,0.5;dlg_delete_mod_cancel;" .. fgettext("No of course not!") .. "]" @@ -841,10 +889,10 @@ end -------------------------------------------------------------------------------- function modmgr.preparemodlist(data) local retval = {} - + local global_mods = {} local game_mods = {} - + --read global mods local modpath = engine.get_modpath() @@ -852,31 +900,31 @@ function modmgr.preparemodlist(data) modpath ~= "" then get_mods(modpath,global_mods) end - + for i=1,#global_mods,1 do global_mods[i].typ = "global_mod" table.insert(retval,global_mods[i]) end - + --read game mods local gamespec = gamemgr.find_by_gameid(data.gameid) gamemgr.get_game_mods(gamespec, game_mods) - + for i=1,#game_mods,1 do game_mods[i].typ = "game_mod" table.insert(retval,game_mods[i]) end - + if data.worldpath == nil then return retval end - + --read world mod configuration local filename = data.worldpath .. DIR_DELIM .. "world.mt" local worldfile = Settings(filename) - + for key,value in pairs(worldfile:to_table()) do if key:sub(1, 9) == "load_mod_" then key = key:sub(10) @@ -890,7 +938,7 @@ function modmgr.preparemodlist(data) if element ~= nil then element.enabled = engine.is_yes(value) else - print("Mod: " .. key .. " " .. dump(value) .. " but not found") + engine.log("info", "Mod: " .. key .. " " .. dump(value) .. " but not found") end end end @@ -902,17 +950,17 @@ end function modmgr.init_worldconfig() modmgr.precheck() local worldspec = engine.get_worlds()[modmgr.world_config_selected_world] - + if worldspec ~= nil then --read worldconfig modmgr.worldconfig = modmgr.get_worldconfig(worldspec.path) - + if modmgr.worldconfig.id == nil or modmgr.worldconfig.id == "" then modmgr.worldconfig = nil return false end - + modmgr.modlist = filterlist.create( modmgr.preparemodlist, --refresh modmgr.comparemod, --compare @@ -920,13 +968,13 @@ function modmgr.init_worldconfig() if element.name == uid then return true end - end, + end, function(element,criteria) if criteria.hide_game and element.typ == "game_mod" then return false end - + if criteria.hide_modpackcontents and element.modpack ~= nil then return false @@ -936,15 +984,15 @@ function modmgr.init_worldconfig() { worldpath= worldspec.path, gameid = worldspec.gameid } ) - + filterlist.set_filtercriteria(modmgr.modlist, { hide_game=modmgr.hide_gamemods, hide_modpackcontents= modmgr.hide_modpackcontents }) filterlist.add_sort_mechanism(modmgr.modlist, "alphabetic", sort_mod_list) filterlist.set_sortmode(modmgr.modlist, "alphabetic") - - return true + + return true end return false @@ -967,34 +1015,34 @@ function modmgr.comparemod(elem1,elem2) if elem1.modpack ~= elem2.modpack then return false end - + if elem1.path ~= elem2.path then return false end - + return true end -------------------------------------------------------------------------------- function modmgr.gettab(name) local retval = "" - + if name == "mod_mgr" then retval = retval .. modmgr.tab() end - + if name == "dialog_rename_modpack" then retval = retval .. modmgr.dialog_rename_modpack() end - + if name == "dialog_delete_mod" then retval = retval .. modmgr.dialog_delete_mod() end - + if name == "dialog_configure_world" then retval = retval .. modmgr.dialog_configure_world() end - + return retval end @@ -1008,7 +1056,7 @@ function modmgr.mod_exists(basename) if filterlist.raw_index_by_uid(modmgr.global_mods,basename) > 0 then return true end - + return false end @@ -1018,7 +1066,7 @@ function modmgr.get_global_mod(idx) if modmgr.global_mods == nil then return nil end - + if idx < 1 or idx > filterlist.size(modmgr.global_mods) then return nil end @@ -1035,7 +1083,7 @@ function modmgr.refresh_globals() if element.name == uid then return true end - end, + end, nil, --filter {} ) @@ -1052,7 +1100,7 @@ function modmgr.identify_filetype(name) type = "zip" } end - + if name:sub(-6):lower() == "tar.gz" or name:sub(-3):lower() == "tgz"then return { @@ -1060,14 +1108,14 @@ function modmgr.identify_filetype(name) type = "tgz" } end - + if name:sub(-6):lower() == "tar.bz2" then return { name = name, type = "tbz" } end - + if name:sub(-2):lower() == "7z" then return { name = name, diff --git a/builtin/modstore.lua b/builtin/modstore.lua index 43f7759ad..b446a0553 100644 --- a/builtin/modstore.lua +++ b/builtin/modstore.lua @@ -21,22 +21,68 @@ modstore = {} -------------------------------------------------------------------------------- +-- @function [parent=#modstore] init function modstore.init() modstore.tabnames = {} - + table.insert(modstore.tabnames,"dialog_modstore_unsorted") table.insert(modstore.tabnames,"dialog_modstore_search") - + modstore.modsperpage = 5 - - modstore.basetexturedir = engine.get_texturepath() .. DIR_DELIM .. "base" .. + + modstore.basetexturedir = engine.get_texturepath() .. DIR_DELIM .. "base" .. DIR_DELIM .. "pack" .. DIR_DELIM + + modstore.lastmodtitle = "" + modstore.last_search = "" + modstore.searchlist = filterlist.create( + function() + if modstore.modlist_unsorted ~= nil and + modstore.modlist_unsorted.data ~= nil then + return modstore.modlist_unsorted.data + end + return {} + end, + function(element,modid) + if element.id == modid then + return true + end + return false + end, --compare fct + nil, --uid match fct + function(element,substring) + if substring == nil or + substring == "" then + return false + end + substring = substring:upper() + + if element.title ~= nil and + element.title:upper():find(substring) ~= nil then + return true + end + + if element.details ~= nil and + element.details.author ~= nil and + element.details.author:upper():find(substring) ~= nil then + return true + end + + if element.details ~= nil and + element.details.description ~= nil and + element.details.description:upper():find(substring) ~= nil then + return true + end + return false + end --filter fct + ) + modstore.current_list = nil - - modstore.details_cache = {} end + -------------------------------------------------------------------------------- +-- @function [parent=#modstore] nametoindex function modstore.nametoindex(name) for i=1,#modstore.tabnames,1 do @@ -49,55 +95,94 @@ function modstore.nametoindex(name) end -------------------------------------------------------------------------------- -function modstore.gettab(tabname) +-- @function [parent=#modstore] getsuccessfuldialog +function modstore.getsuccessfuldialog() local retval = "" + retval = retval .. "size[6,2]" + if modstore.lastmodentry ~= nil then + retval = retval .. "label[0,0.25;" .. fgettext("Successfully installed:") .. "]" + retval = retval .. "label[3,0.25;" .. modstore.lastmodentry.moddetails.title .. "]" - local is_modstore_tab = false - - if tabname == "dialog_modstore_unsorted" then - retval = modstore.getmodlist(modstore.modlist_unsorted) - is_modstore_tab = true - end - - if tabname == "dialog_modstore_search" then - - - is_modstore_tab = true - end - - if is_modstore_tab then - return modstore.tabheader(tabname) .. retval - end - - if tabname == "modstore_mod_installed" then - return "size[6,2]label[0.25,0.25;Mod: " .. modstore.lastmodtitle .. - " installed successfully]" .. - "button[2.5,1.5;1,0.5;btn_confirm_mod_successfull;ok]" - end - - return "" -end + + retval = retval .. "label[0,0.75;" .. fgettext("Shortname:") .. "]" + retval = retval .. "label[3,0.75;" .. engine.formspec_escape(modstore.lastmodentry.moddetails.basename) .. "]" --------------------------------------------------------------------------------- -function modstore.tabheader(tabname) - local retval = "size[12,9.25]" - retval = retval .. "tabheader[-0.3,-0.99;modstore_tab;" .. - "Unsorted,Search;" .. - modstore.nametoindex(tabname) .. ";true;false]" + end + retval = retval .. "button[2.5,1.5;1,0.5;btn_confirm_mod_successfull;" .. fgettext("ok") .. "]" + return retval end -------------------------------------------------------------------------------- +-- @function [parent=#modstore] gettab +function modstore.gettab(tabname) + local retval = "" + + local is_modstore_tab = false + + if tabname == "dialog_modstore_unsorted" then + modstore.modsperpage = 5 + retval = modstore.getmodlist(modstore.modlist_unsorted) + is_modstore_tab = true + end + + if tabname == "dialog_modstore_search" then + retval = modstore.getsearchpage() + is_modstore_tab = true + end + + if is_modstore_tab then + return modstore.tabheader(tabname) .. retval + end + + if tabname == "modstore_mod_installed" then + return modstore.getsuccessfuldialog() + end + + if tabname == "modstore_downloading" then + return "size[6,2]label[0.25,0.75;" .. fgettext("Downloading") .. + " " .. modstore.lastmodtitle .. " " .. + fgettext("please wait...") .. "]" + end + + return "" +end + +-------------------------------------------------------------------------------- +-- @function [parent=#modstore] tabheader +function modstore.tabheader(tabname) + local retval = "size[12,10.25]" + retval = retval .. "tabheader[-0.3,-0.99;modstore_tab;" .. + "Unsorted,Search;" .. + modstore.nametoindex(tabname) .. ";true;false]" .. + "button[4,9.9;4,0.5;btn_modstore_close;" .. + fgettext("Close modstore") .. "]" + + return retval +end + +-------------------------------------------------------------------------------- +-- @function [parent=#modstore] handle_buttons function modstore.handle_buttons(current_tab,fields) - modstore.lastmodtitle = "" - if fields["modstore_tab"] then local index = tonumber(fields["modstore_tab"]) - + if index > 0 and index <= #modstore.tabnames then + if modstore.tabnames[index] == "dialog_modstore_search" then + filterlist.set_filtercriteria(modstore.searchlist,modstore.last_search) + filterlist.refresh(modstore.searchlist) + modstore.modsperpage = 4 + modstore.currentlist = { + page = 0, + pagecount = + math.ceil(filterlist.size(modstore.searchlist) / modstore.modsperpage), + data = filterlist.get_list(modstore.searchlist), + } + end + return { current_tab = modstore.tabnames[index], is_dialog = true, @@ -105,170 +190,426 @@ function modstore.handle_buttons(current_tab,fields) } end - modstore.modlist_page = 0 end - + if fields["btn_modstore_page_up"] then if modstore.current_list ~= nil and modstore.current_list.page > 0 then modstore.current_list.page = modstore.current_list.page - 1 end end - + if fields["btn_modstore_page_down"] then - if modstore.current_list ~= nil and + if modstore.current_list ~= nil and modstore.current_list.page 1 then + local versiony = ypos + 0.05 + retval = retval .. "dropdown[9.1," .. versiony .. ";2.48,0.25;dd_version" .. details.id .. ";" + local versions = "" + for i=1,#details.versions , 1 do + if versions ~= "" then + versions = versions .. "," + end + + versions = versions .. details.versions[i].date:sub(1,10) + end + retval = retval .. versions .. ";1]" + end + + if details.basename then + --install button + local buttony = ypos + 1.2 + retval = retval .."button[9.1," .. buttony .. ";2.5,0.5;btn_install_mod_" .. details.id .. ";" + + if modmgr.mod_exists(details.basename) then + retval = retval .. fgettext("re-Install") .."]" + else + retval = retval .. fgettext("Install") .."]" + end + end + + return retval +end + +-------------------------------------------------------------------------------- +--@function [parent=#modstore] getmodlist +function modstore.getmodlist(list,yoffset) + + modstore.current_list = list + + if #list.data == 0 then + return "" + end + + if yoffset == nil then + yoffset = 0 + end + + local scrollbar = "" + scrollbar = scrollbar .. "label[0.1,9.5;" + .. fgettext("Page $1 of $2", list.page+1, list.pagecount) .. "]" + scrollbar = scrollbar .. "box[11.6," .. (yoffset + 0.35) .. ";0.28," + .. (8.6 - yoffset) .. ";#000000]" + local scrollbarpos = (yoffset + 0.75) + + ((7.7 -yoffset)/(list.pagecount-1)) * list.page + scrollbar = scrollbar .. "box[11.6," ..scrollbarpos .. ";0.28,0.5;#32CD32]" + scrollbar = scrollbar .. "button[11.6," .. (yoffset + (0.3)) + .. ";0.5,0.5;btn_modstore_page_up;^]" + scrollbar = scrollbar .. "button[11.6," .. 9.0 + .. ";0.5,0.5;btn_modstore_page_down;v]" + + local retval = "" + + local endmod = (list.page * modstore.modsperpage) + modstore.modsperpage + if (endmod > #list.data) then endmod = #list.data end for i=(list.page * modstore.modsperpage) +1, endmod, 1 do --getmoddetails - local details = modstore.get_details(list.data[i].id) - + local details = list.data[i].details + + if details == nil then + details = {} + details.title = list.data[i].title + details.author = "" + details.rating = -1 + details.description = "" + end + if details ~= nil then - local screenshot_ypos = (i-1 - (list.page * modstore.modsperpage))*1.9 +0.2 - - retval = retval .. "box[0," .. screenshot_ypos .. ";11.4,1.75;FFFFFF]" - - --screenshot - if details.screenshot_url ~= nil and - details.screenshot_url ~= "" then - if list.data[i].texturename == nil then - local fullurl = engine.setting_get("modstore_download_url") .. - details.screenshot_url - local filename = os.tempfolder() - - if engine.download_file(fullurl,filename) then - list.data[i].texturename = filename - end - end - end - - if list.data[i].texturename == nil then - list.data[i].texturename = modstore.basetexturedir .. "no_screenshot.png" - end - - retval = retval .. "image[0,".. screenshot_ypos .. ";3,2;" .. - engine.formspec_escape(list.data[i].texturename) .. "]" - - --title + author - retval = retval .."label[2.75," .. screenshot_ypos .. ";" .. - engine.formspec_escape(details.title) .. " (" .. details.author .. ")]" - - --description - local descriptiony = screenshot_ypos + 0.5 - retval = retval .. "textarea[3," .. descriptiony .. ";6.5,1.55;;" .. - engine.formspec_escape(details.description) .. ";]" - --rating - local ratingy = screenshot_ypos + 0.6 - retval = retval .."label[10.1," .. ratingy .. ";" .. - fgettext("Rating") .. ": " .. details.rating .."]" - - --install button - local buttony = screenshot_ypos + 1.2 - local buttonnumber = (i - (list.page * modstore.modsperpage)) - retval = retval .."button[9.6," .. buttony .. ";2,0.5;btn_install_mod_" .. buttonnumber .. ";" - - if modmgr.mod_exists(details.basename) then - retval = retval .. fgettext("re-Install") .."]" - else - retval = retval .. fgettext("Install") .."]" - end + local screenshot_ypos = + yoffset +(i-1 - (list.page * modstore.modsperpage))*1.9 +0.2 + + retval = retval .. modstore.getshortmodinfo(screenshot_ypos, + list.data[i], + details) end end - modstore.current_list = list - - return retval + return retval .. scrollbar end -------------------------------------------------------------------------------- -function modstore.get_details(modid) - - if modstore.details_cache[modid] ~= nil then - return modstore.details_cache[modid] - end +--@function [parent=#modstore] getsearchpage +function modstore.getsearchpage() + local retval = "" + local search = "" - local retval = engine.get_modstore_details(tostring(modid)) - modstore.details_cache[modid] = retval - return retval + if modstore.last_search ~= nil then + search = modstore.last_search + end + + retval = retval .. + "button[9.5,0.2;2.5,0.5;btn_modstore_search;".. fgettext("Search") .. "]" .. + "field[0.5,0.5;9,0.5;te_modstore_search;;" .. search .. "]" + + + --show 4 mods only + modstore.modsperpage = 4 + retval = retval .. + modstore.getmodlist( + modstore.currentlist, + 1.75) + + return retval; end diff --git a/builtin/serialize.lua b/builtin/serialize.lua index 61b923ce4..165cc6731 100644 --- a/builtin/serialize.lua +++ b/builtin/serialize.lua @@ -178,7 +178,7 @@ function minetest.deserialize(sdata) if okay then return results end - print('error:'.. results) + minetest.log('error', 'minetest.deserialize(): '.. results) return nil end diff --git a/builtin/vector.lua b/builtin/vector.lua index 839f139ca..77944b612 100644 --- a/builtin/vector.lua +++ b/builtin/vector.lua @@ -1,45 +1,46 @@ vector = {} +local function assert_vector(v) + assert(type(v) == "table" and v.x and v.y and v.z, "Invalid vector") +end + function vector.new(a, b, c) - v = {x=0, y=0, z=0} if type(a) == "table" then - v = {x=a.x, y=a.y, z=a.z} - elseif a and b and c then - v = {x=a, y=b, z=c} + assert(a.x and a.y and a.z, "Invalid vector passed to vector.new()") + return {x=a.x, y=a.y, z=a.z} + elseif a then + assert(b and c, "Invalid arguments for vector.new()") + return {x=a, y=b, z=c} end - setmetatable(v, { - __add = vector.add, - __sub = vector.subtract, - __mul = vector.multiply, - __div = vector.divide, - __umn = function(v) return vector.multiply(v, -1) end, - __len = vector.length, - __eq = vector.equals, - }) - return v + return {x=0, y=0, z=0} end function vector.equals(a, b) + assert_vector(a) + assert_vector(b) return a.x == b.x and a.y == b.y and a.z == b.z end function vector.length(v) + assert_vector(v) return math.hypot(v.x, math.hypot(v.y, v.z)) end function vector.normalize(v) + assert_vector(v) local len = vector.length(v) if len == 0 then - return vector.new() + return {x=0, y=0, z=0} else return vector.divide(v, len) end end function vector.round(v) + assert_vector(v) return { x = math.floor(v.x + 0.5), y = math.floor(v.y + 0.5), @@ -48,6 +49,8 @@ function vector.round(v) end function vector.distance(a, b) + assert_vector(a) + assert_vector(b) local x = a.x - b.x local y = a.y - b.y local z = a.z - b.z @@ -55,6 +58,8 @@ function vector.distance(a, b) end function vector.direction(pos1, pos2) + assert_vector(pos1) + assert_vector(pos2) local x_raw = pos2.x - pos1.x local y_raw = pos2.y - pos1.y local z_raw = pos2.z - pos1.z @@ -84,58 +89,58 @@ end function vector.add(a, b) + assert_vector(a) if type(b) == "table" then - return vector.new( - a.x + b.x, - a.y + b.y, - a.z + b.z) + assert_vector(b) + return {x = a.x + b.x, + y = a.y + b.y, + z = a.z + b.z} else - return vector.new( - a.x + b, - a.y + b, - a.z + b) + return {x = a.x + b, + y = a.y + b, + z = a.z + b} end end function vector.subtract(a, b) + assert_vector(a) if type(b) == "table" then - return vector.new( - a.x - b.x, - a.y - b.y, - a.z - b.z) + assert_vector(b) + return {x = a.x - b.x, + y = a.y - b.y, + z = a.z - b.z} else - return vector.new( - a.x - b, - a.y - b, - a.z - b) + return {x = a.x - b, + y = a.y - b, + z = a.z - b} end end function vector.multiply(a, b) + assert_vector(a) if type(b) == "table" then - return vector.new( - a.x * b.x, - a.y * b.y, - a.z * b.z) + assert_vector(b) + return {x = a.x * b.x, + y = a.y * b.y, + z = a.z * b.z} else - return vector.new( - a.x * b, - a.y * b, - a.z * b) + return {x = a.x * b, + y = a.y * b, + z = a.z * b} end end function vector.divide(a, b) + assert_vector(a) if type(b) == "table" then - return vector.new( - a.x / b.x, - a.y / b.y, - a.z / b.z) + assert_vector(b) + return {x = a.x / b.x, + y = a.y / b.y, + z = a.z / b.z} else - return vector.new( - a.x / b, - a.y / b, - a.z / b) + return {x = a.x / b, + y = a.y / b, + z = a.z / b} end end diff --git a/client/shaders/bumpmaps_liquids/base.txt b/client/shaders/alpha_shader/base.txt similarity index 100% rename from client/shaders/bumpmaps_liquids/base.txt rename to client/shaders/alpha_shader/base.txt diff --git a/client/shaders/alpha_shader/opengl_fragment.glsl b/client/shaders/alpha_shader/opengl_fragment.glsl new file mode 100644 index 000000000..6ed00be25 --- /dev/null +++ b/client/shaders/alpha_shader/opengl_fragment.glsl @@ -0,0 +1,71 @@ +uniform sampler2D baseTexture; +uniform sampler2D normalTexture; +uniform sampler2D useNormalmap; + +uniform vec4 skyBgColor; +uniform float fogDistance; +uniform vec3 eyePosition; + +varying vec3 vPosition; +varying vec3 eyeVec; + +#ifdef ENABLE_PARALLAX_OCCLUSION +varying vec3 tsEyeVec; +#endif + +const float e = 2.718281828459; + +void main (void) +{ + vec3 color; + vec2 uv = gl_TexCoord[0].st; + +#ifdef USE_NORMALMAPS + float use_normalmap = texture2D(useNormalmap,vec2(1.0,1.0)).r; +#endif + +#ifdef ENABLE_PARALLAX_OCCLUSION + float height; + vec2 tsEye = vec2(tsEyeVec.x,-tsEyeVec.y); + + if (use_normalmap > 0.0) { + float map_height = texture2D(normalTexture, uv).a; + if (map_height < 1.0){ + float height = PARALLAX_OCCLUSION_SCALE * map_height - PARALLAX_OCCLUSION_BIAS; + uv = uv + height * tsEye; + } + } +#endif + +#ifdef ENABLE_BUMPMAPPING + if (use_normalmap > 0.0) { + vec3 base = texture2D(baseTexture, uv).rgb; + vec3 vVec = normalize(eyeVec); + vec3 bump = normalize(texture2D(normalTexture, uv).xyz * 2.0 - 1.0); + vec3 R = reflect(-vVec, bump); + vec3 lVec = normalize(vVec); + float diffuse = max(dot(lVec, bump), 0.0); + float specular = pow(clamp(dot(R, lVec), 0.0, 1.0),1.0); + color = mix (base,diffuse*base,1.0) + 0.1 * specular * diffuse; + } else { + color = texture2D(baseTexture, uv).rgb; + } +#else + color = texture2D(baseTexture, uv).rgb; +#endif + + float alpha = texture2D(baseTexture, uv).a; + vec4 col = vec4(color.r, color.g, color.b, alpha); + col *= gl_Color; + col = col * col; // SRGB -> Linear + col *= 1.8; + col.r = 1.0 - exp(1.0 - col.r) / e; + col.g = 1.0 - exp(1.0 - col.g) / e; + col.b = 1.0 - exp(1.0 - col.b) / e; + col = sqrt(col); // Linear -> SRGB + if(fogDistance != 0.0){ + float d = max(0.0, min(vPosition.z / fogDistance * 1.5 - 0.6, 1.0)); + col = mix(col, skyBgColor, d); + } + gl_FragColor = vec4(col.r, col.g, col.b, alpha); +} diff --git a/client/shaders/alpha_shader/opengl_vertex.glsl b/client/shaders/alpha_shader/opengl_vertex.glsl new file mode 100644 index 000000000..e359955d0 --- /dev/null +++ b/client/shaders/alpha_shader/opengl_vertex.glsl @@ -0,0 +1,102 @@ +uniform mat4 mWorldViewProj; +uniform mat4 mInvWorld; +uniform mat4 mTransWorld; +uniform float dayNightRatio; + +uniform vec3 eyePosition; + +varying vec3 vPosition; +varying vec3 eyeVec; + +#ifdef ENABLE_PARALLAX_OCCLUSION +varying vec3 tsEyeVec; +#endif + +void main(void) +{ + gl_Position = mWorldViewProj * gl_Vertex; + vPosition = (mWorldViewProj * gl_Vertex).xyz; + eyeVec = (gl_ModelViewMatrix * gl_Vertex).xyz; + +#ifdef ENABLE_PARALLAX_OCCLUSION + vec3 normal,tangent,binormal; + normal = normalize(gl_NormalMatrix * gl_Normal); + + if (gl_Normal.x > 0.5) { + // 1.0, 0.0, 0.0 + tangent = normalize(gl_NormalMatrix * vec3( 0.0, 0.0, -1.0)); + binormal = normalize(gl_NormalMatrix * vec3( 0.0, -1.0, 0.0)); + } else if (gl_Normal.x < -0.5) { + // -1.0, 0.0, 0.0 + tangent = normalize(gl_NormalMatrix * vec3( 0.0, 0.0, 1.0)); + binormal = normalize(gl_NormalMatrix * vec3( 0.0, -1.0, 0.0)); + } else if (gl_Normal.y > 0.5) { + // 0.0, 1.0, 0.0 + tangent = normalize(gl_NormalMatrix * vec3( 1.0, 0.0, 0.0)); + binormal = normalize(gl_NormalMatrix * vec3( 0.0, 0.0, 1.0)); + } else if (gl_Normal.y < -0.5) { + // 0.0, -1.0, 0.0 + tangent = normalize(gl_NormalMatrix * vec3( 1.0, 0.0, 0.0)); + binormal = normalize(gl_NormalMatrix * vec3( 0.0, 0.0, 1.0)); + } else if (gl_Normal.z > 0.5) { + // 0.0, 0.0, 1.0 + tangent = normalize(gl_NormalMatrix * vec3( 1.0, 0.0, 0.0)); + binormal = normalize(gl_NormalMatrix * vec3( 0.0, -1.0, 0.0)); + } else if (gl_Normal.z < -0.5) { + // 0.0, 0.0, -1.0 + tangent = normalize(gl_NormalMatrix * vec3(-1.0, 0.0, 0.0)); + binormal = normalize(gl_NormalMatrix * vec3( 0.0, -1.0, 0.0)); + } + + mat3 tbnMatrix = mat3( tangent.x, binormal.x, normal.x, + tangent.y, binormal.y, normal.y, + tangent.z, binormal.z, normal.z); + + tsEyeVec = normalize(eyeVec * tbnMatrix); +#endif + + vec4 color; + //color = vec4(1.0, 1.0, 1.0, 1.0); + + float day = gl_Color.r; + float night = gl_Color.g; + float light_source = gl_Color.b; + + /*color.r = mix(night, day, dayNightRatio); + color.g = color.r; + color.b = color.r;*/ + + float rg = mix(night, day, dayNightRatio); + rg += light_source * 2.5; // Make light sources brighter + float b = rg; + + // Moonlight is blue + b += (day - night) / 13.0; + rg -= (day - night) / 13.0; + + // Emphase blue a bit in darker places + // See C++ implementation in mapblock_mesh.cpp finalColorBlend() + b += max(0.0, (1.0 - abs(b - 0.13)/0.17) * 0.025); + + // Artificial light is yellow-ish + // See C++ implementation in mapblock_mesh.cpp finalColorBlend() + rg += max(0.0, (1.0 - abs(rg - 0.85)/0.15) * 0.065); + + color.r = clamp(rg,0.0,1.0); + color.g = clamp(rg,0.0,1.0); + color.b = clamp(b,0.0,1.0); + + // Make sides and bottom darker than the top + color = color * color; // SRGB -> Linear + if(gl_Normal.y <= 0.5) + color *= 0.6; + //color *= 0.7; + color = sqrt(color); // Linear -> SRGB + + color.a = gl_Color.a; + + gl_FrontColor = gl_BackColor = color; + + gl_TexCoord[0] = gl_MultiTexCoord0; + +} diff --git a/client/shaders/bumpmaps_liquids/opengl_fragment.glsl b/client/shaders/bumpmaps_liquids/opengl_fragment.glsl deleted file mode 100644 index bf6bbf043..000000000 --- a/client/shaders/bumpmaps_liquids/opengl_fragment.glsl +++ /dev/null @@ -1,46 +0,0 @@ - -uniform sampler2D myTexture; -uniform sampler2D normalTexture; - -uniform vec4 skyBgColor; -uniform float fogDistance; - -varying vec3 vPosition; - -varying vec3 viewVec; - -void main (void) -{ - vec4 col = texture2D(myTexture, vec2(gl_TexCoord[0])); - float alpha = col.a; - vec2 uv = gl_TexCoord[0].st; - vec4 base = texture2D(myTexture, uv); - vec4 final_color = vec4(0.2, 0.2, 0.2, 1.0) * base; - vec3 vVec = normalize(viewVec); - vec3 bump = normalize(texture2D(normalTexture, uv).xyz * 2.0 - 1.0); - vec3 R = reflect(-vVec, bump); - vec3 lVec = normalize(vec3(0.0, -0.4, 0.5)); - float diffuse = max(dot(lVec, bump), 0.0); - - vec3 color = diffuse * texture2D(myTexture, gl_TexCoord[0].st).rgb; - - - float specular = pow(clamp(dot(R, lVec), 0.0, 1.0),1.0); - color += vec3(0.2*specular*diffuse); - - - col = vec4(color.r, color.g, color.b, alpha); - col *= gl_Color; - col = col * col; // SRGB -> Linear - col *= 1.8; - col.r = 1.0 - exp(1.0 - col.r) / exp(1.0); - col.g = 1.0 - exp(1.0 - col.g) / exp(1.0); - col.b = 1.0 - exp(1.0 - col.b) / exp(1.0); - col = sqrt(col); // Linear -> SRGB - if(fogDistance != 0.0){ - float d = max(0.0, min(vPosition.z / fogDistance * 1.5 - 0.6, 1.0)); - alpha = mix(alpha, 0.0, d); - } - - gl_FragColor = vec4(col.r, col.g, col.b, alpha); -} diff --git a/client/shaders/bumpmaps_solids/opengl_fragment.glsl b/client/shaders/bumpmaps_solids/opengl_fragment.glsl deleted file mode 100644 index c72e8cbce..000000000 --- a/client/shaders/bumpmaps_solids/opengl_fragment.glsl +++ /dev/null @@ -1,45 +0,0 @@ - -uniform sampler2D myTexture; -uniform sampler2D normalTexture; - -uniform vec4 skyBgColor; -uniform float fogDistance; - -varying vec3 vPosition; - -varying vec3 viewVec; - -void main (void) -{ - vec4 col = texture2D(myTexture, vec2(gl_TexCoord[0])); - float alpha = col.a; - vec2 uv = gl_TexCoord[0].st; - vec4 base = texture2D(myTexture, uv); - vec4 final_color = vec4(0.2, 0.2, 0.2, 1.0) * base; - vec3 vVec = normalize(viewVec); - vec3 bump = normalize(texture2D(normalTexture, uv).xyz * 2.0 - 1.0); - vec3 R = reflect(-vVec, bump); - vec3 lVec = normalize(vec3(0.0, -0.4, 0.5)); - float diffuse = max(dot(lVec, bump), 0.0); - - vec3 color = diffuse * texture2D(myTexture, gl_TexCoord[0].st).rgb; - - - float specular = pow(clamp(dot(R, lVec), 0.0, 1.0),1.0); - color += vec3(0.2*specular*diffuse); - - - col = vec4(color.r, color.g, color.b, alpha); - col *= gl_Color; - col = col * col; // SRGB -> Linear - col *= 1.8; - col.r = 1.0 - exp(1.0 - col.r) / exp(1.0); - col.g = 1.0 - exp(1.0 - col.g) / exp(1.0); - col.b = 1.0 - exp(1.0 - col.b) / exp(1.0); - col = sqrt(col); // Linear -> SRGB - if(fogDistance != 0.0){ - float d = max(0.0, min(vPosition.z / fogDistance * 1.5 - 0.6, 1.0)); - col = mix(col, skyBgColor, d); - } - gl_FragColor = vec4(col.r, col.g, col.b, alpha); -} diff --git a/client/shaders/bumpmaps_solids/base.txt b/client/shaders/leaves_shader/base.txt similarity index 100% rename from client/shaders/bumpmaps_solids/base.txt rename to client/shaders/leaves_shader/base.txt diff --git a/client/shaders/leaves_shader/opengl_fragment.glsl b/client/shaders/leaves_shader/opengl_fragment.glsl new file mode 100644 index 000000000..31981f9b9 --- /dev/null +++ b/client/shaders/leaves_shader/opengl_fragment.glsl @@ -0,0 +1,54 @@ +uniform sampler2D baseTexture; +uniform sampler2D normalTexture; +uniform sampler2D useNormalmap; + +uniform vec4 skyBgColor; +uniform float fogDistance; +uniform vec3 eyePosition; + +varying vec3 vPosition; +varying vec3 eyeVec; + +const float e = 2.718281828459; + +void main (void) +{ + vec3 color; + vec2 uv = gl_TexCoord[0].st; + +#ifdef USE_NORMALMAPS + float use_normalmap = texture2D(useNormalmap,vec2(1.0,1.0)).r; +#endif + +#ifdef ENABLE_BUMPMAPPING + if (use_normalmap > 0.0) { + vec3 base = texture2D(baseTexture, uv).rgb; + vec3 vVec = normalize(eyeVec); + vec3 bump = normalize(texture2D(normalTexture, uv).xyz * 2.0 - 1.0); + vec3 R = reflect(-vVec, bump); + vec3 lVec = normalize(vVec); + float diffuse = max(dot(lVec, bump), 0.0); + float specular = pow(clamp(dot(R, lVec), 0.0, 1.0),1.0); + color = mix (base,diffuse*base,1.0) + 0.1 * specular * diffuse; + } else { + color = texture2D(baseTexture, uv).rgb; + } +#else + color = texture2D(baseTexture, uv).rgb; +#endif + + float alpha = texture2D(baseTexture, uv).a; + vec4 col = vec4(color.r, color.g, color.b, alpha); + col *= gl_Color; + col = col * col; // SRGB -> Linear + col *= 1.8; + col.r = 1.0 - exp(1.0 - col.r) / e; + col.g = 1.0 - exp(1.0 - col.g) / e; + col.b = 1.0 - exp(1.0 - col.b) / e; + col = sqrt(col); // Linear -> SRGB + if(fogDistance != 0.0){ + float d = max(0.0, min(vPosition.z / fogDistance * 1.5 - 0.6, 1.0)); + col = mix(col, skyBgColor, d); + } + gl_FragColor = vec4(col.r, col.g, col.b, alpha); +} diff --git a/client/shaders/bumpmaps_solids/opengl_vertex.glsl b/client/shaders/leaves_shader/opengl_vertex.glsl similarity index 50% rename from client/shaders/bumpmaps_solids/opengl_vertex.glsl rename to client/shaders/leaves_shader/opengl_vertex.glsl index 99c208bdd..3702b1b50 100644 --- a/client/shaders/bumpmaps_solids/opengl_vertex.glsl +++ b/client/shaders/leaves_shader/opengl_vertex.glsl @@ -1,37 +1,44 @@ - uniform mat4 mWorldViewProj; uniform mat4 mInvWorld; uniform mat4 mTransWorld; uniform float dayNightRatio; +uniform float animationTimer; + +uniform vec3 eyePosition; varying vec3 vPosition; -varying vec3 viewVec; +varying vec3 eyeVec; + +#ifdef ENABLE_WAVING_LEAVES +float smoothCurve( float x ) { + return x * x *( 3.0 - 2.0 * x ); +} +float triangleWave( float x ) { + return abs( fract( x + 0.5 ) * 2.0 - 1.0 ); +} +float smoothTriangleWave( float x ) { + return smoothCurve( triangleWave( x ) ) * 2.0 - 1.0; +} +#endif void main(void) { + gl_TexCoord[0] = gl_MultiTexCoord0; + +#ifdef ENABLE_WAVING_LEAVES + vec4 pos = gl_Vertex; + vec4 pos2 = mTransWorld*gl_Vertex; + pos.x += (smoothTriangleWave(animationTimer*10.0 + pos2.x * 0.01 + pos2.z * 0.01) * 2.0 - 1.0) * 0.4; + pos.y += (smoothTriangleWave(animationTimer*15.0 + pos2.x * -0.01 + pos2.z * -0.01) * 2.0 - 1.0) * 0.2; + pos.z += (smoothTriangleWave(animationTimer*10.0 + pos2.x * -0.01 + pos2.z * -0.01) * 2.0 - 1.0) * 0.4; + gl_Position = mWorldViewProj * pos; +#else gl_Position = mWorldViewProj * gl_Vertex; +#endif vPosition = (mWorldViewProj * gl_Vertex).xyz; - - vec3 tangent; - vec3 binormal; - vec3 c1 = cross( gl_Normal, vec3(0.0, 0.0, 1.0) ); - vec3 c2 = cross( gl_Normal, vec3(0.0, 1.0, 0.0) ); - - if( length(c1)>length(c2) ) - { - tangent = c1; - } - else - { - tangent = c2; - } - - tangent = normalize(tangent); - -//binormal = cross(gl_Normal, tangent); -//binormal = normalize(binormal); + eyeVec = (gl_ModelViewMatrix * gl_Vertex).xyz; vec4 color; //color = vec4(1.0, 1.0, 1.0, 1.0); @@ -45,7 +52,7 @@ void main(void) color.b = color.r;*/ float rg = mix(night, day, dayNightRatio); - rg += light_source * 1.5; // Make light sources brighter + rg += light_source * 2.5; // Make light sources brighter float b = rg; // Moonlight is blue @@ -60,9 +67,9 @@ void main(void) // See C++ implementation in mapblock_mesh.cpp finalColorBlend() rg += max(0.0, (1.0 - abs(rg - 0.85)/0.15) * 0.065); - color.r = rg; - color.g = rg; - color.b = b; + color.r = clamp(rg,0.0,1.0); + color.g = clamp(rg,0.0,1.0); + color.b = clamp(b,0.0,1.0); // Make sides and bottom darker than the top color = color * color; // SRGB -> Linear @@ -76,23 +83,5 @@ void main(void) gl_FrontColor = gl_BackColor = color; gl_TexCoord[0] = gl_MultiTexCoord0; - - vec3 n1 = normalize(gl_NormalMatrix * gl_Normal); - vec4 tangent1 = vec4(tangent.x, tangent.y, tangent.z, 0); - //vec3 t1 = normalize(gl_NormalMatrix * tangent1); - //vec3 b1 = cross(n1, t1); - vec3 v; - vec3 vVertex = vec3(gl_ModelViewMatrix * gl_Vertex); - vec3 vVec = -vVertex; - //v.x = dot(vVec, t1); - //v.y = dot(vVec, b1); - //v.z = dot(vVec, n1); - //viewVec = vVec; - viewVec = normalize(vec3(0.0, -0.4, 0.5)); - //Vector representing the 0th texture coordinate passed to fragment shader -//gl_TexCoord[0] = vec2(gl_MultiTexCoord0); - -// Transform the current vertex -//gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex; } diff --git a/client/shaders/test_shader_2/base.txt b/client/shaders/liquids_shader/base.txt similarity index 100% rename from client/shaders/test_shader_2/base.txt rename to client/shaders/liquids_shader/base.txt diff --git a/client/shaders/liquids_shader/opengl_fragment.glsl b/client/shaders/liquids_shader/opengl_fragment.glsl new file mode 100644 index 000000000..cab8d8e01 --- /dev/null +++ b/client/shaders/liquids_shader/opengl_fragment.glsl @@ -0,0 +1,54 @@ +uniform sampler2D baseTexture; +uniform sampler2D normalTexture; +uniform sampler2D useNormalmap; + +uniform vec4 skyBgColor; +uniform float fogDistance; +uniform vec3 eyePosition; + +varying vec3 vPosition; +varying vec3 eyeVec; + +const float e = 2.718281828459; + +void main (void) +{ + vec3 color; + vec2 uv = gl_TexCoord[0].st; + +#ifdef USE_NORMALMAPS + float use_normalmap = texture2D(useNormalmap,vec2(1.0,1.0)).r; +#endif + +#ifdef ENABLE_BUMPMAPPING + if (use_normalmap > 0.0) { + vec3 base = texture2D(baseTexture, uv).rgb; + vec3 vVec = normalize(eyeVec); + vec3 bump = normalize(texture2D(normalTexture, uv).xyz * 2.0 - 1.0); + vec3 R = reflect(-vVec, bump); + vec3 lVec = normalize(vVec); + float diffuse = max(dot(vec3(-1.0, -0.4, 0.5), bump), 0.0); + float specular = pow(clamp(dot(R, lVec), 0.0, 1.0),1.0); + color = mix (base,diffuse*base,1.0) + 0.1 * specular * diffuse; + } else { + color = texture2D(baseTexture, uv).rgb; + } +#else + color = texture2D(baseTexture, uv).rgb; +#endif + + float alpha = gl_Color.a; + vec4 col = vec4(color.r, color.g, color.b, alpha); + col *= gl_Color; + col = col * col; // SRGB -> Linear + col *= 1.8; + col.r = 1.0 - exp(1.0 - col.r) / e; + col.g = 1.0 - exp(1.0 - col.g) / e; + col.b = 1.0 - exp(1.0 - col.b) / e; + col = sqrt(col); // Linear -> SRGB + if(fogDistance != 0.0){ + float d = max(0.0, min(vPosition.z / fogDistance * 1.5 - 0.6, 1.0)); + alpha = mix(alpha, 0.0, d); + } + gl_FragColor = vec4(col.r, col.g, col.b, alpha); +} diff --git a/client/shaders/test_shader_1/opengl_vertex.glsl b/client/shaders/liquids_shader/opengl_vertex.glsl similarity index 59% rename from client/shaders/test_shader_1/opengl_vertex.glsl rename to client/shaders/liquids_shader/opengl_vertex.glsl index 3cf1f122b..e8f185825 100644 --- a/client/shaders/test_shader_1/opengl_vertex.glsl +++ b/client/shaders/liquids_shader/opengl_vertex.glsl @@ -1,15 +1,27 @@ - uniform mat4 mWorldViewProj; uniform mat4 mInvWorld; uniform mat4 mTransWorld; uniform float dayNightRatio; +uniform float animationTimer; + +uniform vec3 eyePosition; varying vec3 vPosition; +varying vec3 eyeVec; void main(void) { +#ifdef ENABLE_WAVING_WATER + vec4 pos2 = gl_Vertex; + pos2.y -= 2.0; + pos2.y -= sin (pos2.z/WATER_WAVE_LENGTH + animationTimer * WATER_WAVE_SPEED * WATER_WAVE_LENGTH) * WATER_WAVE_HEIGHT + + sin ((pos2.z/WATER_WAVE_LENGTH + animationTimer * WATER_WAVE_SPEED * WATER_WAVE_LENGTH) / 7.0) * WATER_WAVE_HEIGHT; + gl_Position = mWorldViewProj * pos2; +#else gl_Position = mWorldViewProj * gl_Vertex; +#endif + eyeVec = (gl_ModelViewMatrix * gl_Vertex).xyz; vPosition = (mWorldViewProj * gl_Vertex).xyz; vec4 color; @@ -24,7 +36,7 @@ void main(void) color.b = color.r;*/ float rg = mix(night, day, dayNightRatio); - rg += light_source * 1.0; // Make light sources brighter + rg += light_source * 2.5; // Make light sources brighter float b = rg; // Moonlight is blue @@ -39,20 +51,13 @@ void main(void) // See C++ implementation in mapblock_mesh.cpp finalColorBlend() rg += max(0.0, (1.0 - abs(rg - 0.85)/0.15) * 0.065); - color.r = rg; - color.g = rg; - color.b = b; - - // Make sides and bottom darker than the top - color = color * color; // SRGB -> Linear - if(gl_Normal.y <= 0.5) - color *= 0.6; - //color *= 0.7; - color = sqrt(color); // Linear -> SRGB - + color.r = clamp(rg,0.0,1.0); + color.g = clamp(rg,0.0,1.0); + color.b = clamp(b,0.0,1.0); color.a = gl_Color.a; gl_FrontColor = gl_BackColor = color; gl_TexCoord[0] = gl_MultiTexCoord0; + } diff --git a/client/shaders/test_shader_1/base.txt b/client/shaders/plants_shader/base.txt similarity index 100% rename from client/shaders/test_shader_1/base.txt rename to client/shaders/plants_shader/base.txt diff --git a/client/shaders/plants_shader/opengl_fragment.glsl b/client/shaders/plants_shader/opengl_fragment.glsl new file mode 100644 index 000000000..31981f9b9 --- /dev/null +++ b/client/shaders/plants_shader/opengl_fragment.glsl @@ -0,0 +1,54 @@ +uniform sampler2D baseTexture; +uniform sampler2D normalTexture; +uniform sampler2D useNormalmap; + +uniform vec4 skyBgColor; +uniform float fogDistance; +uniform vec3 eyePosition; + +varying vec3 vPosition; +varying vec3 eyeVec; + +const float e = 2.718281828459; + +void main (void) +{ + vec3 color; + vec2 uv = gl_TexCoord[0].st; + +#ifdef USE_NORMALMAPS + float use_normalmap = texture2D(useNormalmap,vec2(1.0,1.0)).r; +#endif + +#ifdef ENABLE_BUMPMAPPING + if (use_normalmap > 0.0) { + vec3 base = texture2D(baseTexture, uv).rgb; + vec3 vVec = normalize(eyeVec); + vec3 bump = normalize(texture2D(normalTexture, uv).xyz * 2.0 - 1.0); + vec3 R = reflect(-vVec, bump); + vec3 lVec = normalize(vVec); + float diffuse = max(dot(lVec, bump), 0.0); + float specular = pow(clamp(dot(R, lVec), 0.0, 1.0),1.0); + color = mix (base,diffuse*base,1.0) + 0.1 * specular * diffuse; + } else { + color = texture2D(baseTexture, uv).rgb; + } +#else + color = texture2D(baseTexture, uv).rgb; +#endif + + float alpha = texture2D(baseTexture, uv).a; + vec4 col = vec4(color.r, color.g, color.b, alpha); + col *= gl_Color; + col = col * col; // SRGB -> Linear + col *= 1.8; + col.r = 1.0 - exp(1.0 - col.r) / e; + col.g = 1.0 - exp(1.0 - col.g) / e; + col.b = 1.0 - exp(1.0 - col.b) / e; + col = sqrt(col); // Linear -> SRGB + if(fogDistance != 0.0){ + float d = max(0.0, min(vPosition.z / fogDistance * 1.5 - 0.6, 1.0)); + col = mix(col, skyBgColor, d); + } + gl_FragColor = vec4(col.r, col.g, col.b, alpha); +} diff --git a/client/shaders/bumpmaps_liquids/opengl_vertex.glsl b/client/shaders/plants_shader/opengl_vertex.glsl similarity index 51% rename from client/shaders/bumpmaps_liquids/opengl_vertex.glsl rename to client/shaders/plants_shader/opengl_vertex.glsl index 99c208bdd..7987fc16d 100644 --- a/client/shaders/bumpmaps_liquids/opengl_vertex.glsl +++ b/client/shaders/plants_shader/opengl_vertex.glsl @@ -1,37 +1,45 @@ - uniform mat4 mWorldViewProj; uniform mat4 mInvWorld; uniform mat4 mTransWorld; uniform float dayNightRatio; +uniform float animationTimer; + +uniform vec3 eyePosition; varying vec3 vPosition; -varying vec3 viewVec; +varying vec3 eyeVec; + +#ifdef ENABLE_WAVING_PLANTS +float smoothCurve( float x ) { + return x * x *( 3.0 - 2.0 * x ); +} +float triangleWave( float x ) { + return abs( fract( x + 0.5 ) * 2.0 - 1.0 ); +} +float smoothTriangleWave( float x ) { + return smoothCurve( triangleWave( x ) ) * 2.0 - 1.0; +} +#endif void main(void) { + + gl_TexCoord[0] = gl_MultiTexCoord0; + +#ifdef ENABLE_WAVING_PLANTS + vec4 pos = gl_Vertex; + vec4 pos2 = mTransWorld * gl_Vertex; + if (gl_TexCoord[0].y < 0.05) { + pos.x += (smoothTriangleWave(animationTimer * 20.0 + pos2.x * 0.1 + pos2.z * 0.1) * 2.0 - 1.0) * 0.8; + pos.y -= (smoothTriangleWave(animationTimer * 10.0 + pos2.x * -0.5 + pos2.z * -0.5) * 2.0 - 1.0) * 0.4; + } + gl_Position = mWorldViewProj * pos; +#else gl_Position = mWorldViewProj * gl_Vertex; +#endif vPosition = (mWorldViewProj * gl_Vertex).xyz; - - vec3 tangent; - vec3 binormal; - - vec3 c1 = cross( gl_Normal, vec3(0.0, 0.0, 1.0) ); - vec3 c2 = cross( gl_Normal, vec3(0.0, 1.0, 0.0) ); - - if( length(c1)>length(c2) ) - { - tangent = c1; - } - else - { - tangent = c2; - } - - tangent = normalize(tangent); - -//binormal = cross(gl_Normal, tangent); -//binormal = normalize(binormal); + eyeVec = (gl_ModelViewMatrix * gl_Vertex).xyz; vec4 color; //color = vec4(1.0, 1.0, 1.0, 1.0); @@ -45,7 +53,7 @@ void main(void) color.b = color.r;*/ float rg = mix(night, day, dayNightRatio); - rg += light_source * 1.5; // Make light sources brighter + rg += light_source * 2.5; // Make light sources brighter float b = rg; // Moonlight is blue @@ -60,9 +68,9 @@ void main(void) // See C++ implementation in mapblock_mesh.cpp finalColorBlend() rg += max(0.0, (1.0 - abs(rg - 0.85)/0.15) * 0.065); - color.r = rg; - color.g = rg; - color.b = b; + color.r = clamp(rg,0.0,1.0); + color.g = clamp(rg,0.0,1.0); + color.b = clamp(b,0.0,1.0); // Make sides and bottom darker than the top color = color * color; // SRGB -> Linear @@ -75,24 +83,4 @@ void main(void) gl_FrontColor = gl_BackColor = color; - gl_TexCoord[0] = gl_MultiTexCoord0; - - vec3 n1 = normalize(gl_NormalMatrix * gl_Normal); - vec4 tangent1 = vec4(tangent.x, tangent.y, tangent.z, 0); - //vec3 t1 = normalize(gl_NormalMatrix * tangent1); - //vec3 b1 = cross(n1, t1); - - vec3 v; - vec3 vVertex = vec3(gl_ModelViewMatrix * gl_Vertex); - vec3 vVec = -vVertex; - //v.x = dot(vVec, t1); - //v.y = dot(vVec, b1); - //v.z = dot(vVec, n1); - //viewVec = vVec; - viewVec = normalize(vec3(0.0, -0.4, 0.5)); - //Vector representing the 0th texture coordinate passed to fragment shader -//gl_TexCoord[0] = vec2(gl_MultiTexCoord0); - -// Transform the current vertex -//gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex; } diff --git a/client/shaders/solids_shader/base.txt b/client/shaders/solids_shader/base.txt new file mode 100644 index 000000000..080df30dd --- /dev/null +++ b/client/shaders/solids_shader/base.txt @@ -0,0 +1 @@ +trans_alphach_ref diff --git a/client/shaders/solids_shader/opengl_fragment.glsl b/client/shaders/solids_shader/opengl_fragment.glsl new file mode 100644 index 000000000..d81506a4f --- /dev/null +++ b/client/shaders/solids_shader/opengl_fragment.glsl @@ -0,0 +1,90 @@ +uniform sampler2D baseTexture; +uniform sampler2D normalTexture; +uniform sampler2D useNormalmap; + +uniform vec4 skyBgColor; +uniform float fogDistance; +uniform vec3 eyePosition; + +varying vec3 vPosition; +varying vec3 eyeVec; + +#ifdef ENABLE_PARALLAX_OCCLUSION +varying vec3 tsEyeVec; +#endif + +const float e = 2.718281828459; + +void main (void) +{ + vec3 color; + vec2 uv = gl_TexCoord[0].st; + +#ifdef USE_NORMALMAPS + float use_normalmap = texture2D(useNormalmap,vec2(1.0,1.0)).r; +#endif + +#ifdef ENABLE_PARALLAX_OCCLUSION + float height; + vec2 tsEye = vec2(tsEyeVec.x,-tsEyeVec.y); + + if (use_normalmap > 0.0) { + float map_height = texture2D(normalTexture, uv).a; + if (map_height < 1.0){ + float height = PARALLAX_OCCLUSION_SCALE * map_height - PARALLAX_OCCLUSION_BIAS; + uv = uv + height * tsEye; + } + } +#endif + +/* Steep parallax code, for future use + if ((parallaxMappingMode == 2.0) && (use_normalmap > 0.0)) { + const float numSteps = 40.0; + float height = 1.0; + float step = 1.0 / numSteps; + vec4 NB = texture2D(normalTexture, uv); + vec2 delta = tsEye * parallaxMappingScale / numSteps; + for (float i = 0.0; i < numSteps; i++) { + if (NB.a < height) { + height -= step; + uv += delta; + NB = texture2D(normalTexture, uv); + } else { + break; + } + } + } +*/ + +#ifdef ENABLE_BUMPMAPPING + if (use_normalmap > 0.0) { + vec3 base = texture2D(baseTexture, uv).rgb; + vec3 vVec = normalize(eyeVec); + vec3 bump = normalize(texture2D(normalTexture, uv).xyz * 2.0 - 1.0); + vec3 R = reflect(-vVec, bump); + vec3 lVec = normalize(vVec); + float diffuse = max(dot(lVec, bump), 0.0); + float specular = pow(clamp(dot(R, lVec), 0.0, 1.0),1.0); + color = mix (base,diffuse*base,1.0) + 0.1 * specular * diffuse; + } else { + color = texture2D(baseTexture, uv).rgb; + } +#else + color = texture2D(baseTexture, uv).rgb; +#endif + + float alpha = texture2D(baseTexture, uv).a; + vec4 col = vec4(color.r, color.g, color.b, alpha); + col *= gl_Color; + col = col * col; // SRGB -> Linear + col *= 1.8; + col.r = 1.0 - exp(1.0 - col.r) / e; + col.g = 1.0 - exp(1.0 - col.g) / e; + col.b = 1.0 - exp(1.0 - col.b) / e; + col = sqrt(col); // Linear -> SRGB + if(fogDistance != 0.0){ + float d = max(0.0, min(vPosition.z / fogDistance * 1.5 - 0.6, 1.0)); + col = mix(col, skyBgColor, d); + } + gl_FragColor = vec4(col.r, col.g, col.b, alpha); +} diff --git a/client/shaders/solids_shader/opengl_vertex.glsl b/client/shaders/solids_shader/opengl_vertex.glsl new file mode 100644 index 000000000..e359955d0 --- /dev/null +++ b/client/shaders/solids_shader/opengl_vertex.glsl @@ -0,0 +1,102 @@ +uniform mat4 mWorldViewProj; +uniform mat4 mInvWorld; +uniform mat4 mTransWorld; +uniform float dayNightRatio; + +uniform vec3 eyePosition; + +varying vec3 vPosition; +varying vec3 eyeVec; + +#ifdef ENABLE_PARALLAX_OCCLUSION +varying vec3 tsEyeVec; +#endif + +void main(void) +{ + gl_Position = mWorldViewProj * gl_Vertex; + vPosition = (mWorldViewProj * gl_Vertex).xyz; + eyeVec = (gl_ModelViewMatrix * gl_Vertex).xyz; + +#ifdef ENABLE_PARALLAX_OCCLUSION + vec3 normal,tangent,binormal; + normal = normalize(gl_NormalMatrix * gl_Normal); + + if (gl_Normal.x > 0.5) { + // 1.0, 0.0, 0.0 + tangent = normalize(gl_NormalMatrix * vec3( 0.0, 0.0, -1.0)); + binormal = normalize(gl_NormalMatrix * vec3( 0.0, -1.0, 0.0)); + } else if (gl_Normal.x < -0.5) { + // -1.0, 0.0, 0.0 + tangent = normalize(gl_NormalMatrix * vec3( 0.0, 0.0, 1.0)); + binormal = normalize(gl_NormalMatrix * vec3( 0.0, -1.0, 0.0)); + } else if (gl_Normal.y > 0.5) { + // 0.0, 1.0, 0.0 + tangent = normalize(gl_NormalMatrix * vec3( 1.0, 0.0, 0.0)); + binormal = normalize(gl_NormalMatrix * vec3( 0.0, 0.0, 1.0)); + } else if (gl_Normal.y < -0.5) { + // 0.0, -1.0, 0.0 + tangent = normalize(gl_NormalMatrix * vec3( 1.0, 0.0, 0.0)); + binormal = normalize(gl_NormalMatrix * vec3( 0.0, 0.0, 1.0)); + } else if (gl_Normal.z > 0.5) { + // 0.0, 0.0, 1.0 + tangent = normalize(gl_NormalMatrix * vec3( 1.0, 0.0, 0.0)); + binormal = normalize(gl_NormalMatrix * vec3( 0.0, -1.0, 0.0)); + } else if (gl_Normal.z < -0.5) { + // 0.0, 0.0, -1.0 + tangent = normalize(gl_NormalMatrix * vec3(-1.0, 0.0, 0.0)); + binormal = normalize(gl_NormalMatrix * vec3( 0.0, -1.0, 0.0)); + } + + mat3 tbnMatrix = mat3( tangent.x, binormal.x, normal.x, + tangent.y, binormal.y, normal.y, + tangent.z, binormal.z, normal.z); + + tsEyeVec = normalize(eyeVec * tbnMatrix); +#endif + + vec4 color; + //color = vec4(1.0, 1.0, 1.0, 1.0); + + float day = gl_Color.r; + float night = gl_Color.g; + float light_source = gl_Color.b; + + /*color.r = mix(night, day, dayNightRatio); + color.g = color.r; + color.b = color.r;*/ + + float rg = mix(night, day, dayNightRatio); + rg += light_source * 2.5; // Make light sources brighter + float b = rg; + + // Moonlight is blue + b += (day - night) / 13.0; + rg -= (day - night) / 13.0; + + // Emphase blue a bit in darker places + // See C++ implementation in mapblock_mesh.cpp finalColorBlend() + b += max(0.0, (1.0 - abs(b - 0.13)/0.17) * 0.025); + + // Artificial light is yellow-ish + // See C++ implementation in mapblock_mesh.cpp finalColorBlend() + rg += max(0.0, (1.0 - abs(rg - 0.85)/0.15) * 0.065); + + color.r = clamp(rg,0.0,1.0); + color.g = clamp(rg,0.0,1.0); + color.b = clamp(b,0.0,1.0); + + // Make sides and bottom darker than the top + color = color * color; // SRGB -> Linear + if(gl_Normal.y <= 0.5) + color *= 0.6; + //color *= 0.7; + color = sqrt(color); // Linear -> SRGB + + color.a = gl_Color.a; + + gl_FrontColor = gl_BackColor = color; + + gl_TexCoord[0] = gl_MultiTexCoord0; + +} diff --git a/client/shaders/test_shader_1/opengl_fragment.glsl b/client/shaders/test_shader_1/opengl_fragment.glsl deleted file mode 100644 index ebf943ced..000000000 --- a/client/shaders/test_shader_1/opengl_fragment.glsl +++ /dev/null @@ -1,25 +0,0 @@ - -uniform sampler2D myTexture; -uniform vec4 skyBgColor; -uniform float fogDistance; - -varying vec3 vPosition; - -void main (void) -{ - //vec4 col = vec4(1.0, 0.0, 0.0, 1.0); - vec4 col = texture2D(myTexture, vec2(gl_TexCoord[0])); - float a = col.a; - col *= gl_Color; - col = col * col; // SRGB -> Linear - col *= 1.8; - col.r = 1.0 - exp(1.0 - col.r) / exp(1.0); - col.g = 1.0 - exp(1.0 - col.g) / exp(1.0); - col.b = 1.0 - exp(1.0 - col.b) / exp(1.0); - col = sqrt(col); // Linear -> SRGB - if(fogDistance != 0.0){ - float d = max(0.0, min(vPosition.z / fogDistance * 1.5 - 0.6, 1.0)); - col = mix(col, skyBgColor, d); - } - gl_FragColor = vec4(col.r, col.g, col.b, a); -} diff --git a/client/shaders/test_shader_2/opengl_fragment.glsl b/client/shaders/test_shader_2/opengl_fragment.glsl deleted file mode 100644 index 38bc94311..000000000 --- a/client/shaders/test_shader_2/opengl_fragment.glsl +++ /dev/null @@ -1,23 +0,0 @@ - -uniform sampler2D myTexture; -uniform float fogDistance; - -varying vec3 vPosition; - -void main (void) -{ - vec4 col = texture2D(myTexture, vec2(gl_TexCoord[0])); - col *= gl_Color; - float a = gl_Color.a; - col = col * col; // SRGB -> Linear - col *= 1.8; - col.r = 1.0 - exp(1.0 - col.r) / exp(1.0); - col.g = 1.0 - exp(1.0 - col.g) / exp(1.0); - col.b = 1.0 - exp(1.0 - col.b) / exp(1.0); - col = sqrt(col); // Linear -> SRGB - if(fogDistance != 0.0){ - float d = max(0.0, min(vPosition.z / fogDistance * 1.5 - 0.6, 1.0)); - a = mix(a, 0.0, d); - } - gl_FragColor = vec4(col.r, col.g, col.b, a); -} diff --git a/client/shaders/test_shader_2/opengl_vertex.glsl b/client/shaders/test_shader_2/opengl_vertex.glsl deleted file mode 100644 index 2881bad21..000000000 --- a/client/shaders/test_shader_2/opengl_vertex.glsl +++ /dev/null @@ -1,51 +0,0 @@ - -uniform mat4 mWorldViewProj; -uniform mat4 mInvWorld; -uniform mat4 mTransWorld; -uniform float dayNightRatio; - -varying vec3 vPosition; - -void main(void) -{ - gl_Position = mWorldViewProj * gl_Vertex; - - vPosition = (mWorldViewProj * gl_Vertex).xyz; - - vec4 color; - //color = vec4(1.0, 1.0, 1.0, 1.0); - - float day = gl_Color.r; - float night = gl_Color.g; - float light_source = gl_Color.b; - - /*color.r = mix(night, day, dayNightRatio); - color.g = color.r; - color.b = color.r;*/ - - float rg = mix(night, day, dayNightRatio); - rg += light_source * 1.0; // Make light sources brighter - float b = rg; - - // Moonlight is blue - b += (day - night) / 13.0; - rg -= (day - night) / 13.0; - - // Emphase blue a bit in darker places - // See C++ implementation in mapblock_mesh.cpp finalColorBlend() - b += max(0.0, (1.0 - abs(b - 0.13)/0.17) * 0.025); - - // Artificial light is yellow-ish - // See C++ implementation in mapblock_mesh.cpp finalColorBlend() - rg += max(0.0, (1.0 - abs(rg - 0.85)/0.15) * 0.065); - - color.r = rg; - color.g = rg; - color.b = b; - - color.a = gl_Color.a; - - gl_FrontColor = gl_BackColor = color; - - gl_TexCoord[0] = gl_MultiTexCoord0; -} diff --git a/client/shaders/test_shader_3/base.txt b/client/shaders/test_shader_3/base.txt deleted file mode 100644 index 1c2647118..000000000 --- a/client/shaders/test_shader_3/base.txt +++ /dev/null @@ -1 +0,0 @@ -trans_alphach diff --git a/client/shaders/test_shader_3/opengl_fragment.glsl b/client/shaders/test_shader_3/opengl_fragment.glsl deleted file mode 100644 index 535774c11..000000000 --- a/client/shaders/test_shader_3/opengl_fragment.glsl +++ /dev/null @@ -1,25 +0,0 @@ - -uniform sampler2D myTexture; -uniform float fogDistance; - -varying vec3 vPosition; - -void main (void) -{ - vec4 col = texture2D(myTexture, vec2(gl_TexCoord[0])); - col *= gl_Color; - float a = col.a; - col = col * col; // SRGB -> Linear - col *= 1.8; - col.r = 1.0 - exp(1.0 - col.r) / exp(1.0); - col.g = 1.0 - exp(1.0 - col.g) / exp(1.0); - col.b = 1.0 - exp(1.0 - col.b) / exp(1.0); - col = sqrt(col); // Linear -> SRGB - - if(fogDistance != 0.0){ - float d = max(0.0, min(vPosition.z / fogDistance * 1.5 - 0.6, 1.0)); - a = mix(a, 0.0, d); - } - - gl_FragColor = vec4(col.r, col.g, col.b, a); -} diff --git a/client/shaders/test_shader_3/opengl_vertex.glsl b/client/shaders/test_shader_3/opengl_vertex.glsl deleted file mode 100644 index 2881bad21..000000000 --- a/client/shaders/test_shader_3/opengl_vertex.glsl +++ /dev/null @@ -1,51 +0,0 @@ - -uniform mat4 mWorldViewProj; -uniform mat4 mInvWorld; -uniform mat4 mTransWorld; -uniform float dayNightRatio; - -varying vec3 vPosition; - -void main(void) -{ - gl_Position = mWorldViewProj * gl_Vertex; - - vPosition = (mWorldViewProj * gl_Vertex).xyz; - - vec4 color; - //color = vec4(1.0, 1.0, 1.0, 1.0); - - float day = gl_Color.r; - float night = gl_Color.g; - float light_source = gl_Color.b; - - /*color.r = mix(night, day, dayNightRatio); - color.g = color.r; - color.b = color.r;*/ - - float rg = mix(night, day, dayNightRatio); - rg += light_source * 1.0; // Make light sources brighter - float b = rg; - - // Moonlight is blue - b += (day - night) / 13.0; - rg -= (day - night) / 13.0; - - // Emphase blue a bit in darker places - // See C++ implementation in mapblock_mesh.cpp finalColorBlend() - b += max(0.0, (1.0 - abs(b - 0.13)/0.17) * 0.025); - - // Artificial light is yellow-ish - // See C++ implementation in mapblock_mesh.cpp finalColorBlend() - rg += max(0.0, (1.0 - abs(rg - 0.85)/0.15) * 0.065); - - color.r = rg; - color.g = rg; - color.b = b; - - color.a = gl_Color.a; - - gl_FrontColor = gl_BackColor = color; - - gl_TexCoord[0] = gl_MultiTexCoord0; -} diff --git a/cmake/Modules/FindCURL.cmake b/cmake/Modules/FindCURL.cmake index 3644d192e..975b8088c 100644 --- a/cmake/Modules/FindCURL.cmake +++ b/cmake/Modules/FindCURL.cmake @@ -20,6 +20,10 @@ if( UNIX ) else( UNIX ) FIND_PATH(CURL_INCLUDE_DIR NAMES curl/curl.h) # Look for the header file. FIND_LIBRARY(CURL_LIBRARY NAMES curl) # Look for the library. + FIND_FILE(CURL_DLL NAMES libcurl.dll + PATHS + "c:/windows/system32" + DOC "Path of the cURL dll (for installation)") INCLUDE(FindPackageHandleStandardArgs) # handle the QUIETLY and REQUIRED arguments and set CURL_FOUND to TRUE if FIND_PACKAGE_HANDLE_STANDARD_ARGS(CURL DEFAULT_MSG CURL_LIBRARY CURL_INCLUDE_DIR) # all listed variables are TRUE endif( UNIX ) @@ -40,3 +44,4 @@ endif ( WIN32 ) MESSAGE(STATUS "CURL_INCLUDE_DIR = ${CURL_INCLUDE_DIR}") MESSAGE(STATUS "CURL_LIBRARY = ${CURL_LIBRARY}") +MESSAGE(STATUS "CURL_DLL = ${CURL_DLL}") diff --git a/cmake/Modules/GenerateVersion.cmake b/cmake/Modules/GenerateVersion.cmake new file mode 100644 index 000000000..4a7f183da --- /dev/null +++ b/cmake/Modules/GenerateVersion.cmake @@ -0,0 +1,20 @@ +# Always run during 'make' + +if(VERSION_EXTRA) + set(VERSION_GITHASH "${VERSION_STRING}") +else(VERSION_EXTRA) + execute_process(COMMAND git describe --always --tag --dirty + WORKING_DIRECTORY "${GENERATE_VERSION_SOURCE_DIR}" + OUTPUT_VARIABLE VERSION_GITHASH OUTPUT_STRIP_TRAILING_WHITESPACE + ERROR_QUIET) + + if(VERSION_GITHASH) + message(STATUS "*** Detected git version ${VERSION_GITHASH} ***") + else() + set(VERSION_GITHASH "${VERSION_STRING}") + endif() +endif() + +configure_file( + ${GENERATE_VERSION_SOURCE_DIR}/cmake_config_githash.h.in + ${GENERATE_VERSION_BINARY_DIR}/cmake_config_githash.h) diff --git a/doc/gpl-2.0.txt b/doc/gpl-2.0.txt deleted file mode 100644 index d159169d1..000000000 --- a/doc/gpl-2.0.txt +++ /dev/null @@ -1,339 +0,0 @@ - GNU GENERAL PUBLIC LICENSE - Version 2, June 1991 - - Copyright (C) 1989, 1991 Free Software Foundation, Inc., - 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - - Preamble - - The licenses for most software are designed to take away your -freedom to share and change it. By contrast, the GNU General Public -License is intended to guarantee your freedom to share and change free -software--to make sure the software is free for all its users. This -General Public License applies to most of the Free Software -Foundation's software and to any other program whose authors commit to -using it. (Some other Free Software Foundation software is covered by -the GNU Lesser General Public License instead.) You can apply it to -your programs, too. - - When we speak of free software, we are referring to freedom, not -price. Our General Public Licenses are designed to make sure that you -have the freedom to distribute copies of free software (and charge for -this service if you wish), that you receive source code or can get it -if you want it, that you can change the software or use pieces of it -in new free programs; and that you know you can do these things. - - To protect your rights, we need to make restrictions that forbid -anyone to deny you these rights or to ask you to surrender the rights. -These restrictions translate to certain responsibilities for you if you -distribute copies of the software, or if you modify it. - - For example, if you distribute copies of such a program, whether -gratis or for a fee, you must give the recipients all the rights that -you have. You must make sure that they, too, receive or can get the -source code. And you must show them these terms so they know their -rights. - - We protect your rights with two steps: (1) copyright the software, and -(2) offer you this license which gives you legal permission to copy, -distribute and/or modify the software. - - Also, for each author's protection and ours, we want to make certain -that everyone understands that there is no warranty for this free -software. If the software is modified by someone else and passed on, we -want its recipients to know that what they have is not the original, so -that any problems introduced by others will not reflect on the original -authors' reputations. - - Finally, any free program is threatened constantly by software -patents. We wish to avoid the danger that redistributors of a free -program will individually obtain patent licenses, in effect making the -program proprietary. To prevent this, we have made it clear that any -patent must be licensed for everyone's free use or not licensed at all. - - The precise terms and conditions for copying, distribution and -modification follow. - - GNU GENERAL PUBLIC LICENSE - TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION - - 0. This License applies to any program or other work which contains -a notice placed by the copyright holder saying it may be distributed -under the terms of this General Public License. The "Program", below, -refers to any such program or work, and a "work based on the Program" -means either the Program or any derivative work under copyright law: -that is to say, a work containing the Program or a portion of it, -either verbatim or with modifications and/or translated into another -language. (Hereinafter, translation is included without limitation in -the term "modification".) Each licensee is addressed as "you". - -Activities other than copying, distribution and modification are not -covered by this License; they are outside its scope. The act of -running the Program is not restricted, and the output from the Program -is covered only if its contents constitute a work based on the -Program (independent of having been made by running the Program). -Whether that is true depends on what the Program does. - - 1. You may copy and distribute verbatim copies of the Program's -source code as you receive it, in any medium, provided that you -conspicuously and appropriately publish on each copy an appropriate -copyright notice and disclaimer of warranty; keep intact all the -notices that refer to this License and to the absence of any warranty; -and give any other recipients of the Program a copy of this License -along with the Program. - -You may charge a fee for the physical act of transferring a copy, and -you may at your option offer warranty protection in exchange for a fee. - - 2. You may modify your copy or copies of the Program or any portion -of it, thus forming a work based on the Program, and copy and -distribute such modifications or work under the terms of Section 1 -above, provided that you also meet all of these conditions: - - a) You must cause the modified files to carry prominent notices - stating that you changed the files and the date of any change. - - b) You must cause any work that you distribute or publish, that in - whole or in part contains or is derived from the Program or any - part thereof, to be licensed as a whole at no charge to all third - parties under the terms of this License. - - c) If the modified program normally reads commands interactively - when run, you must cause it, when started running for such - interactive use in the most ordinary way, to print or display an - announcement including an appropriate copyright notice and a - notice that there is no warranty (or else, saying that you provide - a warranty) and that users may redistribute the program under - these conditions, and telling the user how to view a copy of this - License. (Exception: if the Program itself is interactive but - does not normally print such an announcement, your work based on - the Program is not required to print an announcement.) - -These requirements apply to the modified work as a whole. If -identifiable sections of that work are not derived from the Program, -and can be reasonably considered independent and separate works in -themselves, then this License, and its terms, do not apply to those -sections when you distribute them as separate works. But when you -distribute the same sections as part of a whole which is a work based -on the Program, the distribution of the whole must be on the terms of -this License, whose permissions for other licensees extend to the -entire whole, and thus to each and every part regardless of who wrote it. - -Thus, it is not the intent of this section to claim rights or contest -your rights to work written entirely by you; rather, the intent is to -exercise the right to control the distribution of derivative or -collective works based on the Program. - -In addition, mere aggregation of another work not based on the Program -with the Program (or with a work based on the Program) on a volume of -a storage or distribution medium does not bring the other work under -the scope of this License. - - 3. You may copy and distribute the Program (or a work based on it, -under Section 2) in object code or executable form under the terms of -Sections 1 and 2 above provided that you also do one of the following: - - a) Accompany it with the complete corresponding machine-readable - source code, which must be distributed under the terms of Sections - 1 and 2 above on a medium customarily used for software interchange; or, - - b) Accompany it with a written offer, valid for at least three - years, to give any third party, for a charge no more than your - cost of physically performing source distribution, a complete - machine-readable copy of the corresponding source code, to be - distributed under the terms of Sections 1 and 2 above on a medium - customarily used for software interchange; or, - - c) Accompany it with the information you received as to the offer - to distribute corresponding source code. (This alternative is - allowed only for noncommercial distribution and only if you - received the program in object code or executable form with such - an offer, in accord with Subsection b above.) - -The source code for a work means the preferred form of the work for -making modifications to it. For an executable work, complete source -code means all the source code for all modules it contains, plus any -associated interface definition files, plus the scripts used to -control compilation and installation of the executable. However, as a -special exception, the source code distributed need not include -anything that is normally distributed (in either source or binary -form) with the major components (compiler, kernel, and so on) of the -operating system on which the executable runs, unless that component -itself accompanies the executable. - -If distribution of executable or object code is made by offering -access to copy from a designated place, then offering equivalent -access to copy the source code from the same place counts as -distribution of the source code, even though third parties are not -compelled to copy the source along with the object code. - - 4. You may not copy, modify, sublicense, or distribute the Program -except as expressly provided under this License. Any attempt -otherwise to copy, modify, sublicense or distribute the Program is -void, and will automatically terminate your rights under this License. -However, parties who have received copies, or rights, from you under -this License will not have their licenses terminated so long as such -parties remain in full compliance. - - 5. You are not required to accept this License, since you have not -signed it. However, nothing else grants you permission to modify or -distribute the Program or its derivative works. These actions are -prohibited by law if you do not accept this License. Therefore, by -modifying or distributing the Program (or any work based on the -Program), you indicate your acceptance of this License to do so, and -all its terms and conditions for copying, distributing or modifying -the Program or works based on it. - - 6. Each time you redistribute the Program (or any work based on the -Program), the recipient automatically receives a license from the -original licensor to copy, distribute or modify the Program subject to -these terms and conditions. You may not impose any further -restrictions on the recipients' exercise of the rights granted herein. -You are not responsible for enforcing compliance by third parties to -this License. - - 7. If, as a consequence of a court judgment or allegation of patent -infringement or for any other reason (not limited to patent issues), -conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot -distribute so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you -may not distribute the Program at all. For example, if a patent -license would not permit royalty-free redistribution of the Program by -all those who receive copies directly or indirectly through you, then -the only way you could satisfy both it and this License would be to -refrain entirely from distribution of the Program. - -If any portion of this section is held invalid or unenforceable under -any particular circumstance, the balance of the section is intended to -apply and the section as a whole is intended to apply in other -circumstances. - -It is not the purpose of this section to induce you to infringe any -patents or other property right claims or to contest validity of any -such claims; this section has the sole purpose of protecting the -integrity of the free software distribution system, which is -implemented by public license practices. Many people have made -generous contributions to the wide range of software distributed -through that system in reliance on consistent application of that -system; it is up to the author/donor to decide if he or she is willing -to distribute software through any other system and a licensee cannot -impose that choice. - -This section is intended to make thoroughly clear what is believed to -be a consequence of the rest of this License. - - 8. If the distribution and/or use of the Program is restricted in -certain countries either by patents or by copyrighted interfaces, the -original copyright holder who places the Program under this License -may add an explicit geographical distribution limitation excluding -those countries, so that distribution is permitted only in or among -countries not thus excluded. In such case, this License incorporates -the limitation as if written in the body of this License. - - 9. The Free Software Foundation may publish revised and/or new versions -of the General Public License from time to time. Such new versions will -be similar in spirit to the present version, but may differ in detail to -address new problems or concerns. - -Each version is given a distinguishing version number. If the Program -specifies a version number of this License which applies to it and "any -later version", you have the option of following the terms and conditions -either of that version or of any later version published by the Free -Software Foundation. If the Program does not specify a version number of -this License, you may choose any version ever published by the Free Software -Foundation. - - 10. If you wish to incorporate parts of the Program into other free -programs whose distribution conditions are different, write to the author -to ask for permission. For software which is copyrighted by the Free -Software Foundation, write to the Free Software Foundation; we sometimes -make exceptions for this. Our decision will be guided by the two goals -of preserving the free status of all derivatives of our free software and -of promoting the sharing and reuse of software generally. - - NO WARRANTY - - 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY -FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN -OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES -PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED -OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF -MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS -TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE -PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, -REPAIR OR CORRECTION. - - 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING -WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR -REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, -INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING -OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED -TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY -YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER -PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE -POSSIBILITY OF SUCH DAMAGES. - - END OF TERMS AND CONDITIONS - - How to Apply These Terms to Your New Programs - - If you develop a new program, and you want it to be of the greatest -possible use to the public, the best way to achieve this is to make it -free software which everyone can redistribute and change under these terms. - - To do so, attach the following notices to the program. It is safest -to attach them to the start of each source file to most effectively -convey the exclusion of warranty; and each file should have at least -the "copyright" line and a pointer to where the full notice is found. - - - Copyright (C) - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License along - with this program; if not, write to the Free Software Foundation, Inc., - 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - -Also add information on how to contact you by electronic and paper mail. - -If the program is interactive, make it output a short notice like this -when it starts in an interactive mode: - - Gnomovision version 69, Copyright (C) year name of author - Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. - This is free software, and you are welcome to redistribute it - under certain conditions; type `show c' for details. - -The hypothetical commands `show w' and `show c' should show the appropriate -parts of the General Public License. Of course, the commands you use may -be called something other than `show w' and `show c'; they could even be -mouse-clicks or menu items--whatever suits your program. - -You should also get your employer (if you work as a programmer) or your -school, if any, to sign a "copyright disclaimer" for the program, if -necessary. Here is a sample; alter the names: - - Yoyodyne, Inc., hereby disclaims all copyright interest in the program - `Gnomovision' (which makes passes at compilers) written by James Hacker. - - , 1 April 1989 - Ty Coon, President of Vice - -This General Public License does not permit incorporating your program into -proprietary programs. If your program is a subroutine library, you may -consider it more useful to permit linking proprietary applications with the -library. If this is what you want to do, use the GNU Lesser General -Public License instead of this License. diff --git a/doc/lgpl-2.1.txt b/doc/lgpl-2.1.txt new file mode 100644 index 000000000..4362b4915 --- /dev/null +++ b/doc/lgpl-2.1.txt @@ -0,0 +1,502 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations below. + + When we speak of free software, we are referring to freedom of use, +not price. Our General Public Licenses are designed to make sure that +you have the freedom to distribute copies of free software (and charge +for this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random Hacker. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! diff --git a/doc/lua_api.txt b/doc/lua_api.txt index 43c719a43..135e2cfcd 100644 --- a/doc/lua_api.txt +++ b/doc/lua_api.txt @@ -1,4 +1,4 @@ -Minetest Lua Modding API Reference 0.4.7 +Minetest Lua Modding API Reference 0.4.8 ======================================== More information at http://www.minetest.net/ Developer Wiki: http://dev.minetest.net/ @@ -100,6 +100,8 @@ Mod directory structure mods |-- modname | |-- depends.txt +| |-- screenshot.png +| |-- description.txt | |-- init.lua | |-- textures | | |-- modname_stuff.png @@ -121,12 +123,11 @@ depends.txt: to a single modname. Their meaning is that if the specified mod is missing, that does not prevent this mod from being loaded. -optdepends.txt: - An alternative way of specifying optional dependencies. - Like depends.txt, a single line contains a single modname. +screenshot.png: + A screenshot shown in modmanager within mainmenu. - NOTE: This file exists for compatibility purposes only and - support for it will be removed from the engine by the end of 2013. +description.txt: + File containing desctiption to be shown within mainmenu. init.lua: The main Lua script. Running this script should register everything it @@ -873,6 +874,22 @@ list[;;,;,;] list[;;,;,;] ^ Show an inventory list +listcolors[;] +^ Sets background color of slots in HEX-Color format +^ Sets background color of slots on mouse hovering + +listcolors[;;] +^ Sets background color of slots in HEX-Color format +^ Sets background color of slots on mouse hovering +^ Sets color of slots border + +listcolors[;;;;] +^ Sets background color of slots in HEX-Color format +^ Sets background color of slots on mouse hovering +^ Sets color of slots border +^ Sets background color of tooltips +^ Sets font color of tooltips + image[,;,;] ^ Show an image ^ Position and size units are inventory slots @@ -881,11 +898,21 @@ item_image[,;,;] ^ Show an inventory image of registered item/node ^ Position and size units are inventory slots +bgcolor[;] +^ Sets background color of formspec in HEX-Color format +^ If true the background color is drawn fullscreen (does not effect the size of the formspec) + background[,;,;] ^ Use a background. Inventory rectangles are not drawn then. ^ Position and size units are inventory slots ^ Example for formspec 8x4 in 16x resolution: image shall be sized 8*16px x 4*16px +background[,;,;;] +^ Use a background. Inventory rectangles are not drawn then. +^ Position and size units are inventory slots +^ Example for formspec 8x4 in 16x resolution: image shall be sized 8*16px x 4*16px +^ If true the background is clipped to formspec size (x and y are used as offset values, w and h are ignored) + pwdfield[,;,;;