Compare commits
105 Commits
0.4.dev-20
...
0.4.2-rc1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d38b465b7c | ||
|
|
fd7ec2da91 | ||
|
|
c9ed379e39 | ||
|
|
e64feefc61 | ||
|
|
3e754382af | ||
|
|
7ef0a13250 | ||
|
|
0de3fb786d | ||
|
|
1d44a98f2f | ||
|
|
98ff4eb4ee | ||
|
|
a9d8df83d2 | ||
|
|
508b7b5e51 | ||
|
|
f7dc72f8aa | ||
|
|
0190f9b077 | ||
|
|
0c91a0d59d | ||
|
|
a26a66a8c4 | ||
|
|
100345f1e4 | ||
|
|
4535166a3b | ||
|
|
0346e68deb | ||
|
|
db62c227c8 | ||
|
|
983e45ae92 | ||
|
|
0a18dda158 | ||
|
|
9eaf93d41d | ||
|
|
2ac20982e0 | ||
|
|
96eac87d47 | ||
|
|
0cf1ed544c | ||
|
|
558e284e25 | ||
|
|
5c31445117 | ||
|
|
717ae67995 | ||
|
|
e8331f0c1d | ||
|
|
c009aa3a22 | ||
|
|
9af9d8f5d0 | ||
|
|
2c027b03db | ||
|
|
aef1332e42 | ||
|
|
16fc8b5fc2 | ||
|
|
fd845f27f5 | ||
|
|
ea62ee4b61 | ||
|
|
cd6becd442 | ||
|
|
829f262c79 | ||
|
|
246520b5cb | ||
|
|
38bb649582 | ||
|
|
82855a04ec | ||
|
|
6dfefaf229 | ||
|
|
d44f8a854b | ||
|
|
acf3a43095 | ||
|
|
4cc98d7add | ||
|
|
506203345b | ||
|
|
c259f7c8bd | ||
|
|
c62a121cca | ||
|
|
136eb32389 | ||
|
|
e3ddbe8c6b | ||
|
|
d085139057 | ||
|
|
28e7443f9b | ||
|
|
e79ad21aeb | ||
|
|
0b61253931 | ||
|
|
a2738dec59 | ||
|
|
1788709e2d | ||
|
|
47d30d12cb | ||
|
|
43df78102c | ||
|
|
cc10eec6c6 | ||
|
|
15bf9a7026 | ||
|
|
2795f44f03 | ||
|
|
b0ba05c9ac | ||
|
|
71c6845a94 | ||
|
|
4b97023251 | ||
|
|
369046bbb4 | ||
|
|
38580fbee7 | ||
|
|
08e1d40d6e | ||
|
|
80f35467d8 | ||
|
|
1b19020bf4 | ||
|
|
61e58ee9b7 | ||
|
|
f21af8da9c | ||
|
|
dece3a3600 | ||
|
|
16ad10e62f | ||
|
|
02fb912a95 | ||
|
|
48790c0751 | ||
|
|
c57e5083e8 | ||
|
|
c9a2058361 | ||
|
|
7039dfafd6 | ||
|
|
22ae83a589 | ||
|
|
1575448b1a | ||
|
|
9f031a6759 | ||
|
|
d0ea6f9920 | ||
|
|
1bc37d576c | ||
|
|
d159591b9a | ||
|
|
57550b2b3d | ||
|
|
d15d6c4e6b | ||
|
|
6b598f61a6 | ||
|
|
9e21204f8b | ||
|
|
e6b86fa304 | ||
|
|
268e50dfbd | ||
|
|
07ccc15fc2 | ||
|
|
2b500d72e5 | ||
|
|
cd0014b24f | ||
|
|
b3786d84c5 | ||
|
|
fd1135c7af | ||
|
|
f0678979b1 | ||
|
|
f4a7e11bce | ||
|
|
6a0388bb4b | ||
|
|
7ba72f2763 | ||
|
|
f7147d9c0a | ||
|
|
523a5fd2e5 | ||
|
|
ff85e2343c | ||
|
|
e74668ef7f | ||
|
|
22502f80db | ||
|
|
4b2cc38aba |
@@ -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 dev-20120606)
|
||||
set(VERSION_PATCH 2-rc1)
|
||||
if(VERSION_EXTRA)
|
||||
set(VERSION_PATCH ${VERSION_PATCH}-${VERSION_EXTRA})
|
||||
endif()
|
||||
@@ -62,7 +62,7 @@ if(WIN32)
|
||||
elseif(APPLE)
|
||||
# Random placeholders; this isn't usually used and may not work
|
||||
# See https://github.com/toabi/minetest-mac/
|
||||
set(SHAREDIR "share/${PROJECT_NAME}")
|
||||
set(SHAREDIR "${CMAKE_INSTALL_PREFIX}/share/${PROJECT_NAME}")
|
||||
set(BINDIR "bin")
|
||||
set(DOCDIR "share/doc/${PROJECT_NAME}")
|
||||
set(EXAMPLE_CONF_DIR ${DOCDIR})
|
||||
@@ -78,17 +78,58 @@ elseif(UNIX) # Linux, BSD etc
|
||||
set(ICONDIR "unix/icons")
|
||||
set(LOCALEDIR "locale")
|
||||
else()
|
||||
set(SHAREDIR "share/${PROJECT_NAME}")
|
||||
set(BINDIR "bin")
|
||||
set(DOCDIR "share/doc/${PROJECT_NAME}")
|
||||
set(MANDIR "share/man")
|
||||
set(SHAREDIR "${CMAKE_INSTALL_PREFIX}/share/${PROJECT_NAME}")
|
||||
set(BINDIR "${CMAKE_INSTALL_PREFIX}/bin")
|
||||
set(DOCDIR "${CMAKE_INSTALL_PREFIX}/share/doc/${PROJECT_NAME}")
|
||||
set(MANDIR "${CMAKE_INSTALL_PREFIX}/share/man")
|
||||
set(EXAMPLE_CONF_DIR ${DOCDIR})
|
||||
set(XDG_APPS_DIR "share/applications")
|
||||
set(ICONDIR "share/icons")
|
||||
set(LOCALEDIR "share/locale")
|
||||
set(XDG_APPS_DIR "${CMAKE_INSTALL_PREFIX}/share/applications")
|
||||
set(ICONDIR "${CMAKE_INSTALL_PREFIX}/share/icons")
|
||||
set(LOCALEDIR "${CMAKE_INSTALL_PREFIX}/share/locale")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
set(CUSTOM_SHAREDIR "" CACHE STRING "Directory to install data files into")
|
||||
if(NOT CUSTOM_SHAREDIR STREQUAL "")
|
||||
set(SHAREDIR "${CUSTOM_SHAREDIR}")
|
||||
message(STATUS "Using SHAREDIR=${SHAREDIR}")
|
||||
endif()
|
||||
set(CUSTOM_BINDIR "" CACHE STRING "Directory to install binaries into")
|
||||
if(NOT CUSTOM_BINDIR STREQUAL "")
|
||||
set(BINDIR "${CUSTOM_BINDIR}")
|
||||
message(STATUS "Using BINDIR=${BINDIR}")
|
||||
endif()
|
||||
set(CUSTOM_DOCDIR "" CACHE STRING "Directory to install documentation into")
|
||||
if(NOT CUSTOM_DOCDIR STREQUAL "")
|
||||
set(DOCDIR "${CUSTOM_DOCDIR}")
|
||||
message(STATUS "Using DOCDIR=${DOCDIR}")
|
||||
endif()
|
||||
set(CUSTOM_MANDIR "" CACHE STRING "Directory to install manpages into")
|
||||
if(NOT CUSTOM_MANDIR STREQUAL "")
|
||||
set(MANDIR "${CUSTOM_MANDIR}")
|
||||
message(STATUS "Using MANDIR=${MANDIR}")
|
||||
endif()
|
||||
set(CUSTOM_EXAMPLE_CONF_DIR "" CACHE STRING "Directory to install example config file into")
|
||||
if(NOT CUSTOM_EXAMPLE_CONF_DIR STREQUAL "")
|
||||
set(EXAMPLE_CONF_DIR "${CUSTOM_EXAMPLE_CONF_DIR}")
|
||||
message(STATUS "Using EXAMPLE_CONF_DIR=${EXAMPLE_CONF_DIR}")
|
||||
endif()
|
||||
set(CUSTOM_XDG_APPS_DIR "" CACHE STRING "Directory to install .desktop files into")
|
||||
if(NOT CUSTOM_XDG_APPS_DIR STREQUAL "")
|
||||
set(XDG_APPS_DIR "${CUSTOM_XDG_APPS_DIR}")
|
||||
message(STATUS "Using XDG_APPS_DIR=${XDG_APPS_DIR}")
|
||||
endif()
|
||||
set(CUSTOM_ICONDIR "" CACHE STRING "Directory to install icons into")
|
||||
if(NOT CUSTOM_ICONDIR STREQUAL "")
|
||||
set(ICONDIR "${CUSTOM_ICONDIR}")
|
||||
message(STATUS "Using ICONDIR=${ICONDIR}")
|
||||
endif()
|
||||
set(CUSTOM_LOCALEDIR "" CACHE STRING "Directory to install l10n files into")
|
||||
if(NOT CUSTOM_LOCALEDIR STREQUAL "")
|
||||
set(LOCALEDIR "${CUSTOM_LOCALEDIR}")
|
||||
message(STATUS "Using LOCALEDIR=${LOCALEDIR}")
|
||||
endif()
|
||||
|
||||
install(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/builtin" DESTINATION "${SHAREDIR}")
|
||||
install(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/games/minimal" DESTINATION "${SHAREDIR}/games")
|
||||
set(MINETEST_GAME_SOURCE "${CMAKE_CURRENT_SOURCE_DIR}/games/minetest_game")
|
||||
|
||||
@@ -21,4 +21,5 @@ dofile(minetest.get_modpath("__builtin").."/privileges.lua")
|
||||
dofile(minetest.get_modpath("__builtin").."/auth.lua")
|
||||
dofile(minetest.get_modpath("__builtin").."/chatcommands.lua")
|
||||
dofile(minetest.get_modpath("__builtin").."/static_spawn.lua")
|
||||
dofile(minetest.get_modpath("__builtin").."/detached_inventory.lua")
|
||||
|
||||
|
||||
@@ -41,7 +41,7 @@ end)
|
||||
--
|
||||
|
||||
-- Register C++ commands without functions
|
||||
minetest.register_chatcommand("me", {params = nil, description = "chat action (eg. /me orders a pizza)"})
|
||||
minetest.register_chatcommand("me", {params = nil, description = "chat action (eg. /me orders a pizza)", privs = {shout=true}})
|
||||
minetest.register_chatcommand("status", {description = "print server status line"})
|
||||
minetest.register_chatcommand("shutdown", {params = "", description = "shutdown server", privs = {server=true}})
|
||||
minetest.register_chatcommand("clearobjects", {params = "", description = "clear all objects in world", privs = {server=true}})
|
||||
@@ -193,12 +193,27 @@ minetest.register_chatcommand("setpassword", {
|
||||
description = "set given password",
|
||||
privs = {password=true},
|
||||
func = function(name, param)
|
||||
if param == "" then
|
||||
minetest.chat_send_player(name, "Password field required")
|
||||
local toname, raw_password = string.match(param, "^([^ ]+) +(.+)$")
|
||||
if not toname then
|
||||
toname = string.match(param, "^([^ ]+) *$")
|
||||
raw_password = nil
|
||||
end
|
||||
if not toname then
|
||||
minetest.chat_send_player(name, "Name field required")
|
||||
return
|
||||
end
|
||||
minetest.set_player_password(name, param)
|
||||
minetest.chat_send_player(name, "Password set")
|
||||
local actstr = "?"
|
||||
if not raw_password then
|
||||
minetest.set_player_password(toname, "")
|
||||
actstr = "cleared"
|
||||
else
|
||||
minetest.set_player_password(toname, minetest.get_password_hash(toname, raw_password))
|
||||
actstr = "set"
|
||||
end
|
||||
minetest.chat_send_player(name, "Password of player \""..toname.."\" "..actstr)
|
||||
if toname ~= name then
|
||||
minetest.chat_send_player(toname, "Your password was "..actstr.." by "..name)
|
||||
end
|
||||
end,
|
||||
})
|
||||
minetest.register_chatcommand("clearpassword", {
|
||||
@@ -206,8 +221,13 @@ minetest.register_chatcommand("clearpassword", {
|
||||
description = "set empty password",
|
||||
privs = {password=true},
|
||||
func = function(name, param)
|
||||
minetest.set_player_password(name, '')
|
||||
minetest.chat_send_player(name, "Password cleared")
|
||||
toname = param
|
||||
if not toname then
|
||||
minetest.chat_send_player(toname, "Name field required")
|
||||
return
|
||||
end
|
||||
minetest.set_player_password(toname, '')
|
||||
minetest.chat_send_player(name, "Password of player \""..toname.."\" cleared")
|
||||
end,
|
||||
})
|
||||
|
||||
@@ -351,3 +371,207 @@ minetest.register_chatcommand("set", {
|
||||
end,
|
||||
})
|
||||
|
||||
minetest.register_chatcommand("mods", {
|
||||
params = "",
|
||||
description = "lists mods installed on the server",
|
||||
privs = {},
|
||||
func = function(name, param)
|
||||
local response = ""
|
||||
local modnames = minetest.get_modnames()
|
||||
for i, mod in ipairs(modnames) do
|
||||
response = response .. mod
|
||||
-- Add space if not at the end
|
||||
if i ~= #modnames then
|
||||
response = response .. " "
|
||||
end
|
||||
end
|
||||
minetest.chat_send_player(name, response)
|
||||
end,
|
||||
})
|
||||
|
||||
local function handle_give_command(cmd, giver, receiver, stackstring)
|
||||
minetest.log("action", giver.." invoked "..cmd..', stackstring="'
|
||||
..stackstring..'"')
|
||||
minetest.log(cmd..' invoked, stackstring="'..stackstring..'"')
|
||||
local itemstack = ItemStack(stackstring)
|
||||
if itemstack:is_empty() then
|
||||
minetest.chat_send_player(giver, 'error: cannot give an empty item')
|
||||
return
|
||||
elseif not itemstack:is_known() then
|
||||
minetest.chat_send_player(giver, 'error: cannot give an unknown item')
|
||||
return
|
||||
end
|
||||
local receiverref = minetest.env:get_player_by_name(receiver)
|
||||
if receiverref == nil then
|
||||
minetest.chat_send_player(giver, receiver..' is not a known player')
|
||||
return
|
||||
end
|
||||
local leftover = receiverref:get_inventory():add_item("main", itemstack)
|
||||
if leftover:is_empty() then
|
||||
partiality = ""
|
||||
elseif leftover:get_count() == itemstack:get_count() then
|
||||
partiality = "could not be "
|
||||
else
|
||||
partiality = "partially "
|
||||
end
|
||||
-- The actual item stack string may be different from what the "giver"
|
||||
-- entered (e.g. big numbers are always interpreted as 2^16-1).
|
||||
stackstring = itemstack:to_string()
|
||||
if giver == receiver then
|
||||
minetest.chat_send_player(giver, '"'..stackstring
|
||||
..'" '..partiality..'added to inventory.');
|
||||
else
|
||||
minetest.chat_send_player(giver, '"'..stackstring
|
||||
..'" '..partiality..'added to '..receiver..'\'s inventory.');
|
||||
minetest.chat_send_player(receiver, '"'..stackstring
|
||||
..'" '..partiality..'added to inventory.');
|
||||
end
|
||||
end
|
||||
|
||||
minetest.register_chatcommand("give", {
|
||||
params = "<name> <itemstring>",
|
||||
description = "give item to player",
|
||||
privs = {give=true},
|
||||
func = function(name, param)
|
||||
local toname, itemstring = string.match(param, "^([^ ]+) +(.+)$")
|
||||
if not toname or not itemstring then
|
||||
minetest.chat_send_player(name, "name and itemstring required")
|
||||
return
|
||||
end
|
||||
handle_give_command("/give", name, toname, itemstring)
|
||||
end,
|
||||
})
|
||||
minetest.register_chatcommand("giveme", {
|
||||
params = "<itemstring>",
|
||||
description = "give item to yourself",
|
||||
privs = {give=true},
|
||||
func = function(name, param)
|
||||
local itemstring = string.match(param, "(.+)$")
|
||||
if not itemstring then
|
||||
minetest.chat_send_player(name, "itemstring required")
|
||||
return
|
||||
end
|
||||
handle_give_command("/giveme", name, name, itemstring)
|
||||
end,
|
||||
})
|
||||
minetest.register_chatcommand("spawnentity", {
|
||||
params = "<entityname>",
|
||||
description = "spawn entity at your position",
|
||||
privs = {give=true, interact=true},
|
||||
func = function(name, param)
|
||||
local entityname = string.match(param, "(.+)$")
|
||||
if not entityname then
|
||||
minetest.chat_send_player(name, "entityname required")
|
||||
return
|
||||
end
|
||||
print('/spawnentity invoked, entityname="'..entityname..'"')
|
||||
local player = minetest.env:get_player_by_name(name)
|
||||
if player == nil then
|
||||
print("Unable to spawn entity, player is nil")
|
||||
return true -- Handled chat message
|
||||
end
|
||||
local p = player:getpos()
|
||||
p.y = p.y + 1
|
||||
minetest.env:add_entity(p, entityname)
|
||||
minetest.chat_send_player(name, '"'..entityname
|
||||
..'" spawned.');
|
||||
end,
|
||||
})
|
||||
minetest.register_chatcommand("pulverize", {
|
||||
params = "",
|
||||
description = "delete item in hand",
|
||||
privs = {},
|
||||
func = function(name, param)
|
||||
local player = minetest.env:get_player_by_name(name)
|
||||
if player == nil then
|
||||
print("Unable to pulverize, player is nil")
|
||||
return true -- Handled chat message
|
||||
end
|
||||
if player:get_wielded_item():is_empty() then
|
||||
minetest.chat_send_player(name, 'Unable to pulverize, no item in hand.')
|
||||
else
|
||||
player:set_wielded_item(nil)
|
||||
minetest.chat_send_player(name, 'An item was pulverized.')
|
||||
end
|
||||
end,
|
||||
})
|
||||
|
||||
-- Key = player name
|
||||
minetest.rollback_punch_callbacks = {}
|
||||
|
||||
minetest.register_on_punchnode(function(pos, node, puncher)
|
||||
local name = puncher:get_player_name()
|
||||
if minetest.rollback_punch_callbacks[name] then
|
||||
minetest.rollback_punch_callbacks[name](pos, node, puncher)
|
||||
minetest.rollback_punch_callbacks[name] = nil
|
||||
end
|
||||
end)
|
||||
|
||||
minetest.register_chatcommand("rollback_check", {
|
||||
params = "[<range>] [<seconds>]",
|
||||
description = "check who has last touched a node or near it, "..
|
||||
"max. <seconds> ago (default range=0, seconds=86400=24h)",
|
||||
privs = {rollback=true},
|
||||
func = function(name, param)
|
||||
local range, seconds = string.match(param, "(%d+) *(%d*)")
|
||||
range = tonumber(range) or 0
|
||||
seconds = tonumber(seconds) or 86400
|
||||
minetest.chat_send_player(name, "Punch a node (limits set: range="..
|
||||
dump(range).." seconds="..dump(seconds).."s)")
|
||||
minetest.rollback_punch_callbacks[name] = function(pos, node, puncher)
|
||||
local name = puncher:get_player_name()
|
||||
minetest.chat_send_player(name, "Checking...")
|
||||
local actor, act_p, act_seconds =
|
||||
minetest.rollback_get_last_node_actor(pos, range, seconds)
|
||||
if actor == "" then
|
||||
minetest.chat_send_player(name, "Nobody has touched the "..
|
||||
"specified location in "..dump(seconds).." seconds")
|
||||
return
|
||||
end
|
||||
local nodedesc = "this node"
|
||||
if act_p.x ~= pos.x or act_p.y ~= pos.y or act_p.z ~= pos.z then
|
||||
nodedesc = minetest.pos_to_string(act_p)
|
||||
end
|
||||
local nodename = minetest.env:get_node(act_p).name
|
||||
minetest.chat_send_player(name, "Last actor on "..nodedesc..
|
||||
" was "..actor..", "..dump(act_seconds)..
|
||||
"s ago (node is now "..nodename..")")
|
||||
end
|
||||
end,
|
||||
})
|
||||
|
||||
minetest.register_chatcommand("rollback", {
|
||||
params = "<player name> [<seconds>] | :<actor> [<seconds>]",
|
||||
description = "revert actions of a player; default for <seconds> is 60",
|
||||
privs = {rollback=true},
|
||||
func = function(name, param)
|
||||
local target_name, seconds = string.match(param, ":([^ ]+) *(%d*)")
|
||||
if not target_name then
|
||||
local player_name = nil;
|
||||
player_name, seconds = string.match(param, "([^ ]+) *(%d*)")
|
||||
if not player_name then
|
||||
minetest.chat_send_player(name, "Invalid parameters. See /help rollback and /help rollback_check")
|
||||
return
|
||||
end
|
||||
target_name = "player:"..player_name
|
||||
end
|
||||
seconds = tonumber(seconds) or 60
|
||||
minetest.chat_send_player(name, "Reverting actions of "..
|
||||
dump(target_name).." since "..dump(seconds).." seconds.")
|
||||
local success, log = minetest.rollback_revert_actions_by(
|
||||
target_name, seconds)
|
||||
if #log > 10 then
|
||||
minetest.chat_send_player(name, "(log is too long to show)")
|
||||
else
|
||||
for _,line in ipairs(log) do
|
||||
minetest.chat_send_player(name, line)
|
||||
end
|
||||
end
|
||||
if success then
|
||||
minetest.chat_send_player(name, "Reverting actions succeeded.")
|
||||
else
|
||||
minetest.chat_send_player(name, "Reverting actions FAILED.")
|
||||
end
|
||||
end,
|
||||
})
|
||||
|
||||
|
||||
@@ -16,4 +16,11 @@ minetest.digprop_woodlike = digprop_err
|
||||
minetest.digprop_leaveslike = digprop_err
|
||||
minetest.digprop_glasslike = digprop_err
|
||||
|
||||
minetest.node_metadata_inventory_move_allow_all = function()
|
||||
minetest.log("info", "WARNING: minetest.node_metadata_inventory_move_allow_all is obsolete and does nothing.")
|
||||
end
|
||||
|
||||
minetest.add_to_creative_inventory = function(itemstring)
|
||||
minetest.log('info', "WARNING: minetest.add_to_creative_inventory: This function is deprecated and does nothing.")
|
||||
end
|
||||
|
||||
|
||||
19
builtin/detached_inventory.lua
Normal file
19
builtin/detached_inventory.lua
Normal file
@@ -0,0 +1,19 @@
|
||||
-- Minetest: builtin/detached_inventory.lua
|
||||
|
||||
minetest.detached_inventories = {}
|
||||
|
||||
function minetest.create_detached_inventory(name, callbacks)
|
||||
local stuff = {}
|
||||
stuff.name = name
|
||||
if callbacks then
|
||||
stuff.allow_move = callbacks.allow_move
|
||||
stuff.allow_put = callbacks.allow_put
|
||||
stuff.allow_take = callbacks.allow_take
|
||||
stuff.on_move = callbacks.on_move
|
||||
stuff.on_put = callbacks.on_put
|
||||
stuff.on_take = callbacks.on_take
|
||||
end
|
||||
minetest.detached_inventories[name] = stuff
|
||||
return minetest.create_detached_inventory_raw(name)
|
||||
end
|
||||
|
||||
205
builtin/item.lua
205
builtin/item.lua
@@ -124,53 +124,84 @@ end
|
||||
function minetest.item_place_node(itemstack, placer, pointed_thing)
|
||||
local item = itemstack:peek_item()
|
||||
local def = itemstack:get_definition()
|
||||
if def.type == "node" and pointed_thing.type == "node" then
|
||||
local pos = pointed_thing.above
|
||||
local oldnode = minetest.env:get_node(pos)
|
||||
local olddef = ItemStack({name=oldnode.name}):get_definition()
|
||||
if def.type ~= "node" or pointed_thing.type ~= "node" then
|
||||
return itemstack
|
||||
end
|
||||
|
||||
if not olddef.buildable_to then
|
||||
minetest.log("info", placer:get_player_name() .. " tried to place"
|
||||
.. " node in invalid position " .. minetest.pos_to_string(pos)
|
||||
.. ", replacing " .. oldnode.name)
|
||||
return
|
||||
end
|
||||
local under = pointed_thing.under
|
||||
local oldnode_under = minetest.env:get_node(under)
|
||||
local olddef_under = ItemStack({name=oldnode_under.name}):get_definition()
|
||||
olddef_under = olddef_under or minetest.nodedef_default
|
||||
local above = pointed_thing.above
|
||||
local oldnode_above = minetest.env:get_node(above)
|
||||
local olddef_above = ItemStack({name=oldnode_above.name}):get_definition()
|
||||
olddef_above = olddef_above or minetest.nodedef_default
|
||||
|
||||
minetest.log("action", placer:get_player_name() .. " places node "
|
||||
.. def.name .. " at " .. minetest.pos_to_string(pos))
|
||||
if not olddef_above.buildable_to and not olddef_under.buildable_to then
|
||||
minetest.log("info", placer:get_player_name() .. " tried to place"
|
||||
.. " node in invalid position " .. minetest.pos_to_string(above)
|
||||
.. ", replacing " .. oldnode_above.name)
|
||||
return
|
||||
end
|
||||
|
||||
local newnode = {name = def.name, param1 = 0, param2 = 0}
|
||||
-- Place above pointed node
|
||||
local place_to = {x = above.x, y = above.y, z = above.z}
|
||||
|
||||
-- Calculate direction for wall mounted stuff like torches and signs
|
||||
if def.paramtype2 == 'wallmounted' then
|
||||
local under = pointed_thing.under
|
||||
local above = pointed_thing.above
|
||||
local dir = {x = under.x - above.x, y = under.y - above.y, z = under.z - above.z}
|
||||
newnode.param2 = minetest.dir_to_wallmounted(dir)
|
||||
-- Calculate the direction for furnaces and chests and stuff
|
||||
elseif def.paramtype2 == 'facedir' then
|
||||
local playerpos = placer:getpos()
|
||||
local dir = {x = pos.x - playerpos.x, y = pos.y - playerpos.y, z = pos.z - playerpos.z}
|
||||
-- If node under is buildable_to, place into it instead (eg. snow)
|
||||
if olddef_under.buildable_to then
|
||||
minetest.log("info", "node under is buildable to")
|
||||
place_to = {x = under.x, y = under.y, z = under.z}
|
||||
end
|
||||
|
||||
minetest.log("action", placer:get_player_name() .. " places node "
|
||||
.. def.name .. " at " .. minetest.pos_to_string(place_to))
|
||||
|
||||
local oldnode = minetest.env:get_node(place_to)
|
||||
local newnode = {name = def.name, param1 = 0, param2 = 0}
|
||||
|
||||
-- Calculate direction for wall mounted stuff like torches and signs
|
||||
if def.paramtype2 == 'wallmounted' then
|
||||
local dir = {
|
||||
x = under.x - above.x,
|
||||
y = under.y - above.y,
|
||||
z = under.z - above.z
|
||||
}
|
||||
newnode.param2 = minetest.dir_to_wallmounted(dir)
|
||||
-- Calculate the direction for furnaces and chests and stuff
|
||||
elseif def.paramtype2 == 'facedir' then
|
||||
local placer_pos = placer:getpos()
|
||||
if placer_pos then
|
||||
local dir = {
|
||||
x = above.x - placer_pos.x,
|
||||
y = above.y - placer_pos.y,
|
||||
z = above.z - placer_pos.z
|
||||
}
|
||||
newnode.param2 = minetest.dir_to_facedir(dir)
|
||||
minetest.log("action", "facedir: " .. newnode.param2)
|
||||
end
|
||||
|
||||
-- Add node and update
|
||||
minetest.env:add_node(pos, newnode)
|
||||
|
||||
-- Run callback
|
||||
if def.after_place_node then
|
||||
def.after_place_node(pos, placer)
|
||||
end
|
||||
|
||||
-- Run script hook (deprecated)
|
||||
local _, callback
|
||||
for _, callback in ipairs(minetest.registered_on_placenodes) do
|
||||
callback(pos, newnode, placer)
|
||||
end
|
||||
|
||||
itemstack:take_item()
|
||||
end
|
||||
|
||||
-- Add node and update
|
||||
minetest.env:add_node(place_to, newnode)
|
||||
|
||||
-- Run callback
|
||||
if def.after_place_node then
|
||||
-- Copy place_to because callback can modify it
|
||||
local place_to_copy = {x=place_to.x, y=place_to.y, z=place_to.z}
|
||||
def.after_place_node(place_to_copy, placer)
|
||||
end
|
||||
|
||||
-- Run script hook
|
||||
local _, callback
|
||||
for _, callback in ipairs(minetest.registered_on_placenodes) do
|
||||
-- Copy pos and node because callback can modify them
|
||||
local place_to_copy = {x=place_to.x, y=place_to.y, z=place_to.z}
|
||||
local newnode_copy = {name=newnode.name, param1=newnode.param1, param2=newnode.param2}
|
||||
local oldnode_copy = {name=oldnode.name, param1=oldnode.param1, param2=oldnode.param2}
|
||||
callback(place_to_copy, newnode_copy, placer, oldnode_copy)
|
||||
end
|
||||
|
||||
itemstack:take_item()
|
||||
return itemstack
|
||||
end
|
||||
|
||||
@@ -220,9 +251,11 @@ function minetest.node_punch(pos, node, puncher)
|
||||
-- Run script hook
|
||||
local _, callback
|
||||
for _, callback in ipairs(minetest.registered_on_punchnodes) do
|
||||
callback(pos, node, puncher)
|
||||
-- Copy pos and node because callback can modify them
|
||||
local pos_copy = {x=pos.x, y=pos.y, z=pos.z}
|
||||
local node_copy = {name=node.name, param1=node.param1, param2=node.param2}
|
||||
callback(pos_copy, node_copy, puncher)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
function minetest.node_dig(pos, node, digger)
|
||||
@@ -240,29 +273,25 @@ function minetest.node_dig(pos, node, digger)
|
||||
minetest.log('action', digger:get_player_name() .. " digs "
|
||||
.. node.name .. " at " .. minetest.pos_to_string(pos))
|
||||
|
||||
if not minetest.setting_getbool("creative_mode") then
|
||||
local wielded = digger:get_wielded_item()
|
||||
local drops = minetest.get_node_drops(node.name, wielded:get_name())
|
||||
local wielded = digger:get_wielded_item()
|
||||
local drops = minetest.get_node_drops(node.name, wielded:get_name())
|
||||
|
||||
-- Wear out tool
|
||||
tp = wielded:get_tool_capabilities()
|
||||
dp = minetest.get_dig_params(def.groups, tp)
|
||||
wielded:add_wear(dp.wear)
|
||||
digger:set_wielded_item(wielded)
|
||||
-- Wear out tool
|
||||
local tp = wielded:get_tool_capabilities()
|
||||
local dp = minetest.get_dig_params(def.groups, tp)
|
||||
wielded:add_wear(dp.wear)
|
||||
digger:set_wielded_item(wielded)
|
||||
|
||||
-- Add dropped items to object's inventory
|
||||
if digger:get_inventory() then
|
||||
local _, dropped_item
|
||||
for _, dropped_item in ipairs(drops) do
|
||||
digger:get_inventory():add_item("main", dropped_item)
|
||||
end
|
||||
-- Add dropped items to object's inventory
|
||||
if digger:get_inventory() then
|
||||
local _, dropped_item
|
||||
for _, dropped_item in ipairs(drops) do
|
||||
digger:get_inventory():add_item("main", dropped_item)
|
||||
end
|
||||
end
|
||||
|
||||
local oldnode = nil
|
||||
|
||||
local oldmetadata = nil
|
||||
if def.after_dig_node then
|
||||
oldnode = node;
|
||||
oldmetadata = minetest.env:get_meta(pos):to_table()
|
||||
end
|
||||
|
||||
@@ -271,51 +300,22 @@ function minetest.node_dig(pos, node, digger)
|
||||
|
||||
-- Run callback
|
||||
if def.after_dig_node then
|
||||
def.after_dig_node(pos, oldnode, oldmetadata, digger)
|
||||
-- Copy pos and node because callback can modify them
|
||||
local pos_copy = {x=pos.x, y=pos.y, z=pos.z}
|
||||
local node_copy = {name=node.name, param1=node.param1, param2=node.param2}
|
||||
def.after_dig_node(pos_copy, node_copy, oldmetadata, digger)
|
||||
end
|
||||
|
||||
-- Run script hook (deprecated)
|
||||
-- Run script hook
|
||||
local _, callback
|
||||
for _, callback in ipairs(minetest.registered_on_dignodes) do
|
||||
callback(pos, node, digger)
|
||||
-- Copy pos and node because callback can modify them
|
||||
local pos_copy = {x=pos.x, y=pos.y, z=pos.z}
|
||||
local node_copy = {name=node.name, param1=node.param1, param2=node.param2}
|
||||
callback(pos_copy, node_copy, digger)
|
||||
end
|
||||
end
|
||||
|
||||
function minetest.node_metadata_inventory_move_allow_all(pos, from_list,
|
||||
from_index, to_list, to_index, count, player)
|
||||
minetest.log("verbose", "node_metadata_inventory_move_allow_all")
|
||||
local meta = minetest.env:get_meta(pos)
|
||||
local inv = meta:get_inventory()
|
||||
|
||||
local from_stack = inv:get_stack(from_list, from_index)
|
||||
local taken_items = from_stack:take_item(count)
|
||||
inv:set_stack(from_list, from_index, from_stack)
|
||||
|
||||
local to_stack = inv:get_stack(to_list, to_index)
|
||||
to_stack:add_item(taken_items)
|
||||
inv:set_stack(to_list, to_index, to_stack)
|
||||
end
|
||||
|
||||
function minetest.node_metadata_inventory_offer_allow_all(pos, listname, index, stack, player)
|
||||
minetest.log("verbose", "node_metadata_inventory_offer_allow_all")
|
||||
local meta = minetest.env:get_meta(pos)
|
||||
local inv = meta:get_inventory()
|
||||
local the_stack = inv:get_stack(listname, index)
|
||||
the_stack:add_item(stack)
|
||||
inv:set_stack(listname, index, the_stack)
|
||||
return ItemStack("")
|
||||
end
|
||||
|
||||
function minetest.node_metadata_inventory_take_allow_all(pos, listname, index, count, player)
|
||||
minetest.log("verbose", "node_metadata_inventory_take_allow_all")
|
||||
local meta = minetest.env:get_meta(pos)
|
||||
local inv = meta:get_inventory()
|
||||
local the_stack = inv:get_stack(listname, index)
|
||||
local taken_items = the_stack:take_item(count)
|
||||
inv:set_stack(listname, index, the_stack)
|
||||
return taken_items
|
||||
end
|
||||
|
||||
-- This is used to allow mods to redefine minetest.item_place and so on
|
||||
-- NOTE: This is not the preferred way. Preferred way is to provide enough
|
||||
-- callbacks to not require redefining global functions. -celeron55
|
||||
@@ -342,6 +342,7 @@ minetest.nodedef_default = {
|
||||
usable = false,
|
||||
liquids_pointable = false,
|
||||
tool_capabilities = nil,
|
||||
node_placement_prediction = nil,
|
||||
|
||||
-- Interaction callbacks
|
||||
on_place = redef_wrapper(minetest, 'item_place'), -- minetest.item_place
|
||||
@@ -361,11 +362,13 @@ minetest.nodedef_default = {
|
||||
-- Node properties
|
||||
drawtype = "normal",
|
||||
visual_scale = 1.0,
|
||||
tile_images = {""},
|
||||
special_materials = {
|
||||
{image="", backface_culling=true},
|
||||
{image="", backface_culling=true},
|
||||
},
|
||||
-- Don't define these because otherwise the old tile_images and
|
||||
-- special_materials wouldn't be read
|
||||
--tiles ={""},
|
||||
--special_tiles = {
|
||||
-- {name="", backface_culling=true},
|
||||
-- {name="", backface_culling=true},
|
||||
--},
|
||||
alpha = 255,
|
||||
post_effect_color = {a=0, r=0, g=0, b=0},
|
||||
paramtype = "none",
|
||||
|
||||
@@ -72,7 +72,8 @@ minetest.register_entity("__builtin:item", {
|
||||
local p = self.object:getpos()
|
||||
p.y = p.y - 0.3
|
||||
local nn = minetest.env:get_node(p).name
|
||||
if minetest.registered_nodes[nn].walkable then
|
||||
-- If node is not registered or node is walkably solid
|
||||
if not minetest.registered_nodes[nn] or minetest.registered_nodes[nn].walkable then
|
||||
if self.physical_state then
|
||||
self.object:setvelocity({x=0,y=0,z=0})
|
||||
self.object:setacceleration({x=0, y=0, z=0})
|
||||
|
||||
@@ -244,6 +244,7 @@ minetest.register_item(":unknown", {
|
||||
inventory_image = "unknown_item.png",
|
||||
on_place = minetest.item_place,
|
||||
on_drop = minetest.item_drop,
|
||||
groups = {not_in_creative_inventory=1},
|
||||
})
|
||||
|
||||
minetest.register_node(":air", {
|
||||
@@ -258,6 +259,7 @@ minetest.register_node(":air", {
|
||||
diggable = false,
|
||||
buildable_to = true,
|
||||
air_equivalent = true,
|
||||
groups = {not_in_creative_inventory=1},
|
||||
})
|
||||
|
||||
minetest.register_node(":ignore", {
|
||||
@@ -272,23 +274,15 @@ minetest.register_node(":ignore", {
|
||||
diggable = false,
|
||||
buildable_to = true, -- A way to remove accidentally placed ignores
|
||||
air_equivalent = true,
|
||||
groups = {not_in_creative_inventory=1},
|
||||
})
|
||||
|
||||
-- The hand (bare definition)
|
||||
minetest.register_item(":", {
|
||||
type = "none",
|
||||
groups = {not_in_creative_inventory=1},
|
||||
})
|
||||
|
||||
--
|
||||
-- Creative inventory
|
||||
--
|
||||
|
||||
minetest.creative_inventory = {}
|
||||
|
||||
minetest.add_to_creative_inventory = function(itemstring)
|
||||
table.insert(minetest.creative_inventory, itemstring)
|
||||
end
|
||||
|
||||
--
|
||||
-- Callback registration
|
||||
--
|
||||
@@ -299,26 +293,22 @@ local function make_registration()
|
||||
return t, registerfunc
|
||||
end
|
||||
|
||||
local function make_registration_reverse()
|
||||
local t = {}
|
||||
local registerfunc = function(func) table.insert(t, 1, func) end
|
||||
return t, registerfunc
|
||||
end
|
||||
|
||||
minetest.registered_on_chat_messages, minetest.register_on_chat_message = make_registration()
|
||||
minetest.registered_globalsteps, minetest.register_globalstep = make_registration()
|
||||
minetest.registered_on_punchnodes, minetest.register_on_punchnode = make_registration()
|
||||
minetest.registered_on_placenodes, minetest.register_on_placenode = make_registration()
|
||||
minetest.registered_on_dignodes, minetest.register_on_dignode = make_registration()
|
||||
minetest.registered_on_generateds, minetest.register_on_generated = make_registration()
|
||||
minetest.registered_on_newplayers, minetest.register_on_newplayer = make_registration()
|
||||
minetest.registered_on_dieplayers, minetest.register_on_dieplayer = make_registration()
|
||||
minetest.registered_on_respawnplayers, minetest.register_on_respawnplayer = make_registration()
|
||||
minetest.registered_on_joinplayers, minetest.register_on_joinplayer = make_registration()
|
||||
minetest.registered_on_leaveplayers, minetest.register_on_leaveplayer = make_registration()
|
||||
|
||||
minetest.registered_on_placenodes = {}
|
||||
minetest.register_on_placenode = function(callback)
|
||||
minetest.log("info", debug.traceback())
|
||||
minetest.log("info", "WARNING: minetest.register_on_placenode is deprecated. Use on_construct or after_place_node in node definition instead.")
|
||||
table.insert(minetest.registered_on_placenodes, callback)
|
||||
end
|
||||
minetest.registered_on_dignodes = {}
|
||||
minetest.register_on_dignode = function(callback)
|
||||
minetest.log("info", debug.traceback())
|
||||
minetest.log("info", "WARNING: minetest.register_on_dignode is deprecated. Use on_destruct or after_dig_node in node definition instead.")
|
||||
table.insert(minetest.registered_on_dignodes, callback)
|
||||
end
|
||||
minetest.registered_on_player_receive_fields, minetest.register_on_player_receive_fields = make_registration_reverse()
|
||||
|
||||
|
||||
@@ -44,5 +44,5 @@ minetest.register_privilege("fast", {
|
||||
description = "Can walk fast using the fast_move mode",
|
||||
give_to_singleplayer = false,
|
||||
})
|
||||
|
||||
minetest.register_privilege("rollback", "Can use the rollback functionality")
|
||||
|
||||
|
||||
340
doc/lua_api.txt
340
doc/lua_api.txt
@@ -1,4 +1,4 @@
|
||||
Minetest Lua Modding API Reference 0.4.dev
|
||||
Minetest Lua Modding API Reference 0.4.0
|
||||
==========================================
|
||||
More information at http://c55.me/minetest/
|
||||
|
||||
@@ -254,9 +254,9 @@ Nodes are passed by value between Lua and the engine.
|
||||
They are represented by a table:
|
||||
{name="name", param1=num, param2=num}
|
||||
|
||||
param1 and param2 are 8 bit and 4 bit integers, respectively. The engine
|
||||
uses them for certain automated functions. If you don't use these
|
||||
functions, you can use them to store arbitrary values.
|
||||
param1 and param2 are 8 bit integers. The engine uses them for certain
|
||||
automated functions. If you don't use these functions, you can use them to
|
||||
store arbitrary values.
|
||||
|
||||
The functions of param1 and param2 are determined by certain fields in the
|
||||
node definition:
|
||||
@@ -280,6 +280,61 @@ param2 is reserved for the engine when any of these are used:
|
||||
|
||||
Nodes can also contain extra data. See "Node Metadata".
|
||||
|
||||
Node drawtypes
|
||||
---------------
|
||||
There are a bunch of different looking node types. These are mostly just
|
||||
copied from Minetest 0.3; more may be made in the future.
|
||||
|
||||
Look for examples in games/minimal or games/minetest_game.
|
||||
|
||||
- normal
|
||||
- airlike
|
||||
- liquid
|
||||
- flowingliquid
|
||||
- glasslike
|
||||
- allfaces
|
||||
- allfaces_optional
|
||||
- torchlike
|
||||
- signlike
|
||||
- plantlike
|
||||
- fencelike
|
||||
- raillike
|
||||
- nodebox -- See below. EXPERIMENTAL
|
||||
|
||||
Node boxes
|
||||
-----------
|
||||
Node selection boxes are defined using "node boxes"
|
||||
|
||||
The "nodebox" node drawtype allows defining visual of nodes consisting of
|
||||
arbitrary number of boxes. It allows defining stuff like stairs. Only the
|
||||
"fixed" box type is supported for these.
|
||||
^ Please note that this is still experimental, and may be incompatibly
|
||||
changed in the future.
|
||||
|
||||
A nodebox is defined as any of:
|
||||
{
|
||||
-- A normal cube; the default in most things
|
||||
type = "regular"
|
||||
}
|
||||
{
|
||||
-- A fixed box (facedir param2 is used, if applicable)
|
||||
type = "fixed",
|
||||
fixed = box OR {box1, box2, ...}
|
||||
}
|
||||
{
|
||||
-- A box like the selection box for torches
|
||||
-- (wallmounted param2 is used, if applicable)
|
||||
type = "wallmounted",
|
||||
wall_top = box,
|
||||
wall_bottom = box,
|
||||
wall_side = box
|
||||
}
|
||||
|
||||
A box is defined as:
|
||||
{x1, y1, z1, x2, y2, z2}
|
||||
A box of a regular node would look like:
|
||||
{-0.5, -0.5, -0.5, 0.5, 0.5, 0.5},
|
||||
|
||||
Representations of simple things
|
||||
--------------------------------
|
||||
Position/vector:
|
||||
@@ -287,6 +342,11 @@ Position/vector:
|
||||
Currently the API does not provide any helper functions for addition,
|
||||
subtraction and whatever; you can define those that you need yourself.
|
||||
|
||||
pointed_thing:
|
||||
{type="nothing"}
|
||||
{type="node", under=pos, above=pos}
|
||||
{type="object", ref=ObjectRef}
|
||||
|
||||
Items
|
||||
------
|
||||
Node (register_node):
|
||||
@@ -363,7 +423,7 @@ effective towards.
|
||||
|
||||
Groups in crafting recipes
|
||||
---------------------------
|
||||
An example:
|
||||
An example: Make meat soup from any meat, any water and any bowl
|
||||
{
|
||||
output = 'food:meat_soup_raw',
|
||||
recipe = {
|
||||
@@ -371,7 +431,13 @@ An example:
|
||||
{'group:water'},
|
||||
{'group:bowl'},
|
||||
},
|
||||
preserve = {'group:bowl'}, -- Not implemented yet (TODO)
|
||||
-- preserve = {'group:bowl'}, -- Not implemented yet (TODO)
|
||||
}
|
||||
An another example: Make red wool from white wool and red dye
|
||||
{
|
||||
type = 'shapeless',
|
||||
output = 'wool:red',
|
||||
recipe = {'wool:white', 'group:dye,basecolor_red'},
|
||||
}
|
||||
|
||||
Special groups
|
||||
@@ -612,10 +678,12 @@ Examples:
|
||||
|
||||
Elements:
|
||||
|
||||
invsize[<W>,<H>;]
|
||||
size[<W>,<H>]
|
||||
^ Define the size of the menu in inventory slots
|
||||
^ deprecated: invsize[<W>,<H>;]
|
||||
|
||||
list[<inventory location>;<list name>;<X>,<Y>;<W>,<H>;]
|
||||
list[<inventory location>;<list name>;<X>,<Y>;<W>,<H>;<starting item index>]
|
||||
^ Show an inventory list
|
||||
|
||||
image[<X>,<Y>;<W>,<H>;<texture name>]
|
||||
@@ -624,20 +692,53 @@ image[<X>,<Y>;<W>,<H>;<texture name>]
|
||||
|
||||
field[<X>,<Y>;<W>,<H>;<name>;<label>;<default>]
|
||||
^ Textual field; will be sent to server when a button is clicked
|
||||
^ x and y position the field relative to the top left of the menu
|
||||
^ w and h are the size of the field
|
||||
^ fields are a set height, but will be vertically centred on h
|
||||
^ Position and size units are inventory slots
|
||||
^ name is the name of the field as returned in fields to on_receive_fields
|
||||
^ label, if not blank, will be text printed on the top left above the field
|
||||
^ default is the default value of the field
|
||||
^ default may contain variable references such as '${text}' which
|
||||
will fill the value from the metadata value 'text'
|
||||
^ Note: no extra text or more than a single variable is supported ATM.
|
||||
|
||||
field[<name>;<label>;<default>]
|
||||
^ as above but without position/size units
|
||||
^ special field for creating simple forms, such as sign text input
|
||||
^ must be used without a size[] element
|
||||
^ a 'Proceed' button will be added automatically
|
||||
|
||||
label[<X>,<Y>;<label>]
|
||||
^ x and y work as per field
|
||||
^ label is the text on the label
|
||||
^ Position and size units are inventory slots
|
||||
^ Not implemented
|
||||
|
||||
button[<X>,<Y>;<W>,<H>;<name>;<label>]
|
||||
^ Clickable button. When clicked, fields will be sent.
|
||||
^ Button will be visible as a field, with the value "active".
|
||||
^ x, y and name work as per field
|
||||
^ w and h are the size of the button
|
||||
^ label is the text on the button
|
||||
^ Position and size units are inventory slots
|
||||
^ Not implemented
|
||||
|
||||
image_button[<X>,<Y>;<W>,<H>;<texture name>;<name>;<label>]
|
||||
^ x, y, w, h, and name work as per button
|
||||
^ image is the filename of an image
|
||||
^ Position and size units are inventory slots
|
||||
|
||||
button_exit[<X>,<Y>;<W>,<H>;<name>;<label>]
|
||||
^ When clicked, fields will be sent and the form will quit.
|
||||
|
||||
image_button_exit[<X>,<Y>;<W>,<H>;<texture name>;<name>;<label>]
|
||||
^ When clicked, fields will be sent and the form will quit.
|
||||
|
||||
Inventory location:
|
||||
|
||||
- "context": Selected node metadata (deprecated: "current_name")
|
||||
- "current_player": Player to whom the menu is shown
|
||||
- "player:<name>": Any player
|
||||
- "nodemeta:<X>,<Y>,<Z>": Any node metadata
|
||||
- "detached:<name>": A detached inventory
|
||||
|
||||
Helper functions
|
||||
-----------------
|
||||
@@ -658,6 +759,8 @@ minetest namespace reference
|
||||
minetest.get_current_modname() -> string
|
||||
minetest.get_modpath(modname) -> eg. "/home/user/.minetest/usermods/modname"
|
||||
^ Useful for loading additional .lua modules or static data from mod
|
||||
minetest.get_modnames() -> list of installed mods
|
||||
^ Return a list of installed mods, sorted alphabetically
|
||||
minetest.get_worldpath() -> eg. "/home/user/.minetest/world"
|
||||
^ Useful for storing custom data
|
||||
minetest.is_singleplayer()
|
||||
@@ -680,11 +783,11 @@ minetest.register_craft(recipe)
|
||||
Global callback registration functions: (Call these only at load time)
|
||||
minetest.register_globalstep(func(dtime))
|
||||
^ Called every server step, usually interval of 0.05s
|
||||
minetest.register_on_placenode(func(pos, newnode, placer))
|
||||
minetest.register_on_placenode(func(pos, newnode, placer, oldnode))
|
||||
^ Called when a node has been placed
|
||||
^ Deprecated: Use on_construct or after_place_node in node definition instead
|
||||
minetest.register_on_dignode(func(pos, oldnode, digger))
|
||||
^ Called when a node has been dug. digger can be nil.
|
||||
^ Called when a node has been dug.
|
||||
^ Deprecated: Use on_destruct or after_dig_node in node definition instead
|
||||
minetest.register_on_punchnode(func(pos, node, puncher))
|
||||
^ Called when a node is punched
|
||||
@@ -699,7 +802,16 @@ minetest.register_on_respawnplayer(func(ObjectRef))
|
||||
^ Called when player is to be respawned
|
||||
^ Called _before_ repositioning of player occurs
|
||||
^ return true in func to disable regular player placement
|
||||
minetest.register_on_joinplayer(func(ObjectRef))
|
||||
^ Called when a player joins the game
|
||||
minetest.register_on_leaveplayer(func(ObjectRef))
|
||||
^ Called when a player leaves the game
|
||||
minetest.register_on_chat_message(func(name, message))
|
||||
^ Called always when a player says something
|
||||
minetest.register_on_player_receive_fields(func(player, formname, fields))
|
||||
^ Called when a button is pressed in player's inventory form
|
||||
^ Newest functions are called first
|
||||
^ If function returns true, remaining functions are not called
|
||||
|
||||
Other registration functions:
|
||||
minetest.register_chatcommand(cmd, chatcommand definition)
|
||||
@@ -744,6 +856,10 @@ Inventory:
|
||||
minetest.get_inventory(location) -> InvRef
|
||||
^ location = eg. {type="player", name="celeron55"}
|
||||
{type="node", pos={x=, y=, z=}}
|
||||
{type="detached", name="creative"}
|
||||
minetest.create_detached_inventory(name, callbacks) -> InvRef
|
||||
^ callbacks: See "Detached inventory callbacks"
|
||||
^ Creates a detached inventory. If it already exists, it is cleared.
|
||||
|
||||
Item handling:
|
||||
minetest.inventorycube(img1, img2, img3)
|
||||
@@ -765,6 +881,21 @@ minetest.get_craft_result(input) -> output, decremented_input
|
||||
^ output.item = ItemStack, if unsuccessful: empty ItemStack
|
||||
^ output.time = number, if unsuccessful: 0
|
||||
^ decremented_input = like input
|
||||
minetest.get_craft_recipe(output) -> input
|
||||
^ output is a node or item type such as 'default:torch'
|
||||
^ input.method = 'normal' or 'cooking' or 'fuel'
|
||||
^ input.width = for example 3
|
||||
^ input.items = for example { stack 1, stack 2, stack 3, stack 4,
|
||||
stack 5, stack 6, stack 7, stack 8, stack 9 }
|
||||
^ input.items = nil if no recipe found
|
||||
|
||||
Rollbacks:
|
||||
minetest.rollback_get_last_node_actor(p, range, seconds) -> actor, p, seconds
|
||||
^ Find who has done something to a node, or near a node
|
||||
^ actor: "player:<name>", also "liquid".
|
||||
minetest.rollback_revert_actions_by(actor, seconds) -> bool, log messages
|
||||
^ Revert latest actions of someone
|
||||
^ actor: "player:<name>", also "liquid".
|
||||
|
||||
Defaults for the on_* item definition functions:
|
||||
(These return the leftover itemstack)
|
||||
@@ -868,12 +999,14 @@ methods:
|
||||
^ Dig node with the same effects that a player would cause
|
||||
- punch_node(pos)
|
||||
^ Punch node with the same effects that a player would cause
|
||||
|
||||
- get_meta(pos) -- Get a NodeMetaRef at that position
|
||||
- get_node_timer(pos) -- Get NodeTimerRef
|
||||
|
||||
- add_entity(pos, name): Spawn Lua-defined entity at position
|
||||
^ Returns ObjectRef, or nil if failed
|
||||
- add_item(pos, item): Spawn item
|
||||
^ Returns ObjectRef, or nil if failed
|
||||
- get_meta(pos) -- Get a NodeMetaRef at that position
|
||||
- get_player_by_name(name) -- Get an ObjectRef to a player
|
||||
- get_objects_inside_radius(pos, radius)
|
||||
- set_timeofday(val): val: 0...1; 0 = midnight, 0.5 = midday
|
||||
@@ -902,6 +1035,26 @@ methods:
|
||||
- to_table() -> nil or {fields = {...}, inventory = {list1 = {}, ...}}
|
||||
- from_table(nil or {})
|
||||
^ See "Node Metadata"
|
||||
|
||||
NodeTimerRef: Node Timers - a high resolution persistent per-node timer
|
||||
- Can be gotten via minetest.env:get_node_timer(pos)
|
||||
methods:
|
||||
- set(timeout,elapsed)
|
||||
^ set a timer's state
|
||||
^ timeout is in seconds, and supports fractional values (0.1 etc)
|
||||
^ elapsed is in seconds, and supports fractional values (0.1 etc)
|
||||
^ will trigger the node's on_timer function after timeout-elapsed seconds
|
||||
- start(timeout)
|
||||
^ start a timer
|
||||
^ equivelent to set(timeout,0)
|
||||
- stop()
|
||||
^ stops the timer
|
||||
- get_timeout() -> current timeout in seconds
|
||||
^ if timeout is 0, timer is inactive
|
||||
- get_elapsed() -> current elapsed time in seconds
|
||||
^ the node's on_timer function will be called after timeout-elapsed seconds
|
||||
- is_started() -> boolean state of timer
|
||||
^ returns true if timer is started, otherwise false
|
||||
|
||||
ObjectRef: Moving things in the game are generally these
|
||||
(basically reference to a C++ ServerActiveObject)
|
||||
@@ -933,8 +1086,8 @@ LuaEntitySAO-only: (no-op for other objects)
|
||||
- settexturemod(mod)
|
||||
- setsprite(p={x=0,y=0}, num_frames=1, framelength=0.2,
|
||||
- select_horiz_by_yawpitch=false)
|
||||
- ^ Select sprite from spritesheet with optional animation and DM-style
|
||||
- texture selection based on yaw relative to camera
|
||||
^ Select sprite from spritesheet with optional animation and DM-style
|
||||
texture selection based on yaw relative to camera
|
||||
- get_entity_name() (DEPRECATED: Will be removed in a future version)
|
||||
- get_luaentity()
|
||||
Player-only: (no-op for other objects)
|
||||
@@ -943,6 +1096,10 @@ Player-only: (no-op for other objects)
|
||||
- get_look_dir(): get camera direction as a unit vector
|
||||
- get_look_pitch(): pitch in radians
|
||||
- get_look_yaw(): yaw in radians (wraps around pretty randomly as of now)
|
||||
- set_inventory_formspec(formspec)
|
||||
^ Redefine player's inventory form
|
||||
^ Should usually be called in on_joinplayer
|
||||
- get_inventory_formspec() -> formspec string
|
||||
|
||||
InvRef: Reference to an inventory
|
||||
methods:
|
||||
@@ -1036,7 +1193,9 @@ Definition tables
|
||||
|
||||
Object Properties
|
||||
{
|
||||
hp_max = 1,
|
||||
physical = true,
|
||||
weight = 5,
|
||||
collisionbox = {-0.5,-0.5,-0.5, 0.5,0.5,0.5},
|
||||
visual = "cube"/"sprite"/"upright_sprite",
|
||||
visual_size = {x=1, y=1},
|
||||
@@ -1045,6 +1204,7 @@ Object Properties
|
||||
initial_sprite_basepos = {x=0, y=0},
|
||||
is_visible = true,
|
||||
makes_footstep_sound = false,
|
||||
automatic_rotate = false,
|
||||
}
|
||||
|
||||
Entity definition (register_entity)
|
||||
@@ -1100,10 +1260,19 @@ Item definition (register_node, register_craftitem, register_tool)
|
||||
choppy={times={[3]=0.90}, maxwear=0.05, maxlevel=0}
|
||||
}
|
||||
}
|
||||
node_placement_prediction = nil,
|
||||
^ If nil and item is node, prediction is made automatically
|
||||
^ If nil and item is not a node, no prediction is made
|
||||
^ If "" and item is anything, no prediction is made
|
||||
^ Otherwise should be name of node which the client immediately places
|
||||
on ground when the player places the item. Server will always update
|
||||
actual result to client in a short moment.
|
||||
|
||||
on_place = func(itemstack, placer, pointed_thing),
|
||||
^ Shall place item and return the leftover itemstack
|
||||
^ default: minetest.item_place
|
||||
on_drop = func(itemstack, dropper, pos),
|
||||
^ Shall drop item and return the leftover itemstack
|
||||
^ default: minetest.item_drop
|
||||
on_use = func(itemstack, user, pointed_thing),
|
||||
^ default: nil
|
||||
@@ -1114,37 +1283,49 @@ Item definition (register_node, register_craftitem, register_tool)
|
||||
^ The default functions handle regular use cases.
|
||||
}
|
||||
|
||||
Tile definition:
|
||||
- "image.png"
|
||||
- {name="image.png", animation={Tile Animation definition}}
|
||||
- {name="image.png", backface_culling=bool}
|
||||
^ backface culling only supported in special tiles
|
||||
- deprecated still supported field names:
|
||||
- image -> name
|
||||
|
||||
Tile animation definition:
|
||||
- {type="vertical_frames", aspect_w=16, aspect_h=16, length=3.0}
|
||||
|
||||
Node definition (register_node)
|
||||
{
|
||||
<all fields allowed in item definitions>,
|
||||
|
||||
drawtype = "normal",
|
||||
drawtype = "normal", -- See "Node drawtypes"
|
||||
visual_scale = 1.0,
|
||||
tile_images = {"default_unknown_block.png"},
|
||||
special_materials = {
|
||||
{image="", backface_culling=true},
|
||||
{image="", backface_culling=true},
|
||||
},
|
||||
tiles = {tile definition 1, def2, def3, def4, def5, def6},
|
||||
^ Textures of node; +Y, -Y, +X, -X, +Z, -Z (old field name: tile_images)
|
||||
^ List can be shortened to needed length
|
||||
special_tiles = {tile definition 1, Tile definition 2},
|
||||
^ Special textures of node; used rarely (old field name: special_materials)
|
||||
^ List can be shortened to needed length
|
||||
alpha = 255,
|
||||
post_effect_color = {a=0, r=0, g=0, b=0},
|
||||
paramtype = "none",
|
||||
paramtype2 = "none",
|
||||
is_ground_content = false,
|
||||
sunlight_propagates = false,
|
||||
walkable = true,
|
||||
pointable = true,
|
||||
diggable = true,
|
||||
climbable = false,
|
||||
buildable_to = false,
|
||||
drop = "",
|
||||
-- alternatively drop = { max_items = ..., items = { ... } }
|
||||
liquidtype = "none",
|
||||
liquid_alternative_flowing = "",
|
||||
liquid_alternative_source = "",
|
||||
liquid_viscosity = 0,
|
||||
light_source = 0,
|
||||
damage_per_second = 0,
|
||||
selection_box = {type="regular"},
|
||||
post_effect_color = {a=0, r=0, g=0, b=0}, -- If player is inside node
|
||||
paramtype = "none", -- See "Nodes"
|
||||
paramtype2 = "none", -- See "Nodes"
|
||||
is_ground_content = false, -- Currently not used for anything
|
||||
sunlight_propagates = false, -- If true, sunlight will go infinitely through this
|
||||
walkable = true, -- If true, objects collide with node
|
||||
pointable = true, -- If true, can be pointed at
|
||||
diggable = true, -- If false, can never be dug
|
||||
climbable = false, -- If true, can be climbed on (ladder)
|
||||
buildable_to = false, -- If true, placed nodes can replace this node
|
||||
drop = "", -- alternatively drop = { max_items = ..., items = { ... } }
|
||||
liquidtype = "none", -- "none"/"source"/"flowing"
|
||||
liquid_alternative_flowing = "", -- Flowing version of source liquid
|
||||
liquid_alternative_source = "", -- Source version of flowing liquid
|
||||
liquid_viscosity = 0, -- Higher viscosity = slower flow (max. 7)
|
||||
light_source = 0, -- Amount of light emitted by 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"
|
||||
legacy_facedir_simple = false, -- Support maps made in and before January 2012
|
||||
legacy_wallmounted = false, -- Support maps made in and before January 2012
|
||||
sounds = {
|
||||
@@ -1180,44 +1361,42 @@ Node definition (register_node)
|
||||
on_punch = func(pos, node, puncher),
|
||||
^ default: minetest.node_punch
|
||||
^ By default: does nothing
|
||||
on_dig = func(pos, node, digger),
|
||||
on_dig = func(pos, node, digger),
|
||||
^ default: minetest.node_dig
|
||||
^ By default: checks privileges, wears out tool and removes node
|
||||
|
||||
on_timer = function(pos,elapsed),
|
||||
^ default: nil
|
||||
^ called by NodeTimers, see EnvRef and NodeTimerRef
|
||||
^ elapsed is the total time passed since the timer was started
|
||||
^ return true to run the timer for another cycle with the same timeout value
|
||||
|
||||
on_receive_fields = func(pos, formname, fields, sender),
|
||||
^ fields = {name1 = value1, name2 = value2, ...}
|
||||
^ Called when an UI form (eg. sign text input) returns data
|
||||
^ default: nil
|
||||
|
||||
on_metadata_inventory_move = func(pos, from_list, from_index,
|
||||
to_list, to_index, count, player),
|
||||
^ Called when a player wants to move items inside the metadata
|
||||
^ Should move items, or some items, if permitted. If not, should do
|
||||
nothing.
|
||||
^ The engine ensures the action is valid, i.e. the stack fits at the
|
||||
given position
|
||||
^ default: minetest.node_metadata_inventory_move_allow_all
|
||||
allow_metadata_inventory_move = func(pos, from_list, from_index,
|
||||
to_list, to_index, count, player),
|
||||
^ Called when a player wants to move items inside the inventory
|
||||
^ Return value: number of items allowed to move
|
||||
|
||||
allow_metadata_inventory_put = func(pos, listname, index, stack, player),
|
||||
^ Called when a player wants to put something into the inventory
|
||||
^ Return value: number of items allowed to put
|
||||
^ Return value: -1: Allow and don't modify item count in inventory
|
||||
|
||||
allow_metadata_inventory_take = func(pos, listname, index, stack, player),
|
||||
^ Called when a player wants to take something out of the inventory
|
||||
^ Return value: number of items allowed to take
|
||||
^ Return value: -1: Allow and don't modify item count in inventory
|
||||
|
||||
on_metadata_inventory_offer = func(pos, listname, index, stack, player),
|
||||
^ Called when a player wants to put something into the metadata
|
||||
inventory
|
||||
^ Should check if the action is permitted (the engine ensures the
|
||||
action is valid, i.e. the stack fits at the given position)
|
||||
^ If permitted, modify the metadata inventory and return the
|
||||
"leftover" stack (normally nil).
|
||||
^ If not permitted, return itemstack.
|
||||
^ default: minetest.node_metadata_inventory_offer_allow_all
|
||||
|
||||
on_metadata_inventory_take = func(pos, listname, index, count, player),
|
||||
^ Called when a player wants to take something out of the metadata
|
||||
inventory
|
||||
^ Should check if the action is permitted (the engine ensures the
|
||||
action is valid, i.e. there's a stack of at least “count” items at
|
||||
that position)
|
||||
^ If permitted, modify the metadata inventory and return the
|
||||
stack of items
|
||||
^ If not permitted, return nil.
|
||||
^ default: minetest.node_metadata_inventory_take_allow_all
|
||||
on_metadata_inventory_move = func(pos, from_list, from_index,
|
||||
to_list, to_index, count, player),
|
||||
on_metadata_inventory_put = func(pos, listname, index, stack, player),
|
||||
on_metadata_inventory_take = func(pos, listname, index, stack, player),
|
||||
^ Called after the actual action has happened, according to what was allowed.
|
||||
^ No return value
|
||||
}
|
||||
|
||||
Recipe for register_craft: (shaped)
|
||||
@@ -1274,3 +1453,26 @@ Chatcommand definition (register_chatcommand)
|
||||
func = function(name, param), -- called when command is run
|
||||
}
|
||||
|
||||
Detached inventory callbacks
|
||||
{
|
||||
allow_move = func(inv, from_list, from_index, to_list, to_index, count, player),
|
||||
^ Called when a player wants to move items inside the inventory
|
||||
^ Return value: number of items allowed to move
|
||||
|
||||
allow_put = func(inv, listname, index, stack, player),
|
||||
^ Called when a player wants to put something into the inventory
|
||||
^ Return value: number of items allowed to put
|
||||
^ Return value: -1: Allow and don't modify item count in inventory
|
||||
|
||||
allow_take = func(inv, listname, index, stack, player),
|
||||
^ Called when a player wants to take something out of the inventory
|
||||
^ Return value: number of items allowed to take
|
||||
^ Return value: -1: Allow and don't modify item count in inventory
|
||||
|
||||
on_move = func(inv, from_list, from_index, to_list, to_index, count, player),
|
||||
on_put = func(inv, listname, index, stack, player),
|
||||
on_take = func(inv, listname, index, stack, player),
|
||||
^ Called after the actual action has happened, according to what was allowed.
|
||||
^ No return value
|
||||
}
|
||||
|
||||
|
||||
@@ -1,13 +1,16 @@
|
||||
=================================================
|
||||
Minetest World Format used as of 0.4.dev-20120322
|
||||
=================================================
|
||||
=============================
|
||||
Minetest World Format 22...25
|
||||
=============================
|
||||
|
||||
This applies to a world format carrying the block serialization version 22
|
||||
which is used at least in version 0.4.dev-20120322.
|
||||
This applies to a world format carrying the block serialization version
|
||||
22...25, used at least in
|
||||
- 0.4.dev-20120322 ... 0.4.dev-20120606 (22...23)
|
||||
- 0.4.0 (23)
|
||||
- 24 was never released as stable and existed for ~2 days
|
||||
|
||||
The block serialization version used is 22. It does not fully specify every
|
||||
aspect of this format; if compliance with this format is to be checked, it
|
||||
needs to be done by detecting if the files and data indeed follows it.
|
||||
The block serialization version does not fully specify every aspect of this
|
||||
format; if compliance with this format is to be checked, it needs to be
|
||||
done by detecting if the files and data indeed follows it.
|
||||
|
||||
Legacy stuff
|
||||
=============
|
||||
@@ -20,8 +23,8 @@ Files
|
||||
Everything is contained in a directory, the name of which is freeform, but
|
||||
often serves as the name of the world.
|
||||
|
||||
Currently the authentication and ban data is stored on a per-world basis. It
|
||||
can be copied over from an old world to a newly created world.
|
||||
Currently the authentication and ban data is stored on a per-world basis.
|
||||
It can be copied over from an old world to a newly created world.
|
||||
|
||||
World
|
||||
|-- auth.txt ----- Authentication data
|
||||
@@ -260,17 +263,26 @@ u8 flags
|
||||
|
||||
u8 content_width
|
||||
- Number of bytes in the content (param0) fields of nodes
|
||||
- Always 1
|
||||
if map format version <= 23:
|
||||
- Always 1
|
||||
if map format version >= 24:
|
||||
- Always 2
|
||||
|
||||
u8 params_width
|
||||
- Number of bytes used for parameters per node
|
||||
- Always 2
|
||||
|
||||
zlib-compressed node data:
|
||||
- content:
|
||||
u8[4096]: param0 fields
|
||||
u8[4096]: param1 fields
|
||||
u8[4096]: param2 fields
|
||||
if content_width == 1:
|
||||
- content:
|
||||
u8[4096]: param0 fields
|
||||
u8[4096]: param1 fields
|
||||
u8[4096]: param2 fields
|
||||
if content_width == 2:
|
||||
- content:
|
||||
u16[4096]: param0 fields
|
||||
u8[4096]: param1 fields
|
||||
u8[4096]: param2 fields
|
||||
- The location of a node in each of those arrays is (z*16*16 + y*16 + x).
|
||||
|
||||
zlib-compressed node metadata list
|
||||
@@ -283,9 +295,19 @@ zlib-compressed node metadata list
|
||||
u16 content_size
|
||||
u8[content_size] (content of metadata)
|
||||
|
||||
u16 mapblockobject_count
|
||||
- Always 0
|
||||
- Should be removed in version 23 (TODO)
|
||||
- Node timers
|
||||
if map format version == 23:
|
||||
u8 unused version (always 0)
|
||||
if map format version == 24: (NOTE: Not released as stable)
|
||||
u8 nodetimer_version
|
||||
if nodetimer_version == 0:
|
||||
(nothing else)
|
||||
if nodetimer_version == 1:
|
||||
u16 num_of_timers
|
||||
foreach num_of_timers:
|
||||
u16 timer position (z*16*16 + y*16 + x)
|
||||
s32 timeout*1000
|
||||
s32 elapsed*1000
|
||||
|
||||
u8 static object version:
|
||||
- Always 0
|
||||
@@ -315,17 +337,29 @@ foreach num_name_id_mappings
|
||||
u16 name_len
|
||||
u8[name_len] name
|
||||
|
||||
- Node timers
|
||||
if map format version == 25:
|
||||
u8 length of the data of a single timer (always 2+4+4=10)
|
||||
u16 num_of_timers
|
||||
foreach num_of_timers:
|
||||
u16 timer position (z*16*16 + y*16 + x)
|
||||
s32 timeout*1000
|
||||
s32 elapsed*1000
|
||||
|
||||
EOF.
|
||||
|
||||
Format of nodes
|
||||
----------------
|
||||
A node is composed of the u8 fields param0, param1 and param2.
|
||||
|
||||
The content id of a node is determined as so:
|
||||
- If param0 < 0x80,
|
||||
content_id = param0
|
||||
- Otherwise
|
||||
content_id = (param0<<4) + (param2>>4)
|
||||
if map format version <= 23:
|
||||
The content id of a node is determined as so:
|
||||
- If param0 < 0x80,
|
||||
content_id = param0
|
||||
- Otherwise
|
||||
content_id = (param0<<4) + (param2>>4)
|
||||
if map format version >= 24:
|
||||
The content id of a node is param0.
|
||||
|
||||
The purpose of param1 and param2 depend on the definition of the node.
|
||||
|
||||
|
||||
@@ -90,6 +90,6 @@ bucket.register_liquid(
|
||||
|
||||
minetest.register_craft({
|
||||
type = "fuel",
|
||||
recipe = "default:bucket_lava",
|
||||
recipe = "bucket:bucket_lava",
|
||||
burntime = 60,
|
||||
})
|
||||
|
||||
@@ -14,6 +14,14 @@ default = {}
|
||||
-- Load other files
|
||||
dofile(minetest.get_modpath("default").."/mapgen.lua")
|
||||
|
||||
-- Set a noticeable inventory formspec for players
|
||||
minetest.register_on_joinplayer(function(player)
|
||||
local cb = function(player)
|
||||
minetest.chat_send_player(player:get_player_name(), "This is the [minimal] \"Minimal Development Test\" game. Use [minetest_game] for the real thing.")
|
||||
end
|
||||
minetest.after(2.0, cb, player)
|
||||
end)
|
||||
|
||||
--
|
||||
-- Tool definition
|
||||
--
|
||||
@@ -701,7 +709,7 @@ end
|
||||
|
||||
minetest.register_node("default:stone", {
|
||||
description = "Stone",
|
||||
tile_images = {"default_stone.png"},
|
||||
tiles ={"default_stone.png"},
|
||||
is_ground_content = true,
|
||||
groups = {cracky=3},
|
||||
drop = 'default:cobble',
|
||||
@@ -711,7 +719,7 @@ minetest.register_node("default:stone", {
|
||||
|
||||
minetest.register_node("default:stone_with_coal", {
|
||||
description = "Stone with coal",
|
||||
tile_images = {"default_stone.png^default_mineral_coal.png"},
|
||||
tiles ={"default_stone.png^default_mineral_coal.png"},
|
||||
is_ground_content = true,
|
||||
groups = {cracky=3},
|
||||
drop = 'default:coal_lump',
|
||||
@@ -720,7 +728,7 @@ minetest.register_node("default:stone_with_coal", {
|
||||
|
||||
minetest.register_node("default:stone_with_iron", {
|
||||
description = "Stone with iron",
|
||||
tile_images = {"default_stone.png^default_mineral_iron.png"},
|
||||
tiles ={"default_stone.png^default_mineral_iron.png"},
|
||||
is_ground_content = true,
|
||||
groups = {cracky=3},
|
||||
drop = 'default:iron_lump',
|
||||
@@ -729,7 +737,7 @@ minetest.register_node("default:stone_with_iron", {
|
||||
|
||||
minetest.register_node("default:dirt_with_grass", {
|
||||
description = "Dirt with grass",
|
||||
tile_images = {"default_grass.png", "default_dirt.png", "default_dirt.png^default_grass_side.png"},
|
||||
tiles ={"default_grass.png", "default_dirt.png", "default_dirt.png^default_grass_side.png"},
|
||||
is_ground_content = true,
|
||||
groups = {crumbly=3},
|
||||
drop = 'default:dirt',
|
||||
@@ -740,7 +748,7 @@ minetest.register_node("default:dirt_with_grass", {
|
||||
|
||||
minetest.register_node("default:dirt_with_grass_footsteps", {
|
||||
description = "Dirt with grass and footsteps",
|
||||
tile_images = {"default_grass_footsteps.png", "default_dirt.png", "default_dirt.png^default_grass_side.png"},
|
||||
tiles ={"default_grass_footsteps.png", "default_dirt.png", "default_dirt.png^default_grass_side.png"},
|
||||
is_ground_content = true,
|
||||
groups = {crumbly=3},
|
||||
drop = 'default:dirt',
|
||||
@@ -751,7 +759,7 @@ minetest.register_node("default:dirt_with_grass_footsteps", {
|
||||
|
||||
minetest.register_node("default:dirt", {
|
||||
description = "Dirt",
|
||||
tile_images = {"default_dirt.png"},
|
||||
tiles ={"default_dirt.png"},
|
||||
is_ground_content = true,
|
||||
groups = {crumbly=3},
|
||||
sounds = default.node_sound_dirt_defaults(),
|
||||
@@ -759,7 +767,7 @@ minetest.register_node("default:dirt", {
|
||||
|
||||
minetest.register_node("default:sand", {
|
||||
description = "Sand",
|
||||
tile_images = {"default_sand.png"},
|
||||
tiles ={"default_sand.png"},
|
||||
is_ground_content = true,
|
||||
groups = {crumbly=3},
|
||||
sounds = default.node_sound_sand_defaults(),
|
||||
@@ -767,7 +775,7 @@ minetest.register_node("default:sand", {
|
||||
|
||||
minetest.register_node("default:gravel", {
|
||||
description = "Gravel",
|
||||
tile_images = {"default_gravel.png"},
|
||||
tiles ={"default_gravel.png"},
|
||||
is_ground_content = true,
|
||||
groups = {crumbly=2},
|
||||
sounds = default.node_sound_dirt_defaults({
|
||||
@@ -777,7 +785,7 @@ minetest.register_node("default:gravel", {
|
||||
|
||||
minetest.register_node("default:sandstone", {
|
||||
description = "Sandstone",
|
||||
tile_images = {"default_sandstone.png"},
|
||||
tiles ={"default_sandstone.png"},
|
||||
is_ground_content = true,
|
||||
groups = {crumbly=2,cracky=2},
|
||||
drop = 'default:sand',
|
||||
@@ -786,7 +794,7 @@ minetest.register_node("default:sandstone", {
|
||||
|
||||
minetest.register_node("default:clay", {
|
||||
description = "Clay",
|
||||
tile_images = {"default_clay.png"},
|
||||
tiles ={"default_clay.png"},
|
||||
is_ground_content = true,
|
||||
groups = {crumbly=3},
|
||||
drop = 'default:clay_lump 4',
|
||||
@@ -797,7 +805,7 @@ minetest.register_node("default:clay", {
|
||||
|
||||
minetest.register_node("default:brick", {
|
||||
description = "Brick",
|
||||
tile_images = {"default_brick.png"},
|
||||
tiles ={"default_brick.png"},
|
||||
is_ground_content = true,
|
||||
groups = {cracky=3},
|
||||
drop = 'default:clay_brick 4',
|
||||
@@ -806,7 +814,7 @@ minetest.register_node("default:brick", {
|
||||
|
||||
minetest.register_node("default:tree", {
|
||||
description = "Tree",
|
||||
tile_images = {"default_tree_top.png", "default_tree_top.png", "default_tree.png"},
|
||||
tiles ={"default_tree_top.png", "default_tree_top.png", "default_tree.png"},
|
||||
is_ground_content = true,
|
||||
groups = {snappy=2,choppy=2,oddly_breakable_by_hand=1},
|
||||
sounds = default.node_sound_wood_defaults(),
|
||||
@@ -814,7 +822,7 @@ minetest.register_node("default:tree", {
|
||||
|
||||
minetest.register_node("default:jungletree", {
|
||||
description = "Jungle Tree",
|
||||
tile_images = {"default_jungletree_top.png", "default_jungletree_top.png", "default_jungletree.png"},
|
||||
tiles ={"default_jungletree_top.png", "default_jungletree_top.png", "default_jungletree.png"},
|
||||
is_ground_content = true,
|
||||
groups = {snappy=2,choppy=2,oddly_breakable_by_hand=1},
|
||||
sounds = default.node_sound_wood_defaults(),
|
||||
@@ -824,7 +832,7 @@ minetest.register_node("default:junglegrass", {
|
||||
description = "Jungle Grass",
|
||||
drawtype = "plantlike",
|
||||
visual_scale = 1.3,
|
||||
tile_images = {"default_junglegrass.png"},
|
||||
tiles ={"default_junglegrass.png"},
|
||||
inventory_image = "default_junglegrass.png",
|
||||
wield_image = "default_junglegrass.png",
|
||||
paramtype = "light",
|
||||
@@ -837,7 +845,7 @@ minetest.register_node("default:leaves", {
|
||||
description = "Leaves",
|
||||
drawtype = "allfaces_optional",
|
||||
visual_scale = 1.3,
|
||||
tile_images = {"default_leaves.png"},
|
||||
tiles ={"default_leaves.png"},
|
||||
paramtype = "light",
|
||||
groups = {snappy=3},
|
||||
drop = {
|
||||
@@ -860,7 +868,7 @@ minetest.register_node("default:leaves", {
|
||||
|
||||
minetest.register_node("default:cactus", {
|
||||
description = "Cactus",
|
||||
tile_images = {"default_cactus_top.png", "default_cactus_top.png", "default_cactus_side.png"},
|
||||
tiles ={"default_cactus_top.png", "default_cactus_top.png", "default_cactus_side.png"},
|
||||
is_ground_content = true,
|
||||
groups = {snappy=2,choppy=3},
|
||||
sounds = default.node_sound_wood_defaults(),
|
||||
@@ -869,7 +877,7 @@ minetest.register_node("default:cactus", {
|
||||
minetest.register_node("default:papyrus", {
|
||||
description = "Papyrus",
|
||||
drawtype = "plantlike",
|
||||
tile_images = {"default_papyrus.png"},
|
||||
tiles ={"default_papyrus.png"},
|
||||
inventory_image = "default_papyrus.png",
|
||||
wield_image = "default_papyrus.png",
|
||||
paramtype = "light",
|
||||
@@ -881,7 +889,7 @@ minetest.register_node("default:papyrus", {
|
||||
|
||||
minetest.register_node("default:bookshelf", {
|
||||
description = "Bookshelf",
|
||||
tile_images = {"default_wood.png", "default_wood.png", "default_bookshelf.png"},
|
||||
tiles ={"default_wood.png", "default_wood.png", "default_bookshelf.png"},
|
||||
is_ground_content = true,
|
||||
groups = {snappy=2,choppy=3,oddly_breakable_by_hand=2},
|
||||
sounds = default.node_sound_wood_defaults(),
|
||||
@@ -890,7 +898,7 @@ minetest.register_node("default:bookshelf", {
|
||||
minetest.register_node("default:glass", {
|
||||
description = "Glass",
|
||||
drawtype = "glasslike",
|
||||
tile_images = {"default_glass.png"},
|
||||
tiles ={"default_glass.png"},
|
||||
inventory_image = minetest.inventorycube("default_glass.png"),
|
||||
paramtype = "light",
|
||||
sunlight_propagates = true,
|
||||
@@ -902,7 +910,7 @@ minetest.register_node("default:glass", {
|
||||
minetest.register_node("default:fence_wood", {
|
||||
description = "Wooden Fence",
|
||||
drawtype = "fencelike",
|
||||
tile_images = {"default_wood.png"},
|
||||
tiles ={"default_wood.png"},
|
||||
inventory_image = "default_fence.png",
|
||||
wield_image = "default_fence.png",
|
||||
paramtype = "light",
|
||||
@@ -918,7 +926,7 @@ minetest.register_node("default:fence_wood", {
|
||||
minetest.register_node("default:rail", {
|
||||
description = "Rail",
|
||||
drawtype = "raillike",
|
||||
tile_images = {"default_rail.png", "default_rail_curved.png", "default_rail_t_junction.png", "default_rail_crossing.png"},
|
||||
tiles ={"default_rail.png", "default_rail_curved.png", "default_rail_t_junction.png", "default_rail_crossing.png"},
|
||||
inventory_image = "default_rail.png",
|
||||
wield_image = "default_rail.png",
|
||||
paramtype = "light",
|
||||
@@ -926,7 +934,7 @@ minetest.register_node("default:rail", {
|
||||
walkable = false,
|
||||
selection_box = {
|
||||
type = "fixed",
|
||||
--fixed = <default>
|
||||
fixed = {-1/2, -1/2, -1/2, 1/2, -1/2+1/16, 1/2},
|
||||
},
|
||||
groups = {bendy=2,snappy=1,dig_immediate=2},
|
||||
})
|
||||
@@ -934,7 +942,7 @@ minetest.register_node("default:rail", {
|
||||
minetest.register_node("default:ladder", {
|
||||
description = "Ladder",
|
||||
drawtype = "signlike",
|
||||
tile_images = {"default_ladder.png"},
|
||||
tiles ={"default_ladder.png"},
|
||||
inventory_image = "default_ladder.png",
|
||||
wield_image = "default_ladder.png",
|
||||
paramtype = "light",
|
||||
@@ -955,7 +963,7 @@ minetest.register_node("default:ladder", {
|
||||
|
||||
minetest.register_node("default:wood", {
|
||||
description = "Wood",
|
||||
tile_images = {"default_wood.png"},
|
||||
tiles ={"default_wood.png"},
|
||||
is_ground_content = true,
|
||||
groups = {snappy=2,choppy=2,oddly_breakable_by_hand=2},
|
||||
sounds = default.node_sound_wood_defaults(),
|
||||
@@ -963,7 +971,7 @@ minetest.register_node("default:wood", {
|
||||
|
||||
minetest.register_node("default:mese", {
|
||||
description = "Mese",
|
||||
tile_images = {"default_mese.png"},
|
||||
tiles ={"default_mese.png"},
|
||||
is_ground_content = true,
|
||||
groups = {cracky=1,level=2},
|
||||
sounds = default.node_sound_defaults(),
|
||||
@@ -971,7 +979,7 @@ minetest.register_node("default:mese", {
|
||||
|
||||
minetest.register_node("default:cloud", {
|
||||
description = "Cloud",
|
||||
tile_images = {"default_cloud.png"},
|
||||
tiles ={"default_cloud.png"},
|
||||
is_ground_content = true,
|
||||
sounds = default.node_sound_defaults(),
|
||||
})
|
||||
@@ -980,7 +988,11 @@ minetest.register_node("default:water_flowing", {
|
||||
description = "Water (flowing)",
|
||||
inventory_image = minetest.inventorycube("default_water.png"),
|
||||
drawtype = "flowingliquid",
|
||||
tile_images = {"default_water.png"},
|
||||
tiles ={"default_water.png"},
|
||||
special_tiles = {
|
||||
{name="default_water.png", backface_culling=false},
|
||||
{name="default_water.png", backface_culling=true},
|
||||
},
|
||||
alpha = WATER_ALPHA,
|
||||
paramtype = "light",
|
||||
walkable = false,
|
||||
@@ -992,10 +1004,6 @@ minetest.register_node("default:water_flowing", {
|
||||
liquid_alternative_source = "default:water_source",
|
||||
liquid_viscosity = WATER_VISC,
|
||||
post_effect_color = {a=64, r=100, g=100, b=200},
|
||||
special_materials = {
|
||||
{image="default_water.png", backface_culling=false},
|
||||
{image="default_water.png", backface_culling=true},
|
||||
},
|
||||
groups = {water=3, liquid=3},
|
||||
})
|
||||
|
||||
@@ -1003,7 +1011,11 @@ minetest.register_node("default:water_source", {
|
||||
description = "Water",
|
||||
inventory_image = minetest.inventorycube("default_water.png"),
|
||||
drawtype = "liquid",
|
||||
tile_images = {"default_water.png"},
|
||||
tiles ={"default_water.png"},
|
||||
special_tiles = {
|
||||
-- New-style water source material (mostly unused)
|
||||
{name="default_water.png", backface_culling=false},
|
||||
},
|
||||
alpha = WATER_ALPHA,
|
||||
paramtype = "light",
|
||||
walkable = false,
|
||||
@@ -1015,10 +1027,6 @@ minetest.register_node("default:water_source", {
|
||||
liquid_alternative_source = "default:water_source",
|
||||
liquid_viscosity = WATER_VISC,
|
||||
post_effect_color = {a=64, r=100, g=100, b=200},
|
||||
special_materials = {
|
||||
-- New-style water source material (mostly unused)
|
||||
{image="default_water.png", backface_culling=false},
|
||||
},
|
||||
groups = {water=3, liquid=3},
|
||||
})
|
||||
|
||||
@@ -1026,7 +1034,19 @@ minetest.register_node("default:lava_flowing", {
|
||||
description = "Lava (flowing)",
|
||||
inventory_image = minetest.inventorycube("default_lava.png"),
|
||||
drawtype = "flowingliquid",
|
||||
tile_images = {"default_lava.png"},
|
||||
tiles ={"default_lava.png"},
|
||||
special_tiles = {
|
||||
{
|
||||
image="default_lava_flowing_animated.png",
|
||||
backface_culling=false,
|
||||
animation={type="vertical_frames", aspect_w=16, aspect_h=16, length=3.3}
|
||||
},
|
||||
{
|
||||
image="default_lava_flowing_animated.png",
|
||||
backface_culling=true,
|
||||
animation={type="vertical_frames", aspect_w=16, aspect_h=16, length=3.3}
|
||||
},
|
||||
},
|
||||
paramtype = "light",
|
||||
light_source = LIGHT_MAX - 1,
|
||||
walkable = false,
|
||||
@@ -1039,10 +1059,6 @@ minetest.register_node("default:lava_flowing", {
|
||||
liquid_viscosity = LAVA_VISC,
|
||||
damage_per_second = 4*2,
|
||||
post_effect_color = {a=192, r=255, g=64, b=0},
|
||||
special_materials = {
|
||||
{image="default_lava.png", backface_culling=false},
|
||||
{image="default_lava.png", backface_culling=true},
|
||||
},
|
||||
groups = {lava=3, liquid=2, hot=3},
|
||||
})
|
||||
|
||||
@@ -1050,7 +1066,14 @@ minetest.register_node("default:lava_source", {
|
||||
description = "Lava",
|
||||
inventory_image = minetest.inventorycube("default_lava.png"),
|
||||
drawtype = "liquid",
|
||||
tile_images = {"default_lava.png"},
|
||||
--tiles ={"default_lava.png"},
|
||||
tiles ={
|
||||
{name="default_lava_source_animated.png", animation={type="vertical_frames", aspect_w=16, aspect_h=16, length=3.0}}
|
||||
},
|
||||
special_tiles = {
|
||||
-- New-style lava source material (mostly unused)
|
||||
{name="default_lava.png", backface_culling=false},
|
||||
},
|
||||
paramtype = "light",
|
||||
light_source = LIGHT_MAX - 1,
|
||||
walkable = false,
|
||||
@@ -1063,17 +1086,13 @@ minetest.register_node("default:lava_source", {
|
||||
liquid_viscosity = LAVA_VISC,
|
||||
damage_per_second = 4*2,
|
||||
post_effect_color = {a=192, r=255, g=64, b=0},
|
||||
special_materials = {
|
||||
-- New-style lava source material (mostly unused)
|
||||
{image="default_lava.png", backface_culling=false},
|
||||
},
|
||||
groups = {lava=3, liquid=2, hot=3},
|
||||
})
|
||||
|
||||
minetest.register_node("default:torch", {
|
||||
description = "Torch",
|
||||
drawtype = "torchlike",
|
||||
tile_images = {"default_torch_on_floor.png", "default_torch_on_ceiling.png", "default_torch.png"},
|
||||
tiles ={"default_torch_on_floor.png", "default_torch_on_ceiling.png", "default_torch.png"},
|
||||
inventory_image = "default_torch_on_floor.png",
|
||||
wield_image = "default_torch_on_floor.png",
|
||||
paramtype = "light",
|
||||
@@ -1095,14 +1114,13 @@ minetest.register_node("default:torch", {
|
||||
minetest.register_node("default:sign_wall", {
|
||||
description = "Sign",
|
||||
drawtype = "signlike",
|
||||
tile_images = {"default_sign_wall.png"},
|
||||
tiles ={"default_sign_wall.png"},
|
||||
inventory_image = "default_sign_wall.png",
|
||||
wield_image = "default_sign_wall.png",
|
||||
paramtype = "light",
|
||||
paramtype2 = "wallmounted",
|
||||
sunlight_propagates = true,
|
||||
walkable = false,
|
||||
metadata_name = "sign",
|
||||
selection_box = {
|
||||
type = "wallmounted",
|
||||
--wall_top = <default>
|
||||
@@ -1115,7 +1133,7 @@ minetest.register_node("default:sign_wall", {
|
||||
on_construct = function(pos)
|
||||
--local n = minetest.env:get_node(pos)
|
||||
local meta = minetest.env:get_meta(pos)
|
||||
meta:set_string("formspec", "hack:sign_text_input")
|
||||
meta:set_string("formspec", "field[text;;${text}]")
|
||||
meta:set_string("infotext", "\"\"")
|
||||
end,
|
||||
on_receive_fields = function(pos, formname, fields, sender)
|
||||
@@ -1131,7 +1149,7 @@ minetest.register_node("default:sign_wall", {
|
||||
|
||||
minetest.register_node("default:chest", {
|
||||
description = "Chest",
|
||||
tile_images = {"default_chest_top.png", "default_chest_top.png", "default_chest_side.png",
|
||||
tiles ={"default_chest_top.png", "default_chest_top.png", "default_chest_side.png",
|
||||
"default_chest_side.png", "default_chest_side.png", "default_chest_front.png"},
|
||||
paramtype2 = "facedir",
|
||||
groups = {snappy=2,choppy=2,oddly_breakable_by_hand=2},
|
||||
@@ -1140,7 +1158,7 @@ minetest.register_node("default:chest", {
|
||||
on_construct = function(pos)
|
||||
local meta = minetest.env:get_meta(pos)
|
||||
meta:set_string("formspec",
|
||||
"invsize[8,9;]"..
|
||||
"size[8,9]"..
|
||||
"list[current_name;main;0,0;8,4;]"..
|
||||
"list[current_player;main;0,5;8,4;]")
|
||||
meta:set_string("infotext", "Chest")
|
||||
@@ -1152,25 +1170,6 @@ minetest.register_node("default:chest", {
|
||||
local inv = meta:get_inventory()
|
||||
return inv:is_empty("main")
|
||||
end,
|
||||
on_metadata_inventory_move = function(pos, from_list, from_index,
|
||||
to_list, to_index, count, player)
|
||||
minetest.log("action", player:get_player_name()..
|
||||
" moves stuff in chest at "..minetest.pos_to_string(pos))
|
||||
return minetest.node_metadata_inventory_move_allow_all(
|
||||
pos, from_list, from_index, to_list, to_index, count, player)
|
||||
end,
|
||||
on_metadata_inventory_offer = function(pos, listname, index, stack, player)
|
||||
minetest.log("action", player:get_player_name()..
|
||||
" moves stuff to chest at "..minetest.pos_to_string(pos))
|
||||
return minetest.node_metadata_inventory_offer_allow_all(
|
||||
pos, listname, index, stack, player)
|
||||
end,
|
||||
on_metadata_inventory_take = function(pos, listname, index, count, player)
|
||||
minetest.log("action", player:get_player_name()..
|
||||
" takes stuff from chest at "..minetest.pos_to_string(pos))
|
||||
return minetest.node_metadata_inventory_take_allow_all(
|
||||
pos, listname, index, count, player)
|
||||
end,
|
||||
})
|
||||
|
||||
local function has_locked_chest_privilege(meta, player)
|
||||
@@ -1182,7 +1181,7 @@ end
|
||||
|
||||
minetest.register_node("default:chest_locked", {
|
||||
description = "Locked Chest",
|
||||
tile_images = {"default_chest_top.png", "default_chest_top.png", "default_chest_side.png",
|
||||
tiles ={"default_chest_top.png", "default_chest_top.png", "default_chest_side.png",
|
||||
"default_chest_side.png", "default_chest_side.png", "default_chest_lock.png"},
|
||||
paramtype2 = "facedir",
|
||||
groups = {snappy=2,choppy=2,oddly_breakable_by_hand=2},
|
||||
@@ -1197,7 +1196,7 @@ minetest.register_node("default:chest_locked", {
|
||||
on_construct = function(pos)
|
||||
local meta = minetest.env:get_meta(pos)
|
||||
meta:set_string("formspec",
|
||||
"invsize[8,9;]"..
|
||||
"size[8,9]"..
|
||||
"list[current_name;main;0,0;8,4;]"..
|
||||
"list[current_player;main;0,5;8,4;]")
|
||||
meta:set_string("infotext", "Locked Chest")
|
||||
@@ -1210,53 +1209,55 @@ minetest.register_node("default:chest_locked", {
|
||||
local inv = meta:get_inventory()
|
||||
return inv:is_empty("main")
|
||||
end,
|
||||
on_metadata_inventory_move = function(pos, from_list, from_index,
|
||||
to_list, to_index, count, player)
|
||||
allow_metadata_inventory_move = function(pos, from_list, from_index, to_list, to_index, count, player)
|
||||
local meta = minetest.env:get_meta(pos)
|
||||
if not has_locked_chest_privilege(meta, player) then
|
||||
minetest.log("action", player:get_player_name()..
|
||||
" tried to access a locked chest belonging to "..
|
||||
meta:get_string("owner").." at "..
|
||||
minetest.pos_to_string(pos))
|
||||
return
|
||||
return 0
|
||||
end
|
||||
return count
|
||||
end,
|
||||
allow_metadata_inventory_put = function(pos, listname, index, stack, player)
|
||||
local meta = minetest.env:get_meta(pos)
|
||||
if not has_locked_chest_privilege(meta, player) then
|
||||
minetest.log("action", player:get_player_name()..
|
||||
" tried to access a locked chest belonging to "..
|
||||
meta:get_string("owner").." at "..
|
||||
minetest.pos_to_string(pos))
|
||||
return 0
|
||||
end
|
||||
return stack:get_count()
|
||||
end,
|
||||
allow_metadata_inventory_take = function(pos, listname, index, stack, player)
|
||||
local meta = minetest.env:get_meta(pos)
|
||||
if not has_locked_chest_privilege(meta, player) then
|
||||
minetest.log("action", player:get_player_name()..
|
||||
" tried to access a locked chest belonging to "..
|
||||
meta:get_string("owner").." at "..
|
||||
minetest.pos_to_string(pos))
|
||||
return 0
|
||||
end
|
||||
return stack:get_count()
|
||||
end,
|
||||
on_metadata_inventory_move = function(pos, from_list, from_index, to_list, to_index, count, player)
|
||||
minetest.log("action", player:get_player_name()..
|
||||
" moves stuff in locked chest at "..minetest.pos_to_string(pos))
|
||||
return minetest.node_metadata_inventory_move_allow_all(
|
||||
pos, from_list, from_index, to_list, to_index, count, player)
|
||||
end,
|
||||
on_metadata_inventory_offer = function(pos, listname, index, stack, player)
|
||||
local meta = minetest.env:get_meta(pos)
|
||||
if not has_locked_chest_privilege(meta, player) then
|
||||
minetest.log("action", player:get_player_name()..
|
||||
" tried to access a locked chest belonging to "..
|
||||
meta:get_string("owner").." at "..
|
||||
minetest.pos_to_string(pos))
|
||||
return stack
|
||||
end
|
||||
on_metadata_inventory_put = function(pos, listname, index, stack, player)
|
||||
minetest.log("action", player:get_player_name()..
|
||||
" moves stuff to locked chest at "..minetest.pos_to_string(pos))
|
||||
return minetest.node_metadata_inventory_offer_allow_all(
|
||||
pos, listname, index, stack, player)
|
||||
end,
|
||||
on_metadata_inventory_take = function(pos, listname, index, count, player)
|
||||
local meta = minetest.env:get_meta(pos)
|
||||
if not has_locked_chest_privilege(meta, player) then
|
||||
minetest.log("action", player:get_player_name()..
|
||||
" tried to access a locked chest belonging to "..
|
||||
meta:get_string("owner").." at "..
|
||||
minetest.pos_to_string(pos))
|
||||
return
|
||||
end
|
||||
on_metadata_inventory_take = function(pos, listname, index, stack, player)
|
||||
minetest.log("action", player:get_player_name()..
|
||||
" takes stuff from locked chest at "..minetest.pos_to_string(pos))
|
||||
return minetest.node_metadata_inventory_take_allow_all(
|
||||
pos, listname, index, count, player)
|
||||
end,
|
||||
})
|
||||
|
||||
default.furnace_inactive_formspec =
|
||||
"invsize[8,9;]"..
|
||||
"size[8,9]"..
|
||||
"image[2,2;1,1;default_furnace_fire_bg.png]"..
|
||||
"list[current_name;fuel;2,3;1,1;]"..
|
||||
"list[current_name;src;2,1;1,1;]"..
|
||||
@@ -1265,7 +1266,7 @@ default.furnace_inactive_formspec =
|
||||
|
||||
minetest.register_node("default:furnace", {
|
||||
description = "Furnace",
|
||||
tile_images = {"default_furnace_side.png", "default_furnace_side.png", "default_furnace_side.png",
|
||||
tiles ={"default_furnace_side.png", "default_furnace_side.png", "default_furnace_side.png",
|
||||
"default_furnace_side.png", "default_furnace_side.png", "default_furnace_front.png"},
|
||||
paramtype2 = "facedir",
|
||||
groups = {cracky=2},
|
||||
@@ -1296,7 +1297,7 @@ minetest.register_node("default:furnace", {
|
||||
|
||||
minetest.register_node("default:furnace_active", {
|
||||
description = "Furnace",
|
||||
tile_images = {"default_furnace_side.png", "default_furnace_side.png", "default_furnace_side.png",
|
||||
tiles ={"default_furnace_side.png", "default_furnace_side.png", "default_furnace_side.png",
|
||||
"default_furnace_side.png", "default_furnace_side.png", "default_furnace_front_active.png"},
|
||||
paramtype2 = "facedir",
|
||||
light_source = 8,
|
||||
@@ -1395,7 +1396,7 @@ minetest.register_abm({
|
||||
meta:set_string("infotext","Furnace active: "..percent.."%")
|
||||
hacky_swap_node(pos,"default:furnace_active")
|
||||
meta:set_string("formspec",
|
||||
"invsize[8,9;]"..
|
||||
"size[8,9]"..
|
||||
"image[2,2;1,1;default_furnace_fire_bg.png^[lowpart:"..
|
||||
(100-percent)..":default_furnace_fire_fg.png]"..
|
||||
"list[current_name;fuel;2,3;1,1;]"..
|
||||
@@ -1444,7 +1445,7 @@ minetest.register_abm({
|
||||
|
||||
minetest.register_node("default:cobble", {
|
||||
description = "Cobble",
|
||||
tile_images = {"default_cobble.png"},
|
||||
tiles ={"default_cobble.png"},
|
||||
is_ground_content = true,
|
||||
groups = {cracky=3},
|
||||
sounds = default.node_sound_stone_defaults(),
|
||||
@@ -1452,7 +1453,7 @@ minetest.register_node("default:cobble", {
|
||||
|
||||
minetest.register_node("default:mossycobble", {
|
||||
description = "Mossy Cobble",
|
||||
tile_images = {"default_mossycobble.png"},
|
||||
tiles ={"default_mossycobble.png"},
|
||||
is_ground_content = true,
|
||||
groups = {cracky=3},
|
||||
sounds = default.node_sound_stone_defaults(),
|
||||
@@ -1460,7 +1461,7 @@ minetest.register_node("default:mossycobble", {
|
||||
|
||||
minetest.register_node("default:steelblock", {
|
||||
description = "Steel Block",
|
||||
tile_images = {"default_steel_block.png"},
|
||||
tiles ={"default_steel_block.png"},
|
||||
is_ground_content = true,
|
||||
groups = {snappy=1,bendy=2},
|
||||
sounds = default.node_sound_stone_defaults(),
|
||||
@@ -1468,7 +1469,7 @@ minetest.register_node("default:steelblock", {
|
||||
|
||||
minetest.register_node("default:nyancat", {
|
||||
description = "Nyancat",
|
||||
tile_images = {"default_nc_side.png", "default_nc_side.png", "default_nc_side.png",
|
||||
tiles ={"default_nc_side.png", "default_nc_side.png", "default_nc_side.png",
|
||||
"default_nc_side.png", "default_nc_back.png", "default_nc_front.png"},
|
||||
inventory_image = "default_nc_front.png",
|
||||
paramtype2 = "facedir",
|
||||
@@ -1479,7 +1480,7 @@ minetest.register_node("default:nyancat", {
|
||||
|
||||
minetest.register_node("default:nyancat_rainbow", {
|
||||
description = "Nyancat Rainbow",
|
||||
tile_images = {"default_nc_rb.png"},
|
||||
tiles ={"default_nc_rb.png"},
|
||||
inventory_image = "default_nc_rb.png",
|
||||
groups = {cracky=2},
|
||||
sounds = default.node_sound_defaults(),
|
||||
@@ -1489,7 +1490,7 @@ minetest.register_node("default:sapling", {
|
||||
description = "Sapling",
|
||||
drawtype = "plantlike",
|
||||
visual_scale = 1.0,
|
||||
tile_images = {"default_sapling.png"},
|
||||
tiles ={"default_sapling.png"},
|
||||
inventory_image = "default_sapling.png",
|
||||
wield_image = "default_sapling.png",
|
||||
paramtype = "light",
|
||||
@@ -1502,7 +1503,7 @@ minetest.register_node("default:apple", {
|
||||
description = "Apple",
|
||||
drawtype = "plantlike",
|
||||
visual_scale = 1.0,
|
||||
tile_images = {"default_apple.png"},
|
||||
tiles ={"default_apple.png"},
|
||||
inventory_image = "default_apple.png",
|
||||
paramtype = "light",
|
||||
sunlight_propagates = true,
|
||||
@@ -1562,40 +1563,6 @@ minetest.register_craftitem("default:scorched_stuff", {
|
||||
inventory_image = "default_scorched_stuff.png",
|
||||
})
|
||||
|
||||
--
|
||||
-- Creative inventory
|
||||
--
|
||||
|
||||
minetest.add_to_creative_inventory('default:pick_mese')
|
||||
minetest.add_to_creative_inventory('default:pick_steel')
|
||||
minetest.add_to_creative_inventory('default:axe_steel')
|
||||
minetest.add_to_creative_inventory('default:shovel_steel')
|
||||
|
||||
minetest.add_to_creative_inventory('default:torch')
|
||||
minetest.add_to_creative_inventory('default:cobble')
|
||||
minetest.add_to_creative_inventory('default:dirt')
|
||||
minetest.add_to_creative_inventory('default:stone')
|
||||
minetest.add_to_creative_inventory('default:sand')
|
||||
minetest.add_to_creative_inventory('default:sandstone')
|
||||
minetest.add_to_creative_inventory('default:clay')
|
||||
minetest.add_to_creative_inventory('default:brick')
|
||||
minetest.add_to_creative_inventory('default:tree')
|
||||
minetest.add_to_creative_inventory('default:wood')
|
||||
minetest.add_to_creative_inventory('default:leaves')
|
||||
minetest.add_to_creative_inventory('default:cactus')
|
||||
minetest.add_to_creative_inventory('default:papyrus')
|
||||
minetest.add_to_creative_inventory('default:bookshelf')
|
||||
minetest.add_to_creative_inventory('default:glass')
|
||||
minetest.add_to_creative_inventory('default:fence_wood')
|
||||
minetest.add_to_creative_inventory('default:rail')
|
||||
minetest.add_to_creative_inventory('default:mese')
|
||||
minetest.add_to_creative_inventory('default:chest')
|
||||
minetest.add_to_creative_inventory('default:furnace')
|
||||
minetest.add_to_creative_inventory('default:sign_wall')
|
||||
minetest.add_to_creative_inventory('default:water_source')
|
||||
minetest.add_to_creative_inventory('default:lava_source')
|
||||
minetest.add_to_creative_inventory('default:ladder')
|
||||
|
||||
--
|
||||
-- Aliases for the current map generator outputs
|
||||
--
|
||||
@@ -1712,133 +1679,24 @@ function on_punchnode(p, node)
|
||||
end
|
||||
minetest.register_on_punchnode(on_punchnode)
|
||||
|
||||
local function handle_give_command(cmd, giver, receiver, stackstring)
|
||||
if not minetest.get_player_privs(giver)["give"] then
|
||||
minetest.chat_send_player(giver, "error: you don't have permission to give")
|
||||
return
|
||||
end
|
||||
minetest.debug("DEBUG: "..cmd..' invoked, stackstring="'..stackstring..'"')
|
||||
minetest.log(cmd..' invoked, stackstring="'..stackstring..'"')
|
||||
local itemstack = ItemStack(stackstring)
|
||||
if itemstack:is_empty() then
|
||||
minetest.chat_send_player(giver, 'error: cannot give an empty item')
|
||||
return
|
||||
elseif not itemstack:is_known() then
|
||||
minetest.chat_send_player(giver, 'error: cannot give an unknown item')
|
||||
return
|
||||
end
|
||||
local receiverref = minetest.env:get_player_by_name(receiver)
|
||||
if receiverref == nil then
|
||||
minetest.chat_send_player(giver, receiver..' is not a known player')
|
||||
return
|
||||
end
|
||||
local leftover = receiverref:get_inventory():add_item("main", itemstack)
|
||||
if leftover:is_empty() then
|
||||
partiality = ""
|
||||
elseif leftover:get_count() == itemstack:get_count() then
|
||||
partiality = "could not be "
|
||||
else
|
||||
partiality = "partially "
|
||||
end
|
||||
-- The actual item stack string may be different from what the "giver"
|
||||
-- entered (e.g. big numbers are always interpreted as 2^16-1).
|
||||
stackstring = itemstack:to_string()
|
||||
if giver == receiver then
|
||||
minetest.chat_send_player(giver, '"'..stackstring
|
||||
..'" '..partiality..'added to inventory.');
|
||||
else
|
||||
minetest.chat_send_player(giver, '"'..stackstring
|
||||
..'" '..partiality..'added to '..receiver..'\'s inventory.');
|
||||
minetest.chat_send_player(receiver, '"'..stackstring
|
||||
..'" '..partiality..'added to inventory.');
|
||||
end
|
||||
end
|
||||
|
||||
minetest.register_on_chat_message(function(name, message)
|
||||
--print("default on_chat_message: name="..dump(name).." message="..dump(message))
|
||||
local cmd = "/giveme"
|
||||
if message:sub(0, #cmd) == cmd then
|
||||
local stackstring = string.match(message, cmd.." (.*)")
|
||||
if stackstring == nil then
|
||||
minetest.chat_send_player(name, 'usage: '..cmd..' stackstring')
|
||||
return true -- Handled chat message
|
||||
end
|
||||
handle_give_command(cmd, name, name, stackstring)
|
||||
return true
|
||||
end
|
||||
local cmd = "/give"
|
||||
if message:sub(0, #cmd) == cmd then
|
||||
local receiver, stackstring = string.match(message, cmd.." ([%a%d_-]+) (.*)")
|
||||
if receiver == nil or stackstring == nil then
|
||||
minetest.chat_send_player(name, 'usage: '..cmd..' name stackstring')
|
||||
return true -- Handled chat message
|
||||
end
|
||||
handle_give_command(cmd, name, receiver, stackstring)
|
||||
return true
|
||||
end
|
||||
local cmd = "/spawnentity"
|
||||
if message:sub(0, #cmd) == cmd then
|
||||
if not minetest.get_player_privs(name)["give"] then
|
||||
minetest.chat_send_player(name, "you don't have permission to spawn (give)")
|
||||
return true -- Handled chat message
|
||||
end
|
||||
if not minetest.get_player_privs(name)["interact"] then
|
||||
minetest.chat_send_player(name, "you don't have permission to interact")
|
||||
return true -- Handled chat message
|
||||
end
|
||||
local entityname = string.match(message, cmd.." (.*)")
|
||||
if entityname == nil then
|
||||
minetest.chat_send_player(name, 'usage: '..cmd..' entityname')
|
||||
return true -- Handled chat message
|
||||
end
|
||||
print(cmd..' invoked, entityname="'..entityname..'"')
|
||||
local player = minetest.env:get_player_by_name(name)
|
||||
if player == nil then
|
||||
print("Unable to spawn entity, player is nil")
|
||||
return true -- Handled chat message
|
||||
end
|
||||
local p = player:getpos()
|
||||
p.y = p.y + 1
|
||||
minetest.env:add_entity(p, entityname)
|
||||
minetest.chat_send_player(name, '"'..entityname
|
||||
..'" spawned.');
|
||||
return true -- Handled chat message
|
||||
end
|
||||
local cmd = "/pulverize"
|
||||
if message:sub(0, #cmd) == cmd then
|
||||
local player = minetest.env:get_player_by_name(name)
|
||||
if player == nil then
|
||||
print("Unable to pulverize, player is nil")
|
||||
return true -- Handled chat message
|
||||
end
|
||||
if player:get_wielded_item():is_empty() then
|
||||
minetest.chat_send_player(name, 'Unable to pulverize, no item in hand.')
|
||||
else
|
||||
player:set_wielded_item(nil)
|
||||
minetest.chat_send_player(name, 'An item was pulverized.')
|
||||
end
|
||||
return true
|
||||
end
|
||||
end)
|
||||
|
||||
--
|
||||
-- Test some things
|
||||
--
|
||||
|
||||
local function test_get_craft_result()
|
||||
print("test_get_craft_result()")
|
||||
minetest.log("info", "test_get_craft_result()")
|
||||
-- normal
|
||||
local input = {
|
||||
method = "normal",
|
||||
width = 2,
|
||||
items = {"", "default:coal_lump", "", "default:stick"}
|
||||
}
|
||||
print("torch crafting input: "..dump(input))
|
||||
minetest.log("info", "torch crafting input: "..dump(input))
|
||||
local output, decremented_input = minetest.get_craft_result(input)
|
||||
print("torch crafting output: "..dump(output))
|
||||
print("torch crafting decremented input: "..dump(decremented_input))
|
||||
minetest.log("info", "torch crafting output: "..dump(output))
|
||||
minetest.log("info", "torch crafting decremented input: "..dump(decremented_input))
|
||||
assert(output.item)
|
||||
print("torch crafting output.item:to_table(): "..dump(output.item:to_table()))
|
||||
minetest.log("info", "torch crafting output.item:to_table(): "..dump(output.item:to_table()))
|
||||
assert(output.item:get_name() == "default:torch")
|
||||
assert(output.item:get_count() == 4)
|
||||
-- fuel
|
||||
@@ -1847,10 +1705,10 @@ local function test_get_craft_result()
|
||||
width = 1,
|
||||
items = {"default:coal_lump"}
|
||||
}
|
||||
print("coal fuel input: "..dump(input))
|
||||
minetest.log("info", "coal fuel input: "..dump(input))
|
||||
local output, decremented_input = minetest.get_craft_result(input)
|
||||
print("coal fuel output: "..dump(output))
|
||||
print("coal fuel decremented input: "..dump(decremented_input))
|
||||
minetest.log("info", "coal fuel output: "..dump(output))
|
||||
minetest.log("info", "coal fuel decremented input: "..dump(decremented_input))
|
||||
assert(output.time)
|
||||
assert(output.time > 0)
|
||||
-- cook
|
||||
@@ -1859,14 +1717,14 @@ local function test_get_craft_result()
|
||||
width = 1,
|
||||
items = {"default:cobble"}
|
||||
}
|
||||
print("cobble cooking input: "..dump(output))
|
||||
minetest.log("info", "cobble cooking input: "..dump(output))
|
||||
local output, decremented_input = minetest.get_craft_result(input)
|
||||
print("cobble cooking output: "..dump(output))
|
||||
print("cobble cooking decremented input: "..dump(decremented_input))
|
||||
minetest.log("info", "cobble cooking output: "..dump(output))
|
||||
minetest.log("info", "cobble cooking decremented input: "..dump(decremented_input))
|
||||
assert(output.time)
|
||||
assert(output.time > 0)
|
||||
assert(output.item)
|
||||
print("cobble cooking output.item:to_table(): "..dump(output.item:to_table()))
|
||||
minetest.log("info", "cobble cooking output.item:to_table(): "..dump(output.item:to_table()))
|
||||
assert(output.item:get_name() == "default:stone")
|
||||
assert(output.item:get_count() == 1)
|
||||
end
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 1.1 KiB |
BIN
games/minimal/mods/default/textures/crack_anylength.png
Normal file
BIN
games/minimal/mods/default/textures/crack_anylength.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 367 B |
Binary file not shown.
|
After Width: | Height: | Size: 8.5 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 3.4 KiB |
@@ -443,15 +443,20 @@ minetest.register_abm({
|
||||
})--]]
|
||||
|
||||
minetest.register_node("experimental:tester_node_1", {
|
||||
description = "Tester Node 1",
|
||||
description = "Tester Node 1 (construct/destruct/timer)",
|
||||
tile_images = {"wieldhand.png"},
|
||||
groups = {oddly_breakable_by_hand=2},
|
||||
sounds = default.node_sound_wood_defaults(),
|
||||
-- This was known to cause a bug in minetest.item_place_node() when used
|
||||
-- via minetest.env:place_node(), causing a placer with no position
|
||||
paramtype2 = "facedir",
|
||||
|
||||
on_construct = function(pos)
|
||||
experimental.print_to_everything("experimental:tester_node_1:on_construct("..minetest.pos_to_string(pos)..")")
|
||||
local meta = minetest.env:get_meta(pos)
|
||||
meta:set_string("mine", "test")
|
||||
local timer = minetest.env:get_node_timer(pos)
|
||||
timer:start(4, 3)
|
||||
end,
|
||||
|
||||
after_place_node = function(pos, placer)
|
||||
@@ -475,6 +480,11 @@ minetest.register_node("experimental:tester_node_1", {
|
||||
after_dig_node = function(pos, oldnode, oldmetadata, digger)
|
||||
experimental.print_to_everything("experimental:tester_node_1:after_dig_node("..minetest.pos_to_string(pos)..")")
|
||||
end,
|
||||
|
||||
on_timer = function(pos, elapsed)
|
||||
experimental.print_to_everything("on_timer(): elapsed="..dump(elapsed))
|
||||
return true
|
||||
end,
|
||||
})
|
||||
|
||||
minetest.register_craftitem("experimental:tester_tool_1", {
|
||||
@@ -504,6 +514,77 @@ minetest.register_craft({
|
||||
}
|
||||
})
|
||||
|
||||
--[[minetest.register_on_joinplayer(function(player)
|
||||
minetest.after(3, function()
|
||||
player:set_inventory_formspec("size[8,7.5]"..
|
||||
"image[1,0.6;1,2;player.png]"..
|
||||
"list[current_player;main;0,3.5;8,4;]"..
|
||||
"list[current_player;craft;3,0;3,3;]"..
|
||||
"list[current_player;craftpreview;7,1;1,1;]")
|
||||
end)
|
||||
end)]]
|
||||
|
||||
-- Create a detached inventory
|
||||
local inv = minetest.create_detached_inventory("test_inventory", {
|
||||
allow_move = function(inv, from_list, from_index, to_list, to_index, count, player)
|
||||
experimental.print_to_everything("allow move asked")
|
||||
return count -- Allow all
|
||||
end,
|
||||
allow_put = function(inv, listname, index, stack, player)
|
||||
experimental.print_to_everything("allow put asked")
|
||||
return 1 -- Allow only 1
|
||||
end,
|
||||
allow_take = function(inv, listname, index, stack, player)
|
||||
experimental.print_to_everything("allow take asked")
|
||||
return 4 -- Allow 4 at max
|
||||
end,
|
||||
on_move = function(inv, from_list, from_index, to_list, to_index, count, player)
|
||||
experimental.print_to_everything(player:get_player_name().." moved items")
|
||||
end,
|
||||
on_put = function(inv, listname, index, stack, player)
|
||||
experimental.print_to_everything(player:get_player_name().." put items")
|
||||
end,
|
||||
on_take = function(inv, listname, index, stack, player)
|
||||
experimental.print_to_everything(player:get_player_name().." took items")
|
||||
end,
|
||||
})
|
||||
inv:set_size("main", 4*6)
|
||||
inv:add_item("main", "experimental:tester_tool_1")
|
||||
inv:add_item("main", "experimental:tnt 5")
|
||||
|
||||
minetest.register_chatcommand("test1", {
|
||||
params = "",
|
||||
description = "Test 1: Modify player's inventory view",
|
||||
func = function(name, param)
|
||||
local player = minetest.env:get_player_by_name(name)
|
||||
if not player then
|
||||
return
|
||||
end
|
||||
player:set_inventory_formspec(
|
||||
"size[13,7.5]"..
|
||||
"image[6,0.6;1,2;player.png]"..
|
||||
"list[current_player;main;5,3.5;8,4;]"..
|
||||
"list[current_player;craft;8,0;3,3;]"..
|
||||
"list[current_player;craftpreview;12,1;1,1;]"..
|
||||
"list[detached:test_inventory;main;0,0;4,6;0]"..
|
||||
"button[0.5,7;2,1;button1;Button 1]"..
|
||||
"button_exit[2.5,7;2,1;button2;Exit Button]"
|
||||
)
|
||||
minetest.chat_send_player(name, "Done.");
|
||||
end,
|
||||
})
|
||||
|
||||
minetest.register_on_player_receive_fields(function(player, formname, fields)
|
||||
experimental.print_to_everything("Inventory fields 1: player="..player:get_player_name()..", fields="..dump(fields))
|
||||
end)
|
||||
minetest.register_on_player_receive_fields(function(player, formname, fields)
|
||||
experimental.print_to_everything("Inventory fields 2: player="..player:get_player_name()..", fields="..dump(fields))
|
||||
return true -- Disable the first callback
|
||||
end)
|
||||
minetest.register_on_player_receive_fields(function(player, formname, fields)
|
||||
experimental.print_to_everything("Inventory fields 3: player="..player:get_player_name()..", fields="..dump(fields))
|
||||
end)
|
||||
|
||||
minetest.log("experimental modname="..dump(minetest.get_current_modname()))
|
||||
minetest.log("experimental modpath="..dump(minetest.get_modpath("experimental")))
|
||||
minetest.log("experimental worldpath="..dump(minetest.get_worldpath()))
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 209 B |
1
games/minimal/mods/stairs/depends.txt
Normal file
1
games/minimal/mods/stairs/depends.txt
Normal file
@@ -0,0 +1 @@
|
||||
default
|
||||
93
games/minimal/mods/stairs/init.lua
Normal file
93
games/minimal/mods/stairs/init.lua
Normal file
@@ -0,0 +1,93 @@
|
||||
stairs = {}
|
||||
|
||||
-- Node will be called stairs:stair_<subname>
|
||||
function stairs.register_stair(subname, recipeitem, groups, images, description)
|
||||
minetest.register_node("stairs:stair_" .. subname, {
|
||||
description = description,
|
||||
drawtype = "nodebox",
|
||||
tile_images = images,
|
||||
paramtype = "light",
|
||||
paramtype2 = "facedir",
|
||||
is_ground_content = true,
|
||||
groups = groups,
|
||||
node_box = {
|
||||
type = "fixed",
|
||||
fixed = {
|
||||
{-0.5, -0.5, -0.5, 0.5, 0, 0.5},
|
||||
{-0.5, 0, 0, 0.5, 0.5, 0.5},
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
minetest.register_craft({
|
||||
output = 'stairs:stair_' .. subname .. ' 4',
|
||||
recipe = {
|
||||
{recipeitem, "", ""},
|
||||
{recipeitem, recipeitem, ""},
|
||||
{recipeitem, recipeitem, recipeitem},
|
||||
},
|
||||
})
|
||||
end
|
||||
|
||||
-- Node will be called stairs:slab_<subname>
|
||||
function stairs.register_slab(subname, recipeitem, groups, images, description)
|
||||
minetest.register_node("stairs:slab_" .. subname, {
|
||||
description = description,
|
||||
drawtype = "nodebox",
|
||||
tile_images = images,
|
||||
paramtype = "light",
|
||||
is_ground_content = true,
|
||||
groups = groups,
|
||||
node_box = {
|
||||
type = "fixed",
|
||||
fixed = {-0.5, -0.5, -0.5, 0.5, 0, 0.5},
|
||||
},
|
||||
selection_box = {
|
||||
type = "fixed",
|
||||
fixed = {-0.5, -0.5, -0.5, 0.5, 0, 0.5},
|
||||
},
|
||||
})
|
||||
|
||||
minetest.register_craft({
|
||||
output = 'stairs:slab_' .. subname .. ' 3',
|
||||
recipe = {
|
||||
{recipeitem, recipeitem, recipeitem},
|
||||
},
|
||||
})
|
||||
end
|
||||
|
||||
-- Nodes will be called stairs:{stair,slab}_<subname>
|
||||
function stairs.register_stair_and_slab(subname, recipeitem, groups, images, desc_stair, desc_slab)
|
||||
stairs.register_stair(subname, recipeitem, groups, images, desc_stair)
|
||||
stairs.register_slab(subname, recipeitem, groups, images, desc_slab)
|
||||
end
|
||||
|
||||
stairs.register_stair_and_slab("wood", "default:wood",
|
||||
{snappy=2,choppy=2,oddly_breakable_by_hand=2},
|
||||
{"default_wood.png"},
|
||||
"Wooden stair",
|
||||
"Wooden slab")
|
||||
|
||||
stairs.register_stair_and_slab("stone", "default:stone",
|
||||
{cracky=3},
|
||||
{"default_stone.png"},
|
||||
"Stone stair",
|
||||
"Stone slab")
|
||||
|
||||
stairs.register_stair_and_slab("cobble", "default:cobble",
|
||||
{cracky=3},
|
||||
{"default_cobble.png"},
|
||||
"Cobble stair",
|
||||
"Cobble slab")
|
||||
|
||||
stairs.register_stair_and_slab("brick", "default:brick",
|
||||
{cracky=3},
|
||||
{"default_brick.png"},
|
||||
"Brick stair",
|
||||
"Brick slab")
|
||||
|
||||
stairs.register_stair_and_slab("sandstone", "default:sandstone",
|
||||
{crumbly=2,cracky=2},
|
||||
{"default_sandstone.png"},
|
||||
"Sandstone stair",
|
||||
"Sandstone slab")
|
||||
@@ -52,7 +52,8 @@
|
||||
# Some (temporary) keys for debugging
|
||||
#keymap_print_debug_stacks = KEY_KEY_P
|
||||
|
||||
# The desired FPS
|
||||
# Minimum FPS
|
||||
# The amount of rendered stuff is dynamically set according to this
|
||||
#wanted_fps = 30
|
||||
# If FPS would go higher than this, limit it by sleeping
|
||||
# (to not waste CPU power for no benefit)
|
||||
@@ -117,6 +118,8 @@
|
||||
# Sound settings
|
||||
#enable_sound = true
|
||||
#sound_volume = 0.7
|
||||
# Whether node texture animations should be desynchronized per MapBlock
|
||||
#desynchronize_mapblock_texture_animation = true
|
||||
|
||||
#
|
||||
# Server stuff
|
||||
@@ -144,14 +147,21 @@
|
||||
#give_initial_stuff = false
|
||||
# New users need to input this password
|
||||
#default_password =
|
||||
# Available privileges: build, teleport, settime, privs, shout
|
||||
#default_privs = build, shout
|
||||
# Available privileges: interact, shout, teleport, settime, privs, ...
|
||||
# See /privs in game for a full list on your server and mod configuration.
|
||||
#default_privs = interact, shout
|
||||
# Whether players are shown to clients without any range limit
|
||||
#unlimited_player_transfer_distance = true
|
||||
# Whether to enable players killing each other
|
||||
#enable_pvp = true
|
||||
# If this is set, players will always (re)spawn at the given position
|
||||
#static_spawnpoint = 0, 10, 0
|
||||
# If true, new players cannot join with an empty password
|
||||
#disallow_empty_password = false
|
||||
# If true, disable cheat prevention in multiplayer
|
||||
#disable_anticheat = false
|
||||
# If true, actions are recorded for rollback
|
||||
#enable_rollback_recording = false
|
||||
|
||||
# Profiler data print interval. #0 = disable.
|
||||
#profiler_print_interval = 0
|
||||
|
||||
@@ -1,10 +1,6 @@
|
||||
project(minetest)
|
||||
cmake_minimum_required( VERSION 2.6 )
|
||||
|
||||
if(RUN_IN_PLACE)
|
||||
add_definitions ( -DRUN_IN_PLACE )
|
||||
endif(RUN_IN_PLACE)
|
||||
|
||||
# Set some random things default to not being visible in the GUI
|
||||
mark_as_advanced(EXECUTABLE_OUTPUT_PATH LIBRARY_OUTPUT_PATH)
|
||||
mark_as_advanced(JTHREAD_INCLUDE_DIR JTHREAD_LIBRARY)
|
||||
@@ -139,13 +135,15 @@ else()
|
||||
#set(CLIENT_PLATFORM_LIBS -lXxf86vm)
|
||||
# This way Xxf86vm is found on OpenBSD too
|
||||
find_library(XXF86VM_LIBRARY Xxf86vm)
|
||||
mark_as_advanced(XXF86VM_LIBRARY)
|
||||
set(CLIENT_PLATFORM_LIBS ${CLIENT_PLATFORM_LIBS} ${XXF86VM_LIBRARY})
|
||||
endif()
|
||||
|
||||
find_package(Jthread REQUIRED)
|
||||
find_package(Sqlite3 REQUIRED)
|
||||
|
||||
# TODO: Create proper find script for Lua
|
||||
# Do not use system-wide installation of Lua, because it'll likely be a
|
||||
# different version and/or has different build options.
|
||||
set(LUA_INCLUDE_DIR "${PROJECT_SOURCE_DIR}/lua/src")
|
||||
set(LUA_LIBRARY "lua")
|
||||
|
||||
@@ -155,6 +153,8 @@ configure_file(
|
||||
)
|
||||
|
||||
set(common_SRCS
|
||||
rollback_interface.cpp
|
||||
rollback.cpp
|
||||
genericobject.cpp
|
||||
voxelalgorithms.cpp
|
||||
sound.cpp
|
||||
@@ -199,11 +199,16 @@ set(common_SRCS
|
||||
mapsector.cpp
|
||||
map.cpp
|
||||
player.cpp
|
||||
utility.cpp
|
||||
test.cpp
|
||||
sha1.cpp
|
||||
base64.cpp
|
||||
ban.cpp
|
||||
util/serialize.cpp
|
||||
util/directiontables.cpp
|
||||
util/numeric.cpp
|
||||
util/pointedthing.cpp
|
||||
util/string.cpp
|
||||
util/timetaker.cpp
|
||||
)
|
||||
|
||||
# This gives us the icon
|
||||
@@ -246,7 +251,7 @@ set(minetest_SRCS
|
||||
guiKeyChangeMenu.cpp
|
||||
guiMessageMenu.cpp
|
||||
guiTextInputMenu.cpp
|
||||
guiInventoryMenu.cpp
|
||||
guiFormSpecMenu.cpp
|
||||
guiPauseMenu.cpp
|
||||
guiPasswordChange.cpp
|
||||
guiDeathScreen.cpp
|
||||
|
||||
@@ -20,7 +20,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
#ifndef ACTIVEOBJECT_HEADER
|
||||
#define ACTIVEOBJECT_HEADER
|
||||
|
||||
#include "irrlichttypes.h"
|
||||
#include "irrlichttypes_bloated.h"
|
||||
#include <string>
|
||||
|
||||
#define ACTIVEOBJECT_TYPE_INVALID 0
|
||||
|
||||
@@ -24,7 +24,6 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
#include <string>
|
||||
#include <jthread.h>
|
||||
#include <jmutex.h>
|
||||
#include "common_irrlicht.h"
|
||||
#include "exceptions.h"
|
||||
|
||||
class BanManager
|
||||
|
||||
@@ -40,7 +40,7 @@ static inline bool is_base64(unsigned char c) {
|
||||
|
||||
bool base64_is_valid(std::string const& s)
|
||||
{
|
||||
for(int i=0; i<s.size(); i++)
|
||||
for(size_t i=0; i<s.size(); i++)
|
||||
if(!is_base64(s[i])) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -33,6 +33,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
#include "gamedef.h"
|
||||
#include "sound.h"
|
||||
#include "event.h"
|
||||
#include "util/numeric.h"
|
||||
#include "util/mathconstants.h"
|
||||
|
||||
Camera::Camera(scene::ISceneManager* smgr, MapDrawControl& draw_control,
|
||||
IGameDef *gamedef):
|
||||
@@ -212,8 +214,22 @@ void Camera::step(f32 dtime)
|
||||
void Camera::update(LocalPlayer* player, f32 frametime, v2u32 screensize,
|
||||
f32 tool_reload_ratio)
|
||||
{
|
||||
// Get player position
|
||||
// Smooth the movement when walking up stairs
|
||||
v3f old_player_position = m_playernode->getPosition();
|
||||
v3f player_position = player->getPosition();
|
||||
//if(player->touching_ground && player_position.Y > old_player_position.Y)
|
||||
if(player->touching_ground &&
|
||||
player_position.Y > old_player_position.Y)
|
||||
{
|
||||
f32 oldy = old_player_position.Y;
|
||||
f32 newy = player_position.Y;
|
||||
f32 t = exp(-23*frametime);
|
||||
player_position.Y = oldy * t + newy * (1-t);
|
||||
}
|
||||
|
||||
// Set player node transformation
|
||||
m_playernode->setPosition(player->getPosition());
|
||||
m_playernode->setPosition(player_position);
|
||||
m_playernode->setRotation(v3f(0, -1 * player->getYaw(), 0));
|
||||
m_playernode->updateAbsolutePosition();
|
||||
|
||||
@@ -234,17 +250,17 @@ void Camera::update(LocalPlayer* player, f32 frametime, v2u32 screensize,
|
||||
|
||||
#if 1
|
||||
f32 bobknob = 1.2;
|
||||
f32 bobtmp = sin(pow(bobfrac, bobknob) * PI);
|
||||
//f32 bobtmp2 = cos(pow(bobfrac, bobknob) * PI);
|
||||
f32 bobtmp = sin(pow(bobfrac, bobknob) * M_PI);
|
||||
//f32 bobtmp2 = cos(pow(bobfrac, bobknob) * M_PI);
|
||||
|
||||
v3f bobvec = v3f(
|
||||
0.3 * bobdir * sin(bobfrac * PI),
|
||||
0.3 * bobdir * sin(bobfrac * M_PI),
|
||||
-0.28 * bobtmp * bobtmp,
|
||||
0.);
|
||||
|
||||
//rel_cam_pos += 0.2 * bobvec;
|
||||
//rel_cam_target += 0.03 * bobvec;
|
||||
//rel_cam_up.rotateXYBy(0.02 * bobdir * bobtmp * PI);
|
||||
//rel_cam_up.rotateXYBy(0.02 * bobdir * bobtmp * M_PI);
|
||||
float f = 1.0;
|
||||
f *= g_settings->getFloat("view_bobbing_amount");
|
||||
rel_cam_pos += bobvec * f;
|
||||
@@ -253,10 +269,10 @@ void Camera::update(LocalPlayer* player, f32 frametime, v2u32 screensize,
|
||||
rel_cam_target.Z -= 0.005 * bobvec.Z * f;
|
||||
//rel_cam_target.X -= 0.005 * bobvec.X * f;
|
||||
//rel_cam_target.Y -= 0.005 * bobvec.Y * f;
|
||||
rel_cam_up.rotateXYBy(-0.03 * bobdir * bobtmp * PI * f);
|
||||
rel_cam_up.rotateXYBy(-0.03 * bobdir * bobtmp * M_PI * f);
|
||||
#else
|
||||
f32 angle_deg = 1 * bobdir * sin(bobfrac * PI);
|
||||
f32 angle_rad = angle_deg * PI / 180;
|
||||
f32 angle_deg = 1 * bobdir * sin(bobfrac * M_PI);
|
||||
f32 angle_rad = angle_deg * M_PI / 180;
|
||||
f32 r = 0.05;
|
||||
v3f off = v3f(
|
||||
r * sin(angle_rad),
|
||||
@@ -289,7 +305,7 @@ void Camera::update(LocalPlayer* player, f32 frametime, v2u32 screensize,
|
||||
|
||||
// FOV and aspect ratio
|
||||
m_aspect = (f32)screensize.X / (f32) screensize.Y;
|
||||
m_fov_y = fov_degrees * PI / 180.0;
|
||||
m_fov_y = fov_degrees * M_PI / 180.0;
|
||||
// Increase vertical FOV on lower aspect ratios (<16:10)
|
||||
m_fov_y *= MYMAX(1.0, MYMIN(1.4, sqrt(16./10. / m_aspect)));
|
||||
// WTF is this? It can't be right
|
||||
@@ -320,22 +336,22 @@ void Camera::update(LocalPlayer* player, f32 frametime, v2u32 screensize,
|
||||
if (m_digging_button != -1)
|
||||
{
|
||||
f32 digfrac = m_digging_anim;
|
||||
wield_position.X -= 30 * sin(pow(digfrac, 0.8f) * PI);
|
||||
wield_position.Y += 15 * sin(digfrac * 2 * PI);
|
||||
wield_position.X -= 30 * sin(pow(digfrac, 0.8f) * M_PI);
|
||||
wield_position.Y += 15 * sin(digfrac * 2 * M_PI);
|
||||
wield_position.Z += 5 * digfrac;
|
||||
|
||||
// Euler angles are PURE EVIL, so why not use quaternions?
|
||||
core::quaternion quat_begin(wield_rotation * core::DEGTORAD);
|
||||
core::quaternion quat_end(v3f(90, -10, -130) * core::DEGTORAD);
|
||||
core::quaternion quat_slerp;
|
||||
quat_slerp.slerp(quat_begin, quat_end, sin(digfrac * PI));
|
||||
quat_slerp.slerp(quat_begin, quat_end, sin(digfrac * M_PI));
|
||||
quat_slerp.toEuler(wield_rotation);
|
||||
wield_rotation *= core::RADTODEG;
|
||||
}
|
||||
else {
|
||||
f32 bobfrac = my_modf(m_view_bobbing_anim);
|
||||
wield_position.X -= sin(bobfrac*PI*2.0) * 3.0;
|
||||
wield_position.Y += sin(my_modf(bobfrac*2.0)*PI) * 3.0;
|
||||
wield_position.X -= sin(bobfrac*M_PI*2.0) * 3.0;
|
||||
wield_position.Y += sin(my_modf(bobfrac*2.0)*M_PI) * 3.0;
|
||||
}
|
||||
m_wieldnode->setPosition(wield_position);
|
||||
m_wieldnode->setRotation(wield_rotation);
|
||||
@@ -538,7 +554,7 @@ void Camera::drawWieldedTool()
|
||||
// Draw the wielded node (in a separate scene manager)
|
||||
scene::ICameraSceneNode* cam = m_wieldmgr->getActiveCamera();
|
||||
cam->setAspectRatio(m_cameranode->getAspectRatio());
|
||||
cam->setFOV(72.0*PI/180.0);
|
||||
cam->setFOV(72.0*M_PI/180.0);
|
||||
cam->setNearValue(0.1);
|
||||
cam->setFarValue(100);
|
||||
m_wieldmgr->drawAll();
|
||||
|
||||
@@ -20,11 +20,11 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
#ifndef CAMERA_HEADER
|
||||
#define CAMERA_HEADER
|
||||
|
||||
#include "common_irrlicht.h"
|
||||
#include "irrlichttypes_extrabloated.h"
|
||||
#include "inventory.h"
|
||||
#include "mesh.h"
|
||||
#include "tile.h"
|
||||
#include "utility.h"
|
||||
#include "util/numeric.h"
|
||||
#include <ICameraSceneNode.h>
|
||||
|
||||
class LocalPlayer;
|
||||
|
||||
@@ -19,10 +19,11 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
|
||||
#include "chat.h"
|
||||
#include "debug.h"
|
||||
#include "utility.h"
|
||||
#include <cassert>
|
||||
#include <cctype>
|
||||
#include <sstream>
|
||||
#include "util/string.h"
|
||||
#include "util/numeric.h"
|
||||
|
||||
ChatBuffer::ChatBuffer(u32 scrollback):
|
||||
m_scrollback(scrollback),
|
||||
|
||||
@@ -20,7 +20,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
#ifndef CHAT_HEADER
|
||||
#define CHAT_HEADER
|
||||
|
||||
#include "common_irrlicht.h"
|
||||
#include "irrlichttypes_bloated.h"
|
||||
#include <string>
|
||||
|
||||
// Chat console related classes, only used by the client
|
||||
|
||||
@@ -18,7 +18,6 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
*/
|
||||
|
||||
#include "client.h"
|
||||
#include "utility.h"
|
||||
#include <iostream>
|
||||
#include "clientserver.h"
|
||||
#include "jmutexautolock.h"
|
||||
@@ -40,7 +39,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
#include "clientmap.h"
|
||||
#include "filecache.h"
|
||||
#include "sound.h"
|
||||
#include "utility_string.h"
|
||||
#include "util/string.h"
|
||||
#include "hex.h"
|
||||
|
||||
static std::string getMediaCacheDir()
|
||||
@@ -305,6 +304,15 @@ Client::~Client()
|
||||
sleep_ms(100);
|
||||
|
||||
delete m_inventory_from_server;
|
||||
|
||||
// Delete detached inventories
|
||||
{
|
||||
for(std::map<std::string, Inventory*>::iterator
|
||||
i = m_detached_inventories.begin();
|
||||
i != m_detached_inventories.end(); i++){
|
||||
delete i->second;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Client::connect(Address address)
|
||||
@@ -1689,6 +1697,34 @@ void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id)
|
||||
}
|
||||
infostream<<std::endl;
|
||||
}
|
||||
else if(command == TOCLIENT_INVENTORY_FORMSPEC)
|
||||
{
|
||||
std::string datastring((char*)&data[2], datasize-2);
|
||||
std::istringstream is(datastring, std::ios_base::binary);
|
||||
|
||||
// Store formspec in LocalPlayer
|
||||
Player *player = m_env.getLocalPlayer();
|
||||
assert(player != NULL);
|
||||
player->inventory_formspec = deSerializeLongString(is);
|
||||
}
|
||||
else if(command == TOCLIENT_DETACHED_INVENTORY)
|
||||
{
|
||||
std::string datastring((char*)&data[2], datasize-2);
|
||||
std::istringstream is(datastring, std::ios_base::binary);
|
||||
|
||||
std::string name = deSerializeString(is);
|
||||
|
||||
infostream<<"Client: Detached inventory update: \""<<name<<"\""<<std::endl;
|
||||
|
||||
Inventory *inv = NULL;
|
||||
if(m_detached_inventories.count(name) > 0)
|
||||
inv = m_detached_inventories[name];
|
||||
else{
|
||||
inv = new Inventory(m_itemdef);
|
||||
m_detached_inventories[name] = inv;
|
||||
}
|
||||
inv->deSerialize(is);
|
||||
}
|
||||
else
|
||||
{
|
||||
infostream<<"Client: Ignoring unknown command "
|
||||
@@ -1764,6 +1800,29 @@ void Client::sendNodemetaFields(v3s16 p, const std::string &formname,
|
||||
Send(0, data, true);
|
||||
}
|
||||
|
||||
void Client::sendInventoryFields(const std::string &formname,
|
||||
const std::map<std::string, std::string> &fields)
|
||||
{
|
||||
std::ostringstream os(std::ios_base::binary);
|
||||
|
||||
writeU16(os, TOSERVER_INVENTORY_FIELDS);
|
||||
os<<serializeString(formname);
|
||||
writeU16(os, fields.size());
|
||||
for(std::map<std::string, std::string>::const_iterator
|
||||
i = fields.begin(); i != fields.end(); i++){
|
||||
const std::string &name = i->first;
|
||||
const std::string &value = i->second;
|
||||
os<<serializeString(name);
|
||||
os<<serializeLongString(value);
|
||||
}
|
||||
|
||||
// Make data buffer
|
||||
std::string s = os.str();
|
||||
SharedBuffer<u8> data((u8*)s.c_str(), s.size());
|
||||
// Send as reliable
|
||||
Send(0, data, true);
|
||||
}
|
||||
|
||||
void Client::sendInventoryAction(InventoryAction *a)
|
||||
{
|
||||
std::ostringstream os(std::ios_base::binary);
|
||||
@@ -2058,6 +2117,13 @@ Inventory* Client::getInventory(const InventoryLocation &loc)
|
||||
return meta->getInventory();
|
||||
}
|
||||
break;
|
||||
case InventoryLocation::DETACHED:
|
||||
{
|
||||
if(m_detached_inventories.count(loc.name) == 0)
|
||||
return NULL;
|
||||
return m_detached_inventories[loc.name];
|
||||
}
|
||||
break;
|
||||
default:
|
||||
assert(0);
|
||||
}
|
||||
|
||||
10
src/client.h
10
src/client.h
@@ -22,18 +22,18 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
|
||||
#include "connection.h"
|
||||
#include "environment.h"
|
||||
#include "common_irrlicht.h"
|
||||
#include "irrlichttypes_extrabloated.h"
|
||||
#include "jmutex.h"
|
||||
#include <ostream>
|
||||
#include <set>
|
||||
#include <vector>
|
||||
#include "clientobject.h"
|
||||
#include "utility.h" // For IntervalLimiter
|
||||
#include "gamedef.h"
|
||||
#include "inventorymanager.h"
|
||||
#include "filesys.h"
|
||||
#include "filecache.h"
|
||||
#include "localplayer.h"
|
||||
#include "util/pointedthing.h"
|
||||
|
||||
struct MeshMakeData;
|
||||
class MapBlockMesh;
|
||||
@@ -212,6 +212,8 @@ class Client : public con::PeerHandler, public InventoryManager, public IGameDef
|
||||
|
||||
void sendNodemetaFields(v3s16 p, const std::string &formname,
|
||||
const std::map<std::string, std::string> &fields);
|
||||
void sendInventoryFields(const std::string &formname,
|
||||
const std::map<std::string, std::string> &fields);
|
||||
void sendInventoryAction(InventoryAction *a);
|
||||
void sendChatMessage(const std::wstring &message);
|
||||
void sendChangePassword(const std::wstring oldpassword,
|
||||
@@ -391,6 +393,10 @@ class Client : public con::PeerHandler, public InventoryManager, public IGameDef
|
||||
|
||||
// Privileges
|
||||
std::set<std::string> m_privileges;
|
||||
|
||||
// Detached inventories
|
||||
// key = name
|
||||
std::map<std::string, Inventory*> m_detached_inventories;
|
||||
};
|
||||
|
||||
#endif // !CLIENT_HEADER
|
||||
|
||||
@@ -29,6 +29,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
#include "mapblock.h"
|
||||
#include "profiler.h"
|
||||
#include "settings.h"
|
||||
#include "util/mathconstants.h"
|
||||
|
||||
#define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")"
|
||||
|
||||
@@ -46,7 +47,7 @@ ClientMap::ClientMap(
|
||||
m_control(control),
|
||||
m_camera_position(0,0,0),
|
||||
m_camera_direction(0,0,1),
|
||||
m_camera_fov(PI)
|
||||
m_camera_fov(M_PI)
|
||||
{
|
||||
m_camera_mutex.Init();
|
||||
assert(m_camera_mutex.IsInitialized());
|
||||
|
||||
@@ -20,7 +20,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
#ifndef CLIENTMAP_HEADER
|
||||
#define CLIENTMAP_HEADER
|
||||
|
||||
#include "common_irrlicht.h"
|
||||
#include "irrlichttypes_extrabloated.h"
|
||||
#include "map.h"
|
||||
|
||||
struct MapDrawControl
|
||||
|
||||
@@ -20,7 +20,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
#ifndef CLIENTOBJECT_HEADER
|
||||
#define CLIENTOBJECT_HEADER
|
||||
|
||||
#include "common_irrlicht.h"
|
||||
#include "irrlichttypes_extrabloated.h"
|
||||
#include "activeobject.h"
|
||||
|
||||
/*
|
||||
|
||||
@@ -20,7 +20,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
#ifndef CLIENTSERVER_HEADER
|
||||
#define CLIENTSERVER_HEADER
|
||||
|
||||
#include "utility.h"
|
||||
#include "util/serialize.h"
|
||||
|
||||
/*
|
||||
changes by PROTOCOL_VERSION:
|
||||
@@ -54,9 +54,20 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
PROTOCOL_VERSION 10:
|
||||
TOCLIENT_PRIVILEGES
|
||||
Version raised to force 'fly' and 'fast' privileges into effect.
|
||||
Node metadata change (came in later; somewhat incompatible)
|
||||
PROTOCOL_VERSION 11:
|
||||
TileDef in ContentFeatures
|
||||
Nodebox drawtype
|
||||
(some dev snapshot)
|
||||
TOCLIENT_INVENTORY_FORMSPEC
|
||||
(0.4.0, 0.4.1)
|
||||
PROTOCOL_VERSION 12:
|
||||
TOSERVER_INVENTORY_FIELDS
|
||||
16-bit node ids
|
||||
TOCLIENT_DETACHED_INVENTORY
|
||||
*/
|
||||
|
||||
#define PROTOCOL_VERSION 10
|
||||
#define PROTOCOL_VERSION 12
|
||||
|
||||
#define PROTOCOL_ID 0x4f457403
|
||||
|
||||
@@ -304,6 +315,21 @@ enum ToClientCommand
|
||||
u16 len
|
||||
u8[len] privilege
|
||||
*/
|
||||
|
||||
TOCLIENT_INVENTORY_FORMSPEC = 0x42,
|
||||
/*
|
||||
u16 command
|
||||
u32 len
|
||||
u8[len] formspec
|
||||
*/
|
||||
|
||||
TOCLIENT_DETACHED_INVENTORY = 0x43,
|
||||
/*
|
||||
[0] u16 command
|
||||
u16 len
|
||||
u8[len] name
|
||||
[2] serialized inventory
|
||||
*/
|
||||
};
|
||||
|
||||
enum ToServerCommand
|
||||
@@ -498,6 +524,19 @@ enum ToServerCommand
|
||||
u8[len] field value
|
||||
*/
|
||||
|
||||
TOSERVER_INVENTORY_FIELDS = 0x3c,
|
||||
/*
|
||||
u16 command
|
||||
u16 len
|
||||
u8[len] form name (reserved for future use)
|
||||
u16 number of fields
|
||||
for each field:
|
||||
u16 len
|
||||
u8[len] field name
|
||||
u32 len
|
||||
u8[len] field value
|
||||
*/
|
||||
|
||||
TOSERVER_REQUEST_MEDIA = 0x40,
|
||||
/*
|
||||
u16 command
|
||||
|
||||
@@ -20,7 +20,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
#ifndef CLIENTSIMPLEOBJECT_HEADER
|
||||
#define CLIENTSIMPLEOBJECT_HEADER
|
||||
|
||||
#include "irrlichttypes.h"
|
||||
#include "irrlichttypes_bloated.h"
|
||||
class ClientEnvironment;
|
||||
|
||||
class ClientSimpleObject
|
||||
|
||||
@@ -20,7 +20,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
#ifndef CLOUDS_HEADER
|
||||
#define CLOUDS_HEADER
|
||||
|
||||
#include "common_irrlicht.h"
|
||||
#include "irrlichttypes_extrabloated.h"
|
||||
#include <iostream>
|
||||
|
||||
class Clouds : public scene::ISceneNode
|
||||
|
||||
@@ -4,16 +4,18 @@
|
||||
#define CMAKE_CONFIG_H
|
||||
|
||||
#define CMAKE_PROJECT_NAME "@PROJECT_NAME@"
|
||||
#define CMAKE_INSTALL_PREFIX "@CMAKE_INSTALL_PREFIX@"
|
||||
#define CMAKE_VERSION_STRING "@VERSION_STRING@"
|
||||
#define CMAKE_RUN_IN_PLACE @RUN_IN_PLACE@
|
||||
#define CMAKE_USE_GETTEXT @USE_GETTEXT@
|
||||
#define CMAKE_USE_SOUND @USE_SOUND@
|
||||
#define CMAKE_STATIC_SHAREDIR "@SHAREDIR@"
|
||||
|
||||
#ifdef NDEBUG
|
||||
#define CMAKE_BUILD_TYPE "Release"
|
||||
#else
|
||||
#define CMAKE_BUILD_TYPE "Debug"
|
||||
#endif
|
||||
#define CMAKE_USE_GETTEXT @USE_GETTEXT@
|
||||
#define CMAKE_USE_SOUND @USE_SOUND@
|
||||
#define CMAKE_BUILD_INFO "VER=@VERSION_STRING@ BUILD_TYPE="CMAKE_BUILD_TYPE" RUN_IN_PLACE=@RUN_IN_PLACE@ USE_GETTEXT=@USE_GETTEXT@ USE_SOUND=@USE_SOUND@ INSTALL_PREFIX=@CMAKE_INSTALL_PREFIX@"
|
||||
#define CMAKE_BUILD_INFO "VER=@VERSION_STRING@ BUILD_TYPE="CMAKE_BUILD_TYPE" RUN_IN_PLACE=@RUN_IN_PLACE@ USE_GETTEXT=@USE_GETTEXT@ USE_SOUND=@USE_SOUND@ STATIC_SHAREDIR=@SHAREDIR@"
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
@@ -22,32 +22,249 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
#include "map.h"
|
||||
#include "nodedef.h"
|
||||
#include "gamedef.h"
|
||||
#include "log.h"
|
||||
#include <vector>
|
||||
#include "util/timetaker.h"
|
||||
#include "main.h" // g_profiler
|
||||
#include "profiler.h"
|
||||
|
||||
// Helper function:
|
||||
// Checks for collision of a moving aabbox with a static aabbox
|
||||
// Returns -1 if no collision, 0 if X collision, 1 if Y collision, 2 if Z collision
|
||||
// The time after which the collision occurs is stored in dtime.
|
||||
int axisAlignedCollision(
|
||||
const aabb3f &staticbox, const aabb3f &movingbox,
|
||||
const v3f &speed, f32 d, f32 &dtime)
|
||||
{
|
||||
//TimeTaker tt("axisAlignedCollision");
|
||||
|
||||
f32 xsize = (staticbox.MaxEdge.X - staticbox.MinEdge.X);
|
||||
f32 ysize = (staticbox.MaxEdge.Y - staticbox.MinEdge.Y);
|
||||
f32 zsize = (staticbox.MaxEdge.Z - staticbox.MinEdge.Z);
|
||||
|
||||
aabb3f relbox(
|
||||
movingbox.MinEdge.X - staticbox.MinEdge.X,
|
||||
movingbox.MinEdge.Y - staticbox.MinEdge.Y,
|
||||
movingbox.MinEdge.Z - staticbox.MinEdge.Z,
|
||||
movingbox.MaxEdge.X - staticbox.MinEdge.X,
|
||||
movingbox.MaxEdge.Y - staticbox.MinEdge.Y,
|
||||
movingbox.MaxEdge.Z - staticbox.MinEdge.Z
|
||||
);
|
||||
|
||||
if(speed.X > 0) // Check for collision with X- plane
|
||||
{
|
||||
if(relbox.MaxEdge.X <= d)
|
||||
{
|
||||
dtime = - relbox.MaxEdge.X / speed.X;
|
||||
if((relbox.MinEdge.Y + speed.Y * dtime < ysize) &&
|
||||
(relbox.MaxEdge.Y + speed.Y * dtime > 0) &&
|
||||
(relbox.MinEdge.Z + speed.Z * dtime < zsize) &&
|
||||
(relbox.MaxEdge.Z + speed.Z * dtime > 0))
|
||||
return 0;
|
||||
}
|
||||
else if(relbox.MinEdge.X > xsize)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
else if(speed.X < 0) // Check for collision with X+ plane
|
||||
{
|
||||
if(relbox.MinEdge.X >= xsize - d)
|
||||
{
|
||||
dtime = (xsize - relbox.MinEdge.X) / speed.X;
|
||||
if((relbox.MinEdge.Y + speed.Y * dtime < ysize) &&
|
||||
(relbox.MaxEdge.Y + speed.Y * dtime > 0) &&
|
||||
(relbox.MinEdge.Z + speed.Z * dtime < zsize) &&
|
||||
(relbox.MaxEdge.Z + speed.Z * dtime > 0))
|
||||
return 0;
|
||||
}
|
||||
else if(relbox.MaxEdge.X < 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
// NO else if here
|
||||
|
||||
if(speed.Y > 0) // Check for collision with Y- plane
|
||||
{
|
||||
if(relbox.MaxEdge.Y <= d)
|
||||
{
|
||||
dtime = - relbox.MaxEdge.Y / speed.Y;
|
||||
if((relbox.MinEdge.X + speed.X * dtime < xsize) &&
|
||||
(relbox.MaxEdge.X + speed.X * dtime > 0) &&
|
||||
(relbox.MinEdge.Z + speed.Z * dtime < zsize) &&
|
||||
(relbox.MaxEdge.Z + speed.Z * dtime > 0))
|
||||
return 1;
|
||||
}
|
||||
else if(relbox.MinEdge.Y > ysize)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
else if(speed.Y < 0) // Check for collision with Y+ plane
|
||||
{
|
||||
if(relbox.MinEdge.Y >= ysize - d)
|
||||
{
|
||||
dtime = (ysize - relbox.MinEdge.Y) / speed.Y;
|
||||
if((relbox.MinEdge.X + speed.X * dtime < xsize) &&
|
||||
(relbox.MaxEdge.X + speed.X * dtime > 0) &&
|
||||
(relbox.MinEdge.Z + speed.Z * dtime < zsize) &&
|
||||
(relbox.MaxEdge.Z + speed.Z * dtime > 0))
|
||||
return 1;
|
||||
}
|
||||
else if(relbox.MaxEdge.Y < 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
// NO else if here
|
||||
|
||||
if(speed.Z > 0) // Check for collision with Z- plane
|
||||
{
|
||||
if(relbox.MaxEdge.Z <= d)
|
||||
{
|
||||
dtime = - relbox.MaxEdge.Z / speed.Z;
|
||||
if((relbox.MinEdge.X + speed.X * dtime < xsize) &&
|
||||
(relbox.MaxEdge.X + speed.X * dtime > 0) &&
|
||||
(relbox.MinEdge.Y + speed.Y * dtime < ysize) &&
|
||||
(relbox.MaxEdge.Y + speed.Y * dtime > 0))
|
||||
return 2;
|
||||
}
|
||||
//else if(relbox.MinEdge.Z > zsize)
|
||||
//{
|
||||
// return -1;
|
||||
//}
|
||||
}
|
||||
else if(speed.Z < 0) // Check for collision with Z+ plane
|
||||
{
|
||||
if(relbox.MinEdge.Z >= zsize - d)
|
||||
{
|
||||
dtime = (zsize - relbox.MinEdge.Z) / speed.Z;
|
||||
if((relbox.MinEdge.X + speed.X * dtime < xsize) &&
|
||||
(relbox.MaxEdge.X + speed.X * dtime > 0) &&
|
||||
(relbox.MinEdge.Y + speed.Y * dtime < ysize) &&
|
||||
(relbox.MaxEdge.Y + speed.Y * dtime > 0))
|
||||
return 2;
|
||||
}
|
||||
//else if(relbox.MaxEdge.Z < 0)
|
||||
//{
|
||||
// return -1;
|
||||
//}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Helper function:
|
||||
// Checks if moving the movingbox up by the given distance would hit a ceiling.
|
||||
bool wouldCollideWithCeiling(
|
||||
const std::vector<aabb3f> &staticboxes,
|
||||
const aabb3f &movingbox,
|
||||
f32 y_increase, f32 d)
|
||||
{
|
||||
//TimeTaker tt("wouldCollideWithCeiling");
|
||||
|
||||
assert(y_increase >= 0);
|
||||
|
||||
for(std::vector<aabb3f>::const_iterator
|
||||
i = staticboxes.begin();
|
||||
i != staticboxes.end(); i++)
|
||||
{
|
||||
const aabb3f& staticbox = *i;
|
||||
if((movingbox.MaxEdge.Y - d <= staticbox.MinEdge.Y) &&
|
||||
(movingbox.MaxEdge.Y + y_increase > staticbox.MinEdge.Y) &&
|
||||
(movingbox.MinEdge.X < staticbox.MaxEdge.X) &&
|
||||
(movingbox.MaxEdge.X > staticbox.MinEdge.X) &&
|
||||
(movingbox.MinEdge.Z < staticbox.MaxEdge.Z) &&
|
||||
(movingbox.MaxEdge.Z > staticbox.MinEdge.Z))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
collisionMoveResult collisionMoveSimple(Map *map, IGameDef *gamedef,
|
||||
f32 pos_max_d, const core::aabbox3d<f32> &box_0,
|
||||
f32 dtime, v3f &pos_f, v3f &speed_f)
|
||||
f32 pos_max_d, const aabb3f &box_0,
|
||||
f32 stepheight, f32 dtime,
|
||||
v3f &pos_f, v3f &speed_f, v3f &accel_f)
|
||||
{
|
||||
//TimeTaker tt("collisionMoveSimple");
|
||||
ScopeProfiler sp(g_profiler, "collisionMoveSimple avg", SPT_AVG);
|
||||
|
||||
collisionMoveResult result;
|
||||
|
||||
// If there is no speed, there are no collisions
|
||||
/*
|
||||
Calculate new velocity
|
||||
*/
|
||||
speed_f += accel_f * dtime;
|
||||
|
||||
// If there is no speed, there are no collisions
|
||||
if(speed_f.getLength() == 0)
|
||||
return result;
|
||||
|
||||
v3f oldpos_f = pos_f;
|
||||
v3s16 oldpos_i = floatToInt(oldpos_f, BS);
|
||||
|
||||
/*
|
||||
Calculate new position
|
||||
Collect node boxes in movement range
|
||||
*/
|
||||
pos_f += speed_f * dtime;
|
||||
std::vector<aabb3f> cboxes;
|
||||
std::vector<bool> is_unloaded;
|
||||
std::vector<bool> is_step_up;
|
||||
{
|
||||
//TimeTaker tt2("collisionMoveSimple collect boxes");
|
||||
ScopeProfiler sp(g_profiler, "collisionMoveSimple collect boxes avg", SPT_AVG);
|
||||
|
||||
v3s16 oldpos_i = floatToInt(pos_f, BS);
|
||||
v3s16 newpos_i = floatToInt(pos_f + speed_f * dtime, BS);
|
||||
s16 min_x = MYMIN(oldpos_i.X, newpos_i.X) + (box_0.MinEdge.X / BS) - 1;
|
||||
s16 min_y = MYMIN(oldpos_i.Y, newpos_i.Y) + (box_0.MinEdge.Y / BS) - 1;
|
||||
s16 min_z = MYMIN(oldpos_i.Z, newpos_i.Z) + (box_0.MinEdge.Z / BS) - 1;
|
||||
s16 max_x = MYMAX(oldpos_i.X, newpos_i.X) + (box_0.MaxEdge.X / BS) + 1;
|
||||
s16 max_y = MYMAX(oldpos_i.Y, newpos_i.Y) + (box_0.MaxEdge.Y / BS) + 1;
|
||||
s16 max_z = MYMAX(oldpos_i.Z, newpos_i.Z) + (box_0.MaxEdge.Z / BS) + 1;
|
||||
|
||||
for(s16 x = min_x; x <= max_x; x++)
|
||||
for(s16 y = min_y; y <= max_y; y++)
|
||||
for(s16 z = min_z; z <= max_z; z++)
|
||||
{
|
||||
try{
|
||||
// Object collides into walkable nodes
|
||||
MapNode n = map->getNode(v3s16(x,y,z));
|
||||
if(gamedef->getNodeDefManager()->get(n).walkable == false)
|
||||
continue;
|
||||
|
||||
std::vector<aabb3f> nodeboxes = n.getNodeBoxes(gamedef->ndef());
|
||||
for(std::vector<aabb3f>::iterator
|
||||
i = nodeboxes.begin();
|
||||
i != nodeboxes.end(); i++)
|
||||
{
|
||||
aabb3f box = *i;
|
||||
box.MinEdge += v3f(x, y, z)*BS;
|
||||
box.MaxEdge += v3f(x, y, z)*BS;
|
||||
cboxes.push_back(box);
|
||||
is_unloaded.push_back(false);
|
||||
is_step_up.push_back(false);
|
||||
}
|
||||
}
|
||||
catch(InvalidPositionException &e)
|
||||
{
|
||||
// Collide with unloaded nodes
|
||||
aabb3f box = getNodeBox(v3s16(x,y,z), BS);
|
||||
cboxes.push_back(box);
|
||||
is_unloaded.push_back(true);
|
||||
is_step_up.push_back(false);
|
||||
}
|
||||
}
|
||||
} // tt2
|
||||
|
||||
assert(cboxes.size() == is_unloaded.size());
|
||||
assert(cboxes.size() == is_step_up.size());
|
||||
|
||||
/*
|
||||
Collision detection
|
||||
*/
|
||||
|
||||
// position in nodes
|
||||
v3s16 pos_i = floatToInt(pos_f, BS);
|
||||
|
||||
|
||||
/*
|
||||
Collision uncertainty radius
|
||||
Make it a bit larger than the maximum distance of movement
|
||||
@@ -58,49 +275,129 @@ collisionMoveResult collisionMoveSimple(Map *map, IGameDef *gamedef,
|
||||
|
||||
// This should always apply, otherwise there are glitches
|
||||
assert(d > pos_max_d);
|
||||
|
||||
/*
|
||||
Calculate collision box
|
||||
*/
|
||||
core::aabbox3d<f32> box = box_0;
|
||||
box.MaxEdge += pos_f;
|
||||
box.MinEdge += pos_f;
|
||||
core::aabbox3d<f32> oldbox = box_0;
|
||||
oldbox.MaxEdge += oldpos_f;
|
||||
oldbox.MinEdge += oldpos_f;
|
||||
|
||||
/*
|
||||
If the object lies on a walkable node, this is set to true.
|
||||
*/
|
||||
result.touching_ground = false;
|
||||
|
||||
/*
|
||||
Go through every node around the object
|
||||
*/
|
||||
s16 min_x = (box_0.MinEdge.X / BS) - 2;
|
||||
s16 min_y = (box_0.MinEdge.Y / BS) - 2;
|
||||
s16 min_z = (box_0.MinEdge.Z / BS) - 2;
|
||||
s16 max_x = (box_0.MaxEdge.X / BS) + 1;
|
||||
s16 max_y = (box_0.MaxEdge.Y / BS) + 1;
|
||||
s16 max_z = (box_0.MaxEdge.Z / BS) + 1;
|
||||
for(s16 y = oldpos_i.Y + min_y; y <= oldpos_i.Y + max_y; y++)
|
||||
for(s16 z = oldpos_i.Z + min_z; z <= oldpos_i.Z + max_z; z++)
|
||||
for(s16 x = oldpos_i.X + min_x; x <= oldpos_i.X + max_x; x++)
|
||||
int loopcount = 0;
|
||||
|
||||
while(dtime > BS*1e-10)
|
||||
{
|
||||
try{
|
||||
// Object collides into walkable nodes
|
||||
MapNode n = map->getNode(v3s16(x,y,z));
|
||||
if(gamedef->getNodeDefManager()->get(n).walkable == false)
|
||||
continue;
|
||||
}
|
||||
catch(InvalidPositionException &e)
|
||||
//TimeTaker tt3("collisionMoveSimple dtime loop");
|
||||
ScopeProfiler sp(g_profiler, "collisionMoveSimple dtime loop avg", SPT_AVG);
|
||||
|
||||
// Avoid infinite loop
|
||||
loopcount++;
|
||||
if(loopcount >= 100)
|
||||
{
|
||||
// Doing nothing here will block the object from
|
||||
// walking over map borders
|
||||
infostream<<"collisionMoveSimple: WARNING: Loop count exceeded, aborting to avoid infiniite loop"<<std::endl;
|
||||
dtime = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
core::aabbox3d<f32> nodebox = getNodeBox(v3s16(x,y,z), BS);
|
||||
|
||||
aabb3f movingbox = box_0;
|
||||
movingbox.MinEdge += pos_f;
|
||||
movingbox.MaxEdge += pos_f;
|
||||
|
||||
int nearest_collided = -1;
|
||||
f32 nearest_dtime = dtime;
|
||||
u32 nearest_boxindex = -1;
|
||||
|
||||
/*
|
||||
Go through every nodebox, find nearest collision
|
||||
*/
|
||||
for(u32 boxindex = 0; boxindex < cboxes.size(); boxindex++)
|
||||
{
|
||||
// Ignore if already stepped up this nodebox.
|
||||
if(is_step_up[boxindex])
|
||||
continue;
|
||||
|
||||
// Find nearest collision of the two boxes (raytracing-like)
|
||||
f32 dtime_tmp;
|
||||
int collided = axisAlignedCollision(
|
||||
cboxes[boxindex], movingbox, speed_f, d, dtime_tmp);
|
||||
|
||||
if(collided == -1 || dtime_tmp >= nearest_dtime)
|
||||
continue;
|
||||
|
||||
nearest_dtime = dtime_tmp;
|
||||
nearest_collided = collided;
|
||||
nearest_boxindex = boxindex;
|
||||
}
|
||||
|
||||
if(nearest_collided == -1)
|
||||
{
|
||||
// No collision with any collision box.
|
||||
pos_f += speed_f * dtime;
|
||||
dtime = 0; // Set to 0 to avoid "infinite" loop due to small FP numbers
|
||||
}
|
||||
else
|
||||
{
|
||||
// Otherwise, a collision occurred.
|
||||
|
||||
const aabb3f& cbox = cboxes[nearest_boxindex];
|
||||
|
||||
// Check for stairs.
|
||||
bool step_up = (nearest_collided != 1) && // must not be Y direction
|
||||
(movingbox.MinEdge.Y < cbox.MaxEdge.Y) &&
|
||||
(movingbox.MinEdge.Y + stepheight > cbox.MaxEdge.Y) &&
|
||||
(!wouldCollideWithCeiling(cboxes, movingbox,
|
||||
cbox.MaxEdge.Y - movingbox.MinEdge.Y,
|
||||
d));
|
||||
|
||||
// Move to the point of collision and reduce dtime by nearest_dtime
|
||||
if(nearest_dtime < 0)
|
||||
{
|
||||
// Handle negative nearest_dtime (can be caused by the d allowance)
|
||||
if(!step_up)
|
||||
{
|
||||
if(nearest_collided == 0)
|
||||
pos_f.X += speed_f.X * nearest_dtime;
|
||||
if(nearest_collided == 1)
|
||||
pos_f.Y += speed_f.Y * nearest_dtime;
|
||||
if(nearest_collided == 2)
|
||||
pos_f.Z += speed_f.Z * nearest_dtime;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
pos_f += speed_f * nearest_dtime;
|
||||
dtime -= nearest_dtime;
|
||||
}
|
||||
|
||||
// Set the speed component that caused the collision to zero
|
||||
if(step_up)
|
||||
{
|
||||
// Special case: Handle stairs
|
||||
is_step_up[nearest_boxindex] = true;
|
||||
}
|
||||
else if(nearest_collided == 0) // X
|
||||
{
|
||||
speed_f.X = 0;
|
||||
result.collides = true;
|
||||
result.collides_xz = true;
|
||||
}
|
||||
else if(nearest_collided == 1) // Y
|
||||
{
|
||||
speed_f.Y = 0;
|
||||
result.collides = true;
|
||||
}
|
||||
else if(nearest_collided == 2) // Z
|
||||
{
|
||||
speed_f.Z = 0;
|
||||
result.collides = true;
|
||||
result.collides_xz = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Final touches: Check if standing on ground, step up stairs.
|
||||
*/
|
||||
aabb3f box = box_0;
|
||||
box.MinEdge += pos_f;
|
||||
box.MaxEdge += pos_f;
|
||||
for(u32 boxindex = 0; boxindex < cboxes.size(); boxindex++)
|
||||
{
|
||||
const aabb3f& cbox = cboxes[boxindex];
|
||||
|
||||
/*
|
||||
See if the object is touching ground.
|
||||
|
||||
@@ -111,112 +408,50 @@ collisionMoveResult collisionMoveSimple(Map *map, IGameDef *gamedef,
|
||||
Use 0.15*BS so that it is easier to get on a node.
|
||||
*/
|
||||
if(
|
||||
//fabs(nodebox.MaxEdge.Y-box.MinEdge.Y) < d
|
||||
fabs(nodebox.MaxEdge.Y-box.MinEdge.Y) < 0.15*BS
|
||||
&& nodebox.MaxEdge.X-d > box.MinEdge.X
|
||||
&& nodebox.MinEdge.X+d < box.MaxEdge.X
|
||||
&& nodebox.MaxEdge.Z-d > box.MinEdge.Z
|
||||
&& nodebox.MinEdge.Z+d < box.MaxEdge.Z
|
||||
cbox.MaxEdge.X-d > box.MinEdge.X &&
|
||||
cbox.MinEdge.X+d < box.MaxEdge.X &&
|
||||
cbox.MaxEdge.Z-d > box.MinEdge.Z &&
|
||||
cbox.MinEdge.Z+d < box.MaxEdge.Z
|
||||
){
|
||||
result.touching_ground = true;
|
||||
}
|
||||
|
||||
// If object doesn't intersect with node, ignore node.
|
||||
if(box.intersectsWithBox(nodebox) == false)
|
||||
continue;
|
||||
|
||||
/*
|
||||
Go through every axis
|
||||
*/
|
||||
v3f dirs[3] = {
|
||||
v3f(0,0,1), // back-front
|
||||
v3f(0,1,0), // top-bottom
|
||||
v3f(1,0,0), // right-left
|
||||
};
|
||||
for(u16 i=0; i<3; i++)
|
||||
{
|
||||
/*
|
||||
Calculate values along the axis
|
||||
*/
|
||||
f32 nodemax = nodebox.MaxEdge.dotProduct(dirs[i]);
|
||||
f32 nodemin = nodebox.MinEdge.dotProduct(dirs[i]);
|
||||
f32 objectmax = box.MaxEdge.dotProduct(dirs[i]);
|
||||
f32 objectmin = box.MinEdge.dotProduct(dirs[i]);
|
||||
f32 objectmax_old = oldbox.MaxEdge.dotProduct(dirs[i]);
|
||||
f32 objectmin_old = oldbox.MinEdge.dotProduct(dirs[i]);
|
||||
|
||||
/*
|
||||
Check collision for the axis.
|
||||
Collision happens when object is going through a surface.
|
||||
*/
|
||||
bool negative_axis_collides =
|
||||
(nodemax > objectmin && nodemax <= objectmin_old + d
|
||||
&& speed_f.dotProduct(dirs[i]) < 0);
|
||||
bool positive_axis_collides =
|
||||
(nodemin < objectmax && nodemin >= objectmax_old - d
|
||||
&& speed_f.dotProduct(dirs[i]) > 0);
|
||||
bool main_axis_collides =
|
||||
negative_axis_collides || positive_axis_collides;
|
||||
|
||||
/*
|
||||
Check overlap of object and node in other axes
|
||||
*/
|
||||
bool other_axes_overlap = true;
|
||||
for(u16 j=0; j<3; j++)
|
||||
if(is_step_up[boxindex])
|
||||
{
|
||||
if(j == i)
|
||||
continue;
|
||||
f32 nodemax = nodebox.MaxEdge.dotProduct(dirs[j]);
|
||||
f32 nodemin = nodebox.MinEdge.dotProduct(dirs[j]);
|
||||
f32 objectmax = box.MaxEdge.dotProduct(dirs[j]);
|
||||
f32 objectmin = box.MinEdge.dotProduct(dirs[j]);
|
||||
if(!(nodemax - d > objectmin && nodemin + d < objectmax))
|
||||
{
|
||||
other_axes_overlap = false;
|
||||
break;
|
||||
}
|
||||
pos_f.Y += (cbox.MaxEdge.Y - box.MinEdge.Y);
|
||||
box = box_0;
|
||||
box.MinEdge += pos_f;
|
||||
box.MaxEdge += pos_f;
|
||||
}
|
||||
|
||||
/*
|
||||
If this is a collision, revert the pos_f in the main
|
||||
direction.
|
||||
*/
|
||||
if(other_axes_overlap && main_axis_collides)
|
||||
if(fabs(cbox.MaxEdge.Y-box.MinEdge.Y) < 0.15*BS)
|
||||
{
|
||||
speed_f -= speed_f.dotProduct(dirs[i]) * dirs[i];
|
||||
pos_f -= pos_f.dotProduct(dirs[i]) * dirs[i];
|
||||
pos_f += oldpos_f.dotProduct(dirs[i]) * dirs[i];
|
||||
result.collides = true;
|
||||
result.touching_ground = true;
|
||||
if(is_unloaded[boxindex])
|
||||
result.standing_on_unloaded = true;
|
||||
}
|
||||
|
||||
}
|
||||
} // xyz
|
||||
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
#if 0
|
||||
// This doesn't seem to work and isn't used
|
||||
collisionMoveResult collisionMovePrecise(Map *map, IGameDef *gamedef,
|
||||
f32 pos_max_d, const core::aabbox3d<f32> &box_0,
|
||||
f32 dtime, v3f &pos_f, v3f &speed_f)
|
||||
f32 pos_max_d, const aabb3f &box_0,
|
||||
f32 stepheight, f32 dtime,
|
||||
v3f &pos_f, v3f &speed_f, v3f &accel_f)
|
||||
{
|
||||
collisionMoveResult final_result;
|
||||
//TimeTaker tt("collisionMovePrecise");
|
||||
ScopeProfiler sp(g_profiler, "collisionMovePrecise avg", SPT_AVG);
|
||||
|
||||
collisionMoveResult final_result;
|
||||
|
||||
// If there is no speed, there are no collisions
|
||||
if(speed_f.getLength() == 0)
|
||||
return final_result;
|
||||
|
||||
// Maximum time increment (for collision detection etc)
|
||||
// time = distance / speed
|
||||
f32 dtime_max_increment = pos_max_d / speed_f.getLength();
|
||||
|
||||
// Maximum time increment is 10ms or lower
|
||||
if(dtime_max_increment > 0.01)
|
||||
dtime_max_increment = 0.01;
|
||||
|
||||
// Don't allow overly huge dtime
|
||||
if(dtime > 2.0)
|
||||
dtime = 2.0;
|
||||
|
||||
|
||||
f32 dtime_downcount = dtime;
|
||||
|
||||
u32 loopcount = 0;
|
||||
@@ -224,6 +459,16 @@ collisionMoveResult collisionMovePrecise(Map *map, IGameDef *gamedef,
|
||||
{
|
||||
loopcount++;
|
||||
|
||||
// Maximum time increment (for collision detection etc)
|
||||
// time = distance / speed
|
||||
f32 dtime_max_increment = 1.0;
|
||||
if(speed_f.getLength() != 0)
|
||||
dtime_max_increment = pos_max_d / speed_f.getLength();
|
||||
|
||||
// Maximum time increment is 10ms or lower
|
||||
if(dtime_max_increment > 0.01)
|
||||
dtime_max_increment = 0.01;
|
||||
|
||||
f32 dtime_part;
|
||||
if(dtime_downcount > dtime_max_increment)
|
||||
{
|
||||
@@ -242,17 +487,20 @@ collisionMoveResult collisionMovePrecise(Map *map, IGameDef *gamedef,
|
||||
}
|
||||
|
||||
collisionMoveResult result = collisionMoveSimple(map, gamedef,
|
||||
pos_max_d, box_0, dtime_part, pos_f, speed_f);
|
||||
pos_max_d, box_0, stepheight, dtime_part,
|
||||
pos_f, speed_f, accel_f);
|
||||
|
||||
if(result.touching_ground)
|
||||
final_result.touching_ground = true;
|
||||
if(result.collides)
|
||||
final_result.collides = true;
|
||||
if(result.collides_xz)
|
||||
final_result.collides_xz = true;
|
||||
if(result.standing_on_unloaded)
|
||||
final_result.standing_on_unloaded = true;
|
||||
}
|
||||
while(dtime_downcount > 0.001);
|
||||
|
||||
|
||||
return final_result;
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
@@ -20,7 +20,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
#ifndef COLLISION_HEADER
|
||||
#define COLLISION_HEADER
|
||||
|
||||
#include "common_irrlicht.h"
|
||||
#include "irrlichttypes_bloated.h"
|
||||
#include <vector>
|
||||
|
||||
class Map;
|
||||
class IGameDef;
|
||||
@@ -29,22 +30,47 @@ struct collisionMoveResult
|
||||
{
|
||||
bool touching_ground;
|
||||
bool collides;
|
||||
bool collides_xz;
|
||||
bool standing_on_unloaded;
|
||||
|
||||
collisionMoveResult():
|
||||
touching_ground(false),
|
||||
collides(false)
|
||||
collides(false),
|
||||
collides_xz(false),
|
||||
standing_on_unloaded(false)
|
||||
{}
|
||||
};
|
||||
|
||||
// Moves using a single iteration; speed should not exceed pos_max_d/dtime
|
||||
collisionMoveResult collisionMoveSimple(Map *map, IGameDef *gamedef,
|
||||
f32 pos_max_d, const core::aabbox3d<f32> &box_0,
|
||||
f32 dtime, v3f &pos_f, v3f &speed_f);
|
||||
f32 pos_max_d, const aabb3f &box_0,
|
||||
f32 stepheight, f32 dtime,
|
||||
v3f &pos_f, v3f &speed_f, v3f &accel_f);
|
||||
|
||||
#if 0
|
||||
// This doesn't seem to work and isn't used
|
||||
// Moves using as many iterations as needed
|
||||
collisionMoveResult collisionMovePrecise(Map *map, IGameDef *gamedef,
|
||||
f32 pos_max_d, const core::aabbox3d<f32> &box_0,
|
||||
f32 dtime, v3f &pos_f, v3f &speed_f);
|
||||
f32 pos_max_d, const aabb3f &box_0,
|
||||
f32 stepheight, f32 dtime,
|
||||
v3f &pos_f, v3f &speed_f, v3f &accel_f);
|
||||
#endif
|
||||
|
||||
// Helper function:
|
||||
// Checks for collision of a moving aabbox with a static aabbox
|
||||
// Returns -1 if no collision, 0 if X collision, 1 if Y collision, 2 if Z collision
|
||||
// dtime receives time until first collision, invalid if -1 is returned
|
||||
int axisAlignedCollision(
|
||||
const aabb3f &staticbox, const aabb3f &movingbox,
|
||||
const v3f &speed, f32 d, f32 &dtime);
|
||||
|
||||
// Helper function:
|
||||
// Checks if moving the movingbox up by the given distance would hit a ceiling.
|
||||
bool wouldCollideWithCeiling(
|
||||
const std::vector<aabb3f> &staticboxes,
|
||||
const aabb3f &movingbox,
|
||||
f32 y_increase, f32 d);
|
||||
|
||||
|
||||
enum CollisionType
|
||||
{
|
||||
|
||||
@@ -8,9 +8,10 @@
|
||||
|
||||
#define PROJECT_NAME "Minetest"
|
||||
#define VERSION_STRING "unknown"
|
||||
#define BUILD_TYPE "unknown"
|
||||
#define RUN_IN_PLACE 0
|
||||
#define USE_GETTEXT 0
|
||||
#define USE_SOUND 0
|
||||
#define STATIC_SHAREDIR ""
|
||||
#define BUILD_INFO "non-cmake"
|
||||
|
||||
#ifdef USE_CMAKE_CONFIG_H
|
||||
@@ -19,12 +20,14 @@
|
||||
#define PROJECT_NAME CMAKE_PROJECT_NAME
|
||||
#undef VERSION_STRING
|
||||
#define VERSION_STRING CMAKE_VERSION_STRING
|
||||
#undef BUILD_INFO
|
||||
#define BUILD_INFO CMAKE_BUILD_INFO
|
||||
#undef RUN_IN_PLACE
|
||||
#define RUN_IN_PLACE CMAKE_RUN_IN_PLACE
|
||||
#undef USE_GETTEXT
|
||||
#define USE_GETTEXT CMAKE_USE_GETTEXT
|
||||
#undef USE_SOUND
|
||||
#define USE_SOUND CMAKE_USE_SOUND
|
||||
#undef STATIC_SHAREDIR
|
||||
#define STATIC_SHAREDIR CMAKE_STATIC_SHAREDIR
|
||||
#undef BUILD_INFO
|
||||
#define BUILD_INFO CMAKE_BUILD_INFO
|
||||
#endif
|
||||
|
||||
@@ -22,10 +22,22 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
#include "serialization.h"
|
||||
#include "log.h"
|
||||
#include "porting.h"
|
||||
#include "util/serialize.h"
|
||||
#include "util/numeric.h"
|
||||
#include "util/string.h"
|
||||
|
||||
namespace con
|
||||
{
|
||||
|
||||
static u16 readPeerId(u8 *packetdata)
|
||||
{
|
||||
return readU16(&packetdata[4]);
|
||||
}
|
||||
static u8 readChannel(u8 *packetdata)
|
||||
{
|
||||
return readU8(&packetdata[6]);
|
||||
}
|
||||
|
||||
BufferedPacket makePacket(Address &address, u8 *data, u32 datasize,
|
||||
u32 protocol_id, u16 sender_peer_id, u8 channel)
|
||||
{
|
||||
|
||||
@@ -20,14 +20,15 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
#ifndef CONNECTION_HEADER
|
||||
#define CONNECTION_HEADER
|
||||
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include "debug.h"
|
||||
#include "common_irrlicht.h"
|
||||
#include "irrlichttypes_bloated.h"
|
||||
#include "socket.h"
|
||||
#include "utility.h"
|
||||
#include "exceptions.h"
|
||||
#include "constants.h"
|
||||
#include "util/pointer.h"
|
||||
#include "util/container.h"
|
||||
#include "util/thread.h"
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
|
||||
namespace con
|
||||
{
|
||||
@@ -107,15 +108,6 @@ class ProcessedSilentlyException : public BaseException
|
||||
{}
|
||||
};
|
||||
|
||||
inline u16 readPeerId(u8 *packetdata)
|
||||
{
|
||||
return readU16(&packetdata[4]);
|
||||
}
|
||||
inline u8 readChannel(u8 *packetdata)
|
||||
{
|
||||
return readU8(&packetdata[6]);
|
||||
}
|
||||
|
||||
#define SEQNUM_MAX 65535
|
||||
inline bool seqnum_higher(u16 higher, u16 lower)
|
||||
{
|
||||
|
||||
@@ -24,11 +24,13 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
All kinds of constants.
|
||||
|
||||
Cross-platform compatibility crap should go in porting.h.
|
||||
|
||||
Some things here are legacy crap.
|
||||
*/
|
||||
|
||||
//#define HAXMODE 0
|
||||
|
||||
#define DEBUGFILE "debug.txt"
|
||||
/*
|
||||
Connection
|
||||
*/
|
||||
|
||||
// Define for simulating the quirks of sending through internet.
|
||||
// Causes the socket class to deliberately drop random packets.
|
||||
@@ -42,63 +44,50 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
// resend_timeout = avg_rtt * this
|
||||
#define RESEND_TIMEOUT_FACTOR 4
|
||||
|
||||
#define PI 3.14159
|
||||
|
||||
// The absolute working limit is (2^15 - viewing_range).
|
||||
// I really don't want to make every algorithm to check if it's
|
||||
// going near the limit or not, so this is lower.
|
||||
#define MAP_GENERATION_LIMIT (31000)
|
||||
|
||||
// Size of node in rendering units
|
||||
#define BS (10.0)
|
||||
|
||||
#define MAP_BLOCKSIZE 16
|
||||
/*
|
||||
This makes mesh updates too slow, as many meshes are updated during
|
||||
the main loop (related to TempMods and day/night)
|
||||
Server
|
||||
*/
|
||||
//#define MAP_BLOCKSIZE 32
|
||||
|
||||
// Sectors are split to SECTOR_HEIGHTMAP_SPLIT^2 heightmaps
|
||||
#define SECTOR_HEIGHTMAP_SPLIT (MAP_BLOCKSIZE/8)
|
||||
|
||||
// Time after building, during which the following limit
|
||||
// is in use
|
||||
//#define FULL_BLOCK_SEND_ENABLE_MIN_TIME_FROM_BUILDING 2.0
|
||||
// This many blocks are sent when player is building
|
||||
#define LIMITED_MAX_SIMULTANEOUS_BLOCK_SENDS 0
|
||||
// Override for the previous one when distance of block
|
||||
// is very low
|
||||
// Override for the previous one when distance of block is very low
|
||||
#define BLOCK_SEND_DISABLE_LIMITS_MAX_D 1
|
||||
|
||||
/*
|
||||
Map-related things
|
||||
*/
|
||||
|
||||
// The absolute working limit is (2^15 - viewing_range).
|
||||
// I really don't want to make every algorithm to check if it's going near
|
||||
// the limit or not, so this is lower.
|
||||
#define MAP_GENERATION_LIMIT (31000)
|
||||
|
||||
// Size of node in floating-point units
|
||||
// The original idea behind this is to disallow plain casts between
|
||||
// floating-point and integer positions, which potentially give wrong
|
||||
// results. (negative coordinates, values between nodes, ...)
|
||||
// Use floatToInt(p, BS) and intToFloat(p, BS).
|
||||
#define BS (10.0)
|
||||
|
||||
// Dimension of a MapBlock
|
||||
#define MAP_BLOCKSIZE 16
|
||||
// This makes mesh updates too slow, as many meshes are updated during
|
||||
// the main loop (related to TempMods and day/night)
|
||||
//#define MAP_BLOCKSIZE 32
|
||||
|
||||
/*
|
||||
Old stuff that shouldn't be hardcoded
|
||||
*/
|
||||
|
||||
// Size of player's main inventory
|
||||
#define PLAYER_INVENTORY_SIZE (8*4)
|
||||
|
||||
#define SIGN_TEXT_MAX_LENGTH 50
|
||||
|
||||
// Whether to catch all std::exceptions.
|
||||
// Assert will be called on such an event.
|
||||
// In debug mode, leave these for the debugger and don't catch them.
|
||||
#ifdef NDEBUG
|
||||
#define CATCH_UNHANDLED_EXCEPTIONS 1
|
||||
#else
|
||||
#define CATCH_UNHANDLED_EXCEPTIONS 0
|
||||
#endif
|
||||
|
||||
/*
|
||||
Collecting active blocks is stopped after object data
|
||||
size reaches this
|
||||
*/
|
||||
#define MAX_OBJECTDATA_SIZE 450
|
||||
|
||||
/*
|
||||
This is good to be a bit different than 0 so that water level
|
||||
is not between two MapBlocks
|
||||
This is good to be a bit different than 0 so that water level is not
|
||||
between two MapBlocks
|
||||
*/
|
||||
#define WATER_LEVEL 1
|
||||
|
||||
// Length of cracking animation in count of images
|
||||
#define CRACK_ANIMATION_LENGTH 5
|
||||
|
||||
// Maximum hit points of a player
|
||||
#define PLAYER_MAX_HP 20
|
||||
|
||||
|
||||
@@ -26,6 +26,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
#include "settings.h"
|
||||
#include "mapblock.h" // For getNodeBlockPos
|
||||
#include "mapgen.h" // For mapgen::make_tree
|
||||
#include "map.h"
|
||||
|
||||
#define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")"
|
||||
|
||||
|
||||
@@ -30,13 +30,17 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
#include "clientobject.h"
|
||||
#include "content_object.h"
|
||||
#include "mesh.h"
|
||||
#include "utility.h" // For IntervalLimiter
|
||||
#include "itemdef.h"
|
||||
#include "tool.h"
|
||||
#include "content_cso.h"
|
||||
#include "sound.h"
|
||||
#include "nodedef.h"
|
||||
#include "localplayer.h"
|
||||
#include "util/numeric.h" // For IntervalLimiter
|
||||
#include "util/serialize.h"
|
||||
#include "util/mathconstants.h"
|
||||
#include "map.h"
|
||||
|
||||
class Settings;
|
||||
struct ToolCapabilities;
|
||||
|
||||
@@ -879,22 +883,24 @@ class GenericCAO : public ClientActiveObject
|
||||
box.MinEdge *= BS;
|
||||
box.MaxEdge *= BS;
|
||||
collisionMoveResult moveresult;
|
||||
f32 pos_max_d = BS*0.25; // Distance per iteration
|
||||
f32 pos_max_d = BS*0.125; // Distance per iteration
|
||||
f32 stepheight = 0;
|
||||
v3f p_pos = m_position;
|
||||
v3f p_velocity = m_velocity;
|
||||
v3f p_acceleration = m_acceleration;
|
||||
IGameDef *gamedef = env->getGameDef();
|
||||
moveresult = collisionMovePrecise(&env->getMap(), gamedef,
|
||||
pos_max_d, box, dtime, p_pos, p_velocity);
|
||||
moveresult = collisionMoveSimple(&env->getMap(), gamedef,
|
||||
pos_max_d, box, stepheight, dtime,
|
||||
p_pos, p_velocity, p_acceleration);
|
||||
// Apply results
|
||||
m_position = p_pos;
|
||||
m_velocity = p_velocity;
|
||||
m_acceleration = p_acceleration;
|
||||
|
||||
bool is_end_position = moveresult.collides;
|
||||
pos_translator.update(m_position, is_end_position, dtime);
|
||||
pos_translator.translate(dtime);
|
||||
updateNodePos();
|
||||
|
||||
m_velocity += dtime * m_acceleration;
|
||||
} else {
|
||||
m_position += dtime * m_velocity + 0.5 * dtime * dtime * m_acceleration;
|
||||
m_velocity += dtime * m_acceleration;
|
||||
@@ -935,7 +941,7 @@ class GenericCAO : public ClientActiveObject
|
||||
}
|
||||
}
|
||||
if(fabs(m_prop.automatic_rotate) > 0.001){
|
||||
m_yaw += dtime * m_prop.automatic_rotate * 180 / PI;
|
||||
m_yaw += dtime * m_prop.automatic_rotate * 180 / M_PI;
|
||||
updateNodePos();
|
||||
}
|
||||
}
|
||||
@@ -961,7 +967,7 @@ class GenericCAO : public ClientActiveObject
|
||||
else if(cam_to_entity.Y < -0.75)
|
||||
col += 4;
|
||||
else{
|
||||
float mob_dir = atan2(cam_to_entity.Z, cam_to_entity.X) / PI * 180.;
|
||||
float mob_dir = atan2(cam_to_entity.Z, cam_to_entity.X) / M_PI * 180.;
|
||||
float dir = mob_dir - m_yaw;
|
||||
dir = wrapDegrees_180(dir);
|
||||
//infostream<<"id="<<m_id<<" dir="<<dir<<std::endl;
|
||||
|
||||
@@ -23,6 +23,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
#include "environment.h"
|
||||
#include "gamedef.h"
|
||||
#include "log.h"
|
||||
#include "map.h"
|
||||
|
||||
static void setBillboardTextureMatrix(scene::IBillboardSceneNode *bill,
|
||||
float txs, float tys, int col, int row)
|
||||
|
||||
@@ -20,7 +20,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
#ifndef CONTENT_CSO_HEADER
|
||||
#define CONTENT_CSO_HEADER
|
||||
|
||||
#include "common_irrlicht.h"
|
||||
#include "irrlichttypes_extrabloated.h"
|
||||
#include "clientsimpleobject.h"
|
||||
|
||||
ClientSimpleObject* createSmokePuff(scene::ISceneManager *smgr,
|
||||
|
||||
@@ -25,6 +25,9 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
#include "nodedef.h"
|
||||
#include "tile.h"
|
||||
#include "gamedef.h"
|
||||
#include "util/numeric.h"
|
||||
#include "util/serialize.h"
|
||||
#include "util/directiontables.h"
|
||||
|
||||
// Create a cuboid.
|
||||
// collector - the MeshCollector for the resulting polygons
|
||||
@@ -471,7 +474,9 @@ void mapblock_mesh_generate_special(MeshMakeData *data,
|
||||
pa_liquid.x0(), pa_liquid.y0()),
|
||||
};
|
||||
|
||||
// This fixes a strange bug
|
||||
// To get backface culling right, the vertices need to go
|
||||
// clockwise around the front of the face. And we happened to
|
||||
// calculate corner levels in exact reverse order.
|
||||
s32 corner_resolve[4] = {3,2,1,0};
|
||||
|
||||
for(s32 i=0; i<4; i++)
|
||||
@@ -482,6 +487,52 @@ void mapblock_mesh_generate_special(MeshMakeData *data,
|
||||
vertices[i].Pos.Y += corner_levels[j];
|
||||
vertices[i].Pos += intToFloat(p, BS);
|
||||
}
|
||||
|
||||
// Default downwards-flowing texture animation goes from
|
||||
// -Z towards +Z, thus the direction is +Z.
|
||||
// Rotate texture to make animation go in flow direction
|
||||
// Positive if liquid moves towards +Z
|
||||
int dz = (corner_levels[side_corners[2][0]] +
|
||||
corner_levels[side_corners[2][1]] <
|
||||
corner_levels[side_corners[3][0]] +
|
||||
corner_levels[side_corners[3][1]]);
|
||||
// Positive if liquid moves towards +X
|
||||
int dx = (corner_levels[side_corners[0][0]] +
|
||||
corner_levels[side_corners[0][1]] <
|
||||
corner_levels[side_corners[1][0]] +
|
||||
corner_levels[side_corners[1][1]]);
|
||||
// -X
|
||||
if(-dx >= abs(dz))
|
||||
{
|
||||
v2f t = vertices[0].TCoords;
|
||||
vertices[0].TCoords = vertices[1].TCoords;
|
||||
vertices[1].TCoords = vertices[2].TCoords;
|
||||
vertices[2].TCoords = vertices[3].TCoords;
|
||||
vertices[3].TCoords = t;
|
||||
}
|
||||
// +X
|
||||
if(dx >= abs(dz))
|
||||
{
|
||||
v2f t = vertices[0].TCoords;
|
||||
vertices[0].TCoords = vertices[3].TCoords;
|
||||
vertices[3].TCoords = vertices[2].TCoords;
|
||||
vertices[2].TCoords = vertices[1].TCoords;
|
||||
vertices[1].TCoords = t;
|
||||
}
|
||||
// -Z
|
||||
if(-dz >= abs(dx))
|
||||
{
|
||||
v2f t = vertices[0].TCoords;
|
||||
vertices[0].TCoords = vertices[3].TCoords;
|
||||
vertices[3].TCoords = vertices[2].TCoords;
|
||||
vertices[2].TCoords = vertices[1].TCoords;
|
||||
vertices[1].TCoords = t;
|
||||
t = vertices[0].TCoords;
|
||||
vertices[0].TCoords = vertices[3].TCoords;
|
||||
vertices[3].TCoords = vertices[2].TCoords;
|
||||
vertices[2].TCoords = vertices[1].TCoords;
|
||||
vertices[1].TCoords = t;
|
||||
}
|
||||
|
||||
u16 indices[] = {0,1,2,2,3,0};
|
||||
// Add to mesh collector
|
||||
@@ -976,6 +1027,63 @@ void mapblock_mesh_generate_special(MeshMakeData *data,
|
||||
u16 indices[] = {0,1,2,2,3,0};
|
||||
collector.append(tile, vertices, 4, indices, 6);
|
||||
break;}
|
||||
case NDT_NODEBOX:
|
||||
{
|
||||
static const v3s16 tile_dirs[6] = {
|
||||
v3s16(0, 1, 0),
|
||||
v3s16(0, -1, 0),
|
||||
v3s16(1, 0, 0),
|
||||
v3s16(-1, 0, 0),
|
||||
v3s16(0, 0, 1),
|
||||
v3s16(0, 0, -1)
|
||||
};
|
||||
|
||||
TileSpec tiles[6];
|
||||
for(int i = 0; i < 6; i++)
|
||||
{
|
||||
// Handles facedir rotation for textures
|
||||
tiles[i] = getNodeTile(n, p, tile_dirs[i], data);
|
||||
}
|
||||
|
||||
u16 l = getInteriorLight(n, 0, data);
|
||||
video::SColor c = MapBlock_LightColor(255, l);
|
||||
|
||||
v3f pos = intToFloat(p, BS);
|
||||
|
||||
std::vector<aabb3f> boxes = n.getNodeBoxes(nodedef);
|
||||
for(std::vector<aabb3f>::iterator
|
||||
i = boxes.begin();
|
||||
i != boxes.end(); i++)
|
||||
{
|
||||
aabb3f box = *i;
|
||||
box.MinEdge += pos;
|
||||
box.MaxEdge += pos;
|
||||
|
||||
// Compute texture coords
|
||||
f32 tx1 = (i->MinEdge.X/BS)+0.5;
|
||||
f32 ty1 = (i->MinEdge.Y/BS)+0.5;
|
||||
f32 tz1 = (i->MinEdge.Z/BS)+0.5;
|
||||
f32 tx2 = (i->MaxEdge.X/BS)+0.5;
|
||||
f32 ty2 = (i->MaxEdge.Y/BS)+0.5;
|
||||
f32 tz2 = (i->MaxEdge.Z/BS)+0.5;
|
||||
f32 txc[24] = {
|
||||
// up
|
||||
tx1, 1-tz2, tx2, 1-tz1,
|
||||
// down
|
||||
tx1, tz1, tx2, tz2,
|
||||
// right
|
||||
tz1, 1-ty2, tz2, 1-ty1,
|
||||
// left
|
||||
1-tz2, 1-ty2, 1-tz1, 1-ty1,
|
||||
// back
|
||||
1-tx2, 1-ty2, 1-tx1, 1-ty1,
|
||||
// front
|
||||
tx1, 1-ty2, tx2, 1-ty1,
|
||||
};
|
||||
|
||||
makeCuboid(&collector, box, tiles, 6, c, txc);
|
||||
}
|
||||
break;}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,10 +19,9 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
|
||||
#include "content_mapnode.h"
|
||||
|
||||
#include "irrlichttypes.h"
|
||||
#include "irrlichttypes_bloated.h"
|
||||
#include "mapnode.h"
|
||||
#include "nodedef.h"
|
||||
#include "utility.h"
|
||||
#include "nameidmapping.h"
|
||||
#include <map>
|
||||
|
||||
@@ -103,23 +102,6 @@ content_t trans_table_19[21][2] = {
|
||||
{CONTENT_BOOKSHELF, 29},
|
||||
};
|
||||
|
||||
MapNode mapnode_translate_from_internal(MapNode n_from, u8 version)
|
||||
{
|
||||
MapNode result = n_from;
|
||||
if(version <= 19)
|
||||
{
|
||||
content_t c_from = n_from.getContent();
|
||||
for(u32 i=0; i<sizeof(trans_table_19)/sizeof(trans_table_19[0]); i++)
|
||||
{
|
||||
if(trans_table_19[i][0] == c_from)
|
||||
{
|
||||
result.setContent(trans_table_19[i][1]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
MapNode mapnode_translate_to_internal(MapNode n_from, u8 version)
|
||||
{
|
||||
MapNode result = n_from;
|
||||
|
||||
@@ -28,7 +28,6 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
|
||||
// Backwards compatibility for non-extended content types in v19
|
||||
extern content_t trans_table_19[21][2];
|
||||
MapNode mapnode_translate_from_internal(MapNode n_from, u8 version);
|
||||
MapNode mapnode_translate_to_internal(MapNode n_from, u8 version);
|
||||
|
||||
// Get legacy node name mapping for loading old blocks
|
||||
|
||||
@@ -20,7 +20,9 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
#include "content_nodemeta.h"
|
||||
#include "inventory.h"
|
||||
#include "log.h"
|
||||
#include "utility.h"
|
||||
#include "util/serialize.h"
|
||||
#include "util/string.h"
|
||||
#include "constants.h" // MAP_BLOCKSIZE
|
||||
#include <sstream>
|
||||
|
||||
#define NODEMETA_GENERIC 1
|
||||
@@ -61,7 +63,7 @@ static bool content_nodemeta_deserialize_legacy_body(
|
||||
//meta->setString("infotext","\"${text}\"");
|
||||
meta->setString("infotext",
|
||||
std::string("\"") + meta->getString("text") + "\"");
|
||||
meta->setString("formspec","hack:sign_text_input");
|
||||
meta->setString("formspec","field[text;;${text}]");
|
||||
return false;
|
||||
}
|
||||
else if(id == NODEMETA_CHEST) // ChestNodeMetadata
|
||||
@@ -75,7 +77,7 @@ static bool content_nodemeta_deserialize_legacy_body(
|
||||
}
|
||||
assert(inv->getList("main") && !inv->getList("0"));
|
||||
|
||||
meta->setString("formspec","invsize[8,9;]"
|
||||
meta->setString("formspec","size[8,9]"
|
||||
"list[current_name;main;0,0;8,4;]"
|
||||
"list[current_player;main;0,5;8,4;]");
|
||||
return false;
|
||||
@@ -92,7 +94,7 @@ static bool content_nodemeta_deserialize_legacy_body(
|
||||
}
|
||||
assert(inv->getList("main") && !inv->getList("0"));
|
||||
|
||||
meta->setString("formspec","invsize[8,9;]"
|
||||
meta->setString("formspec","size[8,9]"
|
||||
"list[current_name;main;0,0;8,4;]"
|
||||
"list[current_player;main;0,5;8,4;]");
|
||||
return false;
|
||||
@@ -113,7 +115,7 @@ static bool content_nodemeta_deserialize_legacy_body(
|
||||
is>>temp;
|
||||
meta->setString("src_time", ftos((float)temp/10));
|
||||
|
||||
meta->setString("formspec","invsize[8,9;]"
|
||||
meta->setString("formspec","size[8,9]"
|
||||
"list[current_name;fuel;2,3;1,1;]"
|
||||
"list[current_name;src;2,1;1,1;]"
|
||||
"list[current_name;dst;5,1;2,2;]"
|
||||
|
||||
@@ -29,6 +29,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
#include "player.h"
|
||||
#include "scriptapi.h"
|
||||
#include "genericobject.h"
|
||||
#include "util/serialize.h"
|
||||
|
||||
core::map<u16, ServerActiveObject::Factory> ServerActiveObject::m_types;
|
||||
|
||||
@@ -141,6 +142,9 @@ TestSAO proto_TestSAO(NULL, v3f(0,0,0));
|
||||
|
||||
/*
|
||||
ItemSAO
|
||||
|
||||
DEPRECATED: New dropped items are implemented in Lua; see
|
||||
builtin/item_entity.lua.
|
||||
*/
|
||||
|
||||
class ItemSAO : public ServerActiveObject
|
||||
@@ -202,9 +206,12 @@ class ItemSAO : public ServerActiveObject
|
||||
m_speed_f *= pos_max_d / (m_speed_f.getLength()*dtime);
|
||||
v3f pos_f = getBasePosition();
|
||||
v3f pos_f_old = pos_f;
|
||||
v3f accel_f = v3f(0,0,0);
|
||||
f32 stepheight = 0;
|
||||
IGameDef *gamedef = m_env->getGameDef();
|
||||
moveresult = collisionMoveSimple(&m_env->getMap(), gamedef,
|
||||
pos_max_d, box, dtime, pos_f, m_speed_f);
|
||||
pos_max_d, box, stepheight, dtime,
|
||||
pos_f, m_speed_f, accel_f);
|
||||
|
||||
if(send_recommended == false)
|
||||
return;
|
||||
@@ -285,13 +292,6 @@ class ItemSAO : public ServerActiveObject
|
||||
ServerActiveObject *puncher,
|
||||
float time_from_last_punch)
|
||||
{
|
||||
// Directly delete item in creative mode
|
||||
if(g_settings->getBool("creative_mode") == true)
|
||||
{
|
||||
m_removed = true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Take item into inventory
|
||||
ItemStack item = createItemStack();
|
||||
Inventory *inv = puncher->getInventory();
|
||||
@@ -448,16 +448,18 @@ void LuaEntitySAO::step(float dtime, bool send_recommended)
|
||||
box.MaxEdge *= BS;
|
||||
collisionMoveResult moveresult;
|
||||
f32 pos_max_d = BS*0.25; // Distance per iteration
|
||||
v3f p_pos = getBasePosition();
|
||||
f32 stepheight = 0; // Maximum climbable step height
|
||||
v3f p_pos = m_base_position;
|
||||
v3f p_velocity = m_velocity;
|
||||
v3f p_acceleration = m_acceleration;
|
||||
IGameDef *gamedef = m_env->getGameDef();
|
||||
moveresult = collisionMovePrecise(&m_env->getMap(), gamedef,
|
||||
pos_max_d, box, dtime, p_pos, p_velocity);
|
||||
moveresult = collisionMoveSimple(&m_env->getMap(), gamedef,
|
||||
pos_max_d, box, stepheight, dtime,
|
||||
p_pos, p_velocity, p_acceleration);
|
||||
// Apply results
|
||||
setBasePosition(p_pos);
|
||||
m_base_position = p_pos;
|
||||
m_velocity = p_velocity;
|
||||
|
||||
m_velocity += dtime * m_acceleration;
|
||||
m_acceleration = p_acceleration;
|
||||
} else {
|
||||
m_base_position += dtime * m_velocity + 0.5 * dtime
|
||||
* dtime * m_acceleration;
|
||||
@@ -755,6 +757,8 @@ PlayerSAO::PlayerSAO(ServerEnvironment *env_, Player *player_, u16 peer_id_,
|
||||
m_last_good_position(0,0,0),
|
||||
m_last_good_position_age(0),
|
||||
m_time_from_last_punch(0),
|
||||
m_nocheat_dig_pos(32767, 32767, 32767),
|
||||
m_nocheat_dig_time(0),
|
||||
m_wield_index(0),
|
||||
m_position_not_sent(false),
|
||||
m_armor_groups_sent(false),
|
||||
@@ -865,8 +869,9 @@ void PlayerSAO::step(float dtime, bool send_recommended)
|
||||
}
|
||||
|
||||
m_time_from_last_punch += dtime;
|
||||
m_nocheat_dig_time += dtime;
|
||||
|
||||
if(m_is_singleplayer)
|
||||
if(m_is_singleplayer || g_settings->getBool("disable_anticheat"))
|
||||
{
|
||||
m_last_good_position = m_player->getPosition();
|
||||
m_last_good_position_age = 0;
|
||||
@@ -879,7 +884,8 @@ void PlayerSAO::step(float dtime, bool send_recommended)
|
||||
NOTE: Actually the server should handle player physics like the
|
||||
client does and compare player's position to what is calculated
|
||||
on our side. This is required when eg. players fly due to an
|
||||
explosion.
|
||||
explosion. Altough a node-based alternative might be possible
|
||||
too, and much more lightweight.
|
||||
*/
|
||||
|
||||
float player_max_speed = 0;
|
||||
@@ -1130,16 +1136,6 @@ void PlayerSAO::disconnected()
|
||||
}
|
||||
}
|
||||
|
||||
void PlayerSAO::createCreativeInventory()
|
||||
{
|
||||
if(m_inventory != &m_player->inventory)
|
||||
delete m_inventory;
|
||||
|
||||
m_inventory = new Inventory(m_player->inventory);
|
||||
m_inventory->clearContents();
|
||||
scriptapi_get_creative_inventory(m_env->getLua(), this);
|
||||
}
|
||||
|
||||
std::string PlayerSAO::getPropertyPacket()
|
||||
{
|
||||
m_prop.is_visible = (getHP() != 0);
|
||||
|
||||
@@ -163,8 +163,6 @@ class PlayerSAO : public ServerActiveObject
|
||||
|
||||
void disconnected();
|
||||
|
||||
void createCreativeInventory();
|
||||
|
||||
Player* getPlayer()
|
||||
{
|
||||
return m_player;
|
||||
@@ -173,6 +171,9 @@ class PlayerSAO : public ServerActiveObject
|
||||
{
|
||||
return m_peer_id;
|
||||
}
|
||||
|
||||
// Cheat prevention
|
||||
|
||||
v3f getLastGoodPosition() const
|
||||
{
|
||||
return m_last_good_position;
|
||||
@@ -183,6 +184,26 @@ class PlayerSAO : public ServerActiveObject
|
||||
m_time_from_last_punch = 0.0;
|
||||
return r;
|
||||
}
|
||||
void noCheatDigStart(v3s16 p)
|
||||
{
|
||||
m_nocheat_dig_pos = p;
|
||||
m_nocheat_dig_time = 0;
|
||||
}
|
||||
v3s16 getNoCheatDigPos()
|
||||
{
|
||||
return m_nocheat_dig_pos;
|
||||
}
|
||||
float getNoCheatDigTime()
|
||||
{
|
||||
return m_nocheat_dig_time;
|
||||
}
|
||||
void noCheatDigEnd()
|
||||
{
|
||||
m_nocheat_dig_pos = v3s16(32767, 32767, 32767);
|
||||
}
|
||||
|
||||
// Other
|
||||
|
||||
void updatePrivileges(const std::set<std::string> &privs,
|
||||
bool is_singleplayer)
|
||||
{
|
||||
@@ -196,9 +217,14 @@ class PlayerSAO : public ServerActiveObject
|
||||
Player *m_player;
|
||||
u16 m_peer_id;
|
||||
Inventory *m_inventory;
|
||||
|
||||
// Cheat prevention
|
||||
v3f m_last_good_position;
|
||||
float m_last_good_position_age;
|
||||
float m_time_from_last_punch;
|
||||
v3s16 m_nocheat_dig_pos;
|
||||
float m_nocheat_dig_time;
|
||||
|
||||
int m_wield_index;
|
||||
bool m_position_not_sent;
|
||||
ItemGroupList m_armor_groups;
|
||||
|
||||
212
src/craftdef.cpp
212
src/craftdef.cpp
@@ -23,9 +23,11 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
#include "log.h"
|
||||
#include <sstream>
|
||||
#include <set>
|
||||
#include "utility.h"
|
||||
#include <algorithm>
|
||||
#include "gamedef.h"
|
||||
#include "inventory.h"
|
||||
#include "util/serialize.h"
|
||||
#include "strfnd.h"
|
||||
|
||||
// Check if input matches recipe
|
||||
// Takes recipe groups into account
|
||||
@@ -35,15 +37,23 @@ static bool inputItemMatchesRecipe(const std::string &inp_name,
|
||||
// Exact name
|
||||
if(inp_name == rec_name)
|
||||
return true;
|
||||
|
||||
|
||||
// Group
|
||||
if(rec_name.substr(0,6) == "group:" && idef->isKnown(inp_name)){
|
||||
std::string rec_group = rec_name.substr(6);
|
||||
const struct ItemDefinition &def = idef->get(inp_name);
|
||||
if(itemgroup_get(def.groups, rec_group) != 0)
|
||||
Strfnd f(rec_name.substr(6));
|
||||
bool all_groups_match = true;
|
||||
do{
|
||||
std::string check_group = f.next(",");
|
||||
if(itemgroup_get(def.groups, check_group) == 0){
|
||||
all_groups_match = false;
|
||||
break;
|
||||
}
|
||||
}while(!f.atend());
|
||||
if(all_groups_match)
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// Didn't match
|
||||
return false;
|
||||
}
|
||||
@@ -84,6 +94,20 @@ static std::vector<std::string> craftGetItemNames(
|
||||
return result;
|
||||
}
|
||||
|
||||
// convert a list of item names, to ItemStacks.
|
||||
static std::vector<ItemStack> craftGetItems(
|
||||
const std::vector<std::string> &items, IGameDef *gamedef)
|
||||
{
|
||||
std::vector<ItemStack> result;
|
||||
for(std::vector<std::string>::const_iterator
|
||||
i = items.begin();
|
||||
i != items.end(); i++)
|
||||
{
|
||||
result.push_back(ItemStack(std::string(*i),(u16)1,(u16)0,"",gamedef->getItemDefManager()));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// Compute bounding rectangle given a matrix of items
|
||||
// Returns false if every item is ""
|
||||
static bool craftGetBounds(const std::vector<std::string> &items, unsigned int width,
|
||||
@@ -126,6 +150,8 @@ static bool craftGetBounds(const std::vector<std::string> &items, unsigned int w
|
||||
return success;
|
||||
}
|
||||
|
||||
#if 0
|
||||
// This became useless when group support was added to shapeless recipes
|
||||
// Convert a list of item names to a multiset
|
||||
static std::multiset<std::string> craftMakeMultiset(const std::vector<std::string> &names)
|
||||
{
|
||||
@@ -139,6 +165,7 @@ static std::multiset<std::string> craftMakeMultiset(const std::vector<std::strin
|
||||
}
|
||||
return set;
|
||||
}
|
||||
#endif
|
||||
|
||||
// Removes 1 from each item stack
|
||||
static void craftDecrementInput(CraftInput &input, IGameDef *gamedef)
|
||||
@@ -439,6 +466,11 @@ CraftOutput CraftDefinitionShaped::getOutput(const CraftInput &input, IGameDef *
|
||||
return CraftOutput(output, 0);
|
||||
}
|
||||
|
||||
CraftInput CraftDefinitionShaped::getInput(const CraftOutput &output, IGameDef *gamedef) const
|
||||
{
|
||||
return CraftInput(CRAFT_METHOD_NORMAL,width,craftGetItems(recipe,gamedef));
|
||||
}
|
||||
|
||||
void CraftDefinitionShaped::decrementInput(CraftInput &input, IGameDef *gamedef) const
|
||||
{
|
||||
craftDecrementOrReplaceInput(input, replacements, gamedef);
|
||||
@@ -489,17 +521,48 @@ bool CraftDefinitionShapeless::check(const CraftInput &input, IGameDef *gamedef)
|
||||
{
|
||||
if(input.method != CRAFT_METHOD_NORMAL)
|
||||
return false;
|
||||
|
||||
// Filter empty items out of input
|
||||
std::vector<std::string> input_filtered;
|
||||
for(std::vector<ItemStack>::const_iterator
|
||||
i = input.items.begin();
|
||||
i != input.items.end(); i++)
|
||||
{
|
||||
if(i->name != "")
|
||||
input_filtered.push_back(i->name);
|
||||
}
|
||||
|
||||
// Get input item multiset
|
||||
std::vector<std::string> inp_names = craftGetItemNames(input.items, gamedef);
|
||||
std::multiset<std::string> inp_names_multiset = craftMakeMultiset(inp_names);
|
||||
// If there is a wrong number of items in input, no match
|
||||
if(input_filtered.size() != recipe.size()){
|
||||
/*dstream<<"Number of input items ("<<input_filtered.size()
|
||||
<<") does not match recipe size ("<<recipe.size()<<") "
|
||||
<<"of recipe with output="<<output<<std::endl;*/
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get recipe item multiset
|
||||
std::vector<std::string> rec_names = craftGetItemNames(recipe, gamedef);
|
||||
std::multiset<std::string> rec_names_multiset = craftMakeMultiset(rec_names);
|
||||
// Try with all permutations of the recipe
|
||||
std::vector<std::string> recipe_copy = recipe;
|
||||
// Start from the lexicographically first permutation (=sorted)
|
||||
std::sort(recipe_copy.begin(), recipe_copy.end());
|
||||
//while(std::prev_permutation(recipe_copy.begin(), recipe_copy.end())){}
|
||||
do{
|
||||
// If all items match, the recipe matches
|
||||
bool all_match = true;
|
||||
//dstream<<"Testing recipe (output="<<output<<"):";
|
||||
for(size_t i=0; i<recipe.size(); i++){
|
||||
//dstream<<" ("<<input_filtered[i]<<" == "<<recipe_copy[i]<<")";
|
||||
if(!inputItemMatchesRecipe(input_filtered[i], recipe_copy[i],
|
||||
gamedef->idef())){
|
||||
all_match = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
//dstream<<" -> match="<<all_match<<std::endl;
|
||||
if(all_match)
|
||||
return true;
|
||||
}while(std::next_permutation(recipe_copy.begin(), recipe_copy.end()));
|
||||
|
||||
// Recipe is matched when the multisets coincide
|
||||
return inp_names_multiset == rec_names_multiset;
|
||||
return false;
|
||||
}
|
||||
|
||||
CraftOutput CraftDefinitionShapeless::getOutput(const CraftInput &input, IGameDef *gamedef) const
|
||||
@@ -507,6 +570,11 @@ CraftOutput CraftDefinitionShapeless::getOutput(const CraftInput &input, IGameDe
|
||||
return CraftOutput(output, 0);
|
||||
}
|
||||
|
||||
CraftInput CraftDefinitionShapeless::getInput(const CraftOutput &output, IGameDef *gamedef) const
|
||||
{
|
||||
return CraftInput(CRAFT_METHOD_NORMAL,0,craftGetItems(recipe,gamedef));
|
||||
}
|
||||
|
||||
void CraftDefinitionShapeless::decrementInput(CraftInput &input, IGameDef *gamedef) const
|
||||
{
|
||||
craftDecrementOrReplaceInput(input, replacements, gamedef);
|
||||
@@ -625,6 +693,13 @@ CraftOutput CraftDefinitionToolRepair::getOutput(const CraftInput &input, IGameD
|
||||
return CraftOutput(repaired.getItemString(), 0);
|
||||
}
|
||||
|
||||
CraftInput CraftDefinitionToolRepair::getInput(const CraftOutput &output, IGameDef *gamedef) const
|
||||
{
|
||||
std::vector<ItemStack> stack;
|
||||
stack.push_back(ItemStack());
|
||||
return CraftInput(CRAFT_METHOD_COOKING,additional_wear,stack);
|
||||
}
|
||||
|
||||
void CraftDefinitionToolRepair::decrementInput(CraftInput &input, IGameDef *gamedef) const
|
||||
{
|
||||
craftDecrementInput(input, gamedef);
|
||||
@@ -663,16 +738,26 @@ bool CraftDefinitionCooking::check(const CraftInput &input, IGameDef *gamedef) c
|
||||
if(input.method != CRAFT_METHOD_COOKING)
|
||||
return false;
|
||||
|
||||
// Get input item multiset
|
||||
std::vector<std::string> inp_names = craftGetItemNames(input.items, gamedef);
|
||||
std::multiset<std::string> inp_names_multiset = craftMakeMultiset(inp_names);
|
||||
// Filter empty items out of input
|
||||
std::vector<std::string> input_filtered;
|
||||
for(std::vector<ItemStack>::const_iterator
|
||||
i = input.items.begin();
|
||||
i != input.items.end(); i++)
|
||||
{
|
||||
if(i->name != "")
|
||||
input_filtered.push_back(i->name);
|
||||
}
|
||||
|
||||
// Get recipe item multiset
|
||||
std::multiset<std::string> rec_names_multiset;
|
||||
rec_names_multiset.insert(craftGetItemName(recipe, gamedef));
|
||||
|
||||
// Recipe is matched when the multisets coincide
|
||||
return inp_names_multiset == rec_names_multiset;
|
||||
// If there is a wrong number of items in input, no match
|
||||
if(input_filtered.size() != 1){
|
||||
/*dstream<<"Number of input items ("<<input_filtered.size()
|
||||
<<") does not match recipe size (1) "
|
||||
<<"of cooking recipe with output="<<output<<std::endl;*/
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check the single input item
|
||||
return inputItemMatchesRecipe(input_filtered[0], recipe, gamedef->idef());
|
||||
}
|
||||
|
||||
CraftOutput CraftDefinitionCooking::getOutput(const CraftInput &input, IGameDef *gamedef) const
|
||||
@@ -680,6 +765,13 @@ CraftOutput CraftDefinitionCooking::getOutput(const CraftInput &input, IGameDef
|
||||
return CraftOutput(output, cooktime);
|
||||
}
|
||||
|
||||
CraftInput CraftDefinitionCooking::getInput(const CraftOutput &output, IGameDef *gamedef) const
|
||||
{
|
||||
std::vector<std::string> rec;
|
||||
rec.push_back(recipe);
|
||||
return CraftInput(CRAFT_METHOD_COOKING,cooktime,craftGetItems(rec,gamedef));
|
||||
}
|
||||
|
||||
void CraftDefinitionCooking::decrementInput(CraftInput &input, IGameDef *gamedef) const
|
||||
{
|
||||
craftDecrementOrReplaceInput(input, replacements, gamedef);
|
||||
@@ -727,16 +819,26 @@ bool CraftDefinitionFuel::check(const CraftInput &input, IGameDef *gamedef) cons
|
||||
if(input.method != CRAFT_METHOD_FUEL)
|
||||
return false;
|
||||
|
||||
// Get input item multiset
|
||||
std::vector<std::string> inp_names = craftGetItemNames(input.items, gamedef);
|
||||
std::multiset<std::string> inp_names_multiset = craftMakeMultiset(inp_names);
|
||||
// Filter empty items out of input
|
||||
std::vector<std::string> input_filtered;
|
||||
for(std::vector<ItemStack>::const_iterator
|
||||
i = input.items.begin();
|
||||
i != input.items.end(); i++)
|
||||
{
|
||||
if(i->name != "")
|
||||
input_filtered.push_back(i->name);
|
||||
}
|
||||
|
||||
// Get recipe item multiset
|
||||
std::multiset<std::string> rec_names_multiset;
|
||||
rec_names_multiset.insert(craftGetItemName(recipe, gamedef));
|
||||
|
||||
// Recipe is matched when the multisets coincide
|
||||
return inp_names_multiset == rec_names_multiset;
|
||||
// If there is a wrong number of items in input, no match
|
||||
if(input_filtered.size() != 1){
|
||||
/*dstream<<"Number of input items ("<<input_filtered.size()
|
||||
<<") does not match recipe size (1) "
|
||||
<<"of fuel recipe with burntime="<<burntime<<std::endl;*/
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check the single input item
|
||||
return inputItemMatchesRecipe(input_filtered[0], recipe, gamedef->idef());
|
||||
}
|
||||
|
||||
CraftOutput CraftDefinitionFuel::getOutput(const CraftInput &input, IGameDef *gamedef) const
|
||||
@@ -744,6 +846,13 @@ CraftOutput CraftDefinitionFuel::getOutput(const CraftInput &input, IGameDef *ga
|
||||
return CraftOutput("", burntime);
|
||||
}
|
||||
|
||||
CraftInput CraftDefinitionFuel::getInput(const CraftOutput &output, IGameDef *gamedef) const
|
||||
{
|
||||
std::vector<std::string> rec;
|
||||
rec.push_back(recipe);
|
||||
return CraftInput(CRAFT_METHOD_COOKING,(int)burntime,craftGetItems(rec,gamedef));
|
||||
}
|
||||
|
||||
void CraftDefinitionFuel::decrementInput(CraftInput &input, IGameDef *gamedef) const
|
||||
{
|
||||
craftDecrementOrReplaceInput(input, replacements, gamedef);
|
||||
@@ -837,6 +946,47 @@ class CCraftDefManager: public IWritableCraftDefManager
|
||||
}
|
||||
return false;
|
||||
}
|
||||
virtual bool getCraftRecipe(CraftInput &input, CraftOutput &output,
|
||||
IGameDef *gamedef) const
|
||||
{
|
||||
CraftOutput tmpout;
|
||||
tmpout.item = "";
|
||||
tmpout.time = 0;
|
||||
|
||||
// If output item is empty, abort.
|
||||
if(output.item.empty())
|
||||
return false;
|
||||
|
||||
// Walk crafting definitions from back to front, so that later
|
||||
// definitions can override earlier ones.
|
||||
for(std::vector<CraftDefinition*>::const_reverse_iterator
|
||||
i = m_craft_definitions.rbegin();
|
||||
i != m_craft_definitions.rend(); i++)
|
||||
{
|
||||
CraftDefinition *def = *i;
|
||||
|
||||
/*infostream<<"Checking "<<input.dump()<<std::endl
|
||||
<<" against "<<def->dump()<<std::endl;*/
|
||||
|
||||
try {
|
||||
tmpout = def->getOutput(input, gamedef);
|
||||
if(tmpout.item.substr(0,output.item.length()) == output.item)
|
||||
{
|
||||
// Get output, then decrement input (if requested)
|
||||
input = def->getInput(output, gamedef);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
catch(SerializationError &e)
|
||||
{
|
||||
errorstream<<"getCraftResult: ERROR: "
|
||||
<<"Serialization error in recipe "
|
||||
<<def->dump()<<std::endl;
|
||||
// then go on with the next craft definition
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
virtual std::string dump() const
|
||||
{
|
||||
std::ostringstream os(std::ios::binary);
|
||||
|
||||
@@ -130,6 +130,8 @@ class CraftDefinition
|
||||
// Returns the output structure, meaning depends on crafting method
|
||||
// The implementation can assume that check(input) returns true
|
||||
virtual CraftOutput getOutput(const CraftInput &input, IGameDef *gamedef) const=0;
|
||||
// the inverse of the above
|
||||
virtual CraftInput getInput(const CraftOutput &output, IGameDef *gamedef) const=0;
|
||||
// Decreases count of every input item
|
||||
virtual void decrementInput(CraftInput &input, IGameDef *gamedef) const=0;
|
||||
|
||||
@@ -164,6 +166,7 @@ class CraftDefinitionShaped: public CraftDefinition
|
||||
virtual std::string getName() const;
|
||||
virtual bool check(const CraftInput &input, IGameDef *gamedef) const;
|
||||
virtual CraftOutput getOutput(const CraftInput &input, IGameDef *gamedef) const;
|
||||
virtual CraftInput getInput(const CraftOutput &output, IGameDef *gamedef) const;
|
||||
virtual void decrementInput(CraftInput &input, IGameDef *gamedef) const;
|
||||
|
||||
virtual std::string dump() const;
|
||||
@@ -205,6 +208,7 @@ class CraftDefinitionShapeless: public CraftDefinition
|
||||
virtual std::string getName() const;
|
||||
virtual bool check(const CraftInput &input, IGameDef *gamedef) const;
|
||||
virtual CraftOutput getOutput(const CraftInput &input, IGameDef *gamedef) const;
|
||||
virtual CraftInput getInput(const CraftOutput &output, IGameDef *gamedef) const;
|
||||
virtual void decrementInput(CraftInput &input, IGameDef *gamedef) const;
|
||||
|
||||
virtual std::string dump() const;
|
||||
@@ -242,6 +246,7 @@ class CraftDefinitionToolRepair: public CraftDefinition
|
||||
virtual std::string getName() const;
|
||||
virtual bool check(const CraftInput &input, IGameDef *gamedef) const;
|
||||
virtual CraftOutput getOutput(const CraftInput &input, IGameDef *gamedef) const;
|
||||
virtual CraftInput getInput(const CraftOutput &output, IGameDef *gamedef) const;
|
||||
virtual void decrementInput(CraftInput &input, IGameDef *gamedef) const;
|
||||
|
||||
virtual std::string dump() const;
|
||||
@@ -281,6 +286,7 @@ class CraftDefinitionCooking: public CraftDefinition
|
||||
virtual std::string getName() const;
|
||||
virtual bool check(const CraftInput &input, IGameDef *gamedef) const;
|
||||
virtual CraftOutput getOutput(const CraftInput &input, IGameDef *gamedef) const;
|
||||
virtual CraftInput getInput(const CraftOutput &output, IGameDef *gamedef) const;
|
||||
virtual void decrementInput(CraftInput &input, IGameDef *gamedef) const;
|
||||
|
||||
virtual std::string dump() const;
|
||||
@@ -320,6 +326,7 @@ class CraftDefinitionFuel: public CraftDefinition
|
||||
virtual std::string getName() const;
|
||||
virtual bool check(const CraftInput &input, IGameDef *gamedef) const;
|
||||
virtual CraftOutput getOutput(const CraftInput &input, IGameDef *gamedef) const;
|
||||
virtual CraftInput getInput(const CraftOutput &output, IGameDef *gamedef) const;
|
||||
virtual void decrementInput(CraftInput &input, IGameDef *gamedef) const;
|
||||
|
||||
virtual std::string dump() const;
|
||||
@@ -349,6 +356,8 @@ class ICraftDefManager
|
||||
// The main crafting function
|
||||
virtual bool getCraftResult(CraftInput &input, CraftOutput &output,
|
||||
bool decrementInput, IGameDef *gamedef) const=0;
|
||||
virtual bool getCraftRecipe(CraftInput &input, CraftOutput &output,
|
||||
IGameDef *gamedef) const=0;
|
||||
|
||||
// Print crafting recipes for debugging
|
||||
virtual std::string dump() const=0;
|
||||
@@ -365,6 +374,8 @@ class IWritableCraftDefManager : public ICraftDefManager
|
||||
// The main crafting function
|
||||
virtual bool getCraftResult(CraftInput &input, CraftOutput &output,
|
||||
bool decrementInput, IGameDef *gamedef) const=0;
|
||||
virtual bool getCraftRecipe(CraftInput &input, CraftOutput &output,
|
||||
IGameDef *gamedef) const=0;
|
||||
|
||||
// Print crafting recipes for debugging
|
||||
virtual std::string dump() const=0;
|
||||
|
||||
@@ -21,6 +21,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
#include "debug.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <cstring>
|
||||
|
||||
/*
|
||||
Debug output
|
||||
|
||||
13
src/debug.h
13
src/debug.h
@@ -24,10 +24,10 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
#include <jmutex.h>
|
||||
#include <jmutexautolock.h>
|
||||
#include <iostream>
|
||||
#include "common_irrlicht.h"
|
||||
#include "irrlichttypes.h"
|
||||
#include <irrMap.h>
|
||||
#include "threads.h"
|
||||
#include "gettime.h"
|
||||
#include "constants.h"
|
||||
#include "exceptions.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
@@ -39,6 +39,15 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
#else
|
||||
#endif
|
||||
|
||||
// Whether to catch all std::exceptions.
|
||||
// Assert will be called on such an event.
|
||||
// In debug mode, leave these for the debugger and don't catch them.
|
||||
#ifdef NDEBUG
|
||||
#define CATCH_UNHANDLED_EXCEPTIONS 1
|
||||
#else
|
||||
#define CATCH_UNHANDLED_EXCEPTIONS 0
|
||||
#endif
|
||||
|
||||
/*
|
||||
Debug output
|
||||
*/
|
||||
|
||||
@@ -103,6 +103,7 @@ void set_default_settings(Settings *settings)
|
||||
settings->setDefault("console_alpha", "200");
|
||||
settings->setDefault("enable_sound", "true");
|
||||
settings->setDefault("sound_volume", "0.8");
|
||||
settings->setDefault("desynchronize_mapblock_texture_animation", "true");
|
||||
|
||||
// Server stuff
|
||||
// "map-dir" doesn't exist by default.
|
||||
@@ -119,6 +120,9 @@ void set_default_settings(Settings *settings)
|
||||
settings->setDefault("default_privs", "interact, shout");
|
||||
settings->setDefault("unlimited_player_transfer_distance", "true");
|
||||
settings->setDefault("enable_pvp", "true");
|
||||
settings->setDefault("disallow_empty_password", "false");
|
||||
settings->setDefault("disable_anticheat", "false");
|
||||
settings->setDefault("enable_rollback_recording", "false");
|
||||
|
||||
settings->setDefault("profiler_print_interval", "0");
|
||||
settings->setDefault("enable_mapgen_debug_info", "false");
|
||||
|
||||
@@ -42,6 +42,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
#include "localplayer.h"
|
||||
#endif
|
||||
#include "daynightratio.h"
|
||||
#include "map.h"
|
||||
|
||||
#define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")"
|
||||
|
||||
@@ -325,6 +326,7 @@ ServerEnvironment::ServerEnvironment(ServerMap *map, lua_State *L,
|
||||
m_emerger(emerger),
|
||||
m_random_spawn_timer(3),
|
||||
m_send_recommended_timer(0),
|
||||
m_active_block_interval_overload_skip(0),
|
||||
m_game_time(0),
|
||||
m_game_time_fraction_counter(0)
|
||||
{
|
||||
@@ -349,6 +351,17 @@ ServerEnvironment::~ServerEnvironment()
|
||||
}
|
||||
}
|
||||
|
||||
Map & ServerEnvironment::getMap()
|
||||
{
|
||||
return *m_map;
|
||||
}
|
||||
|
||||
ServerMap & ServerEnvironment::getServerMap()
|
||||
{
|
||||
return *m_map;
|
||||
}
|
||||
|
||||
|
||||
void ServerEnvironment::serializePlayers(const std::string &savedir)
|
||||
{
|
||||
std::string players_path = savedir + "/players";
|
||||
@@ -774,10 +787,18 @@ void ServerEnvironment::activateBlock(MapBlock *block, u32 additional_dtime)
|
||||
activateObjects(block);
|
||||
|
||||
// Run node timers
|
||||
std::map<v3s16, f32> elapsed_timers =
|
||||
std::map<v3s16, NodeTimer> elapsed_timers =
|
||||
block->m_node_timers.step((float)dtime_s);
|
||||
if(!elapsed_timers.empty())
|
||||
errorstream<<"Node timers don't work yet!"<<std::endl;
|
||||
if(!elapsed_timers.empty()){
|
||||
MapNode n;
|
||||
for(std::map<v3s16, NodeTimer>::iterator
|
||||
i = elapsed_timers.begin();
|
||||
i != elapsed_timers.end(); i++){
|
||||
n = block->getNodeNoEx(i->first);
|
||||
if(scriptapi_node_on_timer(m_lua,i->first,n,i->second.elapsed))
|
||||
block->setNodeTimer(i->first,NodeTimer(i->second.timeout,0));
|
||||
}
|
||||
}
|
||||
|
||||
/* Handle ActiveBlockModifiers */
|
||||
ABMHandler abmhandler(m_abms, dtime_s, this, false);
|
||||
@@ -1058,16 +1079,29 @@ void ServerEnvironment::step(float dtime)
|
||||
"Timestamp older than 60s (step)");
|
||||
|
||||
// Run node timers
|
||||
std::map<v3s16, f32> elapsed_timers =
|
||||
block->m_node_timers.step(dtime);
|
||||
if(!elapsed_timers.empty())
|
||||
errorstream<<"Node timers don't work yet!"<<std::endl;
|
||||
std::map<v3s16, NodeTimer> elapsed_timers =
|
||||
block->m_node_timers.step((float)dtime);
|
||||
if(!elapsed_timers.empty()){
|
||||
MapNode n;
|
||||
for(std::map<v3s16, NodeTimer>::iterator
|
||||
i = elapsed_timers.begin();
|
||||
i != elapsed_timers.end(); i++){
|
||||
n = block->getNodeNoEx(i->first);
|
||||
if(scriptapi_node_on_timer(m_lua,i->first,n,i->second.elapsed))
|
||||
block->setNodeTimer(i->first,NodeTimer(i->second.timeout,0));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const float abm_interval = 1.0;
|
||||
if(m_active_block_modifier_interval.step(dtime, abm_interval))
|
||||
{
|
||||
do{ // breakable
|
||||
if(m_active_block_interval_overload_skip > 0){
|
||||
ScopeProfiler sp(g_profiler, "SEnv: ABM overload skips");
|
||||
m_active_block_interval_overload_skip--;
|
||||
break;
|
||||
}
|
||||
ScopeProfiler sp(g_profiler, "SEnv: modify in blocks avg /1s", SPT_AVG);
|
||||
TimeTaker timer("modify in active blocks");
|
||||
|
||||
@@ -1100,8 +1134,9 @@ void ServerEnvironment::step(float dtime)
|
||||
infostream<<"WARNING: active block modifiers took "
|
||||
<<time_ms<<"ms (longer than "
|
||||
<<max_time_ms<<"ms)"<<std::endl;
|
||||
m_active_block_interval_overload_skip = (time_ms / max_time_ms) + 1;
|
||||
}
|
||||
}
|
||||
}while(0);
|
||||
|
||||
/*
|
||||
Step script environment (run global on_step())
|
||||
|
||||
@@ -31,12 +31,14 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
*/
|
||||
|
||||
#include <set>
|
||||
#include "common_irrlicht.h"
|
||||
#include "irrlichttypes_extrabloated.h"
|
||||
#include "player.h"
|
||||
#include "map.h"
|
||||
#include <ostream>
|
||||
#include "utility.h"
|
||||
#include "activeobject.h"
|
||||
#include "util/container.h"
|
||||
#include "util/numeric.h"
|
||||
#include "mapnode.h"
|
||||
#include "mapblock.h"
|
||||
|
||||
class Server;
|
||||
class ServerEnvironment;
|
||||
@@ -45,6 +47,8 @@ class ServerActiveObject;
|
||||
typedef struct lua_State lua_State;
|
||||
class ITextureSource;
|
||||
class IGameDef;
|
||||
class Map;
|
||||
class ServerMap;
|
||||
class ClientMap;
|
||||
|
||||
class Environment
|
||||
@@ -190,11 +194,9 @@ class ServerEnvironment : public Environment
|
||||
IBackgroundBlockEmerger *emerger);
|
||||
~ServerEnvironment();
|
||||
|
||||
Map & getMap()
|
||||
{ return *m_map; }
|
||||
Map & getMap();
|
||||
|
||||
ServerMap & getServerMap()
|
||||
{ return *m_map; }
|
||||
ServerMap & getServerMap();
|
||||
|
||||
lua_State* getLua()
|
||||
{ return m_lua; }
|
||||
@@ -358,6 +360,7 @@ class ServerEnvironment : public Environment
|
||||
IntervalLimiter m_active_blocks_management_interval;
|
||||
IntervalLimiter m_active_block_modifier_interval;
|
||||
IntervalLimiter m_active_blocks_nodemetadata_interval;
|
||||
int m_active_block_interval_overload_skip;
|
||||
// Time from the beginning of the game in seconds.
|
||||
// Incremented in step().
|
||||
u32 m_game_time;
|
||||
|
||||
@@ -25,7 +25,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
distance according to map seed
|
||||
*/
|
||||
|
||||
#include "common_irrlicht.h"
|
||||
#include "irrlichttypes_extrabloated.h"
|
||||
|
||||
#define FARMESH_MATERIAL_COUNT 2
|
||||
|
||||
|
||||
@@ -19,15 +19,16 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
*/
|
||||
|
||||
#include "filecache.h"
|
||||
|
||||
#include "clientserver.h"
|
||||
#include "log.h"
|
||||
#include "filesys.h"
|
||||
#include "utility.h"
|
||||
#include "hex.h"
|
||||
#include "sha1.h"
|
||||
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
|
||||
bool FileCache::loadByPath(const std::string &path, std::ostream &os)
|
||||
{
|
||||
|
||||
434
src/game.cpp
434
src/game.cpp
@@ -18,7 +18,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
*/
|
||||
|
||||
#include "game.h"
|
||||
#include "common_irrlicht.h"
|
||||
#include "irrlichttypes_extrabloated.h"
|
||||
#include <IGUICheckBox.h>
|
||||
#include <IGUIEditBox.h>
|
||||
#include <IGUIButton.h>
|
||||
@@ -28,7 +28,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
#include "server.h"
|
||||
#include "guiPauseMenu.h"
|
||||
#include "guiPasswordChange.h"
|
||||
#include "guiInventoryMenu.h"
|
||||
#include "guiFormSpecMenu.h"
|
||||
#include "guiTextInputMenu.h"
|
||||
#include "guiDeathScreen.h"
|
||||
#include "tool.h"
|
||||
@@ -61,17 +61,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
#endif
|
||||
#include "event_manager.h"
|
||||
#include <list>
|
||||
|
||||
/*
|
||||
Setting this to 1 enables a special camera mode that forces
|
||||
the renderers to think that the camera statically points from
|
||||
the starting place to a static direction.
|
||||
|
||||
This allows one to move around with the player and see what
|
||||
is actually drawn behind solid things and behind the player.
|
||||
*/
|
||||
#define FIELD_OF_VIEW_TEST 0
|
||||
|
||||
#include "util/directiontables.h"
|
||||
|
||||
/*
|
||||
Text input system
|
||||
@@ -87,6 +77,10 @@ struct TextDestChat : public TextDest
|
||||
{
|
||||
m_client->typeChatMessage(text);
|
||||
}
|
||||
void gotText(std::map<std::string, std::string> fields)
|
||||
{
|
||||
m_client->typeChatMessage(narrow_to_wide(fields["text"]));
|
||||
}
|
||||
|
||||
Client *m_client;
|
||||
};
|
||||
@@ -98,20 +92,39 @@ struct TextDestNodeMetadata : public TextDest
|
||||
m_p = p;
|
||||
m_client = client;
|
||||
}
|
||||
// This is deprecated I guess? -celeron55
|
||||
void gotText(std::wstring text)
|
||||
{
|
||||
std::string ntext = wide_to_narrow(text);
|
||||
infostream<<"Changing text of a sign node: "
|
||||
<<ntext<<std::endl;
|
||||
infostream<<"Submitting 'text' field of node at ("<<m_p.X<<","
|
||||
<<m_p.Y<<","<<m_p.Z<<"): "<<ntext<<std::endl;
|
||||
std::map<std::string, std::string> fields;
|
||||
fields["text"] = ntext;
|
||||
m_client->sendNodemetaFields(m_p, "", fields);
|
||||
}
|
||||
void gotText(std::map<std::string, std::string> fields)
|
||||
{
|
||||
m_client->sendNodemetaFields(m_p, "", fields);
|
||||
}
|
||||
|
||||
v3s16 m_p;
|
||||
Client *m_client;
|
||||
};
|
||||
|
||||
struct TextDestPlayerInventory : public TextDest
|
||||
{
|
||||
TextDestPlayerInventory(Client *client)
|
||||
{
|
||||
m_client = client;
|
||||
}
|
||||
void gotText(std::map<std::string, std::string> fields)
|
||||
{
|
||||
m_client->sendInventoryFields("", fields);
|
||||
}
|
||||
|
||||
Client *m_client;
|
||||
};
|
||||
|
||||
/* Respawn menu callback */
|
||||
|
||||
class MainRespawnInitiator: public IRespawnInitiator
|
||||
@@ -149,11 +162,34 @@ class NodeMetadataFormSource: public IFormSource
|
||||
return "";
|
||||
return meta->getString("formspec");
|
||||
}
|
||||
std::string resolveText(std::string str)
|
||||
{
|
||||
NodeMetadata *meta = m_map->getNodeMetadata(m_p);
|
||||
if(!meta)
|
||||
return str;
|
||||
return meta->resolveString(str);
|
||||
}
|
||||
|
||||
ClientMap *m_map;
|
||||
v3s16 m_p;
|
||||
};
|
||||
|
||||
class PlayerInventoryFormSource: public IFormSource
|
||||
{
|
||||
public:
|
||||
PlayerInventoryFormSource(Client *client):
|
||||
m_client(client)
|
||||
{
|
||||
}
|
||||
std::string getForm()
|
||||
{
|
||||
LocalPlayer* player = m_client->getEnv().getLocalPlayer();
|
||||
return player->inventory_formspec;
|
||||
}
|
||||
|
||||
Client *m_client;
|
||||
};
|
||||
|
||||
/*
|
||||
Hotbar draw routine
|
||||
*/
|
||||
@@ -305,14 +341,12 @@ PointedThing getPointedThing(Client *client, v3f player_position,
|
||||
core::line3d<f32> shootline, f32 d,
|
||||
bool liquids_pointable,
|
||||
bool look_for_object,
|
||||
core::aabbox3d<f32> &hilightbox,
|
||||
bool &should_show_hilightbox,
|
||||
std::vector<aabb3f> &hilightboxes,
|
||||
ClientActiveObject *&selected_object)
|
||||
{
|
||||
PointedThing result;
|
||||
|
||||
hilightbox = core::aabbox3d<f32>(0,0,0,0,0,0);
|
||||
should_show_hilightbox = false;
|
||||
hilightboxes.clear();
|
||||
selected_object = NULL;
|
||||
|
||||
INodeDefManager *nodedef = client->getNodeDefManager();
|
||||
@@ -323,27 +357,27 @@ PointedThing getPointedThing(Client *client, v3f player_position,
|
||||
{
|
||||
selected_object = client->getSelectedActiveObject(d*BS,
|
||||
camera_position, shootline);
|
||||
}
|
||||
if(selected_object != NULL)
|
||||
{
|
||||
core::aabbox3d<f32> *selection_box
|
||||
= selected_object->getSelectionBox();
|
||||
// Box should exist because object was returned in the
|
||||
// first place
|
||||
assert(selection_box);
|
||||
|
||||
v3f pos = selected_object->getPosition();
|
||||
if(selected_object != NULL)
|
||||
{
|
||||
if(selected_object->doShowSelectionBox())
|
||||
{
|
||||
aabb3f *selection_box = selected_object->getSelectionBox();
|
||||
// Box should exist because object was
|
||||
// returned in the first place
|
||||
assert(selection_box);
|
||||
|
||||
hilightbox = core::aabbox3d<f32>(
|
||||
selection_box->MinEdge + pos,
|
||||
selection_box->MaxEdge + pos
|
||||
);
|
||||
v3f pos = selected_object->getPosition();
|
||||
hilightboxes.push_back(aabb3f(
|
||||
selection_box->MinEdge + pos,
|
||||
selection_box->MaxEdge + pos));
|
||||
}
|
||||
|
||||
should_show_hilightbox = selected_object->doShowSelectionBox();
|
||||
|
||||
result.type = POINTEDTHING_OBJECT;
|
||||
result.object_id = selected_object->getId();
|
||||
return result;
|
||||
result.type = POINTEDTHING_OBJECT;
|
||||
result.object_id = selected_object->getId();
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
// That didn't work, try to find a pointed at node
|
||||
@@ -363,6 +397,14 @@ PointedThing getPointedThing(Client *client, v3f player_position,
|
||||
s16 zend = pos_i.Z + (camera_direction.Z>0 ? a : 1);
|
||||
s16 xend = pos_i.X + (camera_direction.X>0 ? a : 1);
|
||||
|
||||
// Prevent signed number overflow
|
||||
if(yend==32767)
|
||||
yend=32766;
|
||||
if(zend==32767)
|
||||
zend=32766;
|
||||
if(xend==32767)
|
||||
xend=32766;
|
||||
|
||||
for(s16 y = ystart; y <= yend; y++)
|
||||
for(s16 z = zstart; z <= zend; z++)
|
||||
for(s16 x = xstart; x <= xend; x++)
|
||||
@@ -379,196 +421,64 @@ PointedThing getPointedThing(Client *client, v3f player_position,
|
||||
if(!isPointableNode(n, client, liquids_pointable))
|
||||
continue;
|
||||
|
||||
std::vector<aabb3f> boxes = n.getSelectionBoxes(nodedef);
|
||||
|
||||
v3s16 np(x,y,z);
|
||||
v3f npf = intToFloat(np, BS);
|
||||
|
||||
f32 d = 0.01;
|
||||
|
||||
v3s16 dirs[6] = {
|
||||
v3s16(0,0,1), // back
|
||||
v3s16(0,1,0), // top
|
||||
v3s16(1,0,0), // right
|
||||
v3s16(0,0,-1), // front
|
||||
v3s16(0,-1,0), // bottom
|
||||
v3s16(-1,0,0), // left
|
||||
};
|
||||
|
||||
const ContentFeatures &f = nodedef->get(n);
|
||||
|
||||
if(f.selection_box.type == NODEBOX_FIXED)
|
||||
|
||||
for(std::vector<aabb3f>::const_iterator
|
||||
i = boxes.begin();
|
||||
i != boxes.end(); i++)
|
||||
{
|
||||
core::aabbox3d<f32> box = f.selection_box.fixed;
|
||||
aabb3f box = *i;
|
||||
box.MinEdge += npf;
|
||||
box.MaxEdge += npf;
|
||||
|
||||
v3s16 facedirs[6] = {
|
||||
v3s16(-1,0,0),
|
||||
v3s16(1,0,0),
|
||||
v3s16(0,-1,0),
|
||||
v3s16(0,1,0),
|
||||
v3s16(0,0,-1),
|
||||
v3s16(0,0,1),
|
||||
};
|
||||
|
||||
core::aabbox3d<f32> faceboxes[6] = {
|
||||
// X-
|
||||
core::aabbox3d<f32>(
|
||||
box.MinEdge.X, box.MinEdge.Y, box.MinEdge.Z,
|
||||
box.MinEdge.X+d, box.MaxEdge.Y, box.MaxEdge.Z
|
||||
),
|
||||
// X+
|
||||
core::aabbox3d<f32>(
|
||||
box.MaxEdge.X-d, box.MinEdge.Y, box.MinEdge.Z,
|
||||
box.MaxEdge.X, box.MaxEdge.Y, box.MaxEdge.Z
|
||||
),
|
||||
// Y-
|
||||
core::aabbox3d<f32>(
|
||||
box.MinEdge.X, box.MinEdge.Y, box.MinEdge.Z,
|
||||
box.MaxEdge.X, box.MinEdge.Y+d, box.MaxEdge.Z
|
||||
),
|
||||
// Y+
|
||||
core::aabbox3d<f32>(
|
||||
box.MinEdge.X, box.MaxEdge.Y-d, box.MinEdge.Z,
|
||||
box.MaxEdge.X, box.MaxEdge.Y, box.MaxEdge.Z
|
||||
),
|
||||
// Z-
|
||||
core::aabbox3d<f32>(
|
||||
box.MinEdge.X, box.MinEdge.Y, box.MinEdge.Z,
|
||||
box.MaxEdge.X, box.MaxEdge.Y, box.MinEdge.Z+d
|
||||
),
|
||||
// Z+
|
||||
core::aabbox3d<f32>(
|
||||
box.MinEdge.X, box.MinEdge.Y, box.MaxEdge.Z-d,
|
||||
box.MaxEdge.X, box.MaxEdge.Y, box.MaxEdge.Z
|
||||
),
|
||||
};
|
||||
|
||||
for(u16 i=0; i<6; i++)
|
||||
for(u16 j=0; j<6; j++)
|
||||
{
|
||||
v3f facedir_f(facedirs[i].X, facedirs[i].Y, facedirs[i].Z);
|
||||
v3f centerpoint = npf + facedir_f * BS/2;
|
||||
v3s16 facedir = g_6dirs[j];
|
||||
aabb3f facebox = box;
|
||||
|
||||
f32 d = 0.001*BS;
|
||||
if(facedir.X > 0)
|
||||
facebox.MinEdge.X = facebox.MaxEdge.X-d;
|
||||
else if(facedir.X < 0)
|
||||
facebox.MaxEdge.X = facebox.MinEdge.X+d;
|
||||
else if(facedir.Y > 0)
|
||||
facebox.MinEdge.Y = facebox.MaxEdge.Y-d;
|
||||
else if(facedir.Y < 0)
|
||||
facebox.MaxEdge.Y = facebox.MinEdge.Y+d;
|
||||
else if(facedir.Z > 0)
|
||||
facebox.MinEdge.Z = facebox.MaxEdge.Z-d;
|
||||
else if(facedir.Z < 0)
|
||||
facebox.MaxEdge.Z = facebox.MinEdge.Z+d;
|
||||
|
||||
v3f centerpoint = facebox.getCenter();
|
||||
f32 distance = (centerpoint - camera_position).getLength();
|
||||
if(distance >= mindistance)
|
||||
continue;
|
||||
if(!faceboxes[i].intersectsWithLine(shootline))
|
||||
if(!facebox.intersectsWithLine(shootline))
|
||||
continue;
|
||||
|
||||
v3s16 np_above = np + facedir;
|
||||
|
||||
result.type = POINTEDTHING_NODE;
|
||||
result.node_undersurface = np;
|
||||
result.node_abovesurface = np+facedirs[i];
|
||||
result.node_abovesurface = np_above;
|
||||
mindistance = distance;
|
||||
hilightbox = box;
|
||||
should_show_hilightbox = true;
|
||||
}
|
||||
}
|
||||
else if(f.selection_box.type == NODEBOX_WALLMOUNTED)
|
||||
{
|
||||
v3s16 dir = n.getWallMountedDir(nodedef);
|
||||
v3f dir_f = v3f(dir.X, dir.Y, dir.Z);
|
||||
dir_f *= BS/2 - BS/6 - BS/20;
|
||||
v3f cpf = npf + dir_f;
|
||||
f32 distance = (cpf - camera_position).getLength();
|
||||
|
||||
core::aabbox3d<f32> box;
|
||||
|
||||
// top
|
||||
if(dir == v3s16(0,1,0)){
|
||||
box = f.selection_box.wall_top;
|
||||
}
|
||||
// bottom
|
||||
else if(dir == v3s16(0,-1,0)){
|
||||
box = f.selection_box.wall_bottom;
|
||||
}
|
||||
// side
|
||||
else{
|
||||
v3f vertices[2] =
|
||||
hilightboxes.clear();
|
||||
for(std::vector<aabb3f>::const_iterator
|
||||
i2 = boxes.begin();
|
||||
i2 != boxes.end(); i2++)
|
||||
{
|
||||
f.selection_box.wall_side.MinEdge,
|
||||
f.selection_box.wall_side.MaxEdge
|
||||
};
|
||||
|
||||
for(s32 i=0; i<2; i++)
|
||||
{
|
||||
if(dir == v3s16(-1,0,0))
|
||||
vertices[i].rotateXZBy(0);
|
||||
if(dir == v3s16(1,0,0))
|
||||
vertices[i].rotateXZBy(180);
|
||||
if(dir == v3s16(0,0,-1))
|
||||
vertices[i].rotateXZBy(90);
|
||||
if(dir == v3s16(0,0,1))
|
||||
vertices[i].rotateXZBy(-90);
|
||||
}
|
||||
|
||||
box = core::aabbox3d<f32>(vertices[0]);
|
||||
box.addInternalPoint(vertices[1]);
|
||||
}
|
||||
|
||||
box.MinEdge += npf;
|
||||
box.MaxEdge += npf;
|
||||
|
||||
if(distance < mindistance)
|
||||
{
|
||||
if(box.intersectsWithLine(shootline))
|
||||
{
|
||||
result.type = POINTEDTHING_NODE;
|
||||
result.node_undersurface = np;
|
||||
result.node_abovesurface = np;
|
||||
mindistance = distance;
|
||||
hilightbox = box;
|
||||
should_show_hilightbox = true;
|
||||
aabb3f box = *i2;
|
||||
box.MinEdge += npf + v3f(-d,-d,-d);
|
||||
box.MaxEdge += npf + v3f(d,d,d);
|
||||
hilightboxes.push_back(box);
|
||||
}
|
||||
}
|
||||
}
|
||||
else // NODEBOX_REGULAR
|
||||
{
|
||||
for(u16 i=0; i<6; i++)
|
||||
{
|
||||
v3f dir_f = v3f(dirs[i].X,
|
||||
dirs[i].Y, dirs[i].Z);
|
||||
v3f centerpoint = npf + dir_f * BS/2;
|
||||
f32 distance =
|
||||
(centerpoint - camera_position).getLength();
|
||||
|
||||
if(distance < mindistance)
|
||||
{
|
||||
core::CMatrix4<f32> m;
|
||||
m.buildRotateFromTo(v3f(0,0,1), dir_f);
|
||||
|
||||
// This is the back face
|
||||
v3f corners[2] = {
|
||||
v3f(BS/2, BS/2, BS/2),
|
||||
v3f(-BS/2, -BS/2, BS/2+d)
|
||||
};
|
||||
|
||||
for(u16 j=0; j<2; j++)
|
||||
{
|
||||
m.rotateVect(corners[j]);
|
||||
corners[j] += npf;
|
||||
}
|
||||
|
||||
core::aabbox3d<f32> facebox(corners[0]);
|
||||
facebox.addInternalPoint(corners[1]);
|
||||
|
||||
if(facebox.intersectsWithLine(shootline))
|
||||
{
|
||||
result.type = POINTEDTHING_NODE;
|
||||
result.node_undersurface = np;
|
||||
result.node_abovesurface = np + dirs[i];
|
||||
mindistance = distance;
|
||||
|
||||
//hilightbox = facebox;
|
||||
|
||||
const float d = 0.502;
|
||||
core::aabbox3d<f32> nodebox
|
||||
(-BS*d, -BS*d, -BS*d, BS*d, BS*d, BS*d);
|
||||
v3f nodepos_f = intToFloat(np, BS);
|
||||
nodebox.MinEdge += nodepos_f;
|
||||
nodebox.MaxEdge += nodepos_f;
|
||||
hilightbox = nodebox;
|
||||
should_show_hilightbox = true;
|
||||
}
|
||||
} // if distance < mindistance
|
||||
} // for dirs
|
||||
} // regular block
|
||||
} // for coords
|
||||
|
||||
return result;
|
||||
@@ -1236,6 +1146,16 @@ void the_game(
|
||||
*/
|
||||
Inventory local_inventory(itemdef);
|
||||
|
||||
/*
|
||||
Find out size of crack animation
|
||||
*/
|
||||
int crack_animation_length = 5;
|
||||
{
|
||||
video::ITexture *t = tsrc->getTextureRaw("crack_anylength.png");
|
||||
v2u32 size = t->getOriginalSize();
|
||||
crack_animation_length = size.Y / size.X;
|
||||
}
|
||||
|
||||
/*
|
||||
Add some gui stuff
|
||||
*/
|
||||
@@ -1515,7 +1435,7 @@ void the_game(
|
||||
hotbar_imagesize = 64;
|
||||
|
||||
// Hilight boxes collected during the loop and displayed
|
||||
core::list< core::aabbox3d<f32> > hilightboxes;
|
||||
std::vector<aabb3f> hilightboxes;
|
||||
|
||||
// Info text
|
||||
std::wstring infotext;
|
||||
@@ -1589,22 +1509,19 @@ void the_game(
|
||||
infostream<<"the_game: "
|
||||
<<"Launching inventory"<<std::endl;
|
||||
|
||||
GUIInventoryMenu *menu =
|
||||
new GUIInventoryMenu(guienv, guiroot, -1,
|
||||
GUIFormSpecMenu *menu =
|
||||
new GUIFormSpecMenu(guienv, guiroot, -1,
|
||||
&g_menumgr,
|
||||
&client, gamedef);
|
||||
|
||||
InventoryLocation inventoryloc;
|
||||
inventoryloc.setCurrentPlayer();
|
||||
|
||||
menu->setFormSpec(
|
||||
"invsize[8,7.5;]"
|
||||
//"image[1,0.6;1,2;player.png]"
|
||||
"list[current_player;main;0,3.5;8,4;]"
|
||||
"list[current_player;craft;3,0;3,3;]"
|
||||
"list[current_player;craftpreview;7,1;1,1;]"
|
||||
, inventoryloc);
|
||||
|
||||
PlayerInventoryFormSource *src = new PlayerInventoryFormSource(&client);
|
||||
assert(src);
|
||||
menu->setFormSpec(src->getForm(), inventoryloc);
|
||||
menu->setFormSource(src);
|
||||
menu->setTextDest(new TextDestPlayerInventory(&client));
|
||||
menu->drop();
|
||||
}
|
||||
else if(input->wasKeyDown(EscapeKey))
|
||||
@@ -2128,8 +2045,6 @@ void the_game(
|
||||
core::line3d<f32> shootline(camera_position,
|
||||
camera_position + camera_direction * BS * (d+1));
|
||||
|
||||
core::aabbox3d<f32> hilightbox;
|
||||
bool should_show_hilightbox = false;
|
||||
ClientActiveObject *selected_object = NULL;
|
||||
|
||||
PointedThing pointed = getPointedThing(
|
||||
@@ -2138,7 +2053,7 @@ void the_game(
|
||||
camera_position, shootline, d,
|
||||
playeritem_liquids_pointable, !ldown_for_dig,
|
||||
// output
|
||||
hilightbox, should_show_hilightbox,
|
||||
hilightboxes,
|
||||
selected_object);
|
||||
|
||||
if(pointed != pointed_old)
|
||||
@@ -2147,12 +2062,6 @@ void the_game(
|
||||
//dstream<<"Pointing at "<<pointed.dump()<<std::endl;
|
||||
}
|
||||
|
||||
/*
|
||||
Visualize selection
|
||||
*/
|
||||
if(should_show_hilightbox)
|
||||
hilightboxes.push_back(hilightbox);
|
||||
|
||||
/*
|
||||
Stop digging when
|
||||
- releasing left mouse button
|
||||
@@ -2217,7 +2126,7 @@ void the_game(
|
||||
infotext = narrow_to_wide(meta->getString("infotext"));
|
||||
} else {
|
||||
MapNode n = map.getNode(nodepos);
|
||||
if(nodedef->get(n).tname_tiles[0] == "unknown_block.png"){
|
||||
if(nodedef->get(n).tiledef[0].name == "unknown_block.png"){
|
||||
infotext = L"Unknown node: ";
|
||||
infotext += narrow_to_wide(nodedef->get(n).name);
|
||||
}
|
||||
@@ -2242,7 +2151,9 @@ void the_game(
|
||||
ldown_for_dig = true;
|
||||
}
|
||||
MapNode n = client.getEnv().getClientMap().getNode(nodepos);
|
||||
|
||||
|
||||
// NOTE: Similar piece of code exists on the server side for
|
||||
// cheat detection.
|
||||
// Get digging parameters
|
||||
DigParams params = getDigParams(nodedef->get(n).groups,
|
||||
&playeritem_toolcap);
|
||||
@@ -2282,20 +2193,20 @@ void the_game(
|
||||
|
||||
if(dig_time_complete >= 0.001)
|
||||
{
|
||||
dig_index = (u16)((float)CRACK_ANIMATION_LENGTH
|
||||
dig_index = (u16)((float)crack_animation_length
|
||||
* dig_time/dig_time_complete);
|
||||
}
|
||||
// This is for torches
|
||||
else
|
||||
{
|
||||
dig_index = CRACK_ANIMATION_LENGTH;
|
||||
dig_index = crack_animation_length;
|
||||
}
|
||||
|
||||
|
||||
// Don't show cracks if not diggable
|
||||
if(dig_time_complete >= 100000.0)
|
||||
{
|
||||
}
|
||||
else if(dig_index < CRACK_ANIMATION_LENGTH)
|
||||
else if(dig_index < crack_animation_length)
|
||||
{
|
||||
//TimeTaker timer("client.setTempMod");
|
||||
//infostream<<"dig_index="<<dig_index<<std::endl;
|
||||
@@ -2313,7 +2224,7 @@ void the_game(
|
||||
digging = false;
|
||||
|
||||
nodig_delay_timer = dig_time_complete
|
||||
/ (float)CRACK_ANIMATION_LENGTH;
|
||||
/ (float)crack_animation_length;
|
||||
|
||||
// We don't want a corresponding delay to
|
||||
// very time consuming nodes
|
||||
@@ -2339,7 +2250,8 @@ void the_game(
|
||||
{
|
||||
infostream<<"Ground right-clicked"<<std::endl;
|
||||
|
||||
// sign special case, at least until formspec is properly implemented
|
||||
// Sign special case, at least until formspec is properly implemented.
|
||||
// Deprecated?
|
||||
if(meta && meta->getString("formspec") == "hack:sign_text_input" && !random_input)
|
||||
{
|
||||
infostream<<"Launching metadata text input"<<std::endl;
|
||||
@@ -2364,21 +2276,62 @@ void the_game(
|
||||
|
||||
/* Create menu */
|
||||
|
||||
GUIInventoryMenu *menu =
|
||||
new GUIInventoryMenu(guienv, guiroot, -1,
|
||||
GUIFormSpecMenu *menu =
|
||||
new GUIFormSpecMenu(guienv, guiroot, -1,
|
||||
&g_menumgr,
|
||||
&client, gamedef);
|
||||
menu->setFormSpec(meta->getString("formspec"),
|
||||
inventoryloc);
|
||||
menu->setFormSource(new NodeMetadataFormSource(
|
||||
&client.getEnv().getClientMap(), nodepos));
|
||||
menu->setTextDest(new TextDestNodeMetadata(nodepos, &client));
|
||||
menu->drop();
|
||||
}
|
||||
// Otherwise report right click to server
|
||||
else
|
||||
{
|
||||
// Report to server
|
||||
client.interact(3, pointed);
|
||||
camera.setDigging(1); // right click animation
|
||||
|
||||
// If the wielded item has node placement prediction,
|
||||
// make that happen
|
||||
const ItemDefinition &def =
|
||||
playeritem.getDefinition(itemdef);
|
||||
if(def.node_placement_prediction != "")
|
||||
do{ // breakable
|
||||
verbosestream<<"Node placement prediction for "
|
||||
<<playeritem.name<<" is "
|
||||
<<def.node_placement_prediction<<std::endl;
|
||||
v3s16 p = neighbourpos;
|
||||
// Place inside node itself if buildable_to
|
||||
try{
|
||||
MapNode n_under = map.getNode(nodepos);
|
||||
if(nodedef->get(n_under).buildable_to)
|
||||
p = nodepos;
|
||||
}catch(InvalidPositionException &e){}
|
||||
// Find id of predicted node
|
||||
content_t id;
|
||||
bool found =
|
||||
nodedef->getId(def.node_placement_prediction, id);
|
||||
if(!found){
|
||||
errorstream<<"Node placement prediction failed for "
|
||||
<<playeritem.name<<" (places "
|
||||
<<def.node_placement_prediction
|
||||
<<") - Name not known"<<std::endl;
|
||||
break;
|
||||
}
|
||||
MapNode n(id);
|
||||
try{
|
||||
// This triggers the required mesh update too
|
||||
client.addNode(p, n);
|
||||
}catch(InvalidPositionException &e){
|
||||
errorstream<<"Node placement prediction failed for "
|
||||
<<playeritem.name<<" (places "
|
||||
<<def.node_placement_prediction
|
||||
<<") - Position not loaded"<<std::endl;
|
||||
}
|
||||
}while(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2786,9 +2739,10 @@ void the_game(
|
||||
|
||||
if(show_hud)
|
||||
{
|
||||
for(core::list<aabb3f>::Iterator i=hilightboxes.begin();
|
||||
i != hilightboxes.end(); i++)
|
||||
{
|
||||
for(std::vector<aabb3f>::const_iterator
|
||||
i = hilightboxes.begin();
|
||||
i != hilightboxes.end(); i++)
|
||||
{
|
||||
/*infostream<<"hilightbox min="
|
||||
<<"("<<i->MinEdge.X<<","<<i->MinEdge.Y<<","<<i->MinEdge.Z<<")"
|
||||
<<" max="
|
||||
|
||||
@@ -20,7 +20,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
#ifndef GAME_HEADER
|
||||
#define GAME_HEADER
|
||||
|
||||
#include "common_irrlicht.h"
|
||||
#include "irrlichttypes_extrabloated.h"
|
||||
#include <string>
|
||||
#include "keycode.h"
|
||||
|
||||
|
||||
@@ -29,6 +29,7 @@ class ICraftDefManager;
|
||||
class ITextureSource;
|
||||
class ISoundManager;
|
||||
class MtEventManager;
|
||||
class IRollbackReportSink;
|
||||
|
||||
/*
|
||||
An interface for fetching game-global definitions like tool and
|
||||
@@ -54,6 +55,10 @@ class IGameDef
|
||||
// Only usable on the client
|
||||
virtual ISoundManager* getSoundManager()=0;
|
||||
virtual MtEventManager* getEventManager()=0;
|
||||
|
||||
// Only usable on the server, and NOT thread-safe. It is usable from the
|
||||
// environment thread.
|
||||
virtual IRollbackReportSink* getRollbackReportSink(){return NULL;}
|
||||
|
||||
// Used on the client
|
||||
virtual bool checkLocalPrivilege(const std::string &priv)
|
||||
@@ -66,6 +71,7 @@ class IGameDef
|
||||
ITextureSource* tsrc(){return getTextureSource();}
|
||||
ISoundManager* sound(){return getSoundManager();}
|
||||
MtEventManager* event(){return getEventManager();}
|
||||
IRollbackReportSink* rollback(){return getRollbackReportSink();}
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -18,8 +18,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
*/
|
||||
|
||||
#include "genericobject.h"
|
||||
#include "utility.h"
|
||||
#include <sstream>
|
||||
#include "util/serialize.h"
|
||||
|
||||
std::string gob_cmd_set_properties(const ObjectProperties &prop)
|
||||
{
|
||||
|
||||
@@ -21,7 +21,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
#define GENERICOBJECT_HEADER
|
||||
|
||||
#include <string>
|
||||
#include "irrlichttypes.h"
|
||||
#include "irrlichttypes_bloated.h"
|
||||
#include <iostream>
|
||||
|
||||
#define GENERIC_CMD_SET_PROPERTIES 0
|
||||
|
||||
@@ -20,7 +20,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
#ifndef GETTIME_HEADER
|
||||
#define GETTIME_HEADER
|
||||
|
||||
#include "common_irrlicht.h"
|
||||
#include "irrlichttypes.h"
|
||||
|
||||
/*
|
||||
Get a millisecond counter value.
|
||||
|
||||
@@ -20,7 +20,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
#ifndef GUICHATCONSOLE_HEADER
|
||||
#define GUICHATCONSOLE_HEADER
|
||||
|
||||
#include "common_irrlicht.h"
|
||||
#include "irrlichttypes_extrabloated.h"
|
||||
#include "chat.h"
|
||||
|
||||
class Client;
|
||||
|
||||
@@ -20,9 +20,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
#ifndef GUICONFIRMMENU_HEADER
|
||||
#define GUICONFIRMMENU_HEADER
|
||||
|
||||
#include "common_irrlicht.h"
|
||||
#include "irrlichttypes_extrabloated.h"
|
||||
#include "modalMenu.h"
|
||||
#include "utility.h"
|
||||
#include <string>
|
||||
|
||||
struct ConfirmDest
|
||||
|
||||
@@ -27,8 +27,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
#include <IGUIStaticText.h>
|
||||
#include <IGUIFont.h>
|
||||
#include <IGUIListBox.h>
|
||||
|
||||
#include "gettext.h"
|
||||
#include "util/string.h"
|
||||
|
||||
enum
|
||||
{
|
||||
|
||||
@@ -20,9 +20,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
#ifndef GUICREATEWORLD_HEADER
|
||||
#define GUICREATEWORLD_HEADER
|
||||
|
||||
#include "common_irrlicht.h"
|
||||
#include "irrlichttypes_extrabloated.h"
|
||||
#include "modalMenu.h"
|
||||
#include "utility.h"
|
||||
#include <string>
|
||||
#include "subgame.h"
|
||||
|
||||
|
||||
@@ -20,9 +20,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
#ifndef GUIMESSAGEMENU_HEADER
|
||||
#define GUIMESSAGEMENU_HEADER
|
||||
|
||||
#include "common_irrlicht.h"
|
||||
#include "irrlichttypes_extrabloated.h"
|
||||
#include "modalMenu.h"
|
||||
#include "utility.h"
|
||||
#include <string>
|
||||
|
||||
class IRespawnInitiator
|
||||
|
||||
@@ -18,7 +18,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
*/
|
||||
|
||||
|
||||
#include "guiInventoryMenu.h"
|
||||
#include "guiFormSpecMenu.h"
|
||||
#include "constants.h"
|
||||
#include "gamedef.h"
|
||||
#include "keycode.h"
|
||||
@@ -30,6 +30,10 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
#include <IGUIFont.h>
|
||||
#include "log.h"
|
||||
#include "tile.h" // ITextureSource
|
||||
#include "util/string.h"
|
||||
#include "util/numeric.h"
|
||||
|
||||
#include "gettext.h"
|
||||
|
||||
void drawItemStack(video::IVideoDriver *driver,
|
||||
gui::IGUIFont *font,
|
||||
@@ -118,10 +122,10 @@ void drawItemStack(video::IVideoDriver *driver,
|
||||
}
|
||||
|
||||
/*
|
||||
GUIInventoryMenu
|
||||
GUIFormSpecMenu
|
||||
*/
|
||||
|
||||
GUIInventoryMenu::GUIInventoryMenu(gui::IGUIEnvironment* env,
|
||||
GUIFormSpecMenu::GUIFormSpecMenu(gui::IGUIEnvironment* env,
|
||||
gui::IGUIElement* parent, s32 id,
|
||||
IMenuManager *menumgr,
|
||||
InventoryManager *invmgr,
|
||||
@@ -131,6 +135,7 @@ GUIInventoryMenu::GUIInventoryMenu(gui::IGUIEnvironment* env,
|
||||
m_invmgr(invmgr),
|
||||
m_gamedef(gamedef),
|
||||
m_form_src(NULL),
|
||||
m_text_dst(NULL),
|
||||
m_selected_item(NULL),
|
||||
m_selected_amount(0),
|
||||
m_selected_dragging(false),
|
||||
@@ -138,15 +143,16 @@ GUIInventoryMenu::GUIInventoryMenu(gui::IGUIEnvironment* env,
|
||||
{
|
||||
}
|
||||
|
||||
GUIInventoryMenu::~GUIInventoryMenu()
|
||||
GUIFormSpecMenu::~GUIFormSpecMenu()
|
||||
{
|
||||
removeChildren();
|
||||
|
||||
delete m_selected_item;
|
||||
delete m_form_src;
|
||||
delete m_text_dst;
|
||||
}
|
||||
|
||||
void GUIInventoryMenu::removeChildren()
|
||||
void GUIFormSpecMenu::removeChildren()
|
||||
{
|
||||
const core::list<gui::IGUIElement*> &children = getChildren();
|
||||
core::list<gui::IGUIElement*> children_copy;
|
||||
@@ -173,7 +179,7 @@ void GUIInventoryMenu::removeChildren()
|
||||
}
|
||||
}
|
||||
|
||||
void GUIInventoryMenu::regenerateGui(v2u32 screensize)
|
||||
void GUIFormSpecMenu::regenerateGui(v2u32 screensize)
|
||||
{
|
||||
// Remove children
|
||||
removeChildren();
|
||||
@@ -181,24 +187,37 @@ void GUIInventoryMenu::regenerateGui(v2u32 screensize)
|
||||
v2s32 size(100,100);
|
||||
s32 helptext_h = 15;
|
||||
core::rect<s32> rect;
|
||||
|
||||
// Base position of contents of form
|
||||
v2s32 basepos = getBasePos();
|
||||
// State of basepos, 0 = not set, 1= set by formspec, 2 = set by size[] element
|
||||
// Used to adjust form size automatically if needed
|
||||
// A proceed button is added if there is no size[] element
|
||||
int bp_set = 0;
|
||||
|
||||
/* Convert m_init_draw_spec to m_inventorylists */
|
||||
|
||||
m_inventorylists.clear();
|
||||
m_images.clear();
|
||||
m_fields.clear();
|
||||
|
||||
Strfnd f(m_formspec_string);
|
||||
while(f.atend() == false)
|
||||
{
|
||||
std::string type = trim(f.next("["));
|
||||
if(type == "invsize")
|
||||
if(type == "invsize" || type == "size")
|
||||
{
|
||||
v2f invsize;
|
||||
invsize.X = stof(f.next(","));
|
||||
invsize.Y = stof(f.next(";"));
|
||||
infostream<<"invsize ("<<invsize.X<<","<<invsize.Y<<")"<<std::endl;
|
||||
f.next("]");
|
||||
if(type == "size")
|
||||
{
|
||||
invsize.Y = stof(f.next("]"));
|
||||
}
|
||||
else{
|
||||
invsize.Y = stof(f.next(";"));
|
||||
f.next("]");
|
||||
}
|
||||
infostream<<"Form size ("<<invsize.X<<","<<invsize.Y<<")"<<std::endl;
|
||||
|
||||
padding = v2s32(screensize.Y/40, screensize.Y/40);
|
||||
spacing = v2s32(screensize.Y/12, screensize.Y/13);
|
||||
@@ -216,6 +235,7 @@ void GUIInventoryMenu::regenerateGui(v2u32 screensize)
|
||||
DesiredRect = rect;
|
||||
recalculateAbsolutePosition(false);
|
||||
basepos = getBasePos();
|
||||
bp_set = 2;
|
||||
}
|
||||
else if(type == "list")
|
||||
{
|
||||
@@ -236,8 +256,13 @@ void GUIInventoryMenu::regenerateGui(v2u32 screensize)
|
||||
<<", pos=("<<pos.X<<","<<pos.Y<<")"
|
||||
<<", geom=("<<geom.X<<","<<geom.Y<<")"
|
||||
<<std::endl;
|
||||
f.next("]");
|
||||
m_inventorylists.push_back(ListDrawSpec(loc, listname, pos, geom));
|
||||
std::string start_i_s = f.next("]");
|
||||
s32 start_i = 0;
|
||||
if(start_i_s != "")
|
||||
start_i = stoi(start_i_s);
|
||||
if(bp_set != 2)
|
||||
errorstream<<"WARNING: invalid use of list without a size[] element"<<std::endl;
|
||||
m_inventorylists.push_back(ListDrawSpec(loc, listname, pos, geom, start_i));
|
||||
}
|
||||
else if(type == "image")
|
||||
{
|
||||
@@ -252,8 +277,193 @@ void GUIInventoryMenu::regenerateGui(v2u32 screensize)
|
||||
<<", pos=("<<pos.X<<","<<pos.Y<<")"
|
||||
<<", geom=("<<geom.X<<","<<geom.Y<<")"
|
||||
<<std::endl;
|
||||
if(bp_set != 2)
|
||||
errorstream<<"WARNING: invalid use of button without a size[] element"<<std::endl;
|
||||
m_images.push_back(ImageDrawSpec(name, pos, geom));
|
||||
}
|
||||
else if(type == "field")
|
||||
{
|
||||
std::string fname = f.next(";");
|
||||
std::string flabel = f.next(";");
|
||||
|
||||
if(fname.find(",") == std::string::npos && flabel.find(",") == std::string::npos)
|
||||
{
|
||||
if(!bp_set)
|
||||
{
|
||||
rect = core::rect<s32>(
|
||||
screensize.X/2 - 580/2,
|
||||
screensize.Y/2 - 300/2,
|
||||
screensize.X/2 + 580/2,
|
||||
screensize.Y/2 + 300/2
|
||||
);
|
||||
DesiredRect = rect;
|
||||
recalculateAbsolutePosition(false);
|
||||
basepos = getBasePos();
|
||||
bp_set = 1;
|
||||
}
|
||||
else if(bp_set == 2)
|
||||
errorstream<<"WARNING: invalid use of unpositioned field in inventory"<<std::endl;
|
||||
|
||||
v2s32 pos = basepos;
|
||||
pos.Y = ((m_fields.size()+2)*60);
|
||||
v2s32 size = DesiredRect.getSize();
|
||||
rect = core::rect<s32>(size.X/2-150, pos.Y, (size.X/2-150)+300, pos.Y+30);
|
||||
}
|
||||
else
|
||||
{
|
||||
v2s32 pos;
|
||||
pos.X = stof(fname.substr(0,fname.find(","))) * (float)spacing.X;
|
||||
pos.Y = stof(fname.substr(fname.find(",")+1)) * (float)spacing.Y;
|
||||
v2s32 geom;
|
||||
geom.X = (stof(flabel.substr(0,flabel.find(","))) * (float)spacing.X)-(spacing.X-imgsize.X);
|
||||
pos.Y += (stof(flabel.substr(flabel.find(",")+1)) * (float)imgsize.Y)/2;
|
||||
|
||||
rect = core::rect<s32>(pos.X, pos.Y-15, pos.X+geom.X, pos.Y+15);
|
||||
|
||||
fname = f.next(";");
|
||||
flabel = f.next(";");
|
||||
if(bp_set != 2)
|
||||
errorstream<<"WARNING: invalid use of positioned field without a size[] element"<<std::endl;
|
||||
|
||||
}
|
||||
|
||||
std::string odefault = f.next("]");
|
||||
std::string fdefault;
|
||||
|
||||
// fdefault may contain a variable reference, which
|
||||
// needs to be resolved from the node metadata
|
||||
if(m_form_src)
|
||||
fdefault = m_form_src->resolveText(odefault);
|
||||
else
|
||||
fdefault = odefault;
|
||||
|
||||
FieldSpec spec = FieldSpec(
|
||||
narrow_to_wide(fname.c_str()),
|
||||
narrow_to_wide(flabel.c_str()),
|
||||
narrow_to_wide(fdefault.c_str()),
|
||||
258+m_fields.size()
|
||||
);
|
||||
|
||||
// three cases: field and no label, label and no field, label and field
|
||||
if (flabel == "")
|
||||
{
|
||||
spec.send = true;
|
||||
gui::IGUIElement *e = Environment->addEditBox(spec.fdefault.c_str(), rect, true, this, spec.fid);
|
||||
Environment->setFocus(e);
|
||||
|
||||
irr::SEvent evt;
|
||||
evt.EventType = EET_KEY_INPUT_EVENT;
|
||||
evt.KeyInput.Key = KEY_END;
|
||||
evt.KeyInput.PressedDown = true;
|
||||
e->OnEvent(evt);
|
||||
}
|
||||
else if (fname == "")
|
||||
{
|
||||
// set spec field id to 0, this stops submit searching for a value that isn't there
|
||||
Environment->addStaticText(spec.flabel.c_str(), rect, false, true, this, spec.fid);
|
||||
}
|
||||
else
|
||||
{
|
||||
spec.send = true;
|
||||
gui::IGUIElement *e = Environment->addEditBox(spec.fdefault.c_str(), rect, true, this, spec.fid);
|
||||
Environment->setFocus(e);
|
||||
rect.UpperLeftCorner.Y -= 15;
|
||||
rect.LowerRightCorner.Y -= 15;
|
||||
Environment->addStaticText(spec.flabel.c_str(), rect, false, true, this, 0);
|
||||
|
||||
irr::SEvent evt;
|
||||
evt.EventType = EET_KEY_INPUT_EVENT;
|
||||
evt.KeyInput.Key = KEY_END;
|
||||
evt.KeyInput.PressedDown = true;
|
||||
e->OnEvent(evt);
|
||||
}
|
||||
|
||||
m_fields.push_back(spec);
|
||||
}
|
||||
else if(type == "label")
|
||||
{
|
||||
v2s32 pos = padding;
|
||||
pos.X += stof(f.next(",")) * (float)spacing.X;
|
||||
pos.Y += stof(f.next(";")) * (float)spacing.Y;
|
||||
|
||||
rect = core::rect<s32>(pos.X, pos.Y+((imgsize.Y/2)-15), pos.X+300, pos.Y+((imgsize.Y/2)+15));
|
||||
|
||||
std::string flabel = f.next("]");
|
||||
if(bp_set != 2)
|
||||
errorstream<<"WARNING: invalid use of label without a size[] element"<<std::endl;
|
||||
|
||||
FieldSpec spec = FieldSpec(
|
||||
narrow_to_wide(""),
|
||||
narrow_to_wide(flabel.c_str()),
|
||||
narrow_to_wide(""),
|
||||
258+m_fields.size()
|
||||
);
|
||||
Environment->addStaticText(spec.flabel.c_str(), rect, false, true, this, spec.fid);
|
||||
m_fields.push_back(spec);
|
||||
}
|
||||
else if(type == "button" || type == "button_exit")
|
||||
{
|
||||
v2s32 pos = padding;
|
||||
pos.X += stof(f.next(",")) * (float)spacing.X;
|
||||
pos.Y += stof(f.next(";")) * (float)spacing.Y;
|
||||
v2s32 geom;
|
||||
geom.X = (stof(f.next(",")) * (float)spacing.X)-(spacing.X-imgsize.X);
|
||||
pos.Y += (stof(f.next(";")) * (float)imgsize.Y)/2;
|
||||
|
||||
rect = core::rect<s32>(pos.X, pos.Y-15, pos.X+geom.X, pos.Y+15);
|
||||
|
||||
std::string fname = f.next(";");
|
||||
std::string flabel = f.next("]");
|
||||
if(bp_set != 2)
|
||||
errorstream<<"WARNING: invalid use of button without a size[] element"<<std::endl;
|
||||
|
||||
FieldSpec spec = FieldSpec(
|
||||
narrow_to_wide(fname.c_str()),
|
||||
narrow_to_wide(flabel.c_str()),
|
||||
narrow_to_wide(""),
|
||||
258+m_fields.size()
|
||||
);
|
||||
spec.is_button = true;
|
||||
if(type == "button_exit")
|
||||
spec.is_exit = true;
|
||||
Environment->addButton(rect, this, spec.fid, spec.flabel.c_str());
|
||||
m_fields.push_back(spec);
|
||||
}
|
||||
else if(type == "image_button" || type == "image_button_exit")
|
||||
{
|
||||
v2s32 pos = padding;
|
||||
pos.X += stof(f.next(",")) * (float)spacing.X;
|
||||
pos.Y += stof(f.next(";")) * (float)spacing.Y;
|
||||
v2s32 geom;
|
||||
geom.X = (stof(f.next(",")) * (float)spacing.X)-(spacing.X-imgsize.X);
|
||||
geom.Y = (stof(f.next(";")) * (float)spacing.Y)-(spacing.Y-imgsize.Y);
|
||||
|
||||
rect = core::rect<s32>(pos.X, pos.Y, pos.X+geom.X, pos.Y+geom.Y);
|
||||
|
||||
std::string fimage = f.next(";");
|
||||
std::string fname = f.next(";");
|
||||
std::string flabel = f.next("]");
|
||||
if(bp_set != 2)
|
||||
errorstream<<"WARNING: invalid use of image_button without a size[] element"<<std::endl;
|
||||
|
||||
FieldSpec spec = FieldSpec(
|
||||
narrow_to_wide(fname.c_str()),
|
||||
narrow_to_wide(flabel.c_str()),
|
||||
narrow_to_wide(fimage.c_str()),
|
||||
258+m_fields.size()
|
||||
);
|
||||
spec.is_button = true;
|
||||
if(type == "image_button_exit")
|
||||
spec.is_exit = true;
|
||||
|
||||
video::ITexture *texture = m_gamedef->tsrc()->getTextureRaw(fimage);
|
||||
gui::IGUIButton *e = Environment->addButton(rect, this, spec.fid, spec.flabel.c_str());
|
||||
e->setImage(texture);
|
||||
e->setPressedImage(texture);
|
||||
e->setScaleImage(true);
|
||||
|
||||
m_fields.push_back(spec);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Ignore others
|
||||
@@ -263,16 +473,44 @@ void GUIInventoryMenu::regenerateGui(v2u32 screensize)
|
||||
}
|
||||
}
|
||||
|
||||
// Add children
|
||||
// If there's inventory, put the usage string at the bottom
|
||||
if (m_inventorylists.size())
|
||||
{
|
||||
changeCtype("");
|
||||
core::rect<s32> rect(0, 0, size.X-padding.X*2, helptext_h);
|
||||
rect = rect + v2s32(size.X/2 - rect.getWidth()/2,
|
||||
size.Y-rect.getHeight()-5);
|
||||
const wchar_t *text =
|
||||
L"Left click: Move all items, Right click: Move single item";
|
||||
const wchar_t *text = wgettext("Left click: Move all items, Right click: Move single item");
|
||||
Environment->addStaticText(text, rect, false, true, this, 256);
|
||||
changeCtype("C");
|
||||
}
|
||||
// If there's fields, add a Proceed button
|
||||
if (m_fields.size() && bp_set != 2)
|
||||
{
|
||||
// if the size wasn't set by an invsize[] or size[] adjust it now to fit all the fields
|
||||
rect = core::rect<s32>(
|
||||
screensize.X/2 - 580/2,
|
||||
screensize.Y/2 - 300/2,
|
||||
screensize.X/2 + 580/2,
|
||||
screensize.Y/2 + 240/2+(m_fields.size()*60)
|
||||
);
|
||||
DesiredRect = rect;
|
||||
recalculateAbsolutePosition(false);
|
||||
basepos = getBasePos();
|
||||
|
||||
// Add tooltip
|
||||
changeCtype("");
|
||||
{
|
||||
v2s32 pos = basepos;
|
||||
pos.Y = ((m_fields.size()+2)*60);
|
||||
|
||||
v2s32 size = DesiredRect.getSize();
|
||||
rect = core::rect<s32>(size.X/2-70, pos.Y, (size.X/2-70)+140, pos.Y+30);
|
||||
Environment->addButton(rect, this, 257, wgettext("Proceed"));
|
||||
}
|
||||
changeCtype("C");
|
||||
}
|
||||
// Add tooltip
|
||||
{
|
||||
// Note: parent != this so that the tooltip isn't clipped by the menu rectangle
|
||||
m_tooltip_element = Environment->addStaticText(L"",core::rect<s32>(0,0,110,18));
|
||||
m_tooltip_element->enableOverrideColor(true);
|
||||
@@ -285,7 +523,7 @@ void GUIInventoryMenu::regenerateGui(v2u32 screensize)
|
||||
}
|
||||
}
|
||||
|
||||
GUIInventoryMenu::ItemSpec GUIInventoryMenu::getItemAtPos(v2s32 p) const
|
||||
GUIFormSpecMenu::ItemSpec GUIFormSpecMenu::getItemAtPos(v2s32 p) const
|
||||
{
|
||||
core::rect<s32> imgrect(0,0,imgsize.X,imgsize.Y);
|
||||
|
||||
@@ -295,13 +533,14 @@ GUIInventoryMenu::ItemSpec GUIInventoryMenu::getItemAtPos(v2s32 p) const
|
||||
|
||||
for(s32 i=0; i<s.geom.X*s.geom.Y; i++)
|
||||
{
|
||||
s32 item_i = i + s.start_item_i;
|
||||
s32 x = (i%s.geom.X) * spacing.X;
|
||||
s32 y = (i/s.geom.X) * spacing.Y;
|
||||
v2s32 p0(x,y);
|
||||
core::rect<s32> rect = imgrect + s.pos + p0;
|
||||
if(rect.isPointInside(p))
|
||||
{
|
||||
return ItemSpec(s.inventoryloc, s.listname, i);
|
||||
return ItemSpec(s.inventoryloc, s.listname, item_i);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -309,7 +548,7 @@ GUIInventoryMenu::ItemSpec GUIInventoryMenu::getItemAtPos(v2s32 p) const
|
||||
return ItemSpec(InventoryLocation(), "", -1);
|
||||
}
|
||||
|
||||
void GUIInventoryMenu::drawList(const ListDrawSpec &s, int phase)
|
||||
void GUIFormSpecMenu::drawList(const ListDrawSpec &s, int phase)
|
||||
{
|
||||
video::IVideoDriver* driver = Environment->getVideoDriver();
|
||||
|
||||
@@ -321,7 +560,7 @@ void GUIInventoryMenu::drawList(const ListDrawSpec &s, int phase)
|
||||
|
||||
Inventory *inv = m_invmgr->getInventory(s.inventoryloc);
|
||||
if(!inv){
|
||||
infostream<<"GUIInventoryMenu::drawList(): WARNING: "
|
||||
infostream<<"GUIFormSpecMenu::drawList(): WARNING: "
|
||||
<<"The inventory location "
|
||||
<<"\""<<s.inventoryloc.dump()<<"\" doesn't exist"
|
||||
<<std::endl;
|
||||
@@ -329,7 +568,7 @@ void GUIInventoryMenu::drawList(const ListDrawSpec &s, int phase)
|
||||
}
|
||||
InventoryList *ilist = inv->getList(s.listname);
|
||||
if(!ilist){
|
||||
infostream<<"GUIInventoryMenu::drawList(): WARNING: "
|
||||
infostream<<"GUIFormSpecMenu::drawList(): WARNING: "
|
||||
<<"The inventory list \""<<s.listname<<"\" @ \""
|
||||
<<s.inventoryloc.dump()<<"\" doesn't exist"
|
||||
<<std::endl;
|
||||
@@ -340,13 +579,16 @@ void GUIInventoryMenu::drawList(const ListDrawSpec &s, int phase)
|
||||
|
||||
for(s32 i=0; i<s.geom.X*s.geom.Y; i++)
|
||||
{
|
||||
u32 item_i = i + s.start_item_i;
|
||||
if(item_i >= ilist->getSize())
|
||||
break;
|
||||
s32 x = (i%s.geom.X) * spacing.X;
|
||||
s32 y = (i/s.geom.X) * spacing.Y;
|
||||
v2s32 p(x,y);
|
||||
core::rect<s32> rect = imgrect + s.pos + p;
|
||||
ItemStack item;
|
||||
if(ilist)
|
||||
item = ilist->getItem(i);
|
||||
item = ilist->getItem(item_i);
|
||||
|
||||
bool selected = m_selected_item
|
||||
&& m_invmgr->getInventory(m_selected_item->inventoryloc) == inv
|
||||
@@ -402,7 +644,7 @@ void GUIInventoryMenu::drawList(const ListDrawSpec &s, int phase)
|
||||
}
|
||||
}
|
||||
|
||||
void GUIInventoryMenu::drawSelectedItem()
|
||||
void GUIFormSpecMenu::drawSelectedItem()
|
||||
{
|
||||
if(!m_selected_item)
|
||||
return;
|
||||
@@ -427,7 +669,7 @@ void GUIInventoryMenu::drawSelectedItem()
|
||||
drawItemStack(driver, font, stack, rect, NULL, m_gamedef);
|
||||
}
|
||||
|
||||
void GUIInventoryMenu::drawMenu()
|
||||
void GUIFormSpecMenu::drawMenu()
|
||||
{
|
||||
if(m_form_src){
|
||||
std::string newform = m_form_src->getForm();
|
||||
@@ -489,7 +731,7 @@ void GUIInventoryMenu::drawMenu()
|
||||
gui::IGUIElement::draw();
|
||||
}
|
||||
|
||||
void GUIInventoryMenu::updateSelectedItem()
|
||||
void GUIFormSpecMenu::updateSelectedItem()
|
||||
{
|
||||
// If the selected stack has become empty for some reason, deselect it.
|
||||
// If the selected stack has become smaller, adjust m_selected_amount.
|
||||
@@ -556,7 +798,36 @@ void GUIInventoryMenu::updateSelectedItem()
|
||||
}
|
||||
}
|
||||
|
||||
bool GUIInventoryMenu::OnEvent(const SEvent& event)
|
||||
void GUIFormSpecMenu::acceptInput()
|
||||
{
|
||||
if(m_text_dst)
|
||||
{
|
||||
std::map<std::string, std::string> fields;
|
||||
gui::IGUIElement *e;
|
||||
for(u32 i=0; i<m_fields.size(); i++)
|
||||
{
|
||||
const FieldSpec &s = m_fields[i];
|
||||
if(s.send)
|
||||
{
|
||||
if(s.is_button)
|
||||
{
|
||||
fields[wide_to_narrow(s.fname.c_str())] = wide_to_narrow(s.flabel.c_str());
|
||||
}
|
||||
else
|
||||
{
|
||||
e = getElementFromId(s.fid);
|
||||
if(e != NULL)
|
||||
{
|
||||
fields[wide_to_narrow(s.fname.c_str())] = wide_to_narrow(e->getText());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
m_text_dst->gotText(fields);
|
||||
}
|
||||
}
|
||||
|
||||
bool GUIFormSpecMenu::OnEvent(const SEvent& event)
|
||||
{
|
||||
if(event.EventType==EET_KEY_INPUT_EVENT)
|
||||
{
|
||||
@@ -567,6 +838,12 @@ bool GUIInventoryMenu::OnEvent(const SEvent& event)
|
||||
quitMenu();
|
||||
return true;
|
||||
}
|
||||
if(event.KeyInput.Key==KEY_RETURN && event.KeyInput.PressedDown)
|
||||
{
|
||||
acceptInput();
|
||||
quitMenu();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if(event.EventType==EET_MOUSE_INPUT_EVENT
|
||||
&& event.MouseInput.Event == EMIE_MOUSE_MOVED)
|
||||
@@ -613,14 +890,14 @@ bool GUIInventoryMenu::OnEvent(const SEvent& event)
|
||||
|
||||
InventoryList *list = inv_s->getList(s.listname);
|
||||
if(list == NULL){
|
||||
errorstream<<"InventoryMenu: The selected inventory list \""
|
||||
verbosestream<<"InventoryMenu: The selected inventory list \""
|
||||
<<s.listname<<"\" does not exist"<<std::endl;
|
||||
s.i = -1; // make it invalid again
|
||||
break;
|
||||
}
|
||||
|
||||
if((u32)s.i >= list->getSize()){
|
||||
errorstream<<"InventoryMenu: The selected inventory list \""
|
||||
infostream<<"InventoryMenu: The selected inventory list \""
|
||||
<<s.listname<<"\" is too small (i="<<s.i<<", size="
|
||||
<<list->getSize()<<")"<<std::endl;
|
||||
s.i = -1; // make it invalid again
|
||||
@@ -858,7 +1135,7 @@ bool GUIInventoryMenu::OnEvent(const SEvent& event)
|
||||
{
|
||||
if(!canTakeFocus(event.GUIEvent.Element))
|
||||
{
|
||||
infostream<<"GUIInventoryMenu: Not allowing focus change."
|
||||
infostream<<"GUIFormSpecMenu: Not allowing focus change."
|
||||
<<std::endl;
|
||||
// Returning true disables focus change
|
||||
return true;
|
||||
@@ -866,15 +1143,45 @@ bool GUIInventoryMenu::OnEvent(const SEvent& event)
|
||||
}
|
||||
if(event.GUIEvent.EventType==gui::EGET_BUTTON_CLICKED)
|
||||
{
|
||||
/*switch(event.GUIEvent.Caller->getID())
|
||||
switch(event.GUIEvent.Caller->getID())
|
||||
{
|
||||
case 256: // continue
|
||||
setVisible(false);
|
||||
break;
|
||||
case 257: // exit
|
||||
dev->closeDevice();
|
||||
break;
|
||||
}*/
|
||||
case 257:
|
||||
acceptInput();
|
||||
quitMenu();
|
||||
// quitMenu deallocates menu
|
||||
return true;
|
||||
}
|
||||
// find the element that was clicked
|
||||
for(u32 i=0; i<m_fields.size(); i++)
|
||||
{
|
||||
FieldSpec &s = m_fields[i];
|
||||
// if its a button, set the send field so
|
||||
// lua knows which button was pressed
|
||||
if (s.is_button && s.fid == event.GUIEvent.Caller->getID())
|
||||
{
|
||||
s.send = true;
|
||||
acceptInput();
|
||||
if(s.is_exit){
|
||||
quitMenu();
|
||||
return true;
|
||||
}else{
|
||||
s.send = false;
|
||||
// Restore focus to the full form
|
||||
Environment->setFocus(this);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if(event.GUIEvent.EventType==gui::EGET_EDITBOX_ENTER)
|
||||
{
|
||||
if(event.GUIEvent.Caller->getID() > 257)
|
||||
{
|
||||
acceptInput();
|
||||
quitMenu();
|
||||
// quitMenu deallocates menu
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,20 +21,29 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
#ifndef GUIINVENTORYMENU_HEADER
|
||||
#define GUIINVENTORYMENU_HEADER
|
||||
|
||||
#include "common_irrlicht.h"
|
||||
#include "irrlichttypes_extrabloated.h"
|
||||
#include "inventory.h"
|
||||
#include "inventorymanager.h"
|
||||
#include "utility.h"
|
||||
#include "modalMenu.h"
|
||||
|
||||
class IGameDef;
|
||||
class InventoryManager;
|
||||
|
||||
struct TextDest
|
||||
{
|
||||
virtual ~TextDest() {};
|
||||
// This is deprecated I guess? -celeron55
|
||||
virtual void gotText(std::wstring text){}
|
||||
virtual void gotText(std::map<std::string, std::string> fields) = 0;
|
||||
};
|
||||
|
||||
class IFormSource
|
||||
{
|
||||
public:
|
||||
virtual ~IFormSource(){}
|
||||
virtual std::string getForm() = 0;
|
||||
// Fill in variables in field text
|
||||
virtual std::string resolveText(std::string str){ return str; }
|
||||
};
|
||||
|
||||
void drawItemStack(video::IVideoDriver *driver,
|
||||
@@ -44,7 +53,7 @@ void drawItemStack(video::IVideoDriver *driver,
|
||||
const core::rect<s32> *clip,
|
||||
IGameDef *gamedef);
|
||||
|
||||
class GUIInventoryMenu : public GUIModalMenu
|
||||
class GUIFormSpecMenu : public GUIModalMenu
|
||||
{
|
||||
struct ItemSpec
|
||||
{
|
||||
@@ -77,11 +86,12 @@ class GUIInventoryMenu : public GUIModalMenu
|
||||
}
|
||||
ListDrawSpec(const InventoryLocation &a_inventoryloc,
|
||||
const std::string &a_listname,
|
||||
v2s32 a_pos, v2s32 a_geom):
|
||||
v2s32 a_pos, v2s32 a_geom, s32 a_start_item_i):
|
||||
inventoryloc(a_inventoryloc),
|
||||
listname(a_listname),
|
||||
pos(a_pos),
|
||||
geom(a_geom)
|
||||
geom(a_geom),
|
||||
start_item_i(a_start_item_i)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -89,6 +99,7 @@ class GUIInventoryMenu : public GUIModalMenu
|
||||
std::string listname;
|
||||
v2s32 pos;
|
||||
v2s32 geom;
|
||||
s32 start_item_i;
|
||||
};
|
||||
|
||||
struct ImageDrawSpec
|
||||
@@ -107,15 +118,39 @@ class GUIInventoryMenu : public GUIModalMenu
|
||||
v2s32 pos;
|
||||
v2s32 geom;
|
||||
};
|
||||
|
||||
struct FieldSpec
|
||||
{
|
||||
FieldSpec()
|
||||
{
|
||||
}
|
||||
FieldSpec(const std::wstring name, const std::wstring label, const std::wstring fdeflt, int id):
|
||||
fname(name),
|
||||
flabel(label),
|
||||
fdefault(fdeflt),
|
||||
fid(id)
|
||||
{
|
||||
send = false;
|
||||
is_button = false;
|
||||
is_exit = false;
|
||||
}
|
||||
std::wstring fname;
|
||||
std::wstring flabel;
|
||||
std::wstring fdefault;
|
||||
int fid;
|
||||
bool send;
|
||||
bool is_button;
|
||||
bool is_exit;
|
||||
};
|
||||
|
||||
public:
|
||||
GUIInventoryMenu(gui::IGUIEnvironment* env,
|
||||
GUIFormSpecMenu(gui::IGUIEnvironment* env,
|
||||
gui::IGUIElement* parent, s32 id,
|
||||
IMenuManager *menumgr,
|
||||
InventoryManager *invmgr,
|
||||
IGameDef *gamedef
|
||||
);
|
||||
~GUIInventoryMenu();
|
||||
~GUIFormSpecMenu();
|
||||
|
||||
void setFormSpec(const std::string &formspec_string,
|
||||
InventoryLocation current_inventory_location)
|
||||
@@ -125,12 +160,18 @@ class GUIInventoryMenu : public GUIModalMenu
|
||||
regenerateGui(m_screensize_old);
|
||||
}
|
||||
|
||||
// form_src is deleted by this GUIInventoryMenu
|
||||
// form_src is deleted by this GUIFormSpecMenu
|
||||
void setFormSource(IFormSource *form_src)
|
||||
{
|
||||
m_form_src = form_src;
|
||||
}
|
||||
|
||||
// text_dst is deleted by this GUIFormSpecMenu
|
||||
void setTextDest(TextDest *text_dst)
|
||||
{
|
||||
m_text_dst = text_dst;
|
||||
}
|
||||
|
||||
void removeChildren();
|
||||
/*
|
||||
Remove and re-add (or reposition) stuff
|
||||
@@ -143,6 +184,7 @@ class GUIInventoryMenu : public GUIModalMenu
|
||||
void drawMenu();
|
||||
void updateSelectedItem();
|
||||
|
||||
void acceptInput();
|
||||
bool OnEvent(const SEvent& event);
|
||||
|
||||
protected:
|
||||
@@ -161,9 +203,11 @@ class GUIInventoryMenu : public GUIModalMenu
|
||||
std::string m_formspec_string;
|
||||
InventoryLocation m_current_inventory_location;
|
||||
IFormSource *m_form_src;
|
||||
TextDest *m_text_dst;
|
||||
|
||||
core::array<ListDrawSpec> m_inventorylists;
|
||||
core::array<ImageDrawSpec> m_images;
|
||||
core::array<FieldSpec> m_fields;
|
||||
|
||||
ItemSpec *m_selected_item;
|
||||
u32 m_selected_amount;
|
||||
@@ -22,8 +22,7 @@
|
||||
#ifndef GUIKEYCHANGEMENU_HEADER
|
||||
#define GUIKEYCHANGEMENU_HEADER
|
||||
|
||||
#include "common_irrlicht.h"
|
||||
#include "utility.h"
|
||||
#include "irrlichttypes_extrabloated.h"
|
||||
#include "modalMenu.h"
|
||||
#include "client.h"
|
||||
#include "gettext.h"
|
||||
|
||||
@@ -36,9 +36,9 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
// For IGameCallback
|
||||
#include "guiPauseMenu.h"
|
||||
#include "gettext.h"
|
||||
#include "utility.h"
|
||||
#include "tile.h" // getTexturePath
|
||||
#include "filesys.h"
|
||||
#include "util/string.h"
|
||||
|
||||
struct CreateWorldDestMainMenu : public CreateWorldDest
|
||||
{
|
||||
@@ -586,14 +586,14 @@ void GUIMainMenu::regenerateGui(v2u32 screensize)
|
||||
t->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_CENTER);
|
||||
}
|
||||
{
|
||||
core::rect<s32> rect(0, 0, 620, 250);
|
||||
rect += m_topleft_client + v2s32(130+14, 50+35);
|
||||
core::rect<s32> rect(0, 0, 454, 250);
|
||||
rect += m_topleft_client + v2s32(110, 50+35);
|
||||
Environment->addStaticText(narrow_to_wide(
|
||||
"Minetest-c55 " VERSION_STRING "\n"
|
||||
"http://minetest.net/\n"
|
||||
"\n"
|
||||
"by Perttu Ahola <celeron55@gmail.com>\n"
|
||||
"and contributors"
|
||||
"and contributors: tango_, kahrl (kaaaaaahrl?), erlehmann (the hippie), SpeedProg, JacobF (sqlite worlds), teddydestodes, marktraceur, darkrose, Jonathan Neuschäfer (who the hell?), Felix Krausse (broke liquids, IIRC), sfan5... and >10 more random people."
|
||||
).c_str(), rect, false, true, this, -1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
#ifndef GUIMAINMENU_HEADER
|
||||
#define GUIMAINMENU_HEADER
|
||||
|
||||
#include "common_irrlicht.h"
|
||||
#include "irrlichttypes_extrabloated.h"
|
||||
#include "modalMenu.h"
|
||||
#include <string>
|
||||
#include <list>
|
||||
|
||||
@@ -20,9 +20,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
#ifndef GUIMESSAGEMENU_HEADER
|
||||
#define GUIMESSAGEMENU_HEADER
|
||||
|
||||
#include "common_irrlicht.h"
|
||||
#include "irrlichttypes_extrabloated.h"
|
||||
#include "modalMenu.h"
|
||||
#include "utility.h"
|
||||
#include <string>
|
||||
|
||||
class GUIMessageMenu : public GUIModalMenu
|
||||
|
||||
@@ -19,9 +19,8 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
#ifndef GUIPASSWORDCHANGE_HEADER
|
||||
#define GUIPASSWORDCHANGE_HEADER
|
||||
|
||||
#include "common_irrlicht.h"
|
||||
#include "irrlichttypes_extrabloated.h"
|
||||
#include "modalMenu.h"
|
||||
#include "utility.h"
|
||||
#include "client.h"
|
||||
#include <string>
|
||||
|
||||
|
||||
@@ -28,8 +28,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
#include <IGUIButton.h>
|
||||
#include <IGUIStaticText.h>
|
||||
#include <IGUIFont.h>
|
||||
|
||||
#include "gettext.h"
|
||||
#include "util/string.h"
|
||||
|
||||
GUIPauseMenu::GUIPauseMenu(gui::IGUIEnvironment* env,
|
||||
gui::IGUIElement* parent, s32 id,
|
||||
|
||||
@@ -20,7 +20,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
#ifndef GUIPAUSEMENU_HEADER
|
||||
#define GUIPAUSEMENU_HEADER
|
||||
|
||||
#include "common_irrlicht.h"
|
||||
#include "irrlichttypes_extrabloated.h"
|
||||
#include "modalMenu.h"
|
||||
|
||||
class IGameCallback
|
||||
|
||||
@@ -20,17 +20,11 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
#ifndef GUITEXTINPUTMENU_HEADER
|
||||
#define GUITEXTINPUTMENU_HEADER
|
||||
|
||||
#include "common_irrlicht.h"
|
||||
#include "irrlichttypes_extrabloated.h"
|
||||
#include "modalMenu.h"
|
||||
#include "utility.h"
|
||||
#include "guiFormSpecMenu.h"
|
||||
#include <string>
|
||||
|
||||
struct TextDest
|
||||
{
|
||||
virtual void gotText(std::wstring text) = 0;
|
||||
virtual ~TextDest() {};
|
||||
};
|
||||
|
||||
class GUITextInputMenu : public GUIModalMenu
|
||||
{
|
||||
public:
|
||||
|
||||
@@ -19,7 +19,6 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
|
||||
#include "inventory.h"
|
||||
#include "serialization.h"
|
||||
#include "utility.h"
|
||||
#include "debug.h"
|
||||
#include <sstream>
|
||||
#include "log.h"
|
||||
@@ -27,6 +26,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
#include "strfnd.h"
|
||||
#include "content_mapnode.h" // For loading legacy MaterialItems
|
||||
#include "nameidmapping.h" // For loading legacy MaterialItems
|
||||
#include "util/serialize.h"
|
||||
#include "util/string.h"
|
||||
|
||||
/*
|
||||
ItemStack
|
||||
|
||||
@@ -24,7 +24,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include "common_irrlicht.h"
|
||||
#include "irrlichttypes_bloated.h"
|
||||
#include "debug.h"
|
||||
#include "itemdef.h"
|
||||
|
||||
|
||||
@@ -24,8 +24,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
#include "serverobject.h"
|
||||
#include "main.h" // for g_settings
|
||||
#include "settings.h"
|
||||
#include "utility.h"
|
||||
#include "craftdef.h"
|
||||
#include "rollback_interface.h"
|
||||
|
||||
#define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")"
|
||||
|
||||
@@ -42,30 +42,25 @@ std::string InventoryLocation::dump() const
|
||||
|
||||
void InventoryLocation::serialize(std::ostream &os) const
|
||||
{
|
||||
switch(type){
|
||||
case InventoryLocation::UNDEFINED:
|
||||
{
|
||||
switch(type){
|
||||
case InventoryLocation::UNDEFINED:
|
||||
os<<"undefined";
|
||||
}
|
||||
break;
|
||||
case InventoryLocation::CURRENT_PLAYER:
|
||||
{
|
||||
break;
|
||||
case InventoryLocation::CURRENT_PLAYER:
|
||||
os<<"current_player";
|
||||
}
|
||||
break;
|
||||
case InventoryLocation::PLAYER:
|
||||
{
|
||||
break;
|
||||
case InventoryLocation::PLAYER:
|
||||
os<<"player:"<<name;
|
||||
}
|
||||
break;
|
||||
case InventoryLocation::NODEMETA:
|
||||
{
|
||||
break;
|
||||
case InventoryLocation::NODEMETA:
|
||||
os<<"nodemeta:"<<p.X<<","<<p.Y<<","<<p.Z;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
assert(0);
|
||||
}
|
||||
break;
|
||||
case InventoryLocation::DETACHED:
|
||||
os<<"detached:"<<name;
|
||||
break;
|
||||
default:
|
||||
assert(0);
|
||||
}
|
||||
}
|
||||
|
||||
void InventoryLocation::deSerialize(std::istream &is)
|
||||
@@ -95,6 +90,11 @@ void InventoryLocation::deSerialize(std::istream &is)
|
||||
p.Y = stoi(fn.next(","));
|
||||
p.Z = stoi(fn.next(","));
|
||||
}
|
||||
else if(tname == "detached")
|
||||
{
|
||||
type = InventoryLocation::DETACHED;
|
||||
std::getline(is, name, '\n');
|
||||
}
|
||||
else
|
||||
{
|
||||
infostream<<"Unknown InventoryLocation type=\""<<tname<<"\""<<std::endl;
|
||||
@@ -199,78 +199,114 @@ void IMoveAction::apply(InventoryManager *mgr, ServerActiveObject *player, IGame
|
||||
<<", to_list=\""<<to_list<<"\""<<std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
Do not handle rollback if both inventories are that of the same player
|
||||
*/
|
||||
bool ignore_rollback = (
|
||||
from_inv.type == InventoryLocation::PLAYER &&
|
||||
to_inv.type == InventoryLocation::PLAYER &&
|
||||
from_inv.name == to_inv.name);
|
||||
|
||||
/*
|
||||
Collect information of endpoints
|
||||
*/
|
||||
|
||||
int try_take_count = count;
|
||||
if(try_take_count == 0)
|
||||
try_take_count = list_from->getItem(from_i).count;
|
||||
|
||||
int src_can_take_count = 0xffff;
|
||||
int dst_can_put_count = 0xffff;
|
||||
|
||||
// Handle node metadata move
|
||||
if(from_inv.type == InventoryLocation::NODEMETA &&
|
||||
to_inv.type == InventoryLocation::NODEMETA &&
|
||||
from_inv.p != to_inv.p)
|
||||
/* Query detached inventories */
|
||||
|
||||
// Move occurs in the same detached inventory
|
||||
if(from_inv.type == InventoryLocation::DETACHED &&
|
||||
to_inv.type == InventoryLocation::DETACHED &&
|
||||
from_inv.name == to_inv.name)
|
||||
{
|
||||
errorstream<<"Directly moving items between two nodes is "
|
||||
<<"disallowed."<<std::endl;
|
||||
return;
|
||||
lua_State *L = player->getEnv()->getLua();
|
||||
src_can_take_count = scriptapi_detached_inventory_allow_move(
|
||||
L, from_inv.name, from_list, from_i,
|
||||
to_list, to_i, try_take_count, player);
|
||||
dst_can_put_count = src_can_take_count;
|
||||
}
|
||||
else if(from_inv.type == InventoryLocation::NODEMETA &&
|
||||
else
|
||||
{
|
||||
// Destination is detached
|
||||
if(to_inv.type == InventoryLocation::DETACHED)
|
||||
{
|
||||
lua_State *L = player->getEnv()->getLua();
|
||||
ItemStack src_item = list_from->getItem(from_i);
|
||||
src_item.count = try_take_count;
|
||||
dst_can_put_count = scriptapi_detached_inventory_allow_put(
|
||||
L, to_inv.name, to_list, to_i, src_item, player);
|
||||
}
|
||||
// Source is detached
|
||||
if(from_inv.type == InventoryLocation::DETACHED)
|
||||
{
|
||||
lua_State *L = player->getEnv()->getLua();
|
||||
ItemStack src_item = list_from->getItem(from_i);
|
||||
src_item.count = try_take_count;
|
||||
src_can_take_count = scriptapi_detached_inventory_allow_take(
|
||||
L, from_inv.name, from_list, from_i, src_item, player);
|
||||
}
|
||||
}
|
||||
|
||||
/* Query node metadata inventories */
|
||||
|
||||
// Both endpoints are nodemeta
|
||||
// Move occurs in the same nodemeta inventory
|
||||
if(from_inv.type == InventoryLocation::NODEMETA &&
|
||||
to_inv.type == InventoryLocation::NODEMETA &&
|
||||
from_inv.p == to_inv.p)
|
||||
{
|
||||
lua_State *L = player->getEnv()->getLua();
|
||||
int count0 = count;
|
||||
if(count0 == 0)
|
||||
count0 = list_from->getItem(from_i).count;
|
||||
infostream<<player->getDescription()<<" moving "<<count0
|
||||
<<" items inside node at "<<PP(from_inv.p)<<std::endl;
|
||||
scriptapi_node_on_metadata_inventory_move(L, from_inv.p,
|
||||
from_list, from_i, to_list, to_i, count0, player);
|
||||
src_can_take_count = scriptapi_nodemeta_inventory_allow_move(
|
||||
L, from_inv.p, from_list, from_i,
|
||||
to_list, to_i, try_take_count, player);
|
||||
dst_can_put_count = src_can_take_count;
|
||||
}
|
||||
// Handle node metadata take
|
||||
else if(from_inv.type == InventoryLocation::NODEMETA)
|
||||
{
|
||||
lua_State *L = player->getEnv()->getLua();
|
||||
int count0 = count;
|
||||
if(count0 == 0)
|
||||
count0 = list_from->getItem(from_i).count;
|
||||
infostream<<player->getDescription()<<" taking "<<count0
|
||||
<<" items from node at "<<PP(from_inv.p)<<std::endl;
|
||||
ItemStack return_stack = scriptapi_node_on_metadata_inventory_take(
|
||||
L, from_inv.p, from_list, from_i, count0, player);
|
||||
if(return_stack.count == 0)
|
||||
infostream<<"Node metadata gave no items"<<std::endl;
|
||||
return_stack = list_to->addItem(to_i, return_stack);
|
||||
list_to->addItem(return_stack); // Force return of everything
|
||||
}
|
||||
// Handle node metadata offer
|
||||
else if(to_inv.type == InventoryLocation::NODEMETA)
|
||||
{
|
||||
lua_State *L = player->getEnv()->getLua();
|
||||
int count0 = count;
|
||||
if(count0 == 0)
|
||||
count0 = list_from->getItem(from_i).count;
|
||||
ItemStack offer_stack = list_from->takeItem(from_i, count0);
|
||||
infostream<<player->getDescription()<<" offering "
|
||||
<<offer_stack.count<<" items to node at "
|
||||
<<PP(to_inv.p)<<std::endl;
|
||||
ItemStack reject_stack = scriptapi_node_on_metadata_inventory_offer(
|
||||
L, to_inv.p, to_list, to_i, offer_stack, player);
|
||||
if(reject_stack.count == offer_stack.count)
|
||||
infostream<<"Node metadata rejected all items"<<std::endl;
|
||||
else if(reject_stack.count != 0)
|
||||
infostream<<"Node metadata rejected some items"<<std::endl;
|
||||
reject_stack = list_from->addItem(from_i, reject_stack);
|
||||
list_from->addItem(reject_stack); // Force return of everything
|
||||
}
|
||||
// Handle regular move
|
||||
else
|
||||
{
|
||||
/*
|
||||
This performs the actual movement
|
||||
// Destination is nodemeta
|
||||
if(to_inv.type == InventoryLocation::NODEMETA)
|
||||
{
|
||||
lua_State *L = player->getEnv()->getLua();
|
||||
ItemStack src_item = list_from->getItem(from_i);
|
||||
src_item.count = try_take_count;
|
||||
dst_can_put_count = scriptapi_nodemeta_inventory_allow_put(
|
||||
L, to_inv.p, to_list, to_i, src_item, player);
|
||||
}
|
||||
// Source is nodemeta
|
||||
if(from_inv.type == InventoryLocation::NODEMETA)
|
||||
{
|
||||
lua_State *L = player->getEnv()->getLua();
|
||||
ItemStack src_item = list_from->getItem(from_i);
|
||||
src_item.count = try_take_count;
|
||||
src_can_take_count = scriptapi_nodemeta_inventory_allow_take(
|
||||
L, from_inv.p, from_list, from_i, src_item, player);
|
||||
}
|
||||
}
|
||||
|
||||
If something is wrong (source item is empty, destination is the
|
||||
same as source), nothing happens
|
||||
*/
|
||||
list_from->moveItem(from_i, list_to, to_i, count);
|
||||
|
||||
infostream<<"IMoveAction::apply(): moved "
|
||||
<<" count="<<count
|
||||
int old_count = count;
|
||||
|
||||
/* Modify count according to collected data */
|
||||
count = try_take_count;
|
||||
if(src_can_take_count != -1 && count > src_can_take_count)
|
||||
count = src_can_take_count;
|
||||
if(dst_can_put_count != -1 && count > dst_can_put_count)
|
||||
count = dst_can_put_count;
|
||||
/* Limit according to source item count */
|
||||
if(count > list_from->getItem(from_i).count)
|
||||
count = list_from->getItem(from_i).count;
|
||||
|
||||
/* If no items will be moved, don't go further */
|
||||
if(count == 0)
|
||||
{
|
||||
infostream<<"IMoveAction::apply(): move was completely disallowed:"
|
||||
<<" count="<<old_count
|
||||
<<" from inv=\""<<from_inv.dump()<<"\""
|
||||
<<" list=\""<<from_list<<"\""
|
||||
<<" i="<<from_i
|
||||
@@ -278,8 +314,142 @@ void IMoveAction::apply(InventoryManager *mgr, ServerActiveObject *player, IGame
|
||||
<<" list=\""<<to_list<<"\""
|
||||
<<" i="<<to_i
|
||||
<<std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
ItemStack src_item = list_from->getItem(from_i);
|
||||
src_item.count = count;
|
||||
ItemStack from_stack_was = list_from->getItem(from_i);
|
||||
ItemStack to_stack_was = list_to->getItem(to_i);
|
||||
|
||||
/*
|
||||
Perform actual move
|
||||
|
||||
If something is wrong (source item is empty, destination is the
|
||||
same as source), nothing happens
|
||||
*/
|
||||
list_from->moveItem(from_i, list_to, to_i, count);
|
||||
|
||||
// If source is infinite, reset it's stack
|
||||
if(src_can_take_count == -1){
|
||||
list_from->deleteItem(from_i);
|
||||
list_from->addItem(from_i, from_stack_was);
|
||||
}
|
||||
// If destination is infinite, reset it's stack and take count from source
|
||||
if(dst_can_put_count == -1){
|
||||
list_to->deleteItem(to_i);
|
||||
list_to->addItem(to_i, to_stack_was);
|
||||
list_from->takeItem(from_i, count);
|
||||
}
|
||||
|
||||
infostream<<"IMoveAction::apply(): moved"
|
||||
<<" count="<<count
|
||||
<<" from inv=\""<<from_inv.dump()<<"\""
|
||||
<<" list=\""<<from_list<<"\""
|
||||
<<" i="<<from_i
|
||||
<<" to inv=\""<<to_inv.dump()<<"\""
|
||||
<<" list=\""<<to_list<<"\""
|
||||
<<" i="<<to_i
|
||||
<<std::endl;
|
||||
|
||||
/*
|
||||
Record rollback information
|
||||
*/
|
||||
if(!ignore_rollback && gamedef->rollback())
|
||||
{
|
||||
IRollbackReportSink *rollback = gamedef->rollback();
|
||||
|
||||
// If source is not infinite, record item take
|
||||
if(!src_can_take_count != -1){
|
||||
RollbackAction action;
|
||||
std::string loc;
|
||||
{
|
||||
std::ostringstream os(std::ios::binary);
|
||||
from_inv.serialize(os);
|
||||
loc = os.str();
|
||||
}
|
||||
action.setModifyInventoryStack(loc, from_list, from_i, false,
|
||||
src_item.getItemString());
|
||||
rollback->reportAction(action);
|
||||
}
|
||||
// If destination is not infinite, record item put
|
||||
if(!dst_can_put_count != -1){
|
||||
RollbackAction action;
|
||||
std::string loc;
|
||||
{
|
||||
std::ostringstream os(std::ios::binary);
|
||||
to_inv.serialize(os);
|
||||
loc = os.str();
|
||||
}
|
||||
action.setModifyInventoryStack(loc, to_list, to_i, true,
|
||||
src_item.getItemString());
|
||||
rollback->reportAction(action);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Report move to endpoints
|
||||
*/
|
||||
|
||||
/* Detached inventories */
|
||||
|
||||
// Both endpoints are same detached
|
||||
if(from_inv.type == InventoryLocation::DETACHED &&
|
||||
to_inv.type == InventoryLocation::DETACHED &&
|
||||
from_inv.name == to_inv.name)
|
||||
{
|
||||
lua_State *L = player->getEnv()->getLua();
|
||||
scriptapi_detached_inventory_on_move(
|
||||
L, from_inv.name, from_list, from_i,
|
||||
to_list, to_i, count, player);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Destination is detached
|
||||
if(to_inv.type == InventoryLocation::DETACHED)
|
||||
{
|
||||
lua_State *L = player->getEnv()->getLua();
|
||||
scriptapi_detached_inventory_on_put(
|
||||
L, to_inv.name, to_list, to_i, src_item, player);
|
||||
}
|
||||
// Source is detached
|
||||
if(from_inv.type == InventoryLocation::DETACHED)
|
||||
{
|
||||
lua_State *L = player->getEnv()->getLua();
|
||||
scriptapi_detached_inventory_on_take(
|
||||
L, from_inv.name, from_list, from_i, src_item, player);
|
||||
}
|
||||
}
|
||||
|
||||
/* Node metadata inventories */
|
||||
|
||||
// Both endpoints are same nodemeta
|
||||
if(from_inv.type == InventoryLocation::NODEMETA &&
|
||||
to_inv.type == InventoryLocation::NODEMETA &&
|
||||
from_inv.p == to_inv.p)
|
||||
{
|
||||
lua_State *L = player->getEnv()->getLua();
|
||||
scriptapi_nodemeta_inventory_on_move(
|
||||
L, from_inv.p, from_list, from_i,
|
||||
to_list, to_i, count, player);
|
||||
}
|
||||
else{
|
||||
// Destination is nodemeta
|
||||
if(to_inv.type == InventoryLocation::NODEMETA)
|
||||
{
|
||||
lua_State *L = player->getEnv()->getLua();
|
||||
scriptapi_nodemeta_inventory_on_put(
|
||||
L, to_inv.p, to_list, to_i, src_item, player);
|
||||
}
|
||||
// Source is nodemeta
|
||||
else if(from_inv.type == InventoryLocation::NODEMETA)
|
||||
{
|
||||
lua_State *L = player->getEnv()->getLua();
|
||||
scriptapi_nodemeta_inventory_on_take(
|
||||
L, from_inv.p, from_list, from_i, src_item, player);
|
||||
}
|
||||
}
|
||||
|
||||
mgr->setInventoryModified(from_inv);
|
||||
if(inv_from != inv_to)
|
||||
mgr->setInventoryModified(to_inv);
|
||||
@@ -362,47 +532,69 @@ void IDropAction::apply(InventoryManager *mgr, ServerActiveObject *player, IGame
|
||||
return;
|
||||
}
|
||||
|
||||
ItemStack item1;
|
||||
/*
|
||||
Do not handle rollback if inventory is player's
|
||||
*/
|
||||
bool ignore_src_rollback = (from_inv.type == InventoryLocation::PLAYER);
|
||||
|
||||
// Handle node metadata take
|
||||
/*
|
||||
Collect information of endpoints
|
||||
*/
|
||||
|
||||
int take_count = list_from->getItem(from_i).count;
|
||||
if(count != 0 && count < take_count)
|
||||
take_count = count;
|
||||
int src_can_take_count = take_count;
|
||||
|
||||
// Source is detached
|
||||
if(from_inv.type == InventoryLocation::DETACHED)
|
||||
{
|
||||
lua_State *L = player->getEnv()->getLua();
|
||||
ItemStack src_item = list_from->getItem(from_i);
|
||||
src_item.count = take_count;
|
||||
src_can_take_count = scriptapi_detached_inventory_allow_take(
|
||||
L, from_inv.name, from_list, from_i, src_item, player);
|
||||
}
|
||||
|
||||
// Source is nodemeta
|
||||
if(from_inv.type == InventoryLocation::NODEMETA)
|
||||
{
|
||||
lua_State *L = player->getEnv()->getLua();
|
||||
int count0 = count;
|
||||
if(count0 == 0)
|
||||
count0 = list_from->getItem(from_i).count;
|
||||
infostream<<player->getDescription()<<" dropping "<<count0
|
||||
<<" items from node at "<<PP(from_inv.p)<<std::endl;
|
||||
ItemStack return_stack = scriptapi_node_on_metadata_inventory_take(
|
||||
L, from_inv.p, from_list, from_i, count0, player);
|
||||
if(return_stack.count == 0)
|
||||
infostream<<"Node metadata gave no items"<<std::endl;
|
||||
item1 = return_stack;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Take item from source list
|
||||
if(count == 0)
|
||||
item1 = list_from->changeItem(from_i, ItemStack());
|
||||
else
|
||||
item1 = list_from->takeItem(from_i, count);
|
||||
ItemStack src_item = list_from->getItem(from_i);
|
||||
src_item.count = take_count;
|
||||
src_can_take_count = scriptapi_nodemeta_inventory_allow_take(
|
||||
L, from_inv.p, from_list, from_i, src_item, player);
|
||||
}
|
||||
|
||||
// Drop the item and apply the returned ItemStack
|
||||
ItemStack item2 = item1;
|
||||
if(scriptapi_item_on_drop(player->getEnv()->getLua(), item2, player,
|
||||
if(src_can_take_count != -1 && src_can_take_count < take_count)
|
||||
take_count = src_can_take_count;
|
||||
|
||||
int actually_dropped_count = 0;
|
||||
|
||||
ItemStack src_item = list_from->getItem(from_i);
|
||||
|
||||
// Drop the item
|
||||
ItemStack item1 = list_from->getItem(from_i);
|
||||
if(scriptapi_item_on_drop(player->getEnv()->getLua(), item1, player,
|
||||
player->getBasePosition() + v3f(0,1,0)))
|
||||
{
|
||||
if(g_settings->getBool("creative_mode") == true
|
||||
&& from_inv.type == InventoryLocation::PLAYER)
|
||||
item2 = item1; // creative mode
|
||||
actually_dropped_count = take_count - item1.count;
|
||||
|
||||
list_from->addItem(from_i, item2);
|
||||
if(actually_dropped_count == 0){
|
||||
infostream<<"Actually dropped no items"<<std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
// If source isn't infinite
|
||||
if(src_can_take_count != -1){
|
||||
// Take item from source list
|
||||
ItemStack item2 = list_from->takeItem(from_i, actually_dropped_count);
|
||||
|
||||
if(item2.count != actually_dropped_count)
|
||||
errorstream<<"Could not take dropped count of items"<<std::endl;
|
||||
|
||||
// Unless we have put the same amount back as we took in the first place,
|
||||
// set inventory modified flag
|
||||
if(item2.count != item1.count)
|
||||
mgr->setInventoryModified(from_inv);
|
||||
}
|
||||
}
|
||||
|
||||
infostream<<"IDropAction::apply(): dropped "
|
||||
@@ -410,6 +602,50 @@ void IDropAction::apply(InventoryManager *mgr, ServerActiveObject *player, IGame
|
||||
<<" list=\""<<from_list<<"\""
|
||||
<<" i="<<from_i
|
||||
<<std::endl;
|
||||
|
||||
src_item.count = actually_dropped_count;
|
||||
|
||||
/*
|
||||
Report drop to endpoints
|
||||
*/
|
||||
|
||||
// Source is detached
|
||||
if(from_inv.type == InventoryLocation::DETACHED)
|
||||
{
|
||||
lua_State *L = player->getEnv()->getLua();
|
||||
scriptapi_detached_inventory_on_take(
|
||||
L, from_inv.name, from_list, from_i, src_item, player);
|
||||
}
|
||||
|
||||
// Source is nodemeta
|
||||
if(from_inv.type == InventoryLocation::NODEMETA)
|
||||
{
|
||||
lua_State *L = player->getEnv()->getLua();
|
||||
scriptapi_nodemeta_inventory_on_take(
|
||||
L, from_inv.p, from_list, from_i, src_item, player);
|
||||
}
|
||||
|
||||
/*
|
||||
Record rollback information
|
||||
*/
|
||||
if(!ignore_src_rollback && gamedef->rollback())
|
||||
{
|
||||
IRollbackReportSink *rollback = gamedef->rollback();
|
||||
|
||||
// If source is not infinite, record item take
|
||||
if(!src_can_take_count != -1){
|
||||
RollbackAction action;
|
||||
std::string loc;
|
||||
{
|
||||
std::ostringstream os(std::ios::binary);
|
||||
from_inv.serialize(os);
|
||||
loc = os.str();
|
||||
}
|
||||
action.setModifyInventoryStack(loc, from_list, from_i,
|
||||
false, src_item.getItemString());
|
||||
rollback->reportAction(action);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void IDropAction::clientApply(InventoryManager *mgr, IGameDef *gamedef)
|
||||
|
||||
@@ -32,9 +32,10 @@ struct InventoryLocation
|
||||
CURRENT_PLAYER,
|
||||
PLAYER,
|
||||
NODEMETA,
|
||||
DETACHED,
|
||||
} type;
|
||||
|
||||
std::string name; // PLAYER
|
||||
std::string name; // PLAYER, DETACHED
|
||||
v3s16 p; // NODEMETA
|
||||
|
||||
InventoryLocation()
|
||||
@@ -59,6 +60,11 @@ struct InventoryLocation
|
||||
type = NODEMETA;
|
||||
p = p_;
|
||||
}
|
||||
void setDetached(const std::string &name_)
|
||||
{
|
||||
type = DETACHED;
|
||||
name = name_;
|
||||
}
|
||||
|
||||
void applyCurrentPlayer(const std::string &name_)
|
||||
{
|
||||
@@ -80,13 +86,11 @@ class InventoryManager
|
||||
InventoryManager(){}
|
||||
virtual ~InventoryManager(){}
|
||||
|
||||
// Get an inventory or set it modified (so it will be updated over
|
||||
// network or so)
|
||||
// Get an inventory (server and client)
|
||||
virtual Inventory* getInventory(const InventoryLocation &loc){return NULL;}
|
||||
virtual std::string getInventoryOwner(const InventoryLocation &loc){return "";}
|
||||
// Set modified (will be saved and sent over network; only on server)
|
||||
virtual void setInventoryModified(const InventoryLocation &loc){}
|
||||
|
||||
// Used on the client to send an action to the server
|
||||
// Send inventory action to server (only on client)
|
||||
virtual void inventoryAction(InventoryAction *a){}
|
||||
};
|
||||
|
||||
|
||||
30
src/irr_aabb3d.h
Normal file
30
src/irr_aabb3d.h
Normal file
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
Minetest-c55
|
||||
Copyright (C) 2010-2012 celeron55, Perttu Ahola <celeron55@gmail.com>
|
||||
|
||||
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 IRR_AABB3D_HEADER
|
||||
#define IRR_AABB3D_HEADER
|
||||
|
||||
#include "irrlichttypes.h"
|
||||
|
||||
#include <aabbox3d.h>
|
||||
|
||||
typedef core::aabbox3d<f32> aabb3f;
|
||||
|
||||
#endif
|
||||
|
||||
34
src/irr_v2d.h
Normal file
34
src/irr_v2d.h
Normal file
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
Minetest-c55
|
||||
Copyright (C) 2010-2012 celeron55, Perttu Ahola <celeron55@gmail.com>
|
||||
|
||||
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 IRR_V2D_HEADER
|
||||
#define IRR_V2D_HEADER
|
||||
|
||||
#include "irrlichttypes.h"
|
||||
|
||||
#include <vector2d.h>
|
||||
|
||||
typedef core::vector2d<f32> v2f;
|
||||
typedef core::vector2d<s16> v2s16;
|
||||
typedef core::vector2d<s32> v2s32;
|
||||
typedef core::vector2d<u32> v2u32;
|
||||
typedef core::vector2d<f32> v2f32;
|
||||
|
||||
#endif
|
||||
|
||||
32
src/irr_v3d.h
Normal file
32
src/irr_v3d.h
Normal file
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
Minetest-c55
|
||||
Copyright (C) 2010-2012 celeron55, Perttu Ahola <celeron55@gmail.com>
|
||||
|
||||
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 IRR_V3D_HEADER
|
||||
#define IRR_V3D_HEADER
|
||||
|
||||
#include "irrlichttypes.h"
|
||||
|
||||
#include <vector3d.h>
|
||||
|
||||
typedef core::vector3df v3f;
|
||||
typedef core::vector3d<s16> v3s16;
|
||||
typedef core::vector3d<s32> v3s32;
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
Minetest-c55
|
||||
Copyright (C) 2010-2011 celeron55, Perttu Ahola <celeron55@gmail.com>
|
||||
Copyright (C) 2010-2012 celeron55, Perttu Ahola <celeron55@gmail.com>
|
||||
|
||||
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
|
||||
@@ -21,25 +21,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
#define IRRLICHTTYPES_HEADER
|
||||
|
||||
#include <irrTypes.h>
|
||||
#include <vector2d.h>
|
||||
#include <vector3d.h>
|
||||
#include <irrMap.h>
|
||||
#include <irrList.h>
|
||||
#include <irrArray.h>
|
||||
#include <aabbox3d.h>
|
||||
#include <SColor.h>
|
||||
|
||||
using namespace irr;
|
||||
typedef core::vector3df v3f;
|
||||
typedef core::vector3d<s16> v3s16;
|
||||
typedef core::vector3d<s32> v3s32;
|
||||
|
||||
typedef core::vector2d<f32> v2f;
|
||||
typedef core::vector2d<s16> v2s16;
|
||||
typedef core::vector2d<s32> v2s32;
|
||||
typedef core::vector2d<u32> v2u32;
|
||||
typedef core::vector2d<f32> v2f32;
|
||||
|
||||
typedef core::aabbox3d<f32> aabb3f;
|
||||
|
||||
#ifdef _MSC_VER
|
||||
// Windows
|
||||
|
||||
35
src/irrlichttypes_bloated.h
Normal file
35
src/irrlichttypes_bloated.h
Normal file
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
Minetest-c55
|
||||
Copyright (C) 2010-2012 celeron55, Perttu Ahola <celeron55@gmail.com>
|
||||
|
||||
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 IRRLICHTTYPES_BLOATED_HEADER
|
||||
#define IRRLICHTTYPES_BLOATED_HEADER
|
||||
|
||||
#include "irrlichttypes.h"
|
||||
|
||||
#include "irr_v2d.h"
|
||||
#include "irr_v3d.h"
|
||||
#include "irr_aabb3d.h"
|
||||
|
||||
#include <irrMap.h>
|
||||
#include <irrList.h>
|
||||
#include <irrArray.h>
|
||||
#include <SColor.h>
|
||||
|
||||
#endif
|
||||
|
||||
@@ -17,13 +17,13 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#ifndef COMMON_IRRLICHT_HEADER
|
||||
#define COMMON_IRRLICHT_HEADER
|
||||
#ifndef IRRLICHTTYPES_EXTRABLOATED_HEADER
|
||||
#define IRRLICHTTYPES_EXTRABLOATED_HEADER
|
||||
|
||||
#define endSceneX(d){d->draw2DLine(v2s32(0,0),v2s32(1,0),\
|
||||
video::SColor(255,30,30,30));d->endScene();}
|
||||
|
||||
#include "irrlichttypes.h"
|
||||
#include "irrlichttypes_bloated.h"
|
||||
|
||||
#ifndef SERVER
|
||||
#include <IMesh.h>
|
||||
@@ -30,7 +30,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
#include "tile.h"
|
||||
#endif
|
||||
#include "log.h"
|
||||
#include "utility.h"
|
||||
#include "util/serialize.h"
|
||||
#include <map>
|
||||
#include <set>
|
||||
|
||||
@@ -70,6 +70,7 @@ ItemDefinition& ItemDefinition::operator=(const ItemDefinition &def)
|
||||
*def.tool_capabilities);
|
||||
}
|
||||
groups = def.groups;
|
||||
node_placement_prediction = def.node_placement_prediction;
|
||||
#ifndef SERVER
|
||||
inventory_texture = def.inventory_texture;
|
||||
if(def.wield_mesh)
|
||||
@@ -115,6 +116,8 @@ void ItemDefinition::reset()
|
||||
}
|
||||
groups.clear();
|
||||
|
||||
node_placement_prediction = "";
|
||||
|
||||
#ifndef SERVER
|
||||
inventory_texture = NULL;
|
||||
if(wield_mesh)
|
||||
@@ -150,6 +153,7 @@ void ItemDefinition::serialize(std::ostream &os) const
|
||||
os<<serializeString(i->first);
|
||||
writeS16(os, i->second);
|
||||
}
|
||||
os<<serializeString(node_placement_prediction);
|
||||
}
|
||||
|
||||
void ItemDefinition::deSerialize(std::istream &is)
|
||||
@@ -184,6 +188,11 @@ void ItemDefinition::deSerialize(std::istream &is)
|
||||
int value = readS16(is);
|
||||
groups[name] = value;
|
||||
}
|
||||
// If you add anything here, insert it primarily inside the try-catch
|
||||
// block to not need to increase the version.
|
||||
try{
|
||||
node_placement_prediction = deSerializeString(is);
|
||||
}catch(SerializationError &e) {};
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -442,7 +451,7 @@ class CItemDefManager: public IWritableItemDefManager
|
||||
if(def->inventory_texture == NULL)
|
||||
{
|
||||
def->inventory_texture =
|
||||
tsrc->getTextureRaw(f.tname_tiles[0]);
|
||||
tsrc->getTextureRaw(f.tiledef[0].name);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
#ifndef ITEMDEF_HEADER
|
||||
#define ITEMDEF_HEADER
|
||||
|
||||
#include "common_irrlicht.h"
|
||||
#include "irrlichttypes_extrabloated.h"
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
#include <set>
|
||||
@@ -67,6 +67,11 @@ struct ItemDefinition
|
||||
ToolCapabilities *tool_capabilities;
|
||||
ItemGroupList groups;
|
||||
|
||||
// Client shall immediately place this node when player places the item.
|
||||
// Server will update the precise end result a moment later.
|
||||
// "" = no prediction
|
||||
std::string node_placement_prediction;
|
||||
|
||||
/*
|
||||
Cached stuff
|
||||
*/
|
||||
|
||||
@@ -20,7 +20,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
#ifndef ITEMGROUP_HEADER
|
||||
#define ITEMGROUP_HEADER
|
||||
|
||||
#include "common_irrlicht.h"
|
||||
#include "irrlichttypes_extrabloated.h"
|
||||
#include <string>
|
||||
#include <map>
|
||||
|
||||
|
||||
@@ -258,9 +258,10 @@ KeyPress::KeyPress(const char *name)
|
||||
try {
|
||||
Key = keyname_to_keycode(name);
|
||||
m_name = name;
|
||||
if (strlen(name) > 8)
|
||||
mbtowc(&Char, name + 8, 1);
|
||||
else
|
||||
if (strlen(name) > 8) {
|
||||
int chars_read = mbtowc(&Char, name + 8, 1);
|
||||
assert (chars_read == 1 && "unexpected multibyte character");
|
||||
} else
|
||||
Char = L'\0';
|
||||
return;
|
||||
} catch (UnknownKeycode &e) {};
|
||||
@@ -270,7 +271,8 @@ KeyPress::KeyPress(const char *name)
|
||||
m_name += name;
|
||||
try {
|
||||
Key = keyname_to_keycode(m_name.c_str());
|
||||
mbtowc(&Char, name, 1);
|
||||
int chars_read = mbtowc(&Char, name, 1);
|
||||
assert (chars_read == 1 && "unexpected multibyte character");
|
||||
return;
|
||||
} catch (UnknownKeycode &e) {};
|
||||
}
|
||||
@@ -279,7 +281,8 @@ KeyPress::KeyPress(const char *name)
|
||||
|
||||
Key = irr::KEY_KEY_CODES_COUNT;
|
||||
|
||||
mbtowc(&Char, name, 1);
|
||||
int mbtowc_ret = mbtowc(&Char, name, 1);
|
||||
assert (mbtowc_ret == 1 && "unexpected multibyte character");
|
||||
m_name = name[0];
|
||||
}
|
||||
|
||||
@@ -290,9 +293,9 @@ KeyPress::KeyPress(const irr::SEvent::SKeyInput &in)
|
||||
if (valid_kcode(Key)) {
|
||||
m_name = KeyNames[Key];
|
||||
} else {
|
||||
size_t maxlen = wctomb(NULL, Char);
|
||||
m_name.resize(maxlen+1, '\0');
|
||||
wctomb(&m_name[0], Char);
|
||||
m_name.resize(MB_CUR_MAX+1, '\0');
|
||||
int written = wctomb(&m_name[0], Char);
|
||||
assert (written >= 0 && "unexpected multibyte character");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -20,7 +20,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
#ifndef KEYCODE_HEADER
|
||||
#define KEYCODE_HEADER
|
||||
|
||||
#include "common_irrlicht.h"
|
||||
#include "irrlichttypes.h"
|
||||
#include <IEventReceiver.h>
|
||||
#include <string>
|
||||
|
||||
/* A key press, consisting of either an Irrlicht keycode
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user