diff options
59 files changed, 3414 insertions, 470 deletions
diff --git a/.gitignore b/.gitignore index 21e2371a8..50d7c43e0 100644 --- a/.gitignore +++ b/.gitignore @@ -37,6 +37,7 @@ src/jthread/Makefile src/jthread/cmake_config.h src/jthread/cmake_install.cmake src/jthread/libjthread.a +src/json/libjson.a src/lua/build/ src/lua/CMakeFiles/ src/cguittfont/CMakeFiles/ diff --git a/CMakeLists.txt b/CMakeLists.txt index 6bbe6454a..ff44e9c26 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -12,7 +12,7 @@ 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 5) +set(VERSION_PATCH 6) if(VERSION_EXTRA) set(VERSION_PATCH ${VERSION_PATCH}-${VERSION_EXTRA}) endif() @@ -144,6 +144,18 @@ if(EXISTS ${MINETEST_GAME_SOURCE} AND IS_DIRECTORY ${MINETEST_GAME_SOURCE}) install(FILES ${MINETEST_GAME_SOURCE}/README.txt DESTINATION "${SHAREDIR}/games/minetest_game/") install(DIRECTORY ${MINETEST_GAME_SOURCE}/mods DESTINATION "${SHAREDIR}/games/minetest_game") endif() +set(MINETEST_BUILD_GAME_SOURCE "${CMAKE_CURRENT_SOURCE_DIR}/games/build") +if(EXISTS ${MINETEST_BUILD_GAME_SOURCE} AND IS_DIRECTORY ${MINETEST_BUILD_GAME_SOURCE}) + install(FILES ${MINETEST_BUILD_GAME_SOURCE}/game.conf DESTINATION "${SHAREDIR}/games/build/") + install(FILES ${MINETEST_BUILD_GAME_SOURCE}/README.txt DESTINATION "${SHAREDIR}/games/build/") + install(DIRECTORY ${MINETEST_BUILD_GAME_SOURCE}/mods DESTINATION "${SHAREDIR}/games/build") +endif() +set(MINETEST_SURVIVAL_GAME_SOURCE "${CMAKE_CURRENT_SOURCE_DIR}/games/survival") +if(EXISTS ${MINETEST_SURVIVAL_GAME_SOURCE} AND IS_DIRECTORY ${MINETEST_SURVIVAL_GAME_SOURCE}) + install(FILES ${MINETEST_SURVIVAL_GAME_SOURCE}/game.conf DESTINATION "${SHAREDIR}/games/survival/") + install(FILES ${MINETEST_SURVIVAL_GAME_SOURCE}/README.txt DESTINATION "${SHAREDIR}/games/survival/") + install(DIRECTORY ${MINETEST_SURVIVAL_GAME_SOURCE}/mods DESTINATION "${SHAREDIR}/games/survival") +endif() if(BUILD_CLIENT) #install(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/sounds/base/pack" DESTINATION "${SHAREDIR}/sounds/base") install(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/textures/base/pack" DESTINATION "${SHAREDIR}/textures/base") diff --git a/builtin/chatcommands.lua b/builtin/chatcommands.lua index 4b09f7b2b..9f14749f4 100644 --- a/builtin/chatcommands.lua +++ b/builtin/chatcommands.lua @@ -665,3 +665,23 @@ minetest.register_chatcommand("clearobjects", { minetest.chat_send_all("*** Cleared all objects.") end, }) + +minetest.register_chatcommand("msg", { + params = "<name> <message>", + description = "Send a private message", + privs = {shout=true}, + func = function(name, param) + local found, _, sendto, message = param:find("^([^%s]+)%s(.+)$") + if found then + if minetest.env:get_player_by_name(sendto) then + minetest.log("action", "PM from "..name.." to "..sendto..": "..message) + minetest.chat_send_player(sendto, "PM from "..name..": "..message) + minetest.chat_send_player(name, "Message sent") + else + minetest.chat_send_player(name, "The player "..sendto.." is not online") + end + else + minetest.chat_send_player(name, "Invalid usage, see /help msg") + end + end, +}) diff --git a/builtin/falling.lua b/builtin/falling.lua index 1c09f9856..f6491991b 100644 --- a/builtin/falling.lua +++ b/builtin/falling.lua @@ -142,7 +142,7 @@ end -- Some common functions -- -function nodeupdate_single(p) +function nodeupdate_single(p, delay) n = minetest.env:get_node(p) if minetest.get_node_group(n.name, "falling_node") ~= 0 then p_bottom = {x=p.x, y=p.y-1, z=p.z} @@ -151,9 +151,13 @@ function nodeupdate_single(p) if minetest.registered_nodes[n_bottom.name] and (not minetest.registered_nodes[n_bottom.name].walkable or minetest.registered_nodes[n_bottom.name].buildable_to) then - minetest.env:remove_node(p) - spawn_falling_node(p, n.name) - nodeupdate(p) + if delay then + minetest.after(0.1, nodeupdate_single, {x=p.x, y=p.y, z=p.z}, false) + else + minetest.env:remove_node(p) + spawn_falling_node(p, n.name) + nodeupdate(p) + end end end @@ -174,8 +178,7 @@ function nodeupdate(p) for x = -1,1 do for y = -1,1 do for z = -1,1 do - p2 = {x=p.x+x, y=p.y+y, z=p.z+z} - nodeupdate_single(p2) + nodeupdate_single({x=p.x+x, y=p.y+y, z=p.z+z}, not (x==0 and y==0 and z==0)) end end end diff --git a/builtin/misc_register.lua b/builtin/misc_register.lua index f9c06a02a..4041fb9e2 100644 --- a/builtin/misc_register.lua +++ b/builtin/misc_register.lua @@ -103,6 +103,10 @@ function minetest.register_item(name, itemdef) -- Apply defaults and add to registered_* table if itemdef.type == "node" then + -- 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 + end setmetatable(itemdef, {__index = minetest.nodedef_default}) minetest.registered_nodes[itemdef.name] = itemdef elseif itemdef.type == "craft" then @@ -249,8 +253,8 @@ minetest.register_item(":unknown", { minetest.register_node(":air", { description = "Air (you hacker you!)", - inventory_image = "unknown_block.png", - wield_image = "unknown_block.png", + inventory_image = "unknown_node.png", + wield_image = "unknown_node.png", drawtype = "airlike", paramtype = "light", sunlight_propagates = true, @@ -265,8 +269,8 @@ minetest.register_node(":air", { minetest.register_node(":ignore", { description = "Ignore (you hacker you!)", - inventory_image = "unknown_block.png", - wield_image = "unknown_block.png", + inventory_image = "unknown_node.png", + wield_image = "unknown_node.png", drawtype = "airlike", paramtype = "none", sunlight_propagates = false, diff --git a/doc/lua_api.txt b/doc/lua_api.txt index ca00fc1f9..285f3d205 100644 --- a/doc/lua_api.txt +++ b/doc/lua_api.txt @@ -1,5 +1,5 @@ -Minetest Lua Modding API Reference 0.4.5 -========================================== +Minetest Lua Modding API Reference 0.4.6 +======================================== More information at http://c55.me/minetest/ Introduction @@ -1185,7 +1185,21 @@ methods: - get_perlin(seeddiff, octaves, persistence, scale) ^ Return world-specific perlin noise (int(worldseed)+seeddiff) - clear_objects() - ^ clear all objects in the environments + ^ clear all objects in the environments +- line_of_sight(pos1,pos2,stepsize) ->true/false + ^ checkif there is a direct line of sight between pos1 and pos2 + ^ pos1 First position + ^ pos2 Second position + ^ stepsize smaller gives more accurate results but requires more computing + time. Default is 1. +-find_path(pos1,pos2,searchdistance,max_jump,max_drop,algorithm) -> table containing path + ^ returns a table of 3d points representing a path from pos1 to pos2 or nil + ^ pos1: start position + ^ pos2: end position + ^ searchdistance: number of blocks to search in each direction + ^ max_jump: maximum height difference to consider walkable + ^ max_drop: maximum height difference to consider droppable + ^ algorithm: A*_noprefetch(default), A*, Dijkstra - spawn_tree (pos, {treedef}) ^ spawns L-System tree at given pos with definition in treedef table treedef={ @@ -1348,6 +1362,10 @@ Player-only: (no-op for other objects) {jump=bool,right=bool,left=bool,LMB=bool,RMB=bool,sneak=bool,aux1=bool,down=bool,up=bool} - get_player_control_bits(): returns integer with bit packed player pressed keys bit nr/meaning: 0/up ,1/down ,2/left ,3/right ,4/jump ,5/aux1 ,6/sneak ,7/LMB ,8/RMB +- set_physics_override(speed, jump, gravity) + modifies per-player walking speed, jump height, and gravity. + Values default to 1 and act as offsets to the physics settings + in minetest.conf. nil will keep the current setting. InvRef: Reference to an inventory methods: @@ -1585,6 +1603,7 @@ Node definition (register_node) damage_per_second = 0, -- If player is inside node, this damage is caused node_box = {type="regular"}, -- See "Node boxes" selection_box = {type="regular"}, -- See "Node boxes" + ^ If drawtype "nodebox" is used and selection_box is nil, then node_box is used legacy_facedir_simple = false, -- Support maps made in and before January 2012 legacy_wallmounted = false, -- Support maps made in and before January 2012 sounds = { diff --git a/games/minimal/mods/default/init.lua b/games/minimal/mods/default/init.lua index 163998883..d8067d211 100644 --- a/games/minimal/mods/default/init.lua +++ b/games/minimal/mods/default/init.lua @@ -1588,99 +1588,6 @@ minetest.register_alias("mapgen_stone_with_coal", "default:stone_with_coal") minetest.register_alias("mapgen_stone_with_iron", "default:stone_with_iron") minetest.register_alias("mapgen_mese", "default:mese") -minetest.register_biome_groups({ -0.35, 0.10, 0.30 -}) - --- --- Register the biomes for the map generator --- -minetest.register_biome({ - group_id = 0, - name = "Ocean", - terrain_type = "liquid", - node_top = "default:gravel", - node_filler = "default:stone", - num_top_nodes = 4, - height_min = -3000, - height_max = 3000, - heat_min = -20.0, - heat_max = 100.0, - humidity_min = 0.0, - humidity_max = 100.0, - scale = 10.0, - offset = -10.0, -}) - -minetest.register_biome({ - group_id = 1, - name = "Beach", - terrain_type = "normal", - node_top = "default:sand", - node_filler = "default:stone", - num_top_nodes = 5, - height_min = -3000, - height_max = 3000, - heat_min = 5.0, - heat_max = 100.0, - humidity_min = 0.0, - humidity_max = 100.0, - scale = 5.0, - offset = 5.0, -}) - -minetest.register_biome({ - group_id = 1, - name = "Gravel Beach", - terrain_type = "normal", - node_top = "default:gravel", - node_filler = "default:cobble", - num_top_nodes = 5, - height_min = -3000, - height_max = 3000, - heat_min = -50.0, - heat_max = 5.0, - humidity_min = 0.0, - humidity_max = 100.0, - scale = 5.0, - offset = 5.0, -}) - -minetest.register_biome({ - group_id = 2, - name = "Land", - terrain_type = "normal", - node_top = "default:dirt_with_grass", - node_filler = "default:stone", - num_top_nodes = 5, - height_min = -3000, - height_max = 3000, - heat_min = -50.0, - heat_max = 100.0, - humidity_min = 0.0, - humidity_max = 100.0, - scale = 12.0, - offset = 20.0, -}) - -minetest.register_biome({ - group_id = 3, - name = "Hills", - terrain_type = "normal", - node_top = "default:dirt", - node_filler = "default:stone", - num_top_nodes = 3, - height_min = -3000, - height_max = 3000, - heat_min = -50.0, - heat_max = 100.0, - humidity_min = 0.0, - humidity_max = 100.0, - scale = 60.0, - offset = 20.0, -}) - - -- Support old code function default.spawn_falling_node(p, nodename) spawn_falling_node(p, nodename) diff --git a/games/minimal/mods/experimental/init.lua b/games/minimal/mods/experimental/init.lua index 2edfd0f67..6fce9698a 100644 --- a/games/minimal/mods/experimental/init.lua +++ b/games/minimal/mods/experimental/init.lua @@ -28,10 +28,10 @@ minetest.after(1.0, switch_player_visual) ]] minetest.register_node("experimental:soundblock", { - tile_images = {"unknown_block.png", "default_tnt_bottom.png", + tile_images = {"unknown_node.png", "default_tnt_bottom.png", "default_tnt_side.png", "default_tnt_side.png", "default_tnt_side.png", "default_tnt_side.png"}, - inventory_image = minetest.inventorycube("unknown_block.png", + inventory_image = minetest.inventorycube("unknown_node.png", "default_tnt_side.png", "default_tnt_side.png"), groups = {dig_immediate=3}, }) diff --git a/po/uk/minetest.po b/po/uk/minetest.po new file mode 100644 index 000000000..58d0d95c9 --- /dev/null +++ b/po/uk/minetest.po @@ -0,0 +1,757 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: minetest\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2013-03-30 19:56+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" +"Language-Team: LANGUAGE <LL@li.org>\n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=CHARSET\n" +"Content-Transfer-Encoding: 8bit\n" + +#: src/guiConfigureWorld.cpp:125 +msgid "" +"Warning: Some mods are not configured yet.\n" +"They will be enabled by default when you save the configuration. " +msgstr "" + +#: src/guiConfigureWorld.cpp:144 +msgid "" +"Warning: Some configured mods are missing.\n" +"Their setting will be removed when you save the configuration. " +msgstr "" + +#: src/guiConfigureWorld.cpp:208 +msgid "enabled" +msgstr "" + +#: src/guiConfigureWorld.cpp:215 +msgid "Enable All" +msgstr "" + +#: src/guiConfigureWorld.cpp:222 +msgid "Disable All" +msgstr "" + +#: src/guiConfigureWorld.cpp:228 +msgid "depends on:" +msgstr "" + +#: src/guiConfigureWorld.cpp:240 +msgid "is required by:" +msgstr "" + +#: src/guiConfigureWorld.cpp:262 src/guiCreateWorld.cpp:165 +#: src/guiKeyChangeMenu.cpp:179 src/keycode.cpp:223 +msgid "Cancel" +msgstr "" + +#: src/guiConfigureWorld.cpp:268 src/guiKeyChangeMenu.cpp:173 +msgid "Save" +msgstr "" + +#: src/guiConfigureWorld.cpp:394 +msgid "Configuration saved. " +msgstr "" + +#: src/guiConfigureWorld.cpp:402 +msgid "Warning: Configuration not consistent. " +msgstr "" + +#: src/guiConfirmMenu.cpp:120 +msgid "Yes" +msgstr "" + +#: src/guiConfirmMenu.cpp:126 +msgid "No" +msgstr "" + +#: src/guiCreateWorld.cpp:116 +msgid "World name" +msgstr "" + +#: src/guiCreateWorld.cpp:135 +msgid "Game" +msgstr "" + +#: src/guiCreateWorld.cpp:159 +msgid "Create" +msgstr "" + +#: src/guiDeathScreen.cpp:96 +msgid "You died." +msgstr "" + +#: src/guiDeathScreen.cpp:104 +msgid "Respawn" +msgstr "" + +#: src/guiFormSpecMenu.cpp:582 +msgid "Left click: Move all items, Right click: Move single item" +msgstr "" + +#: src/guiFormSpecMenu.cpp:607 src/guiMessageMenu.cpp:109 +#: src/guiTextInputMenu.cpp:131 +msgid "Proceed" +msgstr "" + +#: src/guiKeyChangeMenu.cpp:114 +msgid "Keybindings. (If this menu screws up, remove stuff from minetest.conf)" +msgstr "" + +#: src/guiKeyChangeMenu.cpp:151 +msgid "\"Use\" = climb down" +msgstr "" + +#: src/guiKeyChangeMenu.cpp:164 +msgid "Double tap \"jump\" to toggle fly" +msgstr "" + +#: src/guiKeyChangeMenu.cpp:269 +msgid "Key already in use" +msgstr "" + +#: src/guiKeyChangeMenu.cpp:347 +msgid "press key" +msgstr "" + +#: src/guiKeyChangeMenu.cpp:372 +msgid "Forward" +msgstr "" + +#: src/guiKeyChangeMenu.cpp:373 +msgid "Backward" +msgstr "" + +#: src/guiKeyChangeMenu.cpp:374 src/keycode.cpp:228 +msgid "Left" +msgstr "" + +#: src/guiKeyChangeMenu.cpp:375 src/keycode.cpp:228 +msgid "Right" +msgstr "" + +#: src/guiKeyChangeMenu.cpp:376 +msgid "Use" +msgstr "" + +#: src/guiKeyChangeMenu.cpp:377 +msgid "Jump" +msgstr "" + +#: src/guiKeyChangeMenu.cpp:378 +msgid "Sneak" +msgstr "" + +#: src/guiKeyChangeMenu.cpp:379 +msgid "Drop" +msgstr "" + +#: src/guiKeyChangeMenu.cpp:380 +msgid "Inventory" +msgstr "" + +#: src/guiKeyChangeMenu.cpp:381 +msgid "Chat" +msgstr "" + +#: src/guiKeyChangeMenu.cpp:382 +msgid "Command" +msgstr "" + +#: src/guiKeyChangeMenu.cpp:383 +msgid "Console" +msgstr "" + +#: src/guiKeyChangeMenu.cpp:384 +msgid "Toggle fly" +msgstr "" + +#: src/guiKeyChangeMenu.cpp:385 +msgid "Toggle fast" +msgstr "" + +#: src/guiKeyChangeMenu.cpp:386 +msgid "Toggle noclip" +msgstr "" + +#: src/guiKeyChangeMenu.cpp:387 +msgid "Range select" +msgstr "" + +#: src/guiKeyChangeMenu.cpp:388 +msgid "Print stacks" +msgstr "" + +#: src/guiMainMenu.cpp:92 +msgid "Cannot create world: Name contains invalid characters" +msgstr "" + +#: src/guiMainMenu.cpp:101 +msgid "Cannot create world: A world by this name already exists" +msgstr "" + +#: src/guiMainMenu.cpp:283 +msgid "Singleplayer" +msgstr "" + +#: src/guiMainMenu.cpp:284 +msgid "Multiplayer" +msgstr "" + +#: src/guiMainMenu.cpp:285 +msgid "Advanced" +msgstr "" + +#: src/guiMainMenu.cpp:286 +msgid "Settings" +msgstr "" + +#: src/guiMainMenu.cpp:287 +msgid "Credits" +msgstr "" + +#: src/guiMainMenu.cpp:317 +msgid "Select World:" +msgstr "" + +#: src/guiMainMenu.cpp:339 src/guiMainMenu.cpp:511 src/keycode.cpp:229 +msgid "Delete" +msgstr "" + +#: src/guiMainMenu.cpp:346 +msgid "New" +msgstr "" + +#: src/guiMainMenu.cpp:354 +msgid "Configure" +msgstr "" + +#: src/guiMainMenu.cpp:369 src/keycode.cpp:248 +msgid "Play" +msgstr "" + +#: src/guiMainMenu.cpp:380 src/guiMainMenu.cpp:619 +msgid "Creative Mode" +msgstr "" + +#: src/guiMainMenu.cpp:386 src/guiMainMenu.cpp:625 +msgid "Enable Damage" +msgstr "" + +#: src/guiMainMenu.cpp:406 src/guiMainMenu.cpp:541 +msgid "Name/Password" +msgstr "" + +#: src/guiMainMenu.cpp:442 src/guiMainMenu.cpp:459 src/guiMainMenu.cpp:1184 +msgid "Favorites:" +msgstr "" + +#: src/guiMainMenu.cpp:450 src/guiMainMenu.cpp:1194 +msgid "Public Server List:" +msgstr "" + +#: src/guiMainMenu.cpp:470 src/guiMainMenu.cpp:568 +msgid "Address/Port" +msgstr "" + +#: src/guiMainMenu.cpp:497 src/guiMainMenu.cpp:1183 +msgid "Show Public" +msgstr "" + +#: src/guiMainMenu.cpp:501 src/guiMainMenu.cpp:1193 +msgid "Show Favorites" +msgstr "" + +#: src/guiMainMenu.cpp:521 +msgid "Connect" +msgstr "" + +#: src/guiMainMenu.cpp:591 +msgid "Leave address blank to start a local server." +msgstr "" + +#: src/guiMainMenu.cpp:600 +msgid "Start Game / Connect" +msgstr "" + +#: src/guiMainMenu.cpp:632 +msgid "Public" +msgstr "" + +#: src/guiMainMenu.cpp:640 src/guiMainMenu.cpp:1113 +msgid "Delete world" +msgstr "" + +#: src/guiMainMenu.cpp:647 +msgid "Create world" +msgstr "" + +#: src/guiMainMenu.cpp:681 +msgid "Fancy trees" +msgstr "" + +#: src/guiMainMenu.cpp:687 +msgid "Smooth Lighting" +msgstr "" + +#: src/guiMainMenu.cpp:693 +msgid "3D Clouds" +msgstr "" + +#: src/guiMainMenu.cpp:699 +msgid "Opaque water" +msgstr "" + +#: src/guiMainMenu.cpp:709 +msgid "Mip-Mapping" +msgstr "" + +#: src/guiMainMenu.cpp:716 +msgid "Anisotropic Filtering" +msgstr "" + +#: src/guiMainMenu.cpp:723 +msgid "Bi-Linear Filtering" +msgstr "" + +#: src/guiMainMenu.cpp:730 +msgid "Tri-Linear Filtering" +msgstr "" + +#: src/guiMainMenu.cpp:738 +msgid "Shaders" +msgstr "" + +#: src/guiMainMenu.cpp:745 +msgid "Preload item visuals" +msgstr "" + +#: src/guiMainMenu.cpp:752 +msgid "Enable Particles" +msgstr "" + +#: src/guiMainMenu.cpp:759 +msgid "Finite liquid" +msgstr "" + +#: src/guiMainMenu.cpp:769 +msgid "Change keys" +msgstr "" + +#: src/guiMainMenu.cpp:1084 +msgid "Address required." +msgstr "" + +#: src/guiMainMenu.cpp:1102 +msgid "Cannot delete world: Nothing selected" +msgstr "" + +#: src/guiMainMenu.cpp:1117 +msgid "Files to be deleted" +msgstr "" + +#: src/guiMainMenu.cpp:1133 +msgid "Cannot create world: No games found" +msgstr "" + +#: src/guiMainMenu.cpp:1149 +msgid "Cannot configure world: Nothing selected" +msgstr "" + +#: src/guiMainMenu.cpp:1256 +msgid "Failed to delete all world files" +msgstr "" + +#: src/guiPasswordChange.cpp:108 +msgid "Old Password" +msgstr "" + +#: src/guiPasswordChange.cpp:125 +msgid "New Password" +msgstr "" + +#: src/guiPasswordChange.cpp:141 +msgid "Confirm Password" +msgstr "" + +#: src/guiPasswordChange.cpp:158 +msgid "Change" +msgstr "" + +#: src/guiPasswordChange.cpp:167 +msgid "Passwords do not match!" +msgstr "" + +#: src/guiPauseMenu.cpp:123 +msgid "Continue" +msgstr "" + +#: src/guiPauseMenu.cpp:132 +msgid "Change Password" +msgstr "" + +#: src/guiPauseMenu.cpp:140 +msgid "Sound Volume" +msgstr "" + +#: src/guiPauseMenu.cpp:147 +msgid "Exit to Menu" +msgstr "" + +#: src/guiPauseMenu.cpp:154 +msgid "Exit to OS" +msgstr "" + +#: src/guiPauseMenu.cpp:161 +msgid "" +"Default Controls:\n" +"- WASD: Walk\n" +"- Mouse left: dig/hit\n" +"- Mouse right: place/use\n" +"- Mouse wheel: select item\n" +"- 0...9: select item\n" +"- Shift: sneak\n" +"- R: Toggle viewing all loaded chunks\n" +"- I: Inventory menu\n" +"- ESC: This menu\n" +"- T: Chat\n" +msgstr "" + +#: src/guiVolumeChange.cpp:108 +msgid "Sound Volume: " +msgstr "" + +#: src/guiVolumeChange.cpp:121 +msgid "Exit" +msgstr "" + +#: src/keycode.cpp:223 +msgid "Left Button" +msgstr "" + +#: src/keycode.cpp:223 +msgid "Middle Button" +msgstr "" + +#: src/keycode.cpp:223 +msgid "Right Button" +msgstr "" + +#: src/keycode.cpp:223 +msgid "X Button 1" +msgstr "" + +#: src/keycode.cpp:224 +msgid "Back" +msgstr "" + +#: src/keycode.cpp:224 +msgid "Clear" +msgstr "" + +#: src/keycode.cpp:224 +msgid "Return" +msgstr "" + +#: src/keycode.cpp:224 +msgid "Tab" +msgstr "" + +#: src/keycode.cpp:224 +msgid "X Button 2" +msgstr "" + +#: src/keycode.cpp:225 +msgid "Capital" +msgstr "" + +#: src/keycode.cpp:225 +msgid "Control" +msgstr "" + +#: src/keycode.cpp:225 +msgid "Kana" +msgstr "" + +#: src/keycode.cpp:225 +msgid "Menu" +msgstr "" + +#: src/keycode.cpp:225 +msgid "Pause" +msgstr "" + +#: src/keycode.cpp:225 +msgid "Shift" +msgstr "" + +#: src/keycode.cpp:226 +msgid "Convert" +msgstr "" + +#: src/keycode.cpp:226 +msgid "Escape" +msgstr "" + +#: src/keycode.cpp:226 +msgid "Final" +msgstr "" + +#: src/keycode.cpp:226 +msgid "Junja" +msgstr "" + +#: src/keycode.cpp:226 +msgid "Kanji" +msgstr "" + +#: src/keycode.cpp:226 +msgid "Nonconvert" +msgstr "" + +#: src/keycode.cpp:227 +msgid "Accept" +msgstr "" + +#: src/keycode.cpp:227 +msgid "End" +msgstr "" + +#: src/keycode.cpp:227 +msgid "Home" +msgstr "" + +#: src/keycode.cpp:227 +msgid "Mode Change" +msgstr "" + +#: src/keycode.cpp:227 +msgid "Next" +msgstr "" + +#: src/keycode.cpp:227 +msgid "Prior" +msgstr "" + +#: src/keycode.cpp:227 +msgid "Space" +msgstr "" + +#: src/keycode.cpp:228 +msgid "Down" +msgstr "" + +#: src/keycode.cpp:228 +msgid "Execute" +msgstr "" + +#: src/keycode.cpp:228 +msgid "Print" +msgstr "" + +#: src/keycode.cpp:228 +msgid "Select" +msgstr "" + +#: src/keycode.cpp:228 +msgid "Up" +msgstr "" + +#: src/keycode.cpp:229 +msgid "Help" +msgstr "" + +#: src/keycode.cpp:229 +msgid "Insert" +msgstr "" + +#: src/keycode.cpp:229 +msgid "Snapshot" +msgstr "" + +#: src/keycode.cpp:232 +msgid "Left Windows" +msgstr "" + +#: src/keycode.cpp:233 +msgid "Apps" +msgstr "" + +#: src/keycode.cpp:233 +msgid "Numpad 0" +msgstr "" + +#: src/keycode.cpp:233 +msgid "Numpad 1" +msgstr "" + +#: src/keycode.cpp:233 +msgid "Right Windows" +msgstr "" + +#: src/keycode.cpp:233 +msgid "Sleep" +msgstr "" + +#: src/keycode.cpp:234 +msgid "Numpad 2" +msgstr "" + +#: src/keycode.cpp:234 +msgid "Numpad 3" +msgstr "" + +#: src/keycode.cpp:234 +msgid "Numpad 4" +msgstr "" + +#: src/keycode.cpp:234 +msgid "Numpad 5" +msgstr "" + +#: src/keycode.cpp:234 +msgid "Numpad 6" +msgstr "" + +#: src/keycode.cpp:234 +msgid "Numpad 7" +msgstr "" + +#: src/keycode.cpp:235 +msgid "Numpad *" +msgstr "" + +#: src/keycode.cpp:235 +msgid "Numpad +" +msgstr "" + +#: src/keycode.cpp:235 +msgid "Numpad -" +msgstr "" + +#: src/keycode.cpp:235 +msgid "Numpad /" +msgstr "" + +#: src/keycode.cpp:235 +msgid "Numpad 8" +msgstr "" + +#: src/keycode.cpp:235 +msgid "Numpad 9" +msgstr "" + +#: src/keycode.cpp:239 +msgid "Num Lock" +msgstr "" + +#: src/keycode.cpp:239 +msgid "Scroll Lock" +msgstr "" + +#: src/keycode.cpp:240 +msgid "Left Shift" +msgstr "" + +#: src/keycode.cpp:240 +msgid "Right Shift" +msgstr "" + +#: src/keycode.cpp:241 +msgid "Left Control" +msgstr "" + +#: src/keycode.cpp:241 +msgid "Left Menu" +msgstr "" + +#: src/keycode.cpp:241 +msgid "Right Control" +msgstr "" + +#: src/keycode.cpp:241 +msgid "Right Menu" +msgstr "" + +#: src/keycode.cpp:243 +msgid "Comma" +msgstr "" + +#: src/keycode.cpp:243 +msgid "Minus" +msgstr "" + +#: src/keycode.cpp:243 +msgid "Period" +msgstr "" + +#: src/keycode.cpp:243 +msgid "Plus" +msgstr "" + +#: src/keycode.cpp:247 +msgid "Attn" +msgstr "" + +#: src/keycode.cpp:247 +msgid "CrSel" +msgstr "" + +#: src/keycode.cpp:248 +msgid "Erase OEF" +msgstr "" + +#: src/keycode.cpp:248 +msgid "ExSel" +msgstr "" + +#: src/keycode.cpp:248 +msgid "OEM Clear" +msgstr "" + +#: src/keycode.cpp:248 +msgid "PA1" +msgstr "" + +#: src/keycode.cpp:248 +msgid "Zoom" +msgstr "" + +#: src/main.cpp:1506 +msgid "Main Menu" +msgstr "" + +#: src/main.cpp:1830 +msgid "Failed to initialize world" +msgstr "" + +#: src/main.cpp:1842 +msgid "No world selected and no address provided. Nothing to do." +msgstr "" + +#: src/main.cpp:1850 +msgid "Could not find or load game \"" +msgstr "" + +#: src/main.cpp:1864 +msgid "Invalid gamespec." +msgstr "" + +#: src/main.cpp:1904 +msgid "Connection error (timed out?)" +msgstr "" + +#: src/main.cpp:1915 +msgid "" +"\n" +"Check debug.txt for details." +msgstr "" diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index d6182861f..9bef3289e 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -227,6 +227,7 @@ set(common_SRCS emerge.cpp mapgen.cpp mapgen_v6.cpp + mapgen_v7.cpp mapgen_indev.cpp mapgen_singlenode.cpp treegen.cpp @@ -264,6 +265,7 @@ set(common_SRCS clientserver.cpp staticobject.cpp serverlist.cpp + pathfinder.cpp util/serialize.cpp util/directiontables.cpp util/numeric.cpp diff --git a/src/biome.cpp b/src/biome.cpp index 86af75310..b50c562a0 100644 --- a/src/biome.cpp +++ b/src/biome.cpp @@ -1,6 +1,6 @@ /* Minetest -Copyright (C) 2010-2013 kwolekr, Ryan Kwolek <kwolekr2@cs.scranton.edu> +Copyright (C) 2010-2013 kwolekr, Ryan Kwolek <kwolekr@minetest.net> This program is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by @@ -23,68 +23,50 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "log.h" #include "main.h" -#define BT_NONE 0 -#define BT_OCEAN 1 -#define BT_LAKE 2 -#define BT_SBEACH 3 -#define BT_GBEACH 4 -#define BT_PLAINS 5 -#define BT_HILLS 6 -#define BT_EXTREMEHILLS 7 -#define BT_MOUNTAINS 8 -#define BT_DESERT 9 -#define BT_DESERTHILLS 10 -#define BT_HELL 11 -#define BT_AETHER 12 -#define BT_BTMASK 0x3F +NoiseParams nparams_biome_def_heat = + {50, 50, v3f(500.0, 500.0, 500.0), 5349, 3, 0.70}; +NoiseParams nparams_biome_def_humidity = + {50, 50, v3f(500.0, 500.0, 500.0), 842, 3, 0.55}; -#define BTF_SNOW 0x40 -#define BTF_FOREST 0x80 -#define BGFREQ_1 ( 0.40) -#define BGFREQ_2 (BGFREQ_1 + 0.05) -#define BGFREQ_3 (BGFREQ_2 + 0.08) -#define BGFREQ_4 (BGFREQ_3 + 0.35) -#define BGFREQ_5 (BGFREQ_4 + 0.18) -//BGFREQ_5 is not checked as an upper bound; it ought to sum up to 1.00, but it's okay if it doesn't. +BiomeDefManager::BiomeDefManager() { + biome_registration_finished = false; + np_heat = &nparams_biome_def_heat; + np_humidity = &nparams_biome_def_humidity; + // Create default biome to be used in case none exist + Biome *b = new Biome; + + b->id = 0; + b->name = "Default"; + b->flags = 0; -/*float bg1_temps[] = {0.0}; -int bg1_biomes[] = {BT_OCEAN}; + b->c_top = CONTENT_AIR; + b->top_depth = 0; + b->c_filler = b->c_top; + b->filler_height = MAP_GENERATION_LIMIT; -float bg2_temps[] = {10.0}; -int bg2_biomes[] = {BT_GBEACH, BT_SBEACH}; + b->height_min = -MAP_GENERATION_LIMIT; + b->height_max = MAP_GENERATION_LIMIT; + b->heat_point = 0.0; + b->humidity_point = 0.0; -float bg3_temps[] = {30.0, 40.0}; -int bg3_biomes[] = {BT_HILLS, BT_EXTREMEHILLS, BT_MOUNTAINS}; - -float bg4_temps[] = {25.0, 30.0, 35.0, 40.0}; -int bg4_biomes[] = {BT_HILLS, BT_EXTREMEHILLS, BT_MOUNTAINS, BT_DESERT, BT_DESERTHILLS}; - -float bg5_temps[] = {5.0, 40.0}; -int bg5_biomes[] = {BT_LAKE, BT_PLAINS, BT_DESERT};*/ - -NoiseParams np_default = {20.0, 15.0, v3f(250., 250., 250.), 82341, 5, 0.6}; - - -BiomeDefManager::BiomeDefManager(IGameDef *gamedef) { - this->m_gamedef = gamedef; - this->ndef = gamedef->ndef(); - - //the initial biome group - bgroups.push_back(new std::vector<Biome *>); + biomes.push_back(b); } BiomeDefManager::~BiomeDefManager() { - for (unsigned int i = 0; i != bgroups.size(); i++) - delete bgroups[i]; + //if (biomecache) + // delete[] biomecache; + + for (size_t i = 0; i != biomes.size(); i++) + delete biomes[i]; } Biome *BiomeDefManager::createBiome(BiomeTerrainType btt) { - switch (btt) { + /*switch (btt) { case BIOME_TERRAIN_NORMAL: return new Biome; case BIOME_TERRAIN_LIQUID: @@ -92,142 +74,97 @@ Biome *BiomeDefManager::createBiome(BiomeTerrainType btt) { case BIOME_TERRAIN_NETHER: return new BiomeHell; case BIOME_TERRAIN_AETHER: - return new BiomeAether; + return new BiomeSky; case BIOME_TERRAIN_FLAT: return new BiomeSuperflat; } - return NULL; -} - - -void BiomeDefManager::addBiomeGroup(float freq) { - int size = bgroup_freqs.size(); - float newfreq = freq; - - if (size) - newfreq += bgroup_freqs[size - 1]; - bgroup_freqs.push_back(newfreq); - bgroups.push_back(new std::vector<Biome *>); - - verbosestream << "BiomeDefManager: added biome group with frequency " << - newfreq << std::endl; + return NULL;*/ + return new Biome; } -void BiomeDefManager::addBiome(Biome *b) { - std::vector<Biome *> *bgroup; - - if ((unsigned int)b->groupid >= bgroups.size()) { - errorstream << "BiomeDefManager: attempted to add biome '" << b->name - << "' to nonexistent biome group " << b->groupid << std::endl; - return; +// just a PoC, obviously needs optimization later on (precalculate this) +void BiomeDefManager::calcBiomes(BiomeNoiseInput *input, u8 *biomeid_map) { + int i = 0; + for (int y = 0; y != input->mapsize.Y; y++) { + for (int x = 0; x != input->mapsize.X; x++, i++) { + float heat = (input->heat_map[i] + 1) * 50; + float humidity = (input->humidity_map[i] + 1) * 50; + biomeid_map[i] = getBiome(heat, humidity, input->height_map[i])->id; + } } - - bgroup = bgroups[b->groupid]; - bgroup->push_back(b); - - verbosestream << "BiomeDefManager: added biome '" << b->name << - "' to biome group " << (int)b->groupid << std::endl; } -void BiomeDefManager::addDefaultBiomes() { +void BiomeDefManager::resolveNodeNames(INodeDefManager *ndef) { Biome *b; - - b = new Biome; - b->name = "Default"; - b->n_top = MapNode(ndef->getId("mapgen_stone")); - b->n_filler = b->n_top; - b->ntopnodes = 0; - b->height_min = -MAP_GENERATION_LIMIT; - b->height_max = MAP_GENERATION_LIMIT; - b->heat_min = FLT_MIN; - b->heat_max = FLT_MAX; - b->humidity_min = FLT_MIN; - b->humidity_max = FLT_MAX; - b->np = &np_default; - biome_default = b; -} - - -Biome *BiomeDefManager::getBiome(float bgfreq, float heat, float humidity) { - std::vector<Biome *> *bgroup; - Biome *b; - int i; - - int ngroups = bgroup_freqs.size(); - if (!ngroups) - return biome_default; - for (i = 0; (i != ngroups) && (bgfreq > bgroup_freqs[i]); i++); - bgroup = bgroups[i]; - - int nbiomes = bgroup->size(); - for (i = 0; i != nbiomes; i++) { - b = bgroup->operator[](i); - if (heat >= b->heat_min && heat <= b->heat_max && - humidity >= b->humidity_min && humidity <= b->humidity_max) - return b; + + biome_registration_finished = true; + + for (size_t i = 0; i != biomes.size(); i++) { + b = biomes[i]; + + if (b->c_top == CONTENT_IGNORE) { + b->c_top = ndef->getId(b->top_nodename); + if (b->c_top == CONTENT_IGNORE) { + errorstream << "BiomeDefManager::resolveNodeNames: node '" + << b->top_nodename << "' not defined" << std::endl; + b->c_top = CONTENT_AIR; + b->top_depth = 0; + } + } + + if (b->c_filler == CONTENT_IGNORE) { + b->c_filler = ndef->getId(b->filler_nodename); + if (b->c_filler == CONTENT_IGNORE) { + errorstream << "BiomeDefManager::resolveNodeNames: node '" + << b->filler_nodename << "' not defined" << std::endl; + b->c_filler = CONTENT_AIR; + b->filler_height = MAP_GENERATION_LIMIT; + } + } } - - return biome_default; -} - - -//////////////////////////// [ Generic biome ] //////////////////////////////// - - -int Biome::getSurfaceHeight(float noise_terrain) { - return np->offset + np->scale * noise_terrain; -} - - -void Biome::genColumn(Mapgen *mapgen, int x, int z, int y1, int y2) { - } -///////////////////////////// [ Ocean biome ] ///////////////////////////////// - - -void BiomeLiquid::genColumn(Mapgen *mapgen, int x, int z, int y1, int y2) { - -} - - -///////////////////////////// [ Nether biome ] ///////////////////////////////// - - -int BiomeHell::getSurfaceHeight(float noise_terrain) { - return np->offset + np->scale * noise_terrain; -} - - -void BiomeHell::genColumn(Mapgen *mapgen, int x, int z, int y1, int y2) { - -} - - -///////////////////////////// [ Aether biome ] //////////////////////////////// - - -int BiomeAether::getSurfaceHeight(float noise_terrain) { - return np->offset + np->scale * noise_terrain; -} - - -void BiomeAether::genColumn(Mapgen *mapgen, int x, int z, int y1, int y2) { +void BiomeDefManager::addBiome(Biome *b) { + if (biome_registration_finished) { + errorstream << "BIomeDefManager: biome registration already finished, dropping " << b->name <<std::endl; + delete b; + return; + } + + size_t nbiomes = biomes.size(); + if (nbiomes >= 0xFF) { + errorstream << "BiomeDefManager: too many biomes, dropping " << b->name << std::endl; + delete b; + return; + } + b->id = (u8)nbiomes; + biomes.push_back(b); + verbosestream << "BiomeDefManager: added biome " << b->name << std::endl; } -/////////////////////////// [ Superflat biome ] /////////////////////////////// +Biome *BiomeDefManager::getBiome(float heat, float humidity, s16 y) { + Biome *b, *biome_closest = NULL; + float dist_min = FLT_MAX; + for (size_t i = 1; i < biomes.size(); i++) { + b = biomes[i]; + if (y > b->height_max || y < b->height_min) + continue; -int BiomeSuperflat::getSurfaceHeight(float noise_terrain) { - return ntopnodes; -} - - -void BiomeSuperflat::genColumn(Mapgen *mapgen, int x, int z, int y1, int y2) { - + float d_heat = heat - b->heat_point; + float d_humidity = humidity - b->humidity_point; + float dist = (d_heat * d_heat) + + (d_humidity * d_humidity); + if (dist < dist_min) { + dist_min = dist; + biome_closest = b; + } + } + + return biome_closest ? biome_closest : biomes[0]; } diff --git a/src/biome.h b/src/biome.h index c30af46ad..17703db5a 100644 --- a/src/biome.h +++ b/src/biome.h @@ -1,6 +1,6 @@ /* Minetest -Copyright (C) 2010-2013 kwolekr, Ryan Kwolek <kwolekr2@cs.scranton.edu> +Copyright (C) 2010-2013 kwolekr, Ryan Kwolek <kwolekr@minetest.net> This program is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by @@ -27,7 +27,6 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "noise.h" #include "mapgen.h" - enum BiomeTerrainType { BIOME_TERRAIN_NORMAL, @@ -37,62 +36,54 @@ enum BiomeTerrainType BIOME_TERRAIN_FLAT }; +extern NoiseParams nparams_biome_def_heat; +extern NoiseParams nparams_biome_def_humidity; + class Biome { public: - MapNode n_top; - MapNode n_filler; - s16 ntopnodes; - s8 groupid; - s8 flags; - s16 height_min; - s16 height_max; - float heat_min; - float heat_max; - float humidity_min; - float humidity_max; + u8 id; std::string name; - NoiseParams *np; + u32 flags; + + std::string top_nodename; + std::string filler_nodename; - virtual void genColumn(Mapgen *mg, int x, int z, int y1, int y2); - virtual int getSurfaceHeight(float noise_terrain); -}; - -class BiomeLiquid : public Biome { - virtual void genColumn(Mapgen *mg, int x, int z, int y1, int y2); -}; + content_t c_top; + s16 top_depth; -class BiomeHell : public Biome { - virtual void genColumn(Mapgen *mg, int x, int z, int y1, int y2); - virtual int getSurfaceHeight(float noise_terrain); -}; - -class BiomeAether : public Biome { - virtual void genColumn(Mapgen *mg, int x, int z, int y1, int y2); - virtual int getSurfaceHeight(float noise_terrain); + content_t c_filler; + s16 filler_height; + + s16 height_min; + s16 height_max; + float heat_point; + float humidity_point; }; -class BiomeSuperflat : public Biome { - virtual void genColumn(Mapgen *mg, int x, int z, int y1, int y2); - virtual int getSurfaceHeight(float noise_terrain); +struct BiomeNoiseInput { + v2s16 mapsize; + float *heat_map; + float *humidity_map; + s16 *height_map; }; class BiomeDefManager { public: - std::vector<float> bgroup_freqs; - std::vector<std::vector<Biome *> *> bgroups; - Biome *biome_default; - IGameDef *m_gamedef; - INodeDefManager *ndef; + std::vector<Biome *> biomes; - BiomeDefManager(IGameDef *gamedef); - ~BiomeDefManager(); + bool biome_registration_finished; + NoiseParams *np_heat; + NoiseParams *np_humidity; + BiomeDefManager(); + ~BiomeDefManager(); + Biome *createBiome(BiomeTerrainType btt); - Biome *getBiome(float bgfreq, float heat, float humidity); + void calcBiomes(BiomeNoiseInput *input, u8 *biomeid_map); + Biome *getBiome(float heat, float humidity, s16 y); - void addBiomeGroup(float freq); void addBiome(Biome *b); - void addDefaultBiomes(); + void resolveNodeNames(INodeDefManager *ndef); }; #endif diff --git a/src/camera.cpp b/src/camera.cpp index b1e588415..138b022c0 100644 --- a/src/camera.cpp +++ b/src/camera.cpp @@ -218,6 +218,8 @@ void Camera::update(LocalPlayer* player, f32 frametime, v2u32 screensize, // Smooth the movement when walking up stairs v3f old_player_position = m_playernode->getPosition(); v3f player_position = player->getPosition(); + if (player->isAttached && player->parent) + player_position = player->parent->getPosition(); //if(player->touching_ground && player_position.Y > old_player_position.Y) if(player->touching_ground && player_position.Y > old_player_position.Y) diff --git a/src/chat.cpp b/src/chat.cpp index 1135ccdf7..3102e194a 100644 --- a/src/chat.cpp +++ b/src/chat.cpp @@ -464,7 +464,7 @@ void ChatPrompt::historyNext() } } -void ChatPrompt::nickCompletion(const std::list<std::wstring>& names, bool backwards) +void ChatPrompt::nickCompletion(const std::list<std::string>& names, bool backwards) { // Two cases: // (a) m_nick_completion_start == m_nick_completion_end == 0 @@ -493,13 +493,13 @@ void ChatPrompt::nickCompletion(const std::list<std::wstring>& names, bool backw // find all names that start with the selected prefix std::vector<std::wstring> completions; - for (std::list<std::wstring>::const_iterator + for (std::list<std::string>::const_iterator i = names.begin(); i != names.end(); ++i) { - if (str_starts_with(*i, prefix, true)) + if (str_starts_with(narrow_to_wide(*i), prefix, true)) { - std::wstring completion = *i; + std::wstring completion = narrow_to_wide(*i); if (prefix_start == 0) completion += L":"; completions.push_back(completion); diff --git a/src/chat.h b/src/chat.h index 8a40c7ccf..19b48456e 100644 --- a/src/chat.h +++ b/src/chat.h @@ -160,7 +160,7 @@ public: void historyNext(); // Nick completion - void nickCompletion(const std::list<std::wstring>& names, bool backwards); + void nickCompletion(const std::list<std::string>& names, bool backwards); // Update console size and reformat the visible portion of the prompt void reformat(u32 cols); diff --git a/src/client.cpp b/src/client.cpp index f27f95d98..1d5f8f472 100644 --- a/src/client.cpp +++ b/src/client.cpp @@ -2501,18 +2501,9 @@ void Client::printDebugInfo(std::ostream &os) <<std::endl;*/ } -std::list<std::wstring> Client::getConnectedPlayerNames() +std::list<std::string> Client::getConnectedPlayerNames() { - std::list<Player*> players = m_env.getPlayers(true); - std::list<std::wstring> playerNames; - for(std::list<Player*>::iterator - i = players.begin(); - i != players.end(); ++i) - { - Player *player = *i; - playerNames.push_back(narrow_to_wide(player->getName())); - } - return playerNames; + return m_env.getPlayerNames(); } float Client::getAnimationTime() diff --git a/src/client.h b/src/client.h index d476a1d51..16cdc237f 100644 --- a/src/client.h +++ b/src/client.h @@ -315,7 +315,7 @@ public: // Prints a line or two of info void printDebugInfo(std::ostream &os); - std::list<std::wstring> getConnectedPlayerNames(); + std::list<std::string> getConnectedPlayerNames(); float getAnimationTime(); diff --git a/src/clientserver.h b/src/clientserver.h index 28b579971..8b1e0a7e4 100644 --- a/src/clientserver.h +++ b/src/clientserver.h @@ -88,9 +88,11 @@ SharedBuffer<u8> makePacket_TOCLIENT_TIME_OF_DAY(u16 time, float time_speed); PROTOCOL_VERSION 18: damageGroups added to ToolCapabilities sound_place added to ItemDefinition + PROTOCOL_VERSION 19: + GENERIC_CMD_SET_PHYSICS_OVERRIDE */ -#define LATEST_PROTOCOL_VERSION 18 +#define LATEST_PROTOCOL_VERSION 19 // Server's supported network protocol range #define SERVER_PROTOCOL_VERSION_MIN 13 diff --git a/src/collision.cpp b/src/collision.cpp index 806a3b720..cd170196f 100644 --- a/src/collision.cpp +++ b/src/collision.cpp @@ -209,10 +209,15 @@ collisionMoveResult collisionMoveSimple(Environment *env, IGameDef *gamedef, } speed_f += accel_f * dtime; - // If there is no speed, there are no collisions + // If there is no speed, there are no collisions if(speed_f.getLength() == 0) return result; + // Limit speed for avoiding hangs + speed_f.Y=rangelim(speed_f.Y,-5000,5000); + speed_f.X=rangelim(speed_f.X,-5000,5000); + speed_f.Z=rangelim(speed_f.Z,-5000,5000); + /* Collect node boxes in movement range */ diff --git a/src/content_cao.cpp b/src/content_cao.cpp index ee1009b6c..9738dc34c 100644 --- a/src/content_cao.cpp +++ b/src/content_cao.cpp @@ -708,11 +708,15 @@ public: if(player && player->isLocal()){ m_is_local_player = true; } + m_env->addPlayerName(m_name.c_str()); } } ~GenericCAO() { + if(m_is_player){ + m_env->removePlayerName(m_name.c_str()); + } } static ClientActiveObject* create(IGameDef *gamedef, ClientEnvironment *env) @@ -860,7 +864,7 @@ public: m_spritenode = smgr->addBillboardSceneNode( NULL, v2f(1, 1), v3f(0,0,0), -1); m_spritenode->setMaterialTexture(0, - tsrc->getTextureRaw("unknown_block.png")); + tsrc->getTextureRaw("unknown_node.png")); m_spritenode->setMaterialFlag(video::EMF_LIGHTING, false); m_spritenode->setMaterialFlag(video::EMF_BILINEAR_FILTER, false); m_spritenode->setMaterialType(video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF); @@ -1129,6 +1133,7 @@ public: { LocalPlayer *player = m_env->getLocalPlayer(); player->overridePosition = getParent()->getPosition(); + m_env->getLocalPlayer()->parent = getParent(); } } else @@ -1263,7 +1268,7 @@ public: { if(m_prop.visual == "sprite") { - std::string texturestring = "unknown_block.png"; + std::string texturestring = "unknown_node.png"; if(m_prop.textures.size() >= 1) texturestring = m_prop.textures[0]; texturestring += mod; @@ -1329,7 +1334,7 @@ public: { for (u32 i = 0; i < 6; ++i) { - std::string texturestring = "unknown_block.png"; + std::string texturestring = "unknown_node.png"; if(m_prop.textures.size() > i) texturestring = m_prop.textures[i]; texturestring += mod; @@ -1678,6 +1683,19 @@ public: updateTexturePos(); } + else if(cmd == GENERIC_CMD_SET_PHYSICS_OVERRIDE) + { + float override_speed = readF1000(is); + float override_jump = readF1000(is); + float override_gravity = readF1000(is); + if(m_is_local_player) + { + LocalPlayer *player = m_env->getLocalPlayer(); + player->physics_override_speed = override_speed; + player->physics_override_jump = override_jump; + player->physics_override_gravity = override_gravity; + } + } else if(cmd == GENERIC_CMD_SET_ANIMATION) { m_animation_range = readV2F1000(is); diff --git a/src/content_sao.cpp b/src/content_sao.cpp index ae08b4260..e6c8c725c 100644 --- a/src/content_sao.cpp +++ b/src/content_sao.cpp @@ -935,7 +935,11 @@ PlayerSAO::PlayerSAO(ServerEnvironment *env_, Player *player_, u16 peer_id_, m_moved(false), m_inventory_not_sent(false), m_hp_not_sent(false), - m_wielded_item_not_sent(false) + m_wielded_item_not_sent(false), + m_physics_override_speed(1), + m_physics_override_jump(1), + m_physics_override_gravity(1), + m_physics_override_sent(false) { assert(m_player); assert(m_peer_id != 0); @@ -1019,7 +1023,7 @@ std::string PlayerSAO::getClientInitializationData(u16 protocol_version) writeF1000(os, m_player->getYaw()); writeS16(os, getHP()); - writeU8(os, 4 + m_bone_position.size()); // number of messages stuffed in here + writeU8(os, 5 + m_bone_position.size()); // number of messages stuffed in here os<<serializeLongString(getPropertyPacket()); // message 1 os<<serializeLongString(gob_cmd_update_armor_groups(m_armor_groups)); // 2 os<<serializeLongString(gob_cmd_update_animation(m_animation_range, m_animation_speed, m_animation_blend)); // 3 @@ -1027,6 +1031,7 @@ std::string PlayerSAO::getClientInitializationData(u16 protocol_version) os<<serializeLongString(gob_cmd_update_bone_position((*ii).first, (*ii).second.X, (*ii).second.Y)); // m_bone_position.size } os<<serializeLongString(gob_cmd_update_attachment(m_attachment_parent_id, m_attachment_bone, m_attachment_position, m_attachment_rotation)); // 4 + os<<serializeLongString(gob_cmd_update_physics_override(m_physics_override_speed, m_physics_override_jump, m_physics_override_gravity)); // 5 } else { @@ -1196,6 +1201,14 @@ void PlayerSAO::step(float dtime, bool send_recommended) m_messages_out.push_back(aom); } + if(m_physics_override_sent == false){ + m_physics_override_sent = true; + std::string str = gob_cmd_update_physics_override(m_physics_override_speed, m_physics_override_jump, m_physics_override_gravity); + // create message and add to list + ActiveObjectMessage aom(getId(), true, str); + m_messages_out.push_back(aom); + } + if(m_animation_sent == false){ m_animation_sent = true; std::string str = gob_cmd_update_animation(m_animation_range, m_animation_speed, m_animation_blend); diff --git a/src/content_sao.h b/src/content_sao.h index 60ca8f319..dca02bb00 100644 --- a/src/content_sao.h +++ b/src/content_sao.h @@ -101,7 +101,7 @@ private: float m_last_sent_position_timer; float m_last_sent_move_precision; bool m_armor_groups_sent; - + v2f m_animation_range; float m_animation_speed; float m_animation_blend; @@ -257,8 +257,6 @@ private: ItemGroupList m_armor_groups; bool m_armor_groups_sent; - - bool m_properties_sent; struct ObjectProperties m_prop; // Cached privileges for enforcement @@ -285,6 +283,11 @@ public: bool m_inventory_not_sent; bool m_hp_not_sent; bool m_wielded_item_not_sent; + + float m_physics_override_speed; + float m_physics_override_jump; + float m_physics_override_gravity; + bool m_physics_override_sent; }; #endif diff --git a/src/defaultsettings.cpp b/src/defaultsettings.cpp index 592c6bcca..9875c6ecb 100644 --- a/src/defaultsettings.cpp +++ b/src/defaultsettings.cpp @@ -231,10 +231,12 @@ void set_default_settings(Settings *settings) settings->setDefault("mgv6_np_trees", "0, 1, (125, 125, 125), 2, 4, 0.66"); settings->setDefault("mgv6_np_apple_trees", "0, 1, (100, 100, 100), 342902, 3, 0.45"); - settings->setDefault("mgv7_np_terrain", "10, 12, (350, 350, 350), 82341, 5, 0.6"); - settings->setDefault("mgv7_np_bgroup", "0.5, 0.3125, (350, 350, 350), 5923, 2, 0.6"); - settings->setDefault("mgv7_np_heat", "25, 50, (500, 500, 500), 35293, 1, 0"); - settings->setDefault("mgv7_np_humidity", "50, 31.25, (750, 750, 750), 12094, 2, 0.6"); + settings->setDefault("mgv7_np_terrain_base", "0, 80, (250, 250, 250), 82341, 5, 0.6"); + settings->setDefault("mgv7_np_terrain_alt", "0, 20, (250, 250, 250), 5934, 5, 0.6"); + settings->setDefault("mgv7_np_terrain_mod", "0, 1, (350, 350, 350), 85039, 5, 0.6"); + settings->setDefault("mgv7_np_terrain_persist", "0, 1, (500, 500, 500), 539, 3, 0.6"); + settings->setDefault("mgv7_np_height_select", "0.5, 0.5, (250, 250, 250), 4213, 5, 0.69"); + settings->setDefault("mgv7_np_ridge", "0.5, 1, (100, 100, 100), 6467, 4, 0.75"); settings->setDefault("mgindev_np_terrain_base", "-4, 20, (250, 250, 250), 82341, 5, 0.6, 10, 10"); settings->setDefault("mgindev_np_terrain_higher", "20, 16, (500, 500, 500), 85039, 5, 0.6, 10, 10"); @@ -245,7 +247,6 @@ void set_default_settings(Settings *settings) settings->setDefault("mgindev_np_float_islands3", "0, 1, (256, 256, 256), 6412, 2, 0.5, 1, 0.5"); settings->setDefault("mgindev_np_biome", "0, 1, (250, 250, 250), 9130, 3, 0.50, 1, 10"); settings->setDefault("mgindev_float_islands", "500"); - } void override_default_settings(Settings *settings, Settings *from) diff --git a/src/emerge.cpp b/src/emerge.cpp index e4bd997cb..499aaf291 100644 --- a/src/emerge.cpp +++ b/src/emerge.cpp @@ -1,6 +1,7 @@ /* -Minetest-c55 -Copyright (C) 2010-2011 celeron55, Perttu Ahola <celeron55@gmail.com> +Minetest +Copyright (C) 2010-2013 celeron55, Perttu Ahola <celeron55@gmail.com> +Copyright (C) 2010-2013 kwolekr, Ryan Kwolek <kwolekr@minetest.net> This program is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by @@ -39,19 +40,22 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "biome.h" #include "emerge.h" #include "mapgen_v6.h" +#include "mapgen_v7.h" #include "mapgen_indev.h" #include "mapgen_singlenode.h" /////////////////////////////// Emerge Manager //////////////////////////////// -EmergeManager::EmergeManager(IGameDef *gamedef, BiomeDefManager *bdef) { +EmergeManager::EmergeManager(IGameDef *gamedef) { //register built-in mapgens registerMapgen("v6", new MapgenFactoryV6()); + //registerMapgen("v7", new MapgenFactoryV7()); registerMapgen("indev", new MapgenFactoryIndev()); registerMapgen("singlenode", new MapgenFactorySinglenode()); - this->biomedef = bdef ? bdef : new BiomeDefManager(gamedef); + this->ndef = gamedef->getNodeDefManager(); + this->biomedef = new BiomeDefManager(); this->params = NULL; mapgen_debug_info = g_settings->getBool("enable_mapgen_debug_info"); @@ -104,6 +108,8 @@ void EmergeManager::initMapgens(MapgenParams *mgparams) { if (mapgen.size()) return; + biomedef->resolveNodeNames(ndef); + this->params = mgparams; for (unsigned int i = 0; i != emergethread.size(); i++) { mg = createMapgen(params->mg_name, 0, params); @@ -237,6 +243,8 @@ MapgenParams *EmergeManager::createMapgenParams(std::string mgname) { MapgenParams *EmergeManager::getParamsFromSettings(Settings *settings) { std::string mg_name = settings->get("mg_name"); MapgenParams *mgparams = createMapgenParams(mg_name); + if (!mgparams) + return NULL; mgparams->mg_name = mg_name; mgparams->seed = settings->getU64(settings == g_settings ? "fixed_map_seed" : "seed"); diff --git a/src/emerge.h b/src/emerge.h index 3d717bce3..fdca93c06 100644 --- a/src/emerge.h +++ b/src/emerge.h @@ -1,3 +1,22 @@ +/* +Minetest +Copyright (C) 2010-2013 kwolekr, Ryan Kwolek <kwolekr@minetest.net> + +This program 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 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 Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser 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. +*/ + #ifndef EMERGE_HEADER #define EMERGE_HEADER @@ -46,6 +65,8 @@ struct BlockEmergeData { class EmergeManager { public: + INodeDefManager *ndef; + std::map<std::string, MapgenFactory *> mglist; std::vector<Mapgen *> mapgen; @@ -67,7 +88,7 @@ public: BiomeDefManager *biomedef; std::vector<Ore *> ores; - EmergeManager(IGameDef *gamedef, BiomeDefManager *bdef); + EmergeManager(IGameDef *gamedef); ~EmergeManager(); void initMapgens(MapgenParams *mgparams); diff --git a/src/environment.cpp b/src/environment.cpp index 07cdb24d1..fc7972b2c 100644 --- a/src/environment.cpp +++ b/src/environment.cpp @@ -364,6 +364,29 @@ ServerMap & ServerEnvironment::getServerMap() return *m_map; } +bool ServerEnvironment::line_of_sight(v3f pos1, v3f pos2, float stepsize) +{ + float distance = pos1.getDistanceFrom(pos2); + + //calculate normalized direction vector + v3f normalized_vector = v3f((pos2.X - pos1.X)/distance, + (pos2.Y - pos1.Y)/distance, + (pos2.Z - pos1.Z)/distance); + + //find out if there's a node on path between pos1 and pos2 + for (float i = 1; i < distance; i += stepsize) { + v3s16 pos = floatToInt(v3f(normalized_vector.X * i, + normalized_vector.Y * i, + normalized_vector.Z * i) +pos1,BS); + + MapNode n = getMap().getNodeNoEx(pos); + + if(n.param0 != CONTENT_AIR) { + return false; + } + } + return true; +} void ServerEnvironment::serializePlayers(const std::string &savedir) { @@ -2057,7 +2080,7 @@ void ClientEnvironment::step(float dtime) // Gravity v3f speed = lplayer->getSpeed(); if(lplayer->in_liquid == false) - speed.Y -= lplayer->movement_gravity * dtime_part * 2; + speed.Y -= lplayer->movement_gravity * lplayer->physics_override_gravity * dtime_part * 2; // Liquid floating / sinking if(lplayer->in_liquid && !lplayer->swimming_vertical) diff --git a/src/environment.h b/src/environment.h index 02301e5d3..a3e43dbb4 100644 --- a/src/environment.h +++ b/src/environment.h @@ -298,6 +298,9 @@ public: // This makes stuff happen void step(f32 dtime); + //check if there's a line of sight between two positions + bool line_of_sight(v3f pos1, v3f pos2, float stepsize=1.0); + private: /* @@ -470,6 +473,13 @@ public: ClientEnvEvent getClientEvent(); std::vector<core::vector2d<int> > attachment_list; // X is child ID, Y is parent ID + + std::list<std::string> getPlayerNames() + { return m_player_names; } + void addPlayerName(std::string name) + { m_player_names.push_back(name); } + void removePlayerName(std::string name) + { m_player_names.remove(name); } private: ClientMap *m_map; @@ -482,6 +492,7 @@ private: Queue<ClientEnvEvent> m_client_event_queue; IntervalLimiter m_active_object_light_update_interval; IntervalLimiter m_lava_hurt_interval; + std::list<std::string> m_player_names; }; #endif diff --git a/src/game.cpp b/src/game.cpp index 5e4e06148..65feb50bf 100644 --- a/src/game.cpp +++ b/src/game.cpp @@ -2388,7 +2388,7 @@ void the_game( infotext = narrow_to_wide(meta->getString("infotext")); } else { MapNode n = map.getNode(nodepos); - if(nodedef->get(n).tiledef[0].name == "unknown_block.png"){ + if(nodedef->get(n).tiledef[0].name == "unknown_node.png"){ infotext = L"Unknown node: "; infotext += narrow_to_wide(nodedef->get(n).name); } diff --git a/src/genericobject.cpp b/src/genericobject.cpp index f7b272b00..e2fbde838 100644 --- a/src/genericobject.cpp +++ b/src/genericobject.cpp @@ -117,6 +117,18 @@ std::string gob_cmd_update_armor_groups(const ItemGroupList &armor_groups) return os.str(); } +std::string gob_cmd_update_physics_override(float physics_override_speed, float physics_override_jump, float physics_override_gravity) +{ + std::ostringstream os(std::ios::binary); + // command + writeU8(os, GENERIC_CMD_SET_PHYSICS_OVERRIDE); + // parameters + writeF1000(os, physics_override_speed); + writeF1000(os, physics_override_jump); + writeF1000(os, physics_override_gravity); + return os.str(); +} + std::string gob_cmd_update_animation(v2f frames, float frame_speed, float frame_blend) { std::ostringstream os(std::ios::binary); diff --git a/src/genericobject.h b/src/genericobject.h index 9a21baa67..276865ab9 100644 --- a/src/genericobject.h +++ b/src/genericobject.h @@ -33,6 +33,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #define GENERIC_CMD_SET_ANIMATION 6 #define GENERIC_CMD_SET_BONE_POSITION 7 #define GENERIC_CMD_SET_ATTACHMENT 8 +#define GENERIC_CMD_SET_PHYSICS_OVERRIDE 9 #include "object_properties.h" std::string gob_cmd_set_properties(const ObjectProperties &prop); @@ -62,6 +63,8 @@ std::string gob_cmd_punched(s16 damage, s16 result_hp); #include "itemgroup.h" std::string gob_cmd_update_armor_groups(const ItemGroupList &armor_groups); +std::string gob_cmd_update_physics_override(float physics_override_speed, float physics_override_jump, float physics_override_gravity); + std::string gob_cmd_update_animation(v2f frames, float frame_speed, float frame_blend); std::string gob_cmd_update_bone_position(std::string bone, v3f position, v3f rotation); diff --git a/src/guiChatConsole.cpp b/src/guiChatConsole.cpp index 5fc576cf8..ec23648f8 100644 --- a/src/guiChatConsole.cpp +++ b/src/guiChatConsole.cpp @@ -535,7 +535,7 @@ bool GUIChatConsole::OnEvent(const SEvent& event) { // Tab or Shift-Tab pressed // Nick completion - std::list<std::wstring> names = m_client->getConnectedPlayerNames(); + std::list<std::string> names = m_client->getConnectedPlayerNames(); bool backwards = event.KeyInput.Shift; m_chat_backend->getPrompt().nickCompletion(names, backwards); return true; diff --git a/src/guiMainMenu.cpp b/src/guiMainMenu.cpp index 4c2030039..1e3083577 100644 --- a/src/guiMainMenu.cpp +++ b/src/guiMainMenu.cpp @@ -1207,6 +1207,15 @@ bool GUIMainMenu::OnEvent(const SEvent& event) switch(event.GUIEvent.Caller->getID()) { case GUI_ID_ADDRESS_INPUT: case GUI_ID_PORT_INPUT: case GUI_ID_NAME_INPUT: case 264: + MainMenuData cur; + readInput(&cur); + if (getTab() == TAB_MULTIPLAYER && cur.address == L"") + { + (new GUIMessageMenu(env, parent, -1, menumgr, + wgettext("Address required.")) + )->drop(); + return true; + } acceptInput(); quitMenu(); return true; diff --git a/src/localplayer.cpp b/src/localplayer.cpp index ee9b41c58..9c70c8b2f 100644 --- a/src/localplayer.cpp +++ b/src/localplayer.cpp @@ -35,6 +35,7 @@ with this program; if not, write to the Free Software Foundation, Inc., LocalPlayer::LocalPlayer(IGameDef *gamedef): Player(gamedef), + parent(0), isAttached(false), overridePosition(v3f(0,0,0)), last_position(v3f(0,0,0)), @@ -388,7 +389,8 @@ void LocalPlayer::applyControl(float dtime) bool free_move = fly_allowed && g_settings->getBool("free_move"); bool fast_move = fast_allowed && g_settings->getBool("fast_move"); - bool fast_or_aux1_descends = (fast_move && control.aux1) || (fast_move && g_settings->getBool("aux1_descends")); + // When aux1_descends is enabled the fast key is used to go down, so fast isn't possible + bool fast_climb = fast_move && control.aux1 && !g_settings->getBool("aux1_descends"); bool continuous_forward = g_settings->getBool("continuous_forward"); // Whether superspeed mode is used or not @@ -417,14 +419,12 @@ void LocalPlayer::applyControl(float dtime) } else if(in_liquid || in_liquid_stable) { - // Always use fast when aux1_descends & fast_move are enabled in liquid, since the aux1 button would mean both turbo and "swim down" causing a conflict - speedV.Y = -movement_speed_fast; + speedV.Y = -movement_speed_walk; swimming_vertical = true; } else if(is_climbing) { - // Always use fast when aux1_descends & fast_move are enabled when climbing, since the aux1 button would mean both turbo and "descend" causing a conflict - speedV.Y = -movement_speed_fast; + speedV.Y = -movement_speed_climb; } else { @@ -461,8 +461,7 @@ void LocalPlayer::applyControl(float dtime) } else if(in_liquid || in_liquid_stable) { - if(fast_or_aux1_descends) - // Always use fast when aux1_descends & fast_move are enabled in liquid, since the aux1 button would mean both turbo and "swim down" causing a conflict + if(fast_climb) speedV.Y = -movement_speed_fast; else speedV.Y = -movement_speed_walk; @@ -470,8 +469,7 @@ void LocalPlayer::applyControl(float dtime) } else if(is_climbing) { - if(fast_or_aux1_descends) - // Always use fast when aux1_descends & fast_move are enabled when climbing, since the aux1 button would mean both turbo and "descend" causing a conflict + if(fast_climb) speedV.Y = -movement_speed_fast; else speedV.Y = -movement_speed_climb; @@ -528,7 +526,7 @@ void LocalPlayer::applyControl(float dtime) v3f speedJ = getSpeed(); if(speedJ.Y >= -0.5 * BS) { - speedJ.Y = movement_speed_jump; + speedJ.Y = movement_speed_jump * physics_override_jump; setSpeed(speedJ); MtEvent *e = new SimpleTriggerEvent("PlayerJump"); @@ -537,8 +535,7 @@ void LocalPlayer::applyControl(float dtime) } else if(in_liquid) { - if(fast_or_aux1_descends) - // Always use fast when aux1_descends & fast_move are enabled in liquid, since the aux1 button would mean both turbo and "swim down" causing a conflict + if(fast_climb) speedV.Y = movement_speed_fast; else speedV.Y = movement_speed_walk; @@ -546,8 +543,7 @@ void LocalPlayer::applyControl(float dtime) } else if(is_climbing) { - if(fast_or_aux1_descends) - // Always use fast when aux1_descends & fast_move are enabled when climbing, since the aux1 button would mean both turbo and "descend" causing a conflict + if(fast_climb) speedV.Y = movement_speed_fast; else speedV.Y = movement_speed_climb; @@ -555,7 +551,7 @@ void LocalPlayer::applyControl(float dtime) } // The speed of the player (Y is ignored) - if(superspeed || (is_climbing && fast_or_aux1_descends) || ((in_liquid || in_liquid_stable) && fast_or_aux1_descends)) + if(superspeed || (is_climbing && fast_climb) || ((in_liquid || in_liquid_stable) && fast_climb)) speedH = speedH.normalize() * movement_speed_fast; else if(control.sneak && !free_move && !in_liquid && !in_liquid_stable) speedH = speedH.normalize() * movement_speed_crouch; @@ -574,17 +570,14 @@ void LocalPlayer::applyControl(float dtime) incH = movement_acceleration_air * BS * dtime; incV = 0; // No vertical acceleration in air } - else if(superspeed || (fast_move && control.aux1)) - incH = incV = movement_acceleration_fast * BS * dtime; - else if ((in_liquid || in_liquid_stable) && fast_or_aux1_descends) - // Always use fast when aux1_descends & fast_move are enabled in liquid, since the aux1 button would mean both turbo and "swim down" causing a conflict + else if (superspeed || (is_climbing && fast_climb) || ((in_liquid || in_liquid_stable) && fast_climb)) incH = incV = movement_acceleration_fast * BS * dtime; else incH = incV = movement_acceleration_default * BS * dtime; // Accelerate to target speed with maximum increment - accelerateHorizontal(speedH, incH); - accelerateVertical(speedV, incV); + accelerateHorizontal(speedH * physics_override_speed, incH * physics_override_speed); + accelerateVertical(speedV * physics_override_speed, incV * physics_override_speed); } v3s16 LocalPlayer::getStandingNodePos() diff --git a/src/localplayer.h b/src/localplayer.h index 17434d379..9ac2bc682 100644 --- a/src/localplayer.h +++ b/src/localplayer.h @@ -25,6 +25,8 @@ with this program; if not, write to the Free Software Foundation, Inc., class ClientEnvironment; +class ClientActiveObject; + class LocalPlayer : public Player { public: @@ -35,6 +37,8 @@ public: { return true; } + + ClientActiveObject *parent; bool isAttached; diff --git a/src/map.cpp b/src/map.cpp index 5d6b79fb0..a8201f3ee 100644 --- a/src/map.cpp +++ b/src/map.cpp @@ -1763,6 +1763,11 @@ void Map::transformLiquidsFinite(std::map<v3s16, MapBlock*> & modified_blocks) total_level = LIQUID_LEVEL_SOURCE * can_liquid_same_level; } + // prevent lakes in air above unloaded blocks + if (p0.Y > water_level && neighbors[D_BOTTOM].n.getContent() == CONTENT_IGNORE) { + --total_level; + } + // calculate self level 5 blocks u8 want_level = total_level >= LIQUID_LEVEL_SOURCE * can_liquid_same_level @@ -1807,8 +1812,8 @@ void Map::transformLiquidsFinite(std::map<v3s16, MapBlock*> & modified_blocks) } for (u16 ii = 0; ii < 7; ii++) // infinity and cave flood optimization - if (liquid_levels_want[ii] >= 0 && - (neighbors[ii].i || + if ( neighbors[ii].i || + (liquid_levels_want[ii] >= 0 && (fast_flood && p0.Y < water_level && (initial_size >= 1000 && ii != D_TOP @@ -1916,8 +1921,7 @@ void Map::transformLiquidsFinite(std::map<v3s16, MapBlock*> & modified_blocks) if(!suspect.empty()){ // Blame suspect - RollbackScopeActor rollback_scope(m_gamedef->rollback(), - suspect, true); + RollbackScopeActor rollback_scope(m_gamedef->rollback(), suspect, true); // Get old node for rollback RollbackNode rollback_oldnode(this, p0, m_gamedef); // Set node @@ -3462,8 +3466,16 @@ void ServerMap::loadMapMeta() break; params.parseConfigLine(line); } - - MapgenParams *mgparams = m_emerge->getParamsFromSettings(¶ms); + + MapgenParams *mgparams; + try { + mgparams = m_emerge->getParamsFromSettings(¶ms); + } catch (SettingNotFoundException &e) { + infostream << "Couldn't get a setting from map_meta.txt: " + << e.what() << std::endl; + mgparams = NULL; + } + if (mgparams) { if (m_mgparams) delete m_mgparams; diff --git a/src/mapgen.cpp b/src/mapgen.cpp index 64c1886b3..22488bc4c 100644 --- a/src/mapgen.cpp +++ b/src/mapgen.cpp @@ -34,6 +34,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "main.h" // For g_profiler #include "treegen.h" #include "mapgen_v6.h" +#include "mapgen_v7.h" FlagDesc flagdesc_mapgen[] = { {"trees", MG_TREES}, @@ -240,8 +241,7 @@ void Mapgen::updateLiquid(UniqueQueue<v3s16> *trans_liquid, v3s16 nmin, v3s16 nm void Mapgen::setLighting(v3s16 nmin, v3s16 nmax, u8 light) { ScopeProfiler sp(g_profiler, "EmergeThread: mapgen lighting update", SPT_AVG); - VoxelArea a(nmin - v3s16(1,0,1) * MAP_BLOCKSIZE, - nmax + v3s16(1,0,1) * MAP_BLOCKSIZE); + VoxelArea a(nmin, nmax); for (int z = a.MinEdge.Z; z <= a.MaxEdge.Z; z++) { for (int y = a.MinEdge.Y; y <= a.MaxEdge.Y; y++) { @@ -277,8 +277,7 @@ void Mapgen::lightSpread(VoxelArea &a, v3s16 p, u8 light) { void Mapgen::calcLighting(v3s16 nmin, v3s16 nmax) { - VoxelArea a(nmin - v3s16(1,0,1) * MAP_BLOCKSIZE, - nmax + v3s16(1,0,1) * MAP_BLOCKSIZE); + VoxelArea a(nmin, nmax); bool block_is_underground = (water_level >= nmax.Y); ScopeProfiler sp(g_profiler, "EmergeThread: mapgen lighting update", SPT_AVG); @@ -341,9 +340,7 @@ void Mapgen::calcLighting(v3s16 nmin, v3s16 nmax) { void Mapgen::calcLightingOld(v3s16 nmin, v3s16 nmax) { enum LightBank banks[2] = {LIGHTBANK_DAY, LIGHTBANK_NIGHT}; - - VoxelArea a(nmin - v3s16(1,0,1) * MAP_BLOCKSIZE, - nmax + v3s16(1,0,1) * MAP_BLOCKSIZE); + VoxelArea a(nmin, nmax); bool block_is_underground = (water_level > nmax.Y); bool sunlight = !block_is_underground; @@ -409,6 +406,31 @@ void MapgenV6Params::writeParams(Settings *settings) { } +bool MapgenV7Params::readParams(Settings *settings) { + np_terrain_base = settings->getNoiseParams("mgv7_np_terrain_base"); + np_terrain_alt = settings->getNoiseParams("mgv7_np_terrain_alt"); + np_terrain_mod = settings->getNoiseParams("mgv7_np_terrain_mod"); + np_terrain_persist = settings->getNoiseParams("mgv7_np_terrain_persist"); + np_height_select = settings->getNoiseParams("mgv7_np_height_select"); + np_ridge = settings->getNoiseParams("mgv7_np_ridge"); + + bool success = + np_terrain_base && np_terrain_alt && np_terrain_mod && + np_terrain_persist && np_height_select && np_ridge; + return success; +} + + +void MapgenV7Params::writeParams(Settings *settings) { + settings->setNoiseParams("mgv7_np_terrain_base", np_terrain_base); + settings->setNoiseParams("mgv7_np_terrain_alt", np_terrain_alt); + settings->setNoiseParams("mgv7_np_terrain_mod", np_terrain_mod); + settings->setNoiseParams("mgv7_np_terrain_persist", np_terrain_persist); + settings->setNoiseParams("mgv7_np_height_select", np_height_select); + settings->setNoiseParams("mgv7_np_ridge", np_ridge); +} + + /////////////////////////////////// legacy static functions for farmesh diff --git a/src/mapgen_indev.cpp b/src/mapgen_indev.cpp index 5d455827a..5c842b6a5 100644 --- a/src/mapgen_indev.cpp +++ b/src/mapgen_indev.cpp @@ -265,9 +265,9 @@ void MapgenIndev::defineCave(Cave & cave, PseudoRandom ps, v3s16 node_min, bool cave.part_max_length_rs = ps.range(2,4); if (node_min.Y < -100 && !ps.range(0, farscale(0.2, node_min.X,node_min.Y,node_min.Z)*30)) { //huge cave.flooded = !ps.range(0, 3); - cave.tunnel_routepoints = ps.range(5, 20); + cave.tunnel_routepoints = ps.range(5, 30); cave.min_tunnel_diameter = 30; - cave.max_tunnel_diameter = ps.range(40, ps.range(80,120)); + cave.max_tunnel_diameter = ps.range(40, ps.range(80,200)); } else { cave.tunnel_routepoints = ps.range(5, ps.range(15,30)); cave.min_tunnel_diameter = 5; diff --git a/src/mapgen_singlenode.cpp b/src/mapgen_singlenode.cpp index 22b756abb..f05ddd2f4 100644 --- a/src/mapgen_singlenode.cpp +++ b/src/mapgen_singlenode.cpp @@ -91,7 +91,8 @@ void MapgenSinglenode::makeChunk(BlockMakeData *data) { updateLiquid(&data->transforming_liquid, node_min, node_max); // Calculate lighting - calcLighting(node_min, node_max); + calcLighting(node_min - v3s16(1, 0, 1) * MAP_BLOCKSIZE, + node_max + v3s16(1, 0, 1) * MAP_BLOCKSIZE); this->generating = false; } diff --git a/src/mapgen_v6.cpp b/src/mapgen_v6.cpp index 275d4b78f..4a037bb85 100644 --- a/src/mapgen_v6.cpp +++ b/src/mapgen_v6.cpp @@ -471,7 +471,8 @@ void MapgenV6::makeChunk(BlockMakeData *data) { } // Calculate lighting - calcLighting(node_min, node_max); + calcLighting(node_min - v3s16(1, 1, 1) * MAP_BLOCKSIZE, + node_max + v3s16(1, 0, 1) * MAP_BLOCKSIZE); this->generating = false; } diff --git a/src/mapgen_v7.cpp b/src/mapgen_v7.cpp new file mode 100644 index 000000000..6aecfe310 --- /dev/null +++ b/src/mapgen_v7.cpp @@ -0,0 +1,490 @@ +/* +Minetest +Copyright (C) 2010-2013 kwolekr, Ryan Kwolek <kwolekr@minetest.net> + +This program 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 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 Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser 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. +*/ + + +#include "mapgen.h" +#include "voxel.h" +#include "noise.h" +#include "mapblock.h" +#include "mapnode.h" +#include "map.h" +//#include "serverobject.h" +#include "content_sao.h" +#include "nodedef.h" +#include "content_mapnode.h" // For content_mapnode_get_new_name +#include "voxelalgorithms.h" +#include "profiler.h" +#include "settings.h" // For g_settings +#include "main.h" // For g_profiler +#include "emerge.h" +#include "dungeongen.h" +#include "treegen.h" +#include "biome.h" +#include "mapgen_v7.h" + + +/////////////////// Mapgen V7 perlin noise default values +NoiseParams nparams_v7_def_terrain_base = + {0, 80.0, v3f(250.0, 250.0, 250.0), 82341, 5, 0.6}; +NoiseParams nparams_v7_def_terrain_alt = + {0, 20.0, v3f(250.0, 250.0, 250.0), 5934, 5, 0.6}; +NoiseParams nparams_v7_def_terrain_mod = + {0, 1.0, v3f(350.0, 350.0, 350.0), 85039, 5, 0.6}; +NoiseParams nparams_v7_def_terrain_persist = + {0, 1.0, v3f(500.0, 500.0, 500.0), 539, 3, 0.6}; +NoiseParams nparams_v7_def_height_select = + {0.5, 0.5, v3f(250.0, 250.0, 250.0), 4213, 5, 0.69}; +NoiseParams nparams_v7_def_ridge = + {0.5, 1.0, v3f(100.0, 100.0, 100.0), 6467, 4, 0.75}; +/* +NoiseParams nparams_v6_def_beach = + {0.0, 1.0, v3f(250.0, 250.0, 250.0), 59420, 3, 0.50}; +NoiseParams nparams_v6_def_cave = + {6.0, 6.0, v3f(250.0, 250.0, 250.0), 34329, 3, 0.50}; +NoiseParams nparams_v6_def_humidity = + {0.5, 0.5, v3f(500.0, 500.0, 500.0), 72384, 4, 0.66}; +NoiseParams nparams_v6_def_trees = + {0.0, 1.0, v3f(125.0, 125.0, 125.0), 2, 4, 0.66}; +NoiseParams nparams_v6_def_apple_trees = + {0.0, 1.0, v3f(100.0, 100.0, 100.0), 342902, 3, 0.45}; +*/ +/////////////////////////////////////////////////////////////////////////////// + + +MapgenV7::MapgenV7(int mapgenid, MapgenV7Params *params, EmergeManager *emerge) { + this->generating = false; + this->id = mapgenid; + this->emerge = emerge; + this->bmgr = emerge->biomedef; + + this->seed = (int)params->seed; + this->water_level = params->water_level; + this->flags = params->flags; + this->csize = v3s16(1, 1, 1) * params->chunksize * MAP_BLOCKSIZE; +// this->ystride = csize.X; //////fix this + + this->biomemap = new u8[csize.X * csize.Z]; + this->heightmap = new s16[csize.X * csize.Z]; + this->ridge_heightmap = new s16[csize.X * csize.Z]; + + // Terrain noise + noise_terrain_base = new Noise(params->np_terrain_base, seed, csize.X, csize.Z); + noise_terrain_alt = new Noise(params->np_terrain_alt, seed, csize.X, csize.Z); + noise_terrain_mod = new Noise(params->np_terrain_mod, seed, csize.X, csize.Z); + noise_terrain_persist = new Noise(params->np_terrain_persist, seed, csize.X, csize.Z); + noise_height_select = new Noise(params->np_height_select, seed, csize.X, csize.Z); + noise_ridge = new Noise(params->np_ridge, seed, csize.X, csize.Y, csize.Z); + + // Biome noise + noise_heat = new Noise(bmgr->np_heat, seed, csize.X, csize.Z); + noise_humidity = new Noise(bmgr->np_humidity, seed, csize.X, csize.Z); +} + + +MapgenV7::~MapgenV7() { + delete noise_terrain_base; + delete noise_terrain_mod; + delete noise_terrain_persist; + delete noise_height_select; + delete noise_terrain_alt; + delete noise_ridge; + delete noise_heat; + delete noise_humidity; + + delete[] ridge_heightmap; + delete[] heightmap; + delete[] biomemap; +} + + +int MapgenV7::getGroundLevelAtPoint(v2s16 p) { + return 20; +} + + +void MapgenV7::makeChunk(BlockMakeData *data) { + assert(data->vmanip); + assert(data->nodedef); + assert(data->blockpos_requested.X >= data->blockpos_min.X && + data->blockpos_requested.Y >= data->blockpos_min.Y && + data->blockpos_requested.Z >= data->blockpos_min.Z); + assert(data->blockpos_requested.X <= data->blockpos_max.X && + data->blockpos_requested.Y <= data->blockpos_max.Y && + data->blockpos_requested.Z <= data->blockpos_max.Z); + + this->generating = true; + this->vm = data->vmanip; + this->ndef = data->nodedef; + //TimeTaker t("makeChunk"); + + v3s16 blockpos_min = data->blockpos_min; + v3s16 blockpos_max = data->blockpos_max; + v3s16 blockpos_full_min = blockpos_min - v3s16(1, 1, 1); + v3s16 blockpos_full_max = blockpos_max + v3s16(1, 1, 1); + node_min = blockpos_min * MAP_BLOCKSIZE; + node_max = (blockpos_max + v3s16(1, 1, 1)) * MAP_BLOCKSIZE - v3s16(1, 1, 1); + full_node_min = (blockpos_min - 1) * MAP_BLOCKSIZE; + full_node_max = (blockpos_max + 2) * MAP_BLOCKSIZE - v3s16(1, 1, 1); + + //blockseed = emerge->getBlockSeed(full_node_min); + + // Make some noise + calculateNoise(); + + // Calculate height map + s16 stone_surface_max_y = calcHeightMap(); + + // Calculate biomes + BiomeNoiseInput binput; + binput.mapsize = v2s16(csize.X, csize.Z); + binput.heat_map = noise_heat->result; + binput.humidity_map = noise_humidity->result; + binput.height_map = heightmap; + bmgr->calcBiomes(&binput, biomemap); + + c_stone = ndef->getId("mapgen_stone"); + c_dirt = ndef->getId("mapgen_dirt"); + c_dirt_with_grass = ndef->getId("mapgen_dirt_with_grass"); + c_sand = ndef->getId("mapgen_sand"); + c_water_source = ndef->getId("mapgen_water_source"); + + generateTerrain(); + carveRidges(); + + //carveRivers(); + addTopNodes(); + growGrass(); + + //v3s16 central_area_size = node_max - node_min + v3s16(1,1,1); + + if (flags & MG_DUNGEONS) { + DungeonGen dgen(ndef, data->seed, water_level); + dgen.generate(vm, blockseed, full_node_min, full_node_max); + } + + for (size_t i = 0; i != emerge->ores.size(); i++) { + Ore *ore = emerge->ores[i]; + ore->generate(this, blockseed + i, node_min, node_max); + } + + //printf("makeChunk: %dms\n", t.stop()); + + updateLiquid(&data->transforming_liquid, full_node_min, full_node_max); + + calcLighting(node_min - v3s16(1, 0, 1) * MAP_BLOCKSIZE, + node_max + v3s16(1, 0, 1) * MAP_BLOCKSIZE); + //setLighting(node_min, node_max, 0xFF); + + this->generating = false; +} + + +void MapgenV7::calculateNoise() { + //TimeTaker t("calculateNoise", NULL, PRECISION_MICRO); + int x = node_min.X; + int y = node_min.Y; + int z = node_min.Z; + + noise_terrain_mod->perlinMap2D(x, z); + + noise_height_select->perlinMap2D(x, z); + noise_height_select->transformNoiseMap(); + + noise_terrain_persist->perlinMap2D(x, z); + float *persistmap = noise_terrain_persist->result; + for (int i = 0; i != csize.X * csize.Z; i++) + persistmap[i] = abs(persistmap[i]); + + noise_terrain_base->perlinMap2DModulated(x, z, persistmap); + noise_terrain_base->transformNoiseMap(); + + noise_terrain_alt->perlinMap2DModulated(x, z, persistmap); + noise_terrain_alt->transformNoiseMap(); + + noise_ridge->perlinMap3D(x, y, z); + + noise_heat->perlinMap2D(x, z); + + noise_humidity->perlinMap2D(x, z); + + //printf("calculateNoise: %dus\n", t.stop()); +} + + +Biome *MapgenV7::getBiomeAtPoint(v3s16 p) { + float heat = NoisePerlin2D(bmgr->np_heat, p.X, p.Z, seed); + float humidity = NoisePerlin2D(bmgr->np_humidity, p.X, p.Z, seed); + s16 groundlevel = baseTerrainLevelAtPoint(p.X, p.Z); + + return bmgr->getBiome(heat, humidity, groundlevel); +} + + +float MapgenV7::baseTerrainLevelAtPoint(int x, int z) { + float terrain_mod = NoisePerlin2DNoTxfm(noise_terrain_mod->np, x, z, seed); + float hselect = NoisePerlin2D(noise_height_select->np, x, z, seed); + float persist = abs(NoisePerlin2DNoTxfm(noise_terrain_persist->np, x, z, seed)); + + noise_terrain_base->np->persist = persist; + float terrain_base = NoisePerlin2D(noise_terrain_base->np, x, z, seed); + float height_base = terrain_base * terrain_mod; + + noise_terrain_alt->np->persist = persist; + float height_alt = NoisePerlin2D(noise_terrain_alt->np, x, z, seed); + + return (height_base * hselect) + (height_alt * (1.0 - hselect)); +} + + +float MapgenV7::baseTerrainLevelFromMap(int index) { + float terrain_mod = noise_terrain_mod->result[index]; + float hselect = noise_height_select->result[index]; + float terrain_base = noise_terrain_base->result[index]; + float height_base = terrain_base * terrain_mod; + float height_alt = noise_terrain_alt->result[index]; + + return (height_base * hselect) + (height_alt * (1.0 - hselect)); +} + + +#if 0 +// Crap code to test log rivers as a proof-of-concept. Didn't work out too well. +void MapgenV7::carveRivers() { + MapNode n_air(CONTENT_AIR), n_water_source(c_water_source); + MapNode n_stone(c_stone); + u32 index = 0; + + int river_depth = 4; + + for (s16 z = node_min.Z; z <= node_max.Z; z++) + for (s16 x = node_min.X; x <= node_max.X; x++, index++) { + float terrain_mod = noise_terrain_mod->result[index]; + NoiseParams *np = noise_terrain_river->np; + np->persist = noise_terrain_persist->result[index]; + float terrain_river = NoisePerlin2DNoTxfm(np, x, z, seed); + float height = terrain_river * (1 - abs(terrain_mod)) * + noise_terrain_river->np->scale; + height = log(height * height); //log(h^3) is pretty interesting for terrain + + s16 y = heightmap[index]; + if (height < 1.0 && y > river_depth && + y - river_depth >= node_min.Y && y <= node_max.Y) { + + for (s16 ry = y; ry != y - river_depth; ry--) { + u32 vi = vm->m_area.index(x, ry, z); + vm->m_data[vi] = n_air; + } + + u32 vi = vm->m_area.index(x, y - river_depth, z); + vm->m_data[vi] = n_water_source; + } + } +} +#endif + + +int MapgenV7::calcHeightMap() { + int stone_surface_max_y = -MAP_GENERATION_LIMIT; + u32 index = 0; + + for (s16 z = node_min.Z; z <= node_max.Z; z++) + for (s16 x = node_min.X; x <= node_max.X; x++, index++) { + float surface_height = baseTerrainLevelFromMap(index); + s16 surface_y = (s16)surface_height; + + heightmap[index] = surface_y; + ridge_heightmap[index] = surface_y; + + if (surface_y > stone_surface_max_y) + stone_surface_max_y = surface_y; + } + + return stone_surface_max_y; +} + + +void MapgenV7::generateTerrain() { + MapNode n_air(CONTENT_AIR), n_water_source(c_water_source); + MapNode n_stone(c_stone); + + v3s16 em = vm->m_area.getExtent(); + u32 index = 0; + + for (s16 z = node_min.Z; z <= node_max.Z; z++) + for (s16 x = node_min.X; x <= node_max.X; x++, index++) { + s16 surface_y = heightmap[index]; + Biome *biome = bmgr->biomes[biomemap[index]]; + + u32 i = vm->m_area.index(x, node_min.Y, z); + for (s16 y = node_min.Y; y <= node_max.Y; y++) { + if (vm->m_data[i].getContent() == CONTENT_IGNORE) { + if (y <= surface_y) { + vm->m_data[i] = (y > water_level + biome->filler_height) ? + MapNode(biome->c_filler) : n_stone; + } else if (y <= water_level) { + vm->m_data[i] = n_water_source; + } else { + vm->m_data[i] = n_air; + } + } + vm->m_area.add_y(em, i, 1); + } + } +} + + +void MapgenV7::carveRidges() { + if (node_max.Y <= water_level) + return; + + MapNode n_air(CONTENT_AIR); + u32 index = 0; + + for (s16 z = node_min.Z; z <= node_max.Z; z++) + for (s16 y = node_min.Y; y <= node_max.Y; y++) { + u32 vi = vm->m_area.index(node_min.X, y, z); + for (s16 x = node_min.X; x <= node_max.X; x++, index++, vi++) { + // Removing this check will create huge underwater caverns, + // which are interesting but not desirable for gameplay + if (y <= water_level) + continue; + + if (noise_ridge->result[index] * (float)(y * y) < 15.0) + continue; + + int j = (z - node_min.Z) * csize.Z + (x - node_min.X); //////obviously just temporary + if (y < ridge_heightmap[j]) + ridge_heightmap[j] = y - 1; + + vm->m_data[vi] = n_air; + } + } +} + +/* +void MapgenV7::testBiomes() { + u32 index = 0; + + for (s16 z = node_min.Z; z <= node_min.Z; z++) + for (s16 x = node_min.X; x <= node_min.X; x++) {; + Biome *b = bmgr->getBiome(heat, humidity, 0); + } + // make an 80x80 grid with axes heat/humidity as a voroni diagram for biomes + // clear out y space for it first with air + // use absolute positioning, each chunk will be a +1 height +}*/ + + +void MapgenV7::addTopNodes() { + v3s16 em = vm->m_area.getExtent(); + s16 ntopnodes; + u32 index = 0; + + for (s16 z = node_min.Z; z <= node_max.Z; z++) + for (s16 x = node_min.X; x <= node_max.X; x++, index++) { + // First, add top nodes below the ridge + s16 y = ridge_heightmap[index]; + + // This cutoff is good enough, but not perfect. + // It will cut off potentially placed top nodes at chunk boundaries + if (y < node_min.Y) + continue; + if (y > node_max.Y) { + y = node_max.Y; // Let's see if we can still go downward anyway + u32 vi = vm->m_area.index(x, y, z); + if (vm->m_data[vi].getContent() != CONTENT_AIR) + continue; + } + + // N.B. It is necessary to search downward since range_heightmap[i] + // might not be the actual height, just the lowest part in the chunk + // where a ridge had been carved + u32 i = vm->m_area.index(x, y, z); + for (; y >= node_min.Y; y--) { + if (vm->m_data[i].getContent() != CONTENT_AIR) + break; + vm->m_area.add_y(em, i, -1); + } + + Biome *biome = bmgr->biomes[biomemap[index]]; + + if (y != node_min.Y - 1) { + ridge_heightmap[index] = y; //update ridgeheight + ntopnodes = biome->top_depth; + for (; y <= node_max.Y && ntopnodes; y++) { + ntopnodes--; + vm->m_data[i] = MapNode(biome->c_top); + vm->m_area.add_y(em, i, 1); + } + //heightmap[index] = y; + } + + // Now, add top nodes on top of the ridge + y = heightmap[index]; + + i = vm->m_area.index(x, y, z); + for (; y >= node_min.Y; y--) { + if (vm->m_data[i].getContent() != CONTENT_AIR) + break; + vm->m_area.add_y(em, i, -1); + } + + if (y != node_min.Y - 1) { + ntopnodes = biome->top_depth; + // Let's see if we've already added it... + if (y == ridge_heightmap[index] + ntopnodes - 1) + continue; + + for (; y <= node_max.Y && ntopnodes; y++) { + ntopnodes--; + vm->m_data[i] = MapNode(biome->c_top); + vm->m_area.add_y(em, i, 1); + } + } + } +} + + +void MapgenV7::growGrass() { + for (s16 z = node_min.Z; z <= node_max.Z; z++) + for (s16 x = node_min.X; x <= node_max.X; x++) { + // Find the lowest surface to which enough light ends up to make + // grass grow. Basically just wait until not air and not leaves. + s16 surface_y = 0; + { + v3s16 em = vm->m_area.getExtent(); + u32 i = vm->m_area.index(x, node_max.Y, z); + s16 y; + // Go to ground level + for (y = node_max.Y; y >= node_min.Y; y--) { + MapNode &n = vm->m_data[i]; + if (ndef->get(n).param_type != CPT_LIGHT || + ndef->get(n).liquid_type != LIQUID_NONE) + break; + vm->m_area.add_y(em, i, -1); + } + surface_y = (y >= node_min.Y) ? y : node_min.Y; + } + + u32 i = vm->m_area.index(x, surface_y, z); + MapNode *n = &vm->m_data[i]; + if (n->getContent() == c_dirt && surface_y >= water_level - 20) + n->setContent(c_dirt_with_grass); + } +} diff --git a/src/mapgen_v7.h b/src/mapgen_v7.h new file mode 100644 index 000000000..73fa9be03 --- /dev/null +++ b/src/mapgen_v7.h @@ -0,0 +1,125 @@ +/* +Minetest +Copyright (C) 2010-2013 kwolekr, Ryan Kwolek <kwolekr@minetest.net> + +This program 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 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 Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser 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. +*/ + +#ifndef MAPGEN_V7_HEADER +#define MAPGEN_V7_HEADER + +#include "mapgen.h" + +extern NoiseParams nparams_v7_def_terrain_base; +extern NoiseParams nparams_v7_def_terrain_alt; +extern NoiseParams nparams_v7_def_terrain_mod; +extern NoiseParams nparams_v7_def_terrain_persist; +extern NoiseParams nparams_v7_def_height_select; +extern NoiseParams nparams_v7_def_ridge; + +struct MapgenV7Params : public MapgenParams { + NoiseParams *np_terrain_base; + NoiseParams *np_terrain_alt; + NoiseParams *np_terrain_mod; + NoiseParams *np_terrain_persist; + NoiseParams *np_height_select; + NoiseParams *np_ridge; + + MapgenV7Params() { + np_terrain_base = &nparams_v7_def_terrain_base; + np_terrain_alt = &nparams_v7_def_terrain_alt; + np_terrain_mod = &nparams_v7_def_terrain_mod; + np_terrain_persist = &nparams_v7_def_terrain_persist; + np_height_select = &nparams_v7_def_height_select; + np_ridge = &nparams_v7_def_ridge; + } + + bool readParams(Settings *settings); + void writeParams(Settings *settings); +}; + +class MapgenV7 : public Mapgen { +public: + EmergeManager *emerge; + BiomeDefManager *bmgr; + + int ystride; + v3s16 csize; + u32 flags; + + u32 blockseed; + v3s16 node_min; + v3s16 node_max; + v3s16 full_node_min; + v3s16 full_node_max; + + s16 *heightmap; + s16 *ridge_heightmap; + u8 *biomemap; + + Noise *noise_terrain_base; + Noise *noise_terrain_alt; + Noise *noise_terrain_mod; + Noise *noise_terrain_persist; + Noise *noise_height_select; + + Noise *noise_ridge; + + Noise *noise_heat; + Noise *noise_humidity; + + content_t c_stone; + content_t c_dirt; + content_t c_dirt_with_grass; + content_t c_sand; + content_t c_water_source; + content_t c_lava_source; + content_t c_gravel; + content_t c_cobble; + content_t c_desert_sand; + content_t c_desert_stone; + + MapgenV7(int mapgenid, MapgenV7Params *params, EmergeManager *emerge); + ~MapgenV7(); + + void makeChunk(BlockMakeData *data); + int getGroundLevelAtPoint(v2s16 p); + Biome *getBiomeAtPoint(v3s16 p); + + float baseTerrainLevelAtPoint(int x, int z); + float baseTerrainLevelFromMap(int index); + void calculateNoise(); + int calcHeightMap(); + + void generateTerrain(); + void carveRidges(); + //void carveRivers(); //experimental + + void testBiomes(); + void addTopNodes(); + void growGrass(); +}; + +struct MapgenFactoryV7 : public MapgenFactory { + Mapgen *createMapgen(int mgid, MapgenParams *params, EmergeManager *emerge) { + return new MapgenV7(mgid, (MapgenV7Params *)params, emerge); + }; + + MapgenParams *createMapgenParams() { + return new MapgenV7Params(); + }; +}; + +#endif diff --git a/src/nodedef.cpp b/src/nodedef.cpp index ca8898907..e2b72333f 100644 --- a/src/nodedef.cpp +++ b/src/nodedef.cpp @@ -593,7 +593,7 @@ public: { tiledef[j] = f->tiledef[j]; if(tiledef[j].name == "") - tiledef[j].name = "unknown_block.png"; + tiledef[j].name = "unknown_node.png"; } bool is_liquid = false; diff --git a/src/noise.cpp b/src/noise.cpp index 49b5f7e58..5788a8320 100644 --- a/src/noise.cpp +++ b/src/noise.cpp @@ -1,6 +1,7 @@ /* Minetest Copyright (C) 2010-2013 celeron55, Perttu Ahola <celeron55@gmail.com> +Copyright (C) 2010-2013 kwolekr, Ryan Kwolek <kwolekr@minetest.net> This program is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by @@ -536,6 +537,41 @@ float *Noise::perlinMap2D(float x, float y) { } +float *Noise::perlinMap2DModulated(float x, float y, float *persist_map) { + float f = 1.0; + int i, j, index, oct; + + x /= np->spread.X; + y /= np->spread.Y; + + memset(result, 0, sizeof(float) * sx * sy); + + float *g = new float[sx * sy]; + for (index = 0; index != sx * sy; index++) + g[index] = 1.0; + + for (oct = 0; oct < np->octaves; oct++) { + gradientMap2D(x * f, y * f, + f / np->spread.X, f / np->spread.Y, + seed + np->seed + oct); + + index = 0; + for (j = 0; j != sy; j++) { + for (i = 0; i != sx; i++) { + result[index] += g[index] * buf[index]; + g[index] *= persist_map[index]; + index++; + } + } + + f *= 2.0; + } + + delete[] g; + return result; +} + + float *Noise::perlinMap3D(float x, float y, float z) { float f = 1.0, g = 1.0; int i, j, k, index, oct; diff --git a/src/noise.h b/src/noise.h index 34b4d0b41..ace6d7eb4 100644 --- a/src/noise.h +++ b/src/noise.h @@ -1,6 +1,7 @@ /* Minetest Copyright (C) 2010-2013 celeron55, Perttu Ahola <celeron55@gmail.com> +Copyright (C) 2010-2013 kwolekr, Ryan Kwolek <kwolekr@minetest.net> This program is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by @@ -105,6 +106,7 @@ public: float step_x, float step_y, float step_z, int seed); float *perlinMap2D(float x, float y); + float *perlinMap2DModulated(float x, float y, float *persist_map); float *perlinMap3D(float x, float y, float z); void transformNoiseMap(); }; diff --git a/src/pathfinder.cpp b/src/pathfinder.cpp new file mode 100644 index 000000000..c7621177e --- /dev/null +++ b/src/pathfinder.cpp @@ -0,0 +1,1081 @@ +/* +Minetest +Copyright (C) 2013 sapier, sapier at gmx dot net + +This program 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 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 Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser 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. +*/ + +/******************************************************************************/ +/* Includes */ +/******************************************************************************/ + +#include "pathfinder.h" + +#ifdef PATHFINDER_DEBUG +#include <iomanip> +#endif +#ifdef PATHFINDER_CALC_TIME + #include <sys/time.h> +#endif + +/******************************************************************************/ +/* Typedefs and macros */ +/******************************************************************************/ + +//#define PATHFINDER_CALC_TIME + +/** shortcut to print a 3d pos */ +#define PPOS(pos) "(" << pos.X << "," << pos.Y << "," << pos.Z << ")" + +#define LVL "(" << level << ")" << + +#ifdef PATHFINDER_DEBUG +#define DEBUG_OUT(a) std::cout << a +#define INFO_TARGET std::cout +#define VERBOSE_TARGET std::cout +#define ERROR_TARGET std::cout +#else +#define DEBUG_OUT(a) while(0) +#define INFO_TARGET infostream +#define VERBOSE_TARGET verbosestream +#define ERROR_TARGET errorstream +#endif + +/******************************************************************************/ +/* implementation */ +/******************************************************************************/ + +std::vector<v3s16> get_Path(ServerEnvironment* env, + v3s16 source, + v3s16 destination, + unsigned int searchdistance, + unsigned int max_jump, + unsigned int max_drop, + algorithm algo) { + + pathfinder searchclass; + + return searchclass.get_Path(env, + source,destination, + searchdistance,max_jump,max_drop,algo); +} + +/******************************************************************************/ +path_cost::path_cost() +: valid(false), + value(0), + direction(0), + updated(false) +{ + //intentionaly empty +} + +/******************************************************************************/ +path_cost::path_cost(const path_cost& b) { + valid = b.valid; + direction = b.direction; + value = b.value; + updated = b.updated; +} + +/******************************************************************************/ +path_cost& path_cost::operator= (const path_cost& b) { + valid = b.valid; + direction = b.direction; + value = b.value; + updated = b.updated; + + return *this; +} + +/******************************************************************************/ +path_gridnode::path_gridnode() +: valid(false), + target(false), + source(false), + totalcost(-1), + sourcedir(v3s16(0,0,0)), + surfaces(0), + pos(v3s16(0,0,0)), + is_element(false), + type('u') +{ + //intentionaly empty +} + +/******************************************************************************/ +path_gridnode::path_gridnode(const path_gridnode& b) +: valid(b.valid), + target(b.target), + source(b.source), + totalcost(b.totalcost), + sourcedir(b.sourcedir), + surfaces(b.surfaces), + pos(b.pos), + is_element(b.is_element), + type(b.type) + { + + directions[DIR_XP] = b.directions[DIR_XP]; + directions[DIR_XM] = b.directions[DIR_XM]; + directions[DIR_ZP] = b.directions[DIR_ZP]; + directions[DIR_ZM] = b.directions[DIR_ZM]; +} + +/******************************************************************************/ +path_gridnode& path_gridnode::operator= (const path_gridnode& b) { + valid = b.valid; + target = b.target; + source = b.source; + is_element = b.is_element; + totalcost = b.totalcost; + sourcedir = b.sourcedir; + surfaces = b.surfaces; + pos = b.pos; + type = b.type; + + directions[DIR_XP] = b.directions[DIR_XP]; + directions[DIR_XM] = b.directions[DIR_XM]; + directions[DIR_ZP] = b.directions[DIR_ZP]; + directions[DIR_ZM] = b.directions[DIR_ZM]; + + return *this; +} + +/******************************************************************************/ +path_cost path_gridnode::get_cost(v3s16 dir) { + if (dir.X > 0) { + return directions[DIR_XP]; + } + if (dir.X < 0) { + return directions[DIR_XM]; + } + if (dir.Z > 0) { + return directions[DIR_ZP]; + } + if (dir.Z < 0) { + return directions[DIR_ZM]; + } + path_cost retval; + return retval; +} + +/******************************************************************************/ +void path_gridnode::set_cost(v3s16 dir,path_cost cost) { + if (dir.X > 0) { + directions[DIR_XP] = cost; + } + if (dir.X < 0) { + directions[DIR_XM] = cost; + } + if (dir.Z > 0) { + directions[DIR_ZP] = cost; + } + if (dir.Z < 0) { + directions[DIR_ZM] = cost; + } +} + +/******************************************************************************/ +std::vector<v3s16> pathfinder::get_Path(ServerEnvironment* env, + v3s16 source, + v3s16 destination, + unsigned int searchdistance, + unsigned int max_jump, + unsigned int max_drop, + algorithm algo) { +#ifdef PATHFINDER_CALC_TIME + timespec ts; + clock_gettime(CLOCK_REALTIME, &ts); +#endif + std::vector<v3s16> retval; + + //check parameters + if (env == 0) { + std::cout << "missing environment pointer" << std::endl; + return retval; + } + + m_searchdistance = searchdistance; + m_env = env; + m_maxjump = max_jump; + m_maxdrop = max_drop; + m_start = source; + m_destination = destination; + m_min_target_distance = -1; + m_prefetch = true; + + if (algo == A_PLAIN_NP) { + m_prefetch = false; + } + + int min_x = MYMIN(source.X,destination.X); + int max_x = MYMAX(source.X,destination.X); + + int min_y = MYMIN(source.Y,destination.Y); + int max_y = MYMAX(source.Y,destination.Y); + + int min_z = MYMIN(source.Z,destination.Z); + int max_z = MYMAX(source.Z,destination.Z); + + m_limits.X.min = min_x - searchdistance; + m_limits.X.max = max_x + searchdistance; + m_limits.Y.min = min_y - searchdistance; + m_limits.Y.max = max_y + searchdistance; + m_limits.Z.min = min_z - searchdistance; + m_limits.Z.max = max_z + searchdistance; + + m_max_index_x = m_limits.X.max - m_limits.X.min; + m_max_index_y = m_limits.Y.max - m_limits.Y.min; + m_max_index_z = m_limits.Z.max - m_limits.Z.min; + + //build data map + if (!build_costmap()) { + std::cout << "failed to build costmap" << std::endl; + return retval; + } +#ifdef PATHFINDER_DEBUG + print_type(); + print_cost(); + print_ydir(); +#endif + + //validate and mark start and end pos + v3s16 StartIndex = getIndexPos(source); + v3s16 EndIndex = getIndexPos(destination); + + path_gridnode& startpos = getIndexElement(StartIndex); + path_gridnode& endpos = getIndexElement(EndIndex); + + if (!startpos.valid) { + std::cout << "invalid startpos" << + "Index: " << PPOS(StartIndex) << + "Realpos: " << PPOS(getRealPos(StartIndex)) << std::endl; + return retval; + } + if (!endpos.valid) { + std::cout << "invalid stoppos" << + "Index: " << PPOS(EndIndex) << + "Realpos: " << PPOS(getRealPos(EndIndex)) << std::endl; + return retval; + } + + endpos.target = true; + startpos.source = true; + startpos.totalcost = 0; + + bool update_cost_retval = false; + + switch (algo) { + case DIJKSTRA: + update_cost_retval = update_all_costs(StartIndex,v3s16(0,0,0),0,0); + break; + case A_PLAIN_NP: + case A_PLAIN: + update_cost_retval = update_cost_heuristic(StartIndex,v3s16(0,0,0),0,0); + break; + default: + std::cout << "missing algorithm"<< std::endl; + break; + } + + if (update_cost_retval) { + +#ifdef PATHFINDER_DEBUG + std::cout << "Path to target found!" << std::endl; + print_pathlen(); +#endif + + //find path + std::vector<v3s16> path; + build_path(path,EndIndex,0); + +#ifdef PATHFINDER_DEBUG + std::cout << "Full index path:" << std::endl; + print_path(path); +#endif + + //optimize path + std::vector<v3s16> optimized_path; + + std::vector<v3s16>::iterator startpos = path.begin(); + optimized_path.push_back(source); + + for (std::vector<v3s16>::iterator i = path.begin(); + i != path.end(); i++) { + if (!m_env->line_of_sight( + tov3f(getIndexElement(*startpos).pos), + tov3f(getIndexElement(*i).pos))) { + optimized_path.push_back(getIndexElement(*(i-1)).pos); + startpos = (i-1); + } + } + + optimized_path.push_back(destination); + +#ifdef PATHFINDER_DEBUG + std::cout << "Optimized path:" << std::endl; + print_path(optimized_path); +#endif +#ifdef PATHFINDER_CALC_TIME + timespec ts2; + clock_gettime(CLOCK_REALTIME, &ts2); + + int ms = (ts2.tv_nsec - ts.tv_nsec)/(1000*1000); + int us = ((ts2.tv_nsec - ts.tv_nsec) - (ms*1000*1000))/1000; + int ns = ((ts2.tv_nsec - ts.tv_nsec) - ( (ms*1000*1000) + (us*1000))); + + + std::cout << "Calculating path took: " << (ts2.tv_sec - ts.tv_sec) << + "s " << ms << "ms " << us << "us " << ns << "ns " << std::endl; +#endif + return optimized_path; + } + else { +#ifdef PATHFINDER_DEBUG + print_pathlen(); +#endif + std::cout << "failed to update cost map"<< std::endl; + } + + + //return + return retval; +} + +/******************************************************************************/ +pathfinder::pathfinder() : + m_max_index_x(0), + m_max_index_y(0), + m_max_index_z(0), + m_searchdistance(0), + m_maxdrop(0), + m_maxjump(0), + m_min_target_distance(0), + m_prefetch(true), + m_start(0,0,0), + m_destination(0,0,0), + m_limits(), + m_data(), + m_env(0) +{ + //intentionaly empty +} + +/******************************************************************************/ +v3s16 pathfinder::getRealPos(v3s16 ipos) { + + v3s16 retval = ipos; + + retval.X += m_limits.X.min; + retval.Y += m_limits.Y.min; + retval.Z += m_limits.Z.min; + + return retval; +} + +/******************************************************************************/ +bool pathfinder::build_costmap() +{ + INFO_TARGET << "Pathfinder build costmap: (" << m_limits.X.min << "," + << m_limits.Z.min << ") (" + << m_limits.X.max << "," + << m_limits.Z.max << ")" + << std::endl; + m_data.resize(m_max_index_x); + for (int x = 0; x < m_max_index_x; x++) { + m_data[x].resize(m_max_index_z); + for (int z = 0; z < m_max_index_z; z++) { + m_data[x][z].resize(m_max_index_y); + + int surfaces = 0; + for (int y = 0; y < m_max_index_y; y++) { + v3s16 ipos(x,y,z); + + v3s16 realpos = getRealPos(ipos); + + MapNode current = m_env->getMap().getNodeNoEx(realpos); + MapNode below = m_env->getMap().getNodeNoEx(realpos + v3s16(0,-1,0)); + + + if ((current.param0 == CONTENT_IGNORE) || + (below.param0 == CONTENT_IGNORE)) { + DEBUG_OUT("Pathfinder: " << PPOS(realpos) << + " current or below is invalid element" << std::endl); + if (current.param0 == CONTENT_IGNORE) { + m_data[x][z][y].type = 'i'; + DEBUG_OUT(x << "," << y << "," << z << ": " << 'i' << std::endl); + } + continue; + } + + //don't add anything if it isn't an air node + if ((current.param0 != CONTENT_AIR) || + (below.param0 == CONTENT_AIR )) { + DEBUG_OUT("Pathfinder: " << PPOS(realpos) + << " not on surface" << std::endl); + if (current.param0 != CONTENT_AIR) { + m_data[x][z][y].type = 's'; + DEBUG_OUT(x << "," << y << "," << z << ": " << 's' << std::endl); + } + else { + m_data[x][z][y].type = '-'; + DEBUG_OUT(x << "," << y << "," << z << ": " << '-' << std::endl); + } + continue; + } + + surfaces++; + + m_data[x][z][y].valid = true; + m_data[x][z][y].pos = realpos; + m_data[x][z][y].type = 'g'; + DEBUG_OUT(x << "," << y << "," << z << ": " << 'a' << std::endl); + + if (m_prefetch) { + m_data[x][z][y].directions[DIR_XP] = + calc_cost(realpos,v3s16( 1,0, 0)); + m_data[x][z][y].directions[DIR_XM] = + calc_cost(realpos,v3s16(-1,0, 0)); + m_data[x][z][y].directions[DIR_ZP] = + calc_cost(realpos,v3s16( 0,0, 1)); + m_data[x][z][y].directions[DIR_ZM] = + calc_cost(realpos,v3s16( 0,0,-1)); + } + + } + + if (surfaces >= 1 ) { + for (int y = 0; y < m_max_index_y; y++) { + if (m_data[x][z][y].valid) { + m_data[x][z][y].surfaces = surfaces; + } + } + } + } + } + return true; +} + +/******************************************************************************/ +path_cost pathfinder::calc_cost(v3s16 pos,v3s16 dir) { + path_cost retval; + + retval.updated = true; + + v3s16 pos2 = pos + dir; + + //check limits + if ( (pos2.X < m_limits.X.min) || + (pos2.X >= m_limits.X.max) || + (pos2.Z < m_limits.Z.min) || + (pos2.Z >= m_limits.Z.max)) { + DEBUG_OUT("Pathfinder: " << PPOS(pos2) << + " no cost -> out of limits" << std::endl); + return retval; + } + + MapNode node_at_pos2 = m_env->getMap().getNodeNoEx(pos2); + + //did we get information about node? + if (node_at_pos2.param0 == CONTENT_IGNORE ) { + VERBOSE_TARGET << "Pathfinder: (1) area at pos: " + << PPOS(pos2) << " not loaded"; + return retval; + } + + if (node_at_pos2.param0 == CONTENT_AIR) { + MapNode node_below_pos2 = + m_env->getMap().getNodeNoEx(pos2 + v3s16(0,-1,0)); + + //did we get information about node? + if (node_below_pos2.param0 == CONTENT_IGNORE ) { + VERBOSE_TARGET << "Pathfinder: (2) area at pos: " + << PPOS((pos2 + v3s16(0,-1,0))) << " not loaded"; + return retval; + } + + if (node_below_pos2.param0 != CONTENT_AIR) { + retval.valid = true; + retval.value = 1; + retval.direction = 0; + DEBUG_OUT("Pathfinder: "<< PPOS(pos) + << " cost same height found" << std::endl); + } + else { + v3s16 testpos = pos2 - v3s16(0,-1,0); + MapNode node_at_pos = m_env->getMap().getNodeNoEx(testpos); + + while ((node_at_pos.param0 != CONTENT_IGNORE) && + (node_at_pos.param0 == CONTENT_AIR) && + (testpos.Y > m_limits.Y.min)) { + testpos += v3s16(0,-1,0); + node_at_pos = m_env->getMap().getNodeNoEx(testpos); + } + + //did we find surface? + if ((testpos.Y >= m_limits.Y.min) && + (node_at_pos.param0 != CONTENT_IGNORE) && + (node_at_pos.param0 != CONTENT_AIR)) { + if (((pos2.Y - testpos.Y)*-1) <= m_maxdrop) { + retval.valid = true; + retval.value = 2; + //difference of y-pos +1 (target node is ABOVE solid node) + retval.direction = ((testpos.Y - pos2.Y) +1); + DEBUG_OUT("Pathfinder cost below height found" << std::endl); + } + else { + INFO_TARGET << "Pathfinder:" + " distance to surface below to big: " + << (testpos.Y - pos2.Y) << " max: " << m_maxdrop + << std::endl; + } + } + else { + DEBUG_OUT("Pathfinder: no surface below found" << std::endl); + } + } + } + else { + v3s16 testpos = pos2; + MapNode node_at_pos = m_env->getMap().getNodeNoEx(testpos); + + while ((node_at_pos.param0 != CONTENT_IGNORE) && + (node_at_pos.param0 != CONTENT_AIR) && + (testpos.Y < m_limits.Y.max)) { + testpos += v3s16(0,1,0); + node_at_pos = m_env->getMap().getNodeNoEx(testpos); + } + + //did we find surface? + if ((testpos.Y <= m_limits.Y.max) && + (node_at_pos.param0 == CONTENT_AIR)) { + + if (testpos.Y - pos2.Y <= m_maxjump) { + retval.valid = true; + retval.value = 2; + retval.direction = (testpos.Y - pos2.Y); + DEBUG_OUT("Pathfinder cost above found" << std::endl); + } + else { + DEBUG_OUT("Pathfinder: distance to surface above to big: " + << (testpos.Y - pos2.Y) << " max: " << m_maxjump + << std::endl); + } + } + else { + DEBUG_OUT("Pathfinder: no surface above found" << std::endl); + } + } + return retval; +} + +/******************************************************************************/ +v3s16 pathfinder::getIndexPos(v3s16 pos) { + + v3s16 retval = pos; + retval.X -= m_limits.X.min; + retval.Y -= m_limits.Y.min; + retval.Z -= m_limits.Z.min; + + return retval; +} + +/******************************************************************************/ +path_gridnode& pathfinder::getIndexElement(v3s16 ipos) { + return m_data[ipos.X][ipos.Z][ipos.Y]; +} + +/******************************************************************************/ +bool pathfinder::valid_index(v3s16 index) { + if ( (index.X < m_max_index_x) && + (index.Y < m_max_index_y) && + (index.Z < m_max_index_z) && + (index.X >= 0) && + (index.Y >= 0) && + (index.Z >= 0)) + return true; + + return false; +} + +/******************************************************************************/ +v3s16 pathfinder::invert(v3s16 pos) { + v3s16 retval = pos; + + retval.X *=-1; + retval.Y *=-1; + retval.Z *=-1; + + return retval; +} + +/******************************************************************************/ +bool pathfinder::update_all_costs( v3s16 ipos, + v3s16 srcdir, + int current_cost, + int level) { + + path_gridnode& g_pos = getIndexElement(ipos); + g_pos.totalcost = current_cost; + g_pos.sourcedir = srcdir; + + level ++; + + //check if target has been found + if (g_pos.target) { + m_min_target_distance = current_cost; + DEBUG_OUT(LVL " Pathfinder: target found!" << std::endl); + return true; + } + + bool retval = false; + + std::vector<v3s16> directions; + + directions.push_back(v3s16( 1,0, 0)); + directions.push_back(v3s16(-1,0, 0)); + directions.push_back(v3s16( 0,0, 1)); + directions.push_back(v3s16( 0,0,-1)); + + for (unsigned int i=0; i < directions.size(); i++) { + if (directions[i] != srcdir) { + path_cost cost = g_pos.get_cost(directions[i]); + + if (cost.valid) { + directions[i].Y = cost.direction; + + v3s16 ipos2 = ipos + directions[i]; + + if (!valid_index(ipos2)) { + DEBUG_OUT(LVL " Pathfinder: " << PPOS(ipos2) << + " out of range (" << m_limits.X.max << "," << + m_limits.Y.max << "," << m_limits.Z.max + <<")" << std::endl); + continue; + } + + path_gridnode& g_pos2 = getIndexElement(ipos2); + + if (!g_pos2.valid) { + VERBOSE_TARGET << LVL "Pathfinder: no data for new position: " + << PPOS(ipos2) << std::endl; + continue; + } + + assert(cost.value > 0); + + int new_cost = current_cost + cost.value; + + // check if there already is a smaller path + if ((m_min_target_distance > 0) && + (m_min_target_distance < new_cost)) { + return false; + } + + if ((g_pos2.totalcost < 0) || + (g_pos2.totalcost > new_cost)) { + int old_cost = g_pos2.totalcost; + DEBUG_OUT(LVL "Pathfinder: updating path at: "<< + PPOS(ipos2) << " from: " << old_cost << " to "<< + new_cost << std::endl); + if (update_all_costs(ipos2,invert(directions[i]), + new_cost,level)) { + retval = true; + } + } + else { + DEBUG_OUT(LVL "Pathfinder:" + " already found shorter path to: " + << PPOS(ipos2) << std::endl); + } + } + else { + DEBUG_OUT(LVL "Pathfinder:" + " not moving to invalid direction: " + << PPOS(directions[i]) << std::endl); + } + } + } + return retval; +} + +/******************************************************************************/ +int pathfinder::get_manhattandistance(v3s16 pos) { + + int min_x = MYMIN(pos.X,m_destination.X); + int max_x = MYMAX(pos.X,m_destination.X); + int min_z = MYMIN(pos.Z,m_destination.Z); + int max_z = MYMAX(pos.Z,m_destination.Z); + + return (max_x - min_x) + (max_z - min_z); +} + +/******************************************************************************/ +v3s16 pathfinder::get_dir_heuristic(std::vector<v3s16>& directions,path_gridnode& g_pos) { + int minscore = -1; + v3s16 retdir = v3s16(0,0,0); + v3s16 srcpos = g_pos.pos; + DEBUG_OUT("Pathfinder: remaining dirs at beginning:" + << directions.size() << std::endl); + + for (std::vector<v3s16>::iterator iter = directions.begin(); + iter != directions.end(); + iter ++) { + + v3s16 pos1 = v3s16(srcpos.X + iter->X,0,srcpos.Z+iter->Z); + + int cur_manhattan = get_manhattandistance(pos1); + path_cost cost = g_pos.get_cost(*iter); + + if (!cost.updated) { + cost = calc_cost(g_pos.pos,*iter); + g_pos.set_cost(*iter,cost); + } + + if (cost.valid) { + int score = cost.value + cur_manhattan; + + if ((minscore < 0)|| (score < minscore)) { + minscore = score; + retdir = *iter; + } + } + } + + if (retdir != v3s16(0,0,0)) { + for (std::vector<v3s16>::iterator iter = directions.begin(); + iter != directions.end(); + iter ++) { + if(*iter == retdir) { + DEBUG_OUT("Pathfinder: removing return direction" << std::endl); + directions.erase(iter); + break; + } + } + } + else { + DEBUG_OUT("Pathfinder: didn't find any valid direction clearing" + << std::endl); + directions.clear(); + } + DEBUG_OUT("Pathfinder: remaining dirs at end:" << directions.size() + << std::endl); + return retdir; +} + +/******************************************************************************/ +bool pathfinder::update_cost_heuristic( v3s16 ipos, + v3s16 srcdir, + int current_cost, + int level) { + + path_gridnode& g_pos = getIndexElement(ipos); + g_pos.totalcost = current_cost; + g_pos.sourcedir = srcdir; + + level ++; + + //check if target has been found + if (g_pos.target) { + m_min_target_distance = current_cost; + DEBUG_OUT(LVL " Pathfinder: target found!" << std::endl); + return true; + } + + bool retval = false; + + std::vector<v3s16> directions; + + directions.push_back(v3s16( 1,0, 0)); + directions.push_back(v3s16(-1,0, 0)); + directions.push_back(v3s16( 0,0, 1)); + directions.push_back(v3s16( 0,0,-1)); + + v3s16 direction = get_dir_heuristic(directions,g_pos); + + while (direction != v3s16(0,0,0) && (!retval)) { + + if (direction != srcdir) { + path_cost cost = g_pos.get_cost(direction); + + if (cost.valid) { + direction.Y = cost.direction; + + v3s16 ipos2 = ipos + direction; + + if (!valid_index(ipos2)) { + DEBUG_OUT(LVL " Pathfinder: " << PPOS(ipos2) << + " out of range (" << m_limits.X.max << "," << + m_limits.Y.max << "," << m_limits.Z.max + <<")" << std::endl); + continue; + } + + path_gridnode& g_pos2 = getIndexElement(ipos2); + + if (!g_pos2.valid) { + VERBOSE_TARGET << LVL "Pathfinder: no data for new position: " + << PPOS(ipos2) << std::endl; + continue; + } + + assert(cost.value > 0); + + int new_cost = current_cost + cost.value; + + // check if there already is a smaller path + if ((m_min_target_distance > 0) && + (m_min_target_distance < new_cost)) { + DEBUG_OUT(LVL "Pathfinder:" + " already longer than best already found path " + << PPOS(ipos2) << std::endl); + return false; + } + + if ((g_pos2.totalcost < 0) || + (g_pos2.totalcost > new_cost)) { + int old_cost = g_pos2.totalcost; + DEBUG_OUT(LVL "Pathfinder: updating path at: "<< + PPOS(ipos2) << " from: " << old_cost << " to "<< + new_cost << " srcdir=" << + PPOS(invert(direction))<< std::endl); + if (update_cost_heuristic(ipos2,invert(direction), + new_cost,level)) { + retval = true; + } + } + else { + DEBUG_OUT(LVL "Pathfinder:" + " already found shorter path to: " + << PPOS(ipos2) << std::endl); + } + } + else { + DEBUG_OUT(LVL "Pathfinder:" + " not moving to invalid direction: " + << PPOS(direction) << std::endl); + } + } + else { + DEBUG_OUT(LVL "Pathfinder:" + " skipping srcdir: " + << PPOS(direction) << std::endl); + } + direction = get_dir_heuristic(directions,g_pos); + } + return retval; +} + +/******************************************************************************/ +void pathfinder::build_path(std::vector<v3s16>& path,v3s16 pos, int level) { + level ++; + if (level > 1000) { + ERROR_TARGET + << LVL "Pathfinder: path is too long aborting" << std::endl; + return; + } + + path_gridnode& g_pos = getIndexElement(pos); + if (!g_pos.valid) { + ERROR_TARGET + << LVL "Pathfinder: invalid next pos detected aborting" << std::endl; + return; + } + + g_pos.is_element = true; + + //check if source reached + if (g_pos.source) { + path.push_back(pos); + return; + } + + build_path(path,pos + g_pos.sourcedir,level); + path.push_back(pos); +} + +/******************************************************************************/ +v3f pathfinder::tov3f(v3s16 pos) { + return v3f(BS*pos.X,BS*pos.Y,BS*pos.Z); +} + +#ifdef PATHFINDER_DEBUG + +/******************************************************************************/ +void pathfinder::print_cost() { + print_cost(DIR_XP); + print_cost(DIR_XM); + print_cost(DIR_ZP); + print_cost(DIR_ZM); +} + +/******************************************************************************/ +void pathfinder::print_ydir() { + print_ydir(DIR_XP); + print_ydir(DIR_XM); + print_ydir(DIR_ZP); + print_ydir(DIR_ZM); +} + +/******************************************************************************/ +void pathfinder::print_cost(path_directions dir) { + + std::cout << "Cost in direction: " << dir_to_name(dir) << std::endl; + std::cout << std::setfill('-') << std::setw(80) << "-" << std::endl; + std::cout << std::setfill(' '); + for (int y = 0; y < m_max_index_y; y++) { + + std::cout << "Level: " << y << std::endl; + + std::cout << std::setw(4) << " " << " "; + for (int x = 0; x < m_max_index_x; x++) { + std::cout << std::setw(4) << x; + } + std::cout << std::endl; + + for (int z = 0; z < m_max_index_z; z++) { + std::cout << std::setw(4) << z <<": "; + for (int x = 0; x < m_max_index_x; x++) { + if (m_data[x][z][y].directions[dir].valid) + std::cout << std::setw(4) + << m_data[x][z][y].directions[dir].value; + else + std::cout << std::setw(4) << "-"; + } + std::cout << std::endl; + } + std::cout << std::endl; + } +} + +/******************************************************************************/ +void pathfinder::print_ydir(path_directions dir) { + + std::cout << "Height difference in direction: " << dir_to_name(dir) << std::endl; + std::cout << std::setfill('-') << std::setw(80) << "-" << std::endl; + std::cout << std::setfill(' '); + for (int y = 0; y < m_max_index_y; y++) { + + std::cout << "Level: " << y << std::endl; + + std::cout << std::setw(4) << " " << " "; + for (int x = 0; x < m_max_index_x; x++) { + std::cout << std::setw(4) << x; + } + std::cout << std::endl; + + for (int z = 0; z < m_max_index_z; z++) { + std::cout << std::setw(4) << z <<": "; + for (int x = 0; x < m_max_index_x; x++) { + if (m_data[x][z][y].directions[dir].valid) + std::cout << std::setw(4) + << m_data[x][z][y].directions[dir].direction; + else + std::cout << std::setw(4) << "-"; + } + std::cout << std::endl; + } + std::cout << std::endl; + } +} + +/******************************************************************************/ +void pathfinder::print_type() { + std::cout << "Type of node:" << std::endl; + std::cout << std::setfill('-') << std::setw(80) << "-" << std::endl; + std::cout << std::setfill(' '); + for (int y = 0; y < m_max_index_y; y++) { + + std::cout << "Level: " << y << std::endl; + + std::cout << std::setw(3) << " " << " "; + for (int x = 0; x < m_max_index_x; x++) { + std::cout << std::setw(3) << x; + } + std::cout << std::endl; + + for (int z = 0; z < m_max_index_z; z++) { + std::cout << std::setw(3) << z <<": "; + for (int x = 0; x < m_max_index_x; x++) { + char toshow = m_data[x][z][y].type; + std::cout << std::setw(3) << toshow; + } + std::cout << std::endl; + } + std::cout << std::endl; + } + std::cout << std::endl; +} + +/******************************************************************************/ +void pathfinder::print_pathlen() { + std::cout << "Pathlen:" << std::endl; + std::cout << std::setfill('-') << std::setw(80) << "-" << std::endl; + std::cout << std::setfill(' '); + for (int y = 0; y < m_max_index_y; y++) { + + std::cout << "Level: " << y << std::endl; + + std::cout << std::setw(3) << " " << " "; + for (int x = 0; x < m_max_index_x; x++) { + std::cout << std::setw(3) << x; + } + std::cout << std::endl; + + for (int z = 0; z < m_max_index_z; z++) { + std::cout << std::setw(3) << z <<": "; + for (int x = 0; x < m_max_index_x; x++) { + std::cout << std::setw(3) << m_data[x][z][y].totalcost; + } + std::cout << std::endl; + } + std::cout << std::endl; + } + std::cout << std::endl; +} + +/******************************************************************************/ +std::string pathfinder::dir_to_name(path_directions dir) { + switch (dir) { + case DIR_XP: + return "XP"; + break; + case DIR_XM: + return "XM"; + break; + case DIR_ZP: + return "ZP"; + break; + case DIR_ZM: + return "ZM"; + break; + default: + return "UKN"; + } +} + +/******************************************************************************/ +void pathfinder::print_path(std::vector<v3s16> path) { + + unsigned int current = 0; + for (std::vector<v3s16>::iterator i = path.begin(); + i != path.end(); i++) { + std::cout << std::setw(3) << current << ":" << PPOS((*i)) << std::endl; + current++; + } +} + +#endif diff --git a/src/pathfinder.h b/src/pathfinder.h new file mode 100644 index 000000000..7caf5844e --- /dev/null +++ b/src/pathfinder.h @@ -0,0 +1,345 @@ +/* +Minetest +Copyright (C) 2013 sapier, sapier at gmx dot net + +This program 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 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 Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser 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. +*/ + +#ifndef PATHFINDER_H_ +#define PATHFINDER_H_ + +/******************************************************************************/ +/* Includes */ +/******************************************************************************/ +#include <vector> + +#include "server.h" +#include "irr_v3d.h" + + +/******************************************************************************/ +/* Typedefs and macros */ +/******************************************************************************/ + +//#define PATHFINDER_DEBUG + +typedef enum { + DIR_XP, + DIR_XM, + DIR_ZP, + DIR_ZM +} path_directions; + +/** List of supported algorithms */ +typedef enum { + DIJKSTRA, /**< Dijkstra shortest path algorithm */ + A_PLAIN, /**< A* algorithm using heuristics to find a path */ + A_PLAIN_NP /**< A* algorithm without prefetching of map data */ +} algorithm; + +/******************************************************************************/ +/* declarations */ +/******************************************************************************/ + +/** c wrapper function to use from scriptapi */ +std::vector<v3s16> get_Path(ServerEnvironment* env, + v3s16 source, + v3s16 destination, + unsigned int searchdistance, + unsigned int max_jump, + unsigned int max_drop, + algorithm algo); + +/** representation of cost in specific direction */ +class path_cost { +public: + + /** default constructor */ + path_cost(); + + /** copy constructor */ + path_cost(const path_cost& b); + + /** assignment operator */ + path_cost& operator= (const path_cost& b); + + bool valid; /**< movement is possible */ + int value; /**< cost of movement */ + int direction; /**< y-direction of movement */ + bool updated; /**< this cost has ben calculated */ + +}; + + +/** representation of a mapnode to be used for pathfinding */ +class path_gridnode { + +public: + /** default constructor */ + path_gridnode(); + + /** copy constructor */ + path_gridnode(const path_gridnode& b); + + /** + * assignment operator + * @param b node to copy + */ + path_gridnode& operator= (const path_gridnode& b); + + /** + * read cost in a specific direction + * @param dir direction of cost to fetch + */ + path_cost get_cost(v3s16 dir); + + /** + * set cost value for movement + * @param dir direction to set cost for + * @cost cost to set + */ + void set_cost(v3s16 dir,path_cost cost); + + bool valid; /**< node is on surface */ + bool target; /**< node is target position */ + bool source; /**< node is stating position */ + int totalcost; /**< cost to move here from starting point */ + v3s16 sourcedir; /**< origin of movement for current cost */ + int surfaces; /**< number of surfaces with same x,z value*/ + v3s16 pos; /**< real position of node */ + path_cost directions[4]; /**< cost in different directions */ + + /* debug values */ + bool is_element; /**< node is element of path detected */ + char type; /**< type of node */ +}; + +/** class doing pathfinding */ +class pathfinder { + +public: + /** + * default constructor + */ + pathfinder(); + + /** + * path evaluation function + * @param env environment to look for path + * @param source origin of path + * @param destination end position of path + * @param searchdistance maximum number of nodes to look in each direction + * @param max_jump maximum number of blocks a path may jump up + * @param max_drop maximum number of blocks a path may drop + * @param algo algorithm to use for finding a path + */ + std::vector<v3s16> get_Path(ServerEnvironment* env, + v3s16 source, + v3s16 destination, + unsigned int searchdistance, + unsigned int max_jump, + unsigned int max_drop, + algorithm algo); + +private: + /** data struct for storing internal information */ + struct limits { + struct limit { + int min; + int max; + }; + + limit X; + limit Y; + limit Z; + }; + + /* helper functions */ + + /** + * transform index pos to mappos + * @param ipos a index position + * @return map position + */ + v3s16 getRealPos(v3s16 ipos); + + /** + * transform mappos to index pos + * @param pos a real pos + * @return index position + */ + v3s16 getIndexPos(v3s16 pos); + + /** + * get gridnode at a specific index position + * @param ipos index position + * @return gridnode for index + */ + path_gridnode& getIndexElement(v3s16 ipos); + + /** + * invert a 3d position + * @param pos 3d position + * @return pos *-1 + */ + v3s16 invert(v3s16 pos); + + /** + * check if a index is within current search area + * @param index position to validate + * @return true/false + */ + bool valid_index(v3s16 index); + + /** + * translate position to float position + * @param pos integer position + * @return float position + */ + v3f tov3f(v3s16 pos); + + + /* algorithm functions */ + + /** + * calculate 2d manahttan distance to target + * @param pos position to calc distance + * @return integer distance + */ + int get_manhattandistance(v3s16 pos); + + /** + * get best direction based uppon heuristics + * @param directions list of unchecked directions + * @param g_pos mapnode to start from + * @return direction to check + */ + v3s16 get_dir_heuristic(std::vector<v3s16>& directions,path_gridnode& g_pos); + + /** + * build internal data representation of search area + * @return true/false if costmap creation was successfull + */ + bool build_costmap(); + + /** + * calculate cost of movement + * @param pos real world position to start movement + * @param dir direction to move to + * @return cost information + */ + path_cost calc_cost(v3s16 pos,v3s16 dir); + + /** + * recursive update whole search areas total cost information + * @param ipos position to check next + * @param srcdir positionc checked last time + * @param total_cost cost of moving to ipos + * @param level current recursion depth + * @return true/false path to destination has been found + */ + bool update_all_costs(v3s16 ipos,v3s16 srcdir,int total_cost,int level); + + /** + * recursive try to find a patrh to destionation + * @param ipos position to check next + * @param srcdir positionc checked last time + * @param total_cost cost of moving to ipos + * @param level current recursion depth + * @return true/false path to destination has been found + */ + bool update_cost_heuristic(v3s16 ipos,v3s16 srcdir,int current_cost,int level); + + /** + * recursive build a vector containing all nodes from source to destination + * @param path vector to add nodes to + * @param pos pos to check next + * @param level recursion depth + */ + void build_path(std::vector<v3s16>& path,v3s16 pos, int level); + + /* variables */ + int m_max_index_x; /**< max index of search area in x direction */ + int m_max_index_y; /**< max index of search area in y direction */ + int m_max_index_z; /**< max index of search area in z direction */ + + + int m_searchdistance; /**< max distance to search in each direction */ + int m_maxdrop; /**< maximum number of blocks a path may drop */ + int m_maxjump; /**< maximum number of blocks a path may jump */ + int m_min_target_distance; /**< current smalest path to target */ + + bool m_prefetch; /**< prefetch cost data */ + + v3s16 m_start; /**< source position */ + v3s16 m_destination; /**< destination position */ + + limits m_limits; /**< position limits in real map coordinates */ + + /** 3d grid containing all map data already collected and analyzed */ + std::vector<std::vector<std::vector<path_gridnode> > > m_data; + + ServerEnvironment* m_env; /**< minetest environment pointer */ + +#ifdef PATHFINDER_DEBUG + + /** + * print collected cost information + */ + void print_cost(); + + /** + * print collected cost information in a specific direction + * @param dir direction to print + */ + void print_cost(path_directions dir); + + /** + * print type of node as evaluated + */ + void print_type(); + + /** + * print pathlenght for all nodes in search area + */ + void print_pathlen(); + + /** + * print a path + * @param path path to show + */ + void print_path(std::vector<v3s16> path); + + /** + * print y direction for all movements + */ + void print_ydir(); + + /** + * print y direction for moving in a specific direction + * @param dir direction to show data + */ + void print_ydir(path_directions dir); + + /** + * helper function to translate a direction to speaking text + * @param dir direction to translate + * @return textual name of direction + */ + std::string dir_to_name(path_directions dir); +#endif +}; + +#endif /* PATHFINDER_H_ */ diff --git a/src/player.cpp b/src/player.cpp index 4c81887be..1ca9423b0 100644 --- a/src/player.cpp +++ b/src/player.cpp @@ -71,6 +71,11 @@ Player::Player(IGameDef *gamedef): movement_liquid_fluidity_smooth = 0.5 * BS; movement_liquid_sink = 10 * BS; movement_gravity = 9.81 * BS; + + // Movement overrides are multipliers and must be 1 by default + physics_override_speed = 1; + physics_override_jump = 1; + physics_override_gravity = 1; } Player::~Player() diff --git a/src/player.h b/src/player.h index 496c99532..d95e535ff 100644 --- a/src/player.h +++ b/src/player.h @@ -222,6 +222,10 @@ public: f32 movement_liquid_sink; f32 movement_gravity; + float physics_override_speed; + float physics_override_jump; + float physics_override_gravity; + u16 hp; float hurt_tilt_timer; diff --git a/src/scriptapi.cpp b/src/scriptapi.cpp index c372456d4..0227c6a19 100644 --- a/src/scriptapi.cpp +++ b/src/scriptapi.cpp @@ -616,76 +616,36 @@ static int l_get_server_status(lua_State *L) return 1; } -// register_biome_groups({frequencies}) -static int l_register_biome_groups(lua_State *L) -{ - luaL_checktype(L, 1, LUA_TTABLE); - int index = 1; - - BiomeDefManager *bmgr = get_server(L)->getBiomeDef(); - if (!bmgr) { - verbosestream << "register_biome_groups: BiomeDefManager not active" << std::endl; - return 0; - } - - lua_pushnil(L); - for (int i = 1; lua_next(L, index) != 0; i++) { - bmgr->addBiomeGroup(lua_tonumber(L, -1)); - lua_pop(L, 1); - } - lua_pop(L, 1); - - return 0; -} // register_biome({lots of stuff}) static int l_register_biome(lua_State *L) { - luaL_checktype(L, 1, LUA_TTABLE); - int index = 1, groupid; - std::string nodename; + int index = 1; + luaL_checktype(L, index, LUA_TTABLE); - IWritableNodeDefManager *ndef = get_server(L)->getWritableNodeDefManager(); - BiomeDefManager *bmgr = get_server(L)->getBiomeDef(); + BiomeDefManager *bmgr = get_server(L)->getEmergeManager()->biomedef; if (!bmgr) { verbosestream << "register_biome: BiomeDefManager not active" << std::endl; return 0; } - - groupid = getintfield_default(L, index, "group_id", 0); - + enum BiomeTerrainType terrain = (BiomeTerrainType)getenumfield(L, index, "terrain_type", es_BiomeTerrainType, BIOME_TERRAIN_NORMAL); Biome *b = bmgr->createBiome(terrain); - b->name = getstringfield_default(L, index, "name", ""); - - if (getstringfield(L, index, "node_top", nodename)) - b->n_top = MapNode(ndef->getId(nodename)); - else - b->n_top = MapNode(CONTENT_IGNORE); - - if (getstringfield(L, index, "node_filler", nodename)) - b->n_filler = MapNode(ndef->getId(nodename)); - else - b->n_filler = b->n_top; - - b->ntopnodes = getintfield_default(L, index, "num_top_nodes", 0); - - b->height_min = getintfield_default(L, index, "height_min", 0); - b->height_max = getintfield_default(L, index, "height_max", 0); - b->heat_min = getfloatfield_default(L, index, "heat_min", 0.); - b->heat_max = getfloatfield_default(L, index, "heat_max", 0.); - b->humidity_min = getfloatfield_default(L, index, "humidity_min", 0.); - b->humidity_max = getfloatfield_default(L, index, "humidity_max", 0.); - - b->np = new NoiseParams; // should read an entire NoiseParams later on... - getfloatfield(L, index, "scale", b->np->scale); - getfloatfield(L, index, "offset", b->np->offset); - - b->groupid = (s8)groupid; - b->flags = 0; //reserved - + b->name = getstringfield_default(L, index, "name", ""); + b->top_nodename = getstringfield_default(L, index, "top_node", ""); + b->top_depth = getintfield_default(L, index, "top_depth", 0); + b->filler_nodename = getstringfield_default(L, index, "filler_node", ""); + b->filler_height = getintfield_default(L, index, "filler_height", 0); + b->height_min = getintfield_default(L, index, "height_min", 0); + b->height_max = getintfield_default(L, index, "height_max", 0); + b->heat_point = getfloatfield_default(L, index, "heat_point", 0.); + b->humidity_point = getfloatfield_default(L, index, "humidity_point", 0.); + + b->flags = 0; //reserved + b->c_top = CONTENT_IGNORE; + b->c_filler = CONTENT_IGNORE; bmgr->addBiome(b); verbosestream << "register_biome: " << b->name << std::endl; @@ -698,7 +658,6 @@ static int l_register_ore(lua_State *L) int index = 1; luaL_checktype(L, index, LUA_TTABLE); - IWritableNodeDefManager *ndef = get_server(L)->getWritableNodeDefManager(); EmergeManager *emerge = get_server(L)->getEmergeManager(); enum OreType oretype = (OreType)getenumfield(L, index, @@ -1113,7 +1072,6 @@ static const struct luaL_Reg minetest_f [] = { {"register_alias_raw", l_register_alias_raw}, {"register_craft", l_register_craft}, {"register_biome", l_register_biome}, - {"register_biome_groups", l_register_biome_groups}, {"register_ore", l_register_ore}, {"setting_set", l_setting_set}, {"setting_get", l_setting_get}, diff --git a/src/scriptapi_env.cpp b/src/scriptapi_env.cpp index 4e068e377..9bf7f0b55 100644 --- a/src/scriptapi_env.cpp +++ b/src/scriptapi_env.cpp @@ -26,6 +26,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "content_sao.h" #include "script.h" #include "treegen.h" +#include "pathfinder.h" #include "util/pointedthing.h" #include "scriptapi_types.h" #include "scriptapi_noise.h" @@ -647,6 +648,69 @@ int EnvRef::l_clear_objects(lua_State *L) return 0; } +int EnvRef::l_line_of_sight(lua_State *L) { + float stepsize = 1.0; + + //infostream<<"EnvRef::l_get_node()"<<std::endl; + EnvRef *o = checkobject(L, 1); + ServerEnvironment *env = o->m_env; + if(env == NULL) return 0; + + // read position 1 from lua + v3f pos1 = checkFloatPos(L, 2); + // read position 2 from lua + v3f pos2 = checkFloatPos(L, 2); + //read step size from lua + if(lua_isnumber(L, 3)) + stepsize = lua_tonumber(L, 3); + + return (env->line_of_sight(pos1,pos2,stepsize)); +} + +int EnvRef::l_find_path(lua_State *L) +{ + EnvRef *o = checkobject(L, 1); + ServerEnvironment *env = o->m_env; + + if(env == NULL) return 0; + + v3s16 pos1 = read_v3s16(L, 2); + v3s16 pos2 = read_v3s16(L, 3); + unsigned int searchdistance = luaL_checkint(L, 4); + unsigned int max_jump = luaL_checkint(L, 5); + unsigned int max_drop = luaL_checkint(L, 6); + algorithm algo = A_PLAIN_NP; + if(! lua_isnil(L, 7)) { + std::string algorithm = luaL_checkstring(L,7); + + if (algorithm == "A*") + algo = A_PLAIN; + + if (algorithm == "Dijkstra") + algo = DIJKSTRA; + } + + std::vector<v3s16> path = + get_Path(env,pos1,pos2,searchdistance,max_jump,max_drop,algo); + + if (path.size() > 0) + { + lua_newtable(L); + int top = lua_gettop(L); + unsigned int index = 1; + for (std::vector<v3s16>::iterator i = path.begin(); i != path.end();i++) + { + lua_pushnumber(L,index); + push_v3s16(L, *i); + lua_settable(L, top); + index++; + } + return 1; + } + + return 0; +} + int EnvRef::l_spawn_tree(lua_State *L) { EnvRef *o = checkobject(L, 1); @@ -780,6 +844,8 @@ const luaL_reg EnvRef::methods[] = { luamethod(EnvRef, get_perlin_map), luamethod(EnvRef, clear_objects), luamethod(EnvRef, spawn_tree), + luamethod(EnvRef, line_of_sight), + luamethod(EnvRef, find_path), {0,0} }; diff --git a/src/scriptapi_env.h b/src/scriptapi_env.h index 1599969a4..2b7ea9573 100644 --- a/src/scriptapi_env.h +++ b/src/scriptapi_env.h @@ -137,6 +137,12 @@ private: static int l_spawn_tree(lua_State *L); + + static int l_line_of_sight(lua_State *L); + + //find a path between two positions + static int l_find_path(lua_State *L); + public: EnvRef(ServerEnvironment *env); diff --git a/src/scriptapi_object.cpp b/src/scriptapi_object.cpp index a0f93cbba..05433a598 100644 --- a/src/scriptapi_object.cpp +++ b/src/scriptapi_object.cpp @@ -297,6 +297,28 @@ int ObjectRef::l_set_armor_groups(lua_State *L) return 0; } +// set_physics_override(self, physics_override_speed, physics_override_jump, physics_override_gravity) +int ObjectRef::l_set_physics_override(lua_State *L) +{ + ObjectRef *ref = checkobject(L, 1); + PlayerSAO *co = (PlayerSAO *) getobject(ref); + if(co == NULL) return 0; + // Do it + if(!lua_isnil(L, 2)){ + co->m_physics_override_speed = lua_tonumber(L, 2); + co->m_physics_override_sent = false; + } + if(!lua_isnil(L, 3)){ + co->m_physics_override_jump = lua_tonumber(L, 3); + co->m_physics_override_sent = false; + } + if(!lua_isnil(L, 4)){ + co->m_physics_override_gravity = lua_tonumber(L, 4); + co->m_physics_override_sent = false; + } + return 0; +} + // set_animation(self, frame_range, frame_speed, frame_blend) int ObjectRef::l_set_animation(lua_State *L) { @@ -756,6 +778,7 @@ const luaL_reg ObjectRef::methods[] = { luamethod(ObjectRef, get_wielded_item), luamethod(ObjectRef, set_wielded_item), luamethod(ObjectRef, set_armor_groups), + luamethod(ObjectRef, set_physics_override), luamethod(ObjectRef, set_animation), luamethod(ObjectRef, set_bone_position), luamethod(ObjectRef, set_attach), diff --git a/src/scriptapi_object.h b/src/scriptapi_object.h index a37abbb78..a44016933 100644 --- a/src/scriptapi_object.h +++ b/src/scriptapi_object.h @@ -103,6 +103,9 @@ private: // set_armor_groups(self, groups) static int l_set_armor_groups(lua_State *L); + // set_physics_override(self, physics_override_speed, physics_override_jump, physics_override_gravity) + static int l_set_physics_override(lua_State *L); + // set_animation(self, frame_range, frame_speed, frame_blend) static int l_set_animation(lua_State *L); diff --git a/src/server.cpp b/src/server.cpp index c4dd0ab0f..05075a72c 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -646,7 +646,6 @@ Server::Server( m_rollback_sink_enabled(true), m_enable_rollback_recording(false), m_emerge(NULL), - m_biomedef(NULL), m_lua(NULL), m_itemdef(createItemDefManager()), m_nodedef(createNodeDefManager()), @@ -694,12 +693,9 @@ Server::Server( Settings gamedefaults; getGameMinetestConfig(gamespec.path, gamedefaults); override_default_settings(g_settings, &gamedefaults); - - // Create biome definition manager - m_biomedef = new BiomeDefManager(this); // Create emerge manager - m_emerge = new EmergeManager(this, m_biomedef); + m_emerge = new EmergeManager(this); // Create rollback manager std::string rollback_path = m_path_world+DIR_DELIM+"rollback.txt"; @@ -813,9 +809,6 @@ Server::Server( // Apply item aliases in the node definition manager m_nodedef->updateAliases(m_itemdef); - // Add default biomes after nodedef had its aliases added - m_biomedef->addDefaultBiomes(); - // Initialize Environment ServerMap *servermap = new ServerMap(path_world, this, m_emerge); m_env = new ServerEnvironment(servermap, m_lua, this, this); diff --git a/src/server.h b/src/server.h index 04e693fc8..ea1cb79af 100644 --- a/src/server.h +++ b/src/server.h @@ -485,7 +485,6 @@ public: void deleteParticleSpawner(const char *playername, u32 id); void deleteParticleSpawnerAll(u32 id); - void queueBlockEmerge(v3s16 blockpos, bool allow_generate); // Creates or resets inventory @@ -497,11 +496,9 @@ public: // Envlock should be locked when using the rollback manager IRollbackManager *getRollbackManager(){ return m_rollback; } - //TODO: determine what should be locked when accessing the emerge manager + //TODO: determine what (if anything) should be locked to access EmergeManager EmergeManager *getEmergeManager(){ return m_emerge; } - BiomeDefManager *getBiomeDef(){ return m_biomedef; } - // actions: time-reversed list // Return value: success/failure bool rollbackRevertActions(const std::list<RollbackAction> &actions, @@ -734,9 +731,6 @@ private: // Emerge manager EmergeManager *m_emerge; - // Biome Definition Manager - BiomeDefManager *m_biomedef; - // Scripting // Envlock and conlock should be locked when using Lua lua_State *m_lua; diff --git a/src/serverobject.h b/src/serverobject.h index 7a5b47bd1..13a075a25 100644 --- a/src/serverobject.h +++ b/src/serverobject.h @@ -152,6 +152,8 @@ public: virtual void setArmorGroups(const ItemGroupList &armor_groups) {} + virtual void setPhysicsOverride(float physics_override_speed, float physics_override_jump, float physics_override_gravity) + {} virtual void setAnimation(v2f frames, float frame_speed, float frame_blend) {} virtual void setBonePosition(std::string bone, v3f position, v3f rotation) diff --git a/src/tile.cpp b/src/tile.cpp index aea9665f5..c5e8a2a9d 100644 --- a/src/tile.cpp +++ b/src/tile.cpp @@ -826,7 +826,7 @@ void TextureSource::rebuildImagesAndTextures() video::ITexture *t = NULL; if(img) t = driver->addTexture(sap->name.c_str(), img); - + video::ITexture *t_old = sap->a.atlas; // Replace texture sap->a.atlas = t; sap->a.pos = v2f(0,0); @@ -835,6 +835,9 @@ void TextureSource::rebuildImagesAndTextures() sap->atlas_img = img; sap->intpos = v2s32(0,0); sap->intsize = img->getDimension(); + + if (t_old != 0) + driver->removeTexture(t_old); } } diff --git a/textures/base/pack/unknown_block.png b/textures/base/pack/unknown_block.png Binary files differdeleted file mode 100644 index a27cb8ca9..000000000 --- a/textures/base/pack/unknown_block.png +++ /dev/null diff --git a/textures/base/pack/unknown_node.png b/textures/base/pack/unknown_node.png Binary files differnew file mode 100644 index 000000000..9cae26d5c --- /dev/null +++ b/textures/base/pack/unknown_node.png |