Merge celeron55

This commit is contained in:
Sascha Heylik 2012-09-05 17:45:42 +02:00
commit fcca601e2d
39 changed files with 970 additions and 771 deletions

View File

@ -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 # Also remember to set PROTOCOL_VERSION in clientserver.h when releasing
set(VERSION_MAJOR 0) set(VERSION_MAJOR 0)
set(VERSION_MINOR 4) set(VERSION_MINOR 4)
set(VERSION_PATCH 2-rc1) set(VERSION_PATCH 3)
if(VERSION_EXTRA) if(VERSION_EXTRA)
set(VERSION_PATCH ${VERSION_PATCH}-${VERSION_EXTRA}) set(VERSION_PATCH ${VERSION_PATCH}-${VERSION_EXTRA})
endif() endif()

View File

@ -227,10 +227,12 @@ function minetest.item_drop(itemstack, dropper, pos)
local v = dropper:get_look_dir() local v = dropper:get_look_dir()
local p = {x=pos.x+v.x, y=pos.y+1.5+v.y, z=pos.z+v.z} local p = {x=pos.x+v.x, y=pos.y+1.5+v.y, z=pos.z+v.z}
local obj = minetest.env:add_item(p, itemstack) local obj = minetest.env:add_item(p, itemstack)
v.x = v.x*2 if obj then
v.y = v.y*2 + 1 v.x = v.x*2
v.z = v.z*2 v.y = v.y*2 + 1
obj:setvelocity(v) v.z = v.z*2
obj:setvelocity(v)
end
else else
minetest.env:add_item(pos, itemstack) minetest.env:add_item(pos, itemstack)
end end
@ -258,6 +260,16 @@ function minetest.node_punch(pos, node, puncher)
end end
end end
function minetest.handle_node_drops(pos, drops, digger)
-- 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
end
function minetest.node_dig(pos, node, digger) function minetest.node_dig(pos, node, digger)
minetest.debug("node_dig") minetest.debug("node_dig")
@ -282,14 +294,9 @@ function minetest.node_dig(pos, node, digger)
local dp = minetest.get_dig_params(def.groups, tp) local dp = minetest.get_dig_params(def.groups, tp)
wielded:add_wear(dp.wear) wielded:add_wear(dp.wear)
digger:set_wielded_item(wielded) digger:set_wielded_item(wielded)
-- Add dropped items to object's inventory -- Handle drops
if digger:get_inventory() then minetest.handle_node_drops(pos, drops, digger)
local _, dropped_item
for _, dropped_item in ipairs(drops) do
digger:get_inventory():add_item("main", dropped_item)
end
end
local oldmetadata = nil local oldmetadata = nil
if def.after_dig_node then if def.after_dig_node then

View File

@ -453,6 +453,9 @@ Special groups
- 2: node is removed without tool wear after 0.5 seconds or so - 2: node is removed without tool wear after 0.5 seconds or so
(rail, sign) (rail, sign)
- 3: node is removed without tool wear immediately (torch) - 3: node is removed without tool wear immediately (torch)
- disable_jump: Player (and possibly other things) cannot jump from node
- fall_damage_add_percent: damage speed = speed * (1 + value/100)
- bouncy: value is bounce speed in percent
Known damage and digging time defining groups Known damage and digging time defining groups
---------------------------------------------- ----------------------------------------------
@ -888,6 +891,12 @@ minetest.get_craft_recipe(output) -> input
^ input.items = for example { stack 1, stack 2, stack 3, stack 4, ^ input.items = for example { stack 1, stack 2, stack 3, stack 4,
stack 5, stack 6, stack 7, stack 8, stack 9 } stack 5, stack 6, stack 7, stack 8, stack 9 }
^ input.items = nil if no recipe found ^ input.items = nil if no recipe found
minetest.handle_node_drops(pos, drops, digger)
^ drops: list of itemstrings
^ Handles drops from nodes after digging: Default action is to put them into
digger's inventory
^ Can be overridden to get different functionality (eg. dropping items on
ground)
Rollbacks: Rollbacks:
minetest.rollback_get_last_node_actor(p, range, seconds) -> actor, p, seconds minetest.rollback_get_last_node_actor(p, range, seconds) -> actor, p, seconds
@ -1118,6 +1127,8 @@ methods:
- is_empty(listname): return true if list is empty - is_empty(listname): return true if list is empty
- get_size(listname): get size of a list - get_size(listname): get size of a list
- set_size(listname, size): set size of a list - set_size(listname, size): set size of a list
- get_width(listname): get width of a list
- set_width(listname, width): set width of list; currently used for crafting
- get_stack(listname, i): get a copy of stack index i in list - get_stack(listname, i): get a copy of stack index i in list
- set_stack(listname, i, stack): copy stack to index i in list - set_stack(listname, i, stack): copy stack to index i in list
- get_list(listname): return full list - get_list(listname): return full list

View File

@ -15,7 +15,7 @@
# https://bitbucket.org/celeron55/minetest/src/tip/src/defaultsettings.cpp # https://bitbucket.org/celeron55/minetest/src/tip/src/defaultsettings.cpp
# #
# A vim command to convert most of defaultsettings.cpp to conf file format: # A vim command to convert most of defaultsettings.cpp to conf file format:
# :'<,'>s/\tg_settings\.setDefault("\([^"]*\)", "\([^"]*\)");.*/#\1 = \2/g # :'<,'>s/\tsettings->setDefault("\([^"]*\)", "\([^"]*\)");.*/#\1 = \2/g
# Note: Some of the settings are implemented in Lua # Note: Some of the settings are implemented in Lua
# #
@ -49,8 +49,14 @@
#keymap_freemove = KEY_KEY_K #keymap_freemove = KEY_KEY_K
#keymap_fastmove = KEY_KEY_J #keymap_fastmove = KEY_KEY_J
#keymap_screenshot = KEY_F12 #keymap_screenshot = KEY_F12
# If true, keymap_special1 instead of keymap_sneak is used for climbing down and descending
#aux1_descends = false
# Some (temporary) keys for debugging # Some (temporary) keys for debugging
#keymap_print_debug_stacks = KEY_KEY_P #keymap_print_debug_stacks = KEY_KEY_P
#keymap_quicktune_prev = KEY_HOME
#keymap_quicktune_next = KEY_END
#keymap_quicktune_dec = KEY_NEXT
#keymap_quicktune_inc = KEY_PRIOR
# Minimum FPS # Minimum FPS
# The amount of rendered stuff is dynamically set according to this # The amount of rendered stuff is dynamically set according to this

View File

@ -157,43 +157,21 @@ static bool isOccluded(Map *map, v3s16 p0, v3s16 p1, float step, float stepfac,
return false; return false;
} }
void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass) void ClientMap::updateDrawList(video::IVideoDriver* driver)
{ {
ScopeProfiler sp(g_profiler, "CM::updateDrawList()", SPT_AVG);
g_profiler->add("CM::updateDrawList() count", 1);
INodeDefManager *nodemgr = m_gamedef->ndef(); INodeDefManager *nodemgr = m_gamedef->ndef();
//m_dout<<DTIME<<"Rendering map..."<<std::endl; for(core::map<v3s16, MapBlock*>::Iterator
DSTACK(__FUNCTION_NAME); i = m_drawlist.getIterator();
i.atEnd() == false; i++)
bool is_transparent_pass = pass == scene::ESNRP_TRANSPARENT;
std::string prefix;
if(pass == scene::ESNRP_SOLID)
prefix = "CM: solid: ";
else
prefix = "CM: transparent: ";
/*
This is called two times per frame, reset on the non-transparent one
*/
if(pass == scene::ESNRP_SOLID)
{ {
m_last_drawn_sectors.clear(); MapBlock *block = i.getNode()->getValue();
block->refDrop();
} }
m_drawlist.clear();
/*
Get time for measuring timeout.
Measuring time is very useful for long delays when the
machine is swapping a lot.
*/
int time1 = time(0);
/*
Get animation parameters
*/
float animation_time = m_client->getAnimationTime();
int crack = m_client->getCrackLevel();
u32 daynight_ratio = m_client->getEnv().getDayNightRatio();
m_camera_mutex.Lock(); m_camera_mutex.Lock();
v3f camera_position = m_camera_position; v3f camera_position = m_camera_position;
@ -201,17 +179,15 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
f32 camera_fov = m_camera_fov; f32 camera_fov = m_camera_fov;
m_camera_mutex.Unlock(); m_camera_mutex.Unlock();
/* // Use a higher fov to accomodate faster camera movements.
Get all blocks and draw all visible ones // Blocks are cropped better when they are drawn.
*/ // Or maybe they aren't? Well whatever.
camera_fov *= 1.2;
v3s16 cam_pos_nodes = floatToInt(camera_position, BS); v3s16 cam_pos_nodes = floatToInt(camera_position, BS);
v3s16 box_nodes_d = m_control.wanted_range * v3s16(1,1,1); v3s16 box_nodes_d = m_control.wanted_range * v3s16(1,1,1);
v3s16 p_nodes_min = cam_pos_nodes - box_nodes_d; v3s16 p_nodes_min = cam_pos_nodes - box_nodes_d;
v3s16 p_nodes_max = cam_pos_nodes + box_nodes_d; v3s16 p_nodes_max = cam_pos_nodes + box_nodes_d;
// Take a fair amount as we will be dropping more out later // Take a fair amount as we will be dropping more out later
// Umm... these additions are a bit strange but they are needed. // Umm... these additions are a bit strange but they are needed.
v3s16 p_blocks_min( v3s16 p_blocks_min(
@ -223,13 +199,6 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
p_nodes_max.Y / MAP_BLOCKSIZE + 1, p_nodes_max.Y / MAP_BLOCKSIZE + 1,
p_nodes_max.Z / MAP_BLOCKSIZE + 1); p_nodes_max.Z / MAP_BLOCKSIZE + 1);
u32 vertex_count = 0;
u32 meshbuffer_count = 0;
// For limiting number of mesh animations per frame
u32 mesh_animate_count = 0;
u32 mesh_animate_count_far = 0;
// Number of blocks in rendering range // Number of blocks in rendering range
u32 blocks_in_range = 0; u32 blocks_in_range = 0;
// Number of blocks occlusion culled // Number of blocks occlusion culled
@ -242,18 +211,9 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
// Blocks that were drawn and had a mesh // Blocks that were drawn and had a mesh
u32 blocks_drawn = 0; u32 blocks_drawn = 0;
// Blocks which had a corresponding meshbuffer for this pass // Blocks which had a corresponding meshbuffer for this pass
u32 blocks_had_pass_meshbuf = 0; //u32 blocks_had_pass_meshbuf = 0;
// Blocks from which stuff was actually drawn // Blocks from which stuff was actually drawn
u32 blocks_without_stuff = 0; //u32 blocks_without_stuff = 0;
/*
Collect a set of blocks for drawing
*/
core::map<v3s16, MapBlock*> drawset;
{
ScopeProfiler sp(g_profiler, prefix+"collecting blocks for drawing", SPT_AVG);
for(core::map<v2s16, MapSector*>::Iterator for(core::map<v2s16, MapSector*>::Iterator
si = m_sectors.getIterator(); si = m_sectors.getIterator();
@ -380,36 +340,10 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
&& d > m_control.wanted_min_range * BS) && d > m_control.wanted_min_range * BS)
continue; continue;
// Mesh animation
{
//JMutexAutoLock lock(block->mesh_mutex);
MapBlockMesh *mapBlockMesh = block->mesh;
// Pretty random but this should work somewhat nicely
bool faraway = d >= BS*50;
//bool faraway = d >= m_control.wanted_range * BS;
if(mapBlockMesh->isAnimationForced() ||
!faraway ||
mesh_animate_count_far < (m_control.range_all ? 200 : 50))
{
bool animated = mapBlockMesh->animate(
faraway,
animation_time,
crack,
daynight_ratio);
if(animated)
mesh_animate_count++;
if(animated && faraway)
mesh_animate_count_far++;
}
else
{
mapBlockMesh->decreaseAnimationForceTimer();
}
}
// Add to set // Add to set
drawset[block->getPos()] = block; block->refGrab();
m_drawlist[block->getPos()] = block;
sector_blocks_drawn++; sector_blocks_drawn++;
blocks_drawn++; blocks_drawn++;
@ -418,8 +352,127 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
if(sector_blocks_drawn != 0) if(sector_blocks_drawn != 0)
m_last_drawn_sectors[sp] = true; m_last_drawn_sectors[sp] = true;
} }
} // ScopeProfiler
g_profiler->avg("CM: blocks in range", blocks_in_range);
g_profiler->avg("CM: blocks occlusion culled", blocks_occlusion_culled);
if(blocks_in_range != 0)
g_profiler->avg("CM: blocks in range without mesh (frac)",
(float)blocks_in_range_without_mesh/blocks_in_range);
g_profiler->avg("CM: blocks drawn", blocks_drawn);
}
struct MeshBufList
{
video::SMaterial m;
core::list<scene::IMeshBuffer*> bufs;
};
struct MeshBufListList
{
core::list<MeshBufList> lists;
void clear()
{
lists.clear();
}
void add(scene::IMeshBuffer *buf)
{
for(core::list<MeshBufList>::Iterator i = lists.begin();
i != lists.end(); i++){
MeshBufList &l = *i;
if(l.m == buf->getMaterial()){
l.bufs.push_back(buf);
return;
}
}
MeshBufList l;
l.m = buf->getMaterial();
l.bufs.push_back(buf);
lists.push_back(l);
}
};
void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
{
DSTACK(__FUNCTION_NAME);
bool is_transparent_pass = pass == scene::ESNRP_TRANSPARENT;
std::string prefix;
if(pass == scene::ESNRP_SOLID)
prefix = "CM: solid: ";
else
prefix = "CM: transparent: ";
/*
This is called two times per frame, reset on the non-transparent one
*/
if(pass == scene::ESNRP_SOLID)
{
m_last_drawn_sectors.clear();
}
/*
Get time for measuring timeout.
Measuring time is very useful for long delays when the
machine is swapping a lot.
*/
int time1 = time(0);
/*
Get animation parameters
*/
float animation_time = m_client->getAnimationTime();
int crack = m_client->getCrackLevel();
u32 daynight_ratio = m_client->getEnv().getDayNightRatio();
m_camera_mutex.Lock();
v3f camera_position = m_camera_position;
v3f camera_direction = m_camera_direction;
f32 camera_fov = m_camera_fov;
m_camera_mutex.Unlock();
/*
Get all blocks and draw all visible ones
*/
v3s16 cam_pos_nodes = floatToInt(camera_position, BS);
v3s16 box_nodes_d = m_control.wanted_range * v3s16(1,1,1);
v3s16 p_nodes_min = cam_pos_nodes - box_nodes_d;
v3s16 p_nodes_max = cam_pos_nodes + box_nodes_d;
// Take a fair amount as we will be dropping more out later
// Umm... these additions are a bit strange but they are needed.
v3s16 p_blocks_min(
p_nodes_min.X / MAP_BLOCKSIZE - 3,
p_nodes_min.Y / MAP_BLOCKSIZE - 3,
p_nodes_min.Z / MAP_BLOCKSIZE - 3);
v3s16 p_blocks_max(
p_nodes_max.X / MAP_BLOCKSIZE + 1,
p_nodes_max.Y / MAP_BLOCKSIZE + 1,
p_nodes_max.Z / MAP_BLOCKSIZE + 1);
u32 vertex_count = 0;
u32 meshbuffer_count = 0;
// For limiting number of mesh animations per frame
u32 mesh_animate_count = 0;
u32 mesh_animate_count_far = 0;
// Blocks that had mesh that would have been drawn according to
// rendering range (if max blocks limit didn't kick in)
u32 blocks_would_have_drawn = 0;
// Blocks that were drawn and had a mesh
u32 blocks_drawn = 0;
// Blocks which had a corresponding meshbuffer for this pass
u32 blocks_had_pass_meshbuf = 0;
// Blocks from which stuff was actually drawn
u32 blocks_without_stuff = 0;
/* /*
Draw the selected MapBlocks Draw the selected MapBlocks
*/ */
@ -427,10 +480,90 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
{ {
ScopeProfiler sp(g_profiler, prefix+"drawing blocks", SPT_AVG); ScopeProfiler sp(g_profiler, prefix+"drawing blocks", SPT_AVG);
int timecheck_counter = 0; MeshBufListList drawbufs;
for(core::map<v3s16, MapBlock*>::Iterator for(core::map<v3s16, MapBlock*>::Iterator
i = drawset.getIterator(); i = m_drawlist.getIterator();
i.atEnd() == false; i++) i.atEnd() == false; i++)
{
MapBlock *block = i.getNode()->getValue();
// If the mesh of the block happened to get deleted, ignore it
if(block->mesh == NULL)
continue;
float d = 0.0;
if(isBlockInSight(block->getPos(), camera_position,
camera_direction, camera_fov,
100000*BS, &d) == false)
{
continue;
}
// Mesh animation
{
//JMutexAutoLock lock(block->mesh_mutex);
MapBlockMesh *mapBlockMesh = block->mesh;
assert(mapBlockMesh);
// Pretty random but this should work somewhat nicely
bool faraway = d >= BS*50;
//bool faraway = d >= m_control.wanted_range * BS;
if(mapBlockMesh->isAnimationForced() ||
!faraway ||
mesh_animate_count_far < (m_control.range_all ? 200 : 50))
{
bool animated = mapBlockMesh->animate(
faraway,
animation_time,
crack,
daynight_ratio);
if(animated)
mesh_animate_count++;
if(animated && faraway)
mesh_animate_count_far++;
}
else
{
mapBlockMesh->decreaseAnimationForceTimer();
}
}
/*
Get the meshbuffers of the block
*/
{
//JMutexAutoLock lock(block->mesh_mutex);
MapBlockMesh *mapBlockMesh = block->mesh;
assert(mapBlockMesh);
scene::SMesh *mesh = mapBlockMesh->getMesh();
assert(mesh);
u32 c = mesh->getMeshBufferCount();
for(u32 i=0; i<c; i++)
{
scene::IMeshBuffer *buf = mesh->getMeshBuffer(i);
const video::SMaterial& material = buf->getMaterial();
video::IMaterialRenderer* rnd =
driver->getMaterialRenderer(material.MaterialType);
bool transparent = (rnd && rnd->isTransparent());
if(transparent == is_transparent_pass)
{
if(buf->getVertexCount() == 0)
errorstream<<"Block ["<<analyze_block(block)
<<"] contains an empty meshbuf"<<std::endl;
drawbufs.add(buf);
}
}
}
}
core::list<MeshBufList> &lists = drawbufs.lists;
int timecheck_counter = 0;
for(core::list<MeshBufList>::Iterator i = lists.begin();
i != lists.end(); i++)
{ {
{ {
timecheck_counter++; timecheck_counter++;
@ -447,9 +580,20 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
} }
} }
} }
MapBlock *block = i.getNode()->getValue();
MeshBufList &list = *i;
driver->setMaterial(list.m);
for(core::list<scene::IMeshBuffer*>::Iterator j = list.bufs.begin();
j != list.bufs.end(); j++)
{
scene::IMeshBuffer *buf = *j;
driver->drawMeshBuffer(buf);
vertex_count += buf->getVertexCount();
meshbuffer_count++;
}
#if 0
/* /*
Draw the faces of the block Draw the faces of the block
*/ */
@ -502,17 +646,12 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
else else
blocks_without_stuff++; blocks_without_stuff++;
} }
#endif
} }
} // ScopeProfiler } // ScopeProfiler
// Log only on solid pass because values are the same // Log only on solid pass because values are the same
if(pass == scene::ESNRP_SOLID){ if(pass == scene::ESNRP_SOLID){
g_profiler->avg("CM: blocks in range", blocks_in_range);
g_profiler->avg("CM: blocks occlusion culled", blocks_occlusion_culled);
if(blocks_in_range != 0)
g_profiler->avg("CM: blocks in range without mesh (frac)",
(float)blocks_in_range_without_mesh/blocks_in_range);
g_profiler->avg("CM: blocks drawn", blocks_drawn);
g_profiler->avg("CM: animated meshes", mesh_animate_count); g_profiler->avg("CM: animated meshes", mesh_animate_count);
g_profiler->avg("CM: animated meshes (far)", mesh_animate_count_far); g_profiler->avg("CM: animated meshes (far)", mesh_animate_count_far);
} }

View File

@ -113,7 +113,8 @@ public:
{ {
return m_box; return m_box;
} }
void updateDrawList(video::IVideoDriver* driver);
void renderMap(video::IVideoDriver* driver, s32 pass); void renderMap(video::IVideoDriver* driver, s32 pass);
int getBackgroundBrightness(float max_d, u32 daylight_factor, int getBackgroundBrightness(float max_d, u32 daylight_factor,
@ -141,6 +142,8 @@ private:
v3f m_camera_direction; v3f m_camera_direction;
f32 m_camera_fov; f32 m_camera_fov;
JMutex m_camera_mutex; JMutex m_camera_mutex;
core::map<v3s16, MapBlock*> m_drawlist;
core::map<v2s16, bool> m_last_drawn_sectors; core::map<v2s16, bool> m_last_drawn_sectors;
}; };

View File

@ -65,9 +65,11 @@ with this program; if not, write to the Free Software Foundation, Inc.,
TOSERVER_INVENTORY_FIELDS TOSERVER_INVENTORY_FIELDS
16-bit node ids 16-bit node ids
TOCLIENT_DETACHED_INVENTORY TOCLIENT_DETACHED_INVENTORY
PROTOCOL_VERSION 13:
InventoryList field "Width" (deserialization fails with old versions)
*/ */
#define PROTOCOL_VERSION 12 #define PROTOCOL_VERSION 13
#define PROTOCOL_ID 0x4f457403 #define PROTOCOL_ID 0x4f457403

View File

@ -211,6 +211,8 @@ collisionMoveResult collisionMoveSimple(Map *map, IGameDef *gamedef,
std::vector<aabb3f> cboxes; std::vector<aabb3f> cboxes;
std::vector<bool> is_unloaded; std::vector<bool> is_unloaded;
std::vector<bool> is_step_up; std::vector<bool> is_step_up;
std::vector<int> bouncy_values;
std::vector<v3s16> node_positions;
{ {
//TimeTaker tt2("collisionMoveSimple collect boxes"); //TimeTaker tt2("collisionMoveSimple collect boxes");
ScopeProfiler sp(g_profiler, "collisionMoveSimple collect boxes avg", SPT_AVG); ScopeProfiler sp(g_profiler, "collisionMoveSimple collect boxes avg", SPT_AVG);
@ -228,11 +230,14 @@ collisionMoveResult collisionMoveSimple(Map *map, IGameDef *gamedef,
for(s16 y = min_y; y <= max_y; y++) for(s16 y = min_y; y <= max_y; y++)
for(s16 z = min_z; z <= max_z; z++) for(s16 z = min_z; z <= max_z; z++)
{ {
v3s16 p(x,y,z);
try{ try{
// Object collides into walkable nodes // Object collides into walkable nodes
MapNode n = map->getNode(v3s16(x,y,z)); MapNode n = map->getNode(p);
if(gamedef->getNodeDefManager()->get(n).walkable == false) const ContentFeatures &f = gamedef->getNodeDefManager()->get(n);
if(f.walkable == false)
continue; continue;
int n_bouncy_value = itemgroup_get(f.groups, "bouncy");
std::vector<aabb3f> nodeboxes = n.getNodeBoxes(gamedef->ndef()); std::vector<aabb3f> nodeboxes = n.getNodeBoxes(gamedef->ndef());
for(std::vector<aabb3f>::iterator for(std::vector<aabb3f>::iterator
@ -245,21 +250,27 @@ collisionMoveResult collisionMoveSimple(Map *map, IGameDef *gamedef,
cboxes.push_back(box); cboxes.push_back(box);
is_unloaded.push_back(false); is_unloaded.push_back(false);
is_step_up.push_back(false); is_step_up.push_back(false);
bouncy_values.push_back(n_bouncy_value);
node_positions.push_back(p);
} }
} }
catch(InvalidPositionException &e) catch(InvalidPositionException &e)
{ {
// Collide with unloaded nodes // Collide with unloaded nodes
aabb3f box = getNodeBox(v3s16(x,y,z), BS); aabb3f box = getNodeBox(p, BS);
cboxes.push_back(box); cboxes.push_back(box);
is_unloaded.push_back(true); is_unloaded.push_back(true);
is_step_up.push_back(false); is_step_up.push_back(false);
bouncy_values.push_back(0);
node_positions.push_back(p);
} }
} }
} // tt2 } // tt2
assert(cboxes.size() == is_unloaded.size()); assert(cboxes.size() == is_unloaded.size());
assert(cboxes.size() == is_step_up.size()); assert(cboxes.size() == is_step_up.size());
assert(cboxes.size() == bouncy_values.size());
assert(cboxes.size() == node_positions.size());
/* /*
Collision detection Collision detection
@ -342,6 +353,10 @@ collisionMoveResult collisionMoveSimple(Map *map, IGameDef *gamedef,
cbox.MaxEdge.Y - movingbox.MinEdge.Y, cbox.MaxEdge.Y - movingbox.MinEdge.Y,
d)); d));
// Get bounce multiplier
bool bouncy = (bouncy_values[nearest_boxindex] >= 1);
float bounce = -(float)bouncy_values[nearest_boxindex] / 100.0;
// Move to the point of collision and reduce dtime by nearest_dtime // Move to the point of collision and reduce dtime by nearest_dtime
if(nearest_dtime < 0) if(nearest_dtime < 0)
{ {
@ -361,30 +376,58 @@ collisionMoveResult collisionMoveSimple(Map *map, IGameDef *gamedef,
pos_f += speed_f * nearest_dtime; pos_f += speed_f * nearest_dtime;
dtime -= nearest_dtime; dtime -= nearest_dtime;
} }
bool is_collision = true;
if(is_unloaded[nearest_boxindex])
is_collision = false;
CollisionInfo info;
info.type = COLLISION_NODE;
info.node_p = node_positions[nearest_boxindex];
info.bouncy = bouncy;
info.old_speed = speed_f;
// Set the speed component that caused the collision to zero // Set the speed component that caused the collision to zero
if(step_up) if(step_up)
{ {
// Special case: Handle stairs // Special case: Handle stairs
is_step_up[nearest_boxindex] = true; is_step_up[nearest_boxindex] = true;
is_collision = false;
} }
else if(nearest_collided == 0) // X else if(nearest_collided == 0) // X
{ {
speed_f.X = 0; if(fabs(speed_f.X) > BS*3)
speed_f.X *= bounce;
else
speed_f.X = 0;
result.collides = true; result.collides = true;
result.collides_xz = true; result.collides_xz = true;
} }
else if(nearest_collided == 1) // Y else if(nearest_collided == 1) // Y
{ {
speed_f.Y = 0; if(fabs(speed_f.Y) > BS*3)
speed_f.Y *= bounce;
else
speed_f.Y = 0;
result.collides = true; result.collides = true;
} }
else if(nearest_collided == 2) // Z else if(nearest_collided == 2) // Z
{ {
speed_f.Z = 0; if(fabs(speed_f.Z) > BS*3)
speed_f.Z *= bounce;
else
speed_f.Z = 0;
result.collides = true; result.collides = true;
result.collides_xz = true; result.collides_xz = true;
} }
info.new_speed = speed_f;
if(info.new_speed.getDistanceFrom(info.old_speed) < 0.1*BS)
is_collision = false;
if(is_collision){
result.collisions.push_back(info);
}
} }
} }

View File

@ -26,12 +26,35 @@ with this program; if not, write to the Free Software Foundation, Inc.,
class Map; class Map;
class IGameDef; class IGameDef;
enum CollisionType
{
COLLISION_NODE
};
struct CollisionInfo
{
enum CollisionType type;
v3s16 node_p; // COLLISION_NODE
bool bouncy;
v3f old_speed;
v3f new_speed;
CollisionInfo():
type(COLLISION_NODE),
node_p(-32768,-32768,-32768),
bouncy(false),
old_speed(0,0,0),
new_speed(0,0,0)
{}
};
struct collisionMoveResult struct collisionMoveResult
{ {
bool touching_ground; bool touching_ground;
bool collides; bool collides;
bool collides_xz; bool collides_xz;
bool standing_on_unloaded; bool standing_on_unloaded;
std::vector<CollisionInfo> collisions;
collisionMoveResult(): collisionMoveResult():
touching_ground(false), touching_ground(false),
@ -72,16 +95,5 @@ bool wouldCollideWithCeiling(
f32 y_increase, f32 d); f32 y_increase, f32 d);
enum CollisionType
{
COLLISION_FALL
};
struct CollisionInfo
{
CollisionType t;
f32 speed;
};
#endif #endif

View File

@ -52,6 +52,7 @@ void set_default_settings(Settings *settings)
settings->setDefault("keymap_toggle_profiler", "KEY_F6"); settings->setDefault("keymap_toggle_profiler", "KEY_F6");
settings->setDefault("keymap_increase_viewing_range_min", "+"); settings->setDefault("keymap_increase_viewing_range_min", "+");
settings->setDefault("keymap_decrease_viewing_range_min", "-"); settings->setDefault("keymap_decrease_viewing_range_min", "-");
settings->setDefault("aux1_descends", "false");
// Some (temporary) keys for debugging // Some (temporary) keys for debugging
settings->setDefault("keymap_print_debug_stacks", "KEY_KEY_P"); settings->setDefault("keymap_print_debug_stacks", "KEY_KEY_P");
settings->setDefault("keymap_quicktune_prev", "KEY_HOME"); settings->setDefault("keymap_quicktune_prev", "KEY_HOME");

View File

@ -2034,19 +2034,32 @@ void ClientEnvironment::step(float dtime)
i != player_collisions.end(); i++) i != player_collisions.end(); i++)
{ {
CollisionInfo &info = *i; CollisionInfo &info = *i;
if(info.t == COLLISION_FALL) v3f speed_diff = info.new_speed - info.old_speed;;
// Handle only fall damage
// (because otherwise walking against something in fast_move kills you)
if(speed_diff.Y < 0 || info.old_speed.Y >= 0)
continue;
// Get rid of other components
speed_diff.X = 0;
speed_diff.Z = 0;
f32 pre_factor = 1; // 1 hp per node/s
f32 tolerance = BS*14; // 5 without damage
f32 post_factor = 1; // 1 hp per node/s
if(info.type == COLLISION_NODE)
{ {
//f32 tolerance = BS*10; // 2 without damage const ContentFeatures &f = m_gamedef->ndef()->
//f32 tolerance = BS*12; // 3 without damage get(m_map->getNodeNoEx(info.node_p));
f32 tolerance = BS*14; // 5 without damage // Determine fall damage multiplier
f32 factor = 1; int addp = itemgroup_get(f.groups, "fall_damage_add_percent");
if(info.speed > tolerance) pre_factor = 1.0 + (float)addp/100.0;
{ }
f32 damage_f = (info.speed - tolerance)/BS*factor; float speed = pre_factor * speed_diff.getLength();
u16 damage = (u16)(damage_f+0.5); if(speed > tolerance)
if(damage != 0) {
damageLocalPlayer(damage, true); f32 damage_f = (speed - tolerance)/BS * post_factor;
} u16 damage = (u16)(damage_f+0.5);
if(damage != 0)
damageLocalPlayer(damage, true);
} }
} }

View File

@ -1235,6 +1235,9 @@ void the_game(
float object_hit_delay_timer = 0.0; float object_hit_delay_timer = 0.0;
float time_from_last_punch = 10; float time_from_last_punch = 10;
float update_draw_list_timer = 0.0;
v3f update_draw_list_last_cam_dir;
bool invert_mouse = g_settings->getBool("invert_mouse"); bool invert_mouse = g_settings->getBool("invert_mouse");
bool respawn_menu_active = false; bool respawn_menu_active = false;
@ -2697,7 +2700,19 @@ void the_game(
item = mlist->getItem(client.getPlayerItem()); item = mlist->getItem(client.getPlayerItem());
camera.wield(item); camera.wield(item);
} }
/*
Update block draw list every 200ms or when camera direction has
changed much
*/
update_draw_list_timer += dtime;
if(update_draw_list_timer >= 0.2 ||
update_draw_list_last_cam_dir.getDistanceFrom(camera_direction) > 0.2){
update_draw_list_timer = 0;
client.getEnv().getClientMap().updateDrawList(driver);
update_draw_list_last_cam_dir = camera_direction;
}
/* /*
Drawing begins Drawing begins
*/ */

View File

@ -733,6 +733,54 @@ void GUIFormSpecMenu::drawMenu()
void GUIFormSpecMenu::updateSelectedItem() void GUIFormSpecMenu::updateSelectedItem()
{ {
// WARNING: BLACK MAGIC
// See if there is a stack suited for our current guess.
// If such stack does not exist, clear the guess.
if(m_selected_content_guess.name != "")
{
bool found = false;
for(u32 i=0; i<m_inventorylists.size() && !found; i++){
const ListDrawSpec &s = m_inventorylists[i];
Inventory *inv = m_invmgr->getInventory(s.inventoryloc);
if(!inv)
continue;
InventoryList *list = inv->getList(s.listname);
if(!list)
continue;
for(s32 i=0; i<s.geom.X*s.geom.Y && !found; i++){
u32 item_i = i + s.start_item_i;
if(item_i >= list->getSize())
continue;
ItemStack stack = list->getItem(item_i);
if(stack.name == m_selected_content_guess.name &&
stack.count == m_selected_content_guess.count){
found = true;
if(m_selected_item){
// If guessed stack is already selected, all is fine
if(m_selected_item->inventoryloc == s.inventoryloc &&
m_selected_item->listname == s.listname &&
m_selected_item->i == (s32)item_i &&
m_selected_amount == stack.count){
break;
}
delete m_selected_item;
m_selected_item = NULL;
}
infostream<<"Client: Changing selected content guess to "
<<s.inventoryloc.dump()<<" "<<s.listname
<<" "<<item_i<<std::endl;
m_selected_item = new ItemSpec(s.inventoryloc, s.listname, item_i);
m_selected_amount = stack.count;
break;
}
}
}
if(!found){
infostream<<"Client: Discarding selected content guess: "
<<m_selected_content_guess.getItemString()<<std::endl;
m_selected_content_guess.name = "";
}
}
// If the selected stack has become empty for some reason, deselect it. // If the selected stack has become empty for some reason, deselect it.
// If the selected stack has become smaller, adjust m_selected_amount. // If the selected stack has become smaller, adjust m_selected_amount.
if(m_selected_item) if(m_selected_item)
@ -1054,21 +1102,28 @@ bool GUIFormSpecMenu::OnEvent(const SEvent& event)
// Check how many items can be moved // Check how many items can be moved
move_amount = stack_from.count = MYMIN(move_amount, stack_from.count); move_amount = stack_from.count = MYMIN(move_amount, stack_from.count);
ItemStack leftover = stack_to.addItem(stack_from, m_gamedef->idef()); ItemStack leftover = stack_to.addItem(stack_from, m_gamedef->idef());
if(leftover.count == stack_from.count) // If source stack cannot be added to destination stack at all,
// they are swapped
if(leftover.count == stack_from.count && leftover.name == stack_from.name)
{ {
// Swap the stacks m_selected_amount = stack_to.count;
m_selected_amount -= stack_to.count; // In case the server doesn't directly swap them but instead
// moves stack_to somewhere else, set this
m_selected_content_guess = stack_to;
m_selected_content_guess_inventory = s.inventoryloc;
} }
// Source stack goes fully into destination stack
else if(leftover.empty()) else if(leftover.empty())
{ {
// Item fits
m_selected_amount -= move_amount; m_selected_amount -= move_amount;
m_selected_content_guess = ItemStack(); // Clear
} }
// Source stack goes partly into destination stack
else else
{ {
// Item only fits partially
move_amount -= leftover.count; move_amount -= leftover.count;
m_selected_amount -= move_amount; m_selected_amount -= move_amount;
m_selected_content_guess = ItemStack(); // Clear
} }
infostream<<"Handing IACTION_MOVE to manager"<<std::endl; infostream<<"Handing IACTION_MOVE to manager"<<std::endl;
@ -1084,6 +1139,8 @@ bool GUIFormSpecMenu::OnEvent(const SEvent& event)
} }
else if(drop_amount > 0) else if(drop_amount > 0)
{ {
m_selected_content_guess = ItemStack(); // Clear
// Send IACTION_DROP // Send IACTION_DROP
assert(m_selected_item && m_selected_item->isValid()); assert(m_selected_item && m_selected_item->isValid());
@ -1107,6 +1164,8 @@ bool GUIFormSpecMenu::OnEvent(const SEvent& event)
} }
else if(craft_amount > 0) else if(craft_amount > 0)
{ {
m_selected_content_guess = ItemStack(); // Clear
// Send IACTION_CRAFT // Send IACTION_CRAFT
assert(s.isValid()); assert(s.isValid());
@ -1126,6 +1185,7 @@ bool GUIFormSpecMenu::OnEvent(const SEvent& event)
m_selected_item = NULL; m_selected_item = NULL;
m_selected_amount = 0; m_selected_amount = 0;
m_selected_dragging = false; m_selected_dragging = false;
m_selected_content_guess = ItemStack();
} }
} }
if(event.EventType==EET_GUI_EVENT) if(event.EventType==EET_GUI_EVENT)

View File

@ -212,6 +212,12 @@ protected:
ItemSpec *m_selected_item; ItemSpec *m_selected_item;
u32 m_selected_amount; u32 m_selected_amount;
bool m_selected_dragging; bool m_selected_dragging;
// WARNING: BLACK MAGIC
// Used to guess and keep up with some special things the server can do.
// If name is "", no guess exists.
ItemStack m_selected_content_guess;
InventoryLocation m_selected_content_guess_inventory;
v2s32 m_pointer; v2s32 m_pointer;
gui::IGUIStaticText *m_tooltip_element; gui::IGUIStaticText *m_tooltip_element;

View File

@ -30,11 +30,14 @@
#include <IGUIStaticText.h> #include <IGUIStaticText.h>
#include <IGUIFont.h> #include <IGUIFont.h>
#include "settings.h" #include "settings.h"
#include <algorithm>
#define KMaxButtonPerColumns 12
enum enum
{ {
GUI_ID_BACK_BUTTON = 101, GUI_ID_ABORT_BUTTON, GUI_ID_SCROLL_BAR, GUI_ID_BACK_BUTTON = 101, GUI_ID_ABORT_BUTTON, GUI_ID_SCROLL_BAR,
//buttons // buttons
GUI_ID_KEY_FORWARD_BUTTON, GUI_ID_KEY_FORWARD_BUTTON,
GUI_ID_KEY_BACKWARD_BUTTON, GUI_ID_KEY_BACKWARD_BUTTON,
GUI_ID_KEY_LEFT_BUTTON, GUI_ID_KEY_LEFT_BUTTON,
@ -50,15 +53,21 @@ enum
GUI_ID_KEY_DROP_BUTTON, GUI_ID_KEY_DROP_BUTTON,
GUI_ID_KEY_INVENTORY_BUTTON, GUI_ID_KEY_INVENTORY_BUTTON,
GUI_ID_KEY_DUMP_BUTTON, GUI_ID_KEY_DUMP_BUTTON,
GUI_ID_KEY_RANGE_BUTTON GUI_ID_KEY_RANGE_BUTTON,
// other
GUI_ID_CB_AUX1_DESCENDS,
}; };
GUIKeyChangeMenu::GUIKeyChangeMenu(gui::IGUIEnvironment* env, GUIKeyChangeMenu::GUIKeyChangeMenu(gui::IGUIEnvironment* env,
gui::IGUIElement* parent, s32 id, IMenuManager *menumgr) : gui::IGUIElement* parent, s32 id, IMenuManager *menumgr) :
GUIModalMenu(env, parent, id, menumgr) GUIModalMenu(env, parent, id, menumgr)
{ {
shift_down = false;
activeKey = -1; activeKey = -1;
this->key_used_text = NULL;
init_keys(); init_keys();
for(size_t i=0; i<key_settings.size(); i++)
this->key_used.push_back(key_settings.at(i)->key);
} }
GUIKeyChangeMenu::~GUIKeyChangeMenu() GUIKeyChangeMenu::~GUIKeyChangeMenu()
@ -71,12 +80,12 @@ void GUIKeyChangeMenu::removeChildren()
const core::list<gui::IGUIElement*> &children = getChildren(); const core::list<gui::IGUIElement*> &children = getChildren();
core::list<gui::IGUIElement*> children_copy; core::list<gui::IGUIElement*> children_copy;
for (core::list<gui::IGUIElement*>::ConstIterator i = children.begin(); i for (core::list<gui::IGUIElement*>::ConstIterator i = children.begin(); i
!= children.end(); i++) != children.end(); i++)
{ {
children_copy.push_back(*i); children_copy.push_back(*i);
} }
for (core::list<gui::IGUIElement*>::Iterator i = children_copy.begin(); i for (core::list<gui::IGUIElement*>::Iterator i = children_copy.begin(); i
!= children_copy.end(); i++) != children_copy.end(); i++)
{ {
(*i)->remove(); (*i)->remove();
} }
@ -84,20 +93,12 @@ void GUIKeyChangeMenu::removeChildren()
void GUIKeyChangeMenu::regenerateGui(v2u32 screensize) void GUIKeyChangeMenu::regenerateGui(v2u32 screensize)
{ {
/*
Remove stuff
*/
removeChildren(); removeChildren();
/*
Calculate new sizes and positions
*/
v2s32 size(620, 430); v2s32 size(620, 430);
core::rect < s32 > rect(screensize.X / 2 - size.X / 2, core::rect < s32 > rect(screensize.X / 2 - size.X / 2,
screensize.Y / 2 - size.Y / 2, screensize.X / 2 + size.X / 2, screensize.Y / 2 - size.Y / 2, screensize.X / 2 + size.X / 2,
screensize.Y / 2 + size.Y / 2); screensize.Y / 2 + size.Y / 2);
DesiredRect = rect; DesiredRect = rect;
recalculateAbsolutePosition(false); recalculateAbsolutePosition(false);
@ -109,271 +110,60 @@ void GUIKeyChangeMenu::regenerateGui(v2u32 screensize)
rect += topleft + v2s32(25, 3); rect += topleft + v2s32(25, 3);
//gui::IGUIStaticText *t = //gui::IGUIStaticText *t =
Environment->addStaticText(wgettext("Keybindings. (If this menu screws up, remove stuff from minetest.conf)"), Environment->addStaticText(wgettext("Keybindings. (If this menu screws up, remove stuff from minetest.conf)"),
rect, false, true, this, -1); rect, false, true, this, -1);
//t->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_UPPERLEFT); //t->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_UPPERLEFT);
} }
// Build buttons
v2s32 offset(25, 60); v2s32 offset(25, 60);
// buttons
for(size_t i = 0; i < key_settings.size(); i++)
{ {
core::rect < s32 > rect(0, 0, 100, 20); key_setting *k = key_settings.at(i);
rect += topleft + v2s32(offset.X, offset.Y); {
Environment->addStaticText(wgettext("Forward"), core::rect < s32 > rect(0, 0, 100, 20);
rect, false, true, this, -1); rect += topleft + v2s32(offset.X, offset.Y);
//t->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_UPPERLEFT); Environment->addStaticText(k->button_name, rect, false, true, this, -1);
}
{
core::rect < s32 > rect(0, 0, 100, 30);
rect += topleft + v2s32(offset.X + 105, offset.Y - 5);
k->button = Environment->addButton(rect, this, k->id, wgettext(k->key.name()));
}
if(i + 1 == KMaxButtonPerColumns)
offset = v2s32(250, 60);
else
offset += v2s32(0, 25);
}
{
s32 option_x = offset.X + 10;
s32 option_y = offset.Y;
u32 option_w = 180;
{
core::rect<s32> rect(0, 0, option_w, 30);
rect += topleft + v2s32(option_x, option_y);
Environment->addCheckBox(g_settings->getBool("aux1_descends"), rect, this,
GUI_ID_CB_AUX1_DESCENDS, wgettext("\"Use\" = climb down"));
}
} }
{
core::rect < s32 > rect(0, 0, 100, 30);
rect += topleft + v2s32(offset.X + 105, offset.Y - 5);
this->forward = Environment->addButton(rect, this,
GUI_ID_KEY_FORWARD_BUTTON,
wgettext(key_forward.name()));
}
offset += v2s32(0, 25);
{
core::rect < s32 > rect(0, 0, 100, 20);
rect += topleft + v2s32(offset.X, offset.Y);
Environment->addStaticText(wgettext("Backward"),
rect, false, true, this, -1);
//t->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_UPPERLEFT);
}
{
core::rect < s32 > rect(0, 0, 100, 30);
rect += topleft + v2s32(offset.X + 105, offset.Y - 5);
this->backward = Environment->addButton(rect, this,
GUI_ID_KEY_BACKWARD_BUTTON,
wgettext(key_backward.name()));
}
offset += v2s32(0, 25);
{
core::rect < s32 > rect(0, 0, 100, 20);
rect += topleft + v2s32(offset.X, offset.Y);
Environment->addStaticText(wgettext("Left"),
rect, false, true, this, -1);
//t->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_UPPERLEFT);
}
{
core::rect < s32 > rect(0, 0, 100, 30);
rect += topleft + v2s32(offset.X + 105, offset.Y - 5);
this->left = Environment->addButton(rect, this, GUI_ID_KEY_LEFT_BUTTON,
wgettext(key_left.name()));
}
offset += v2s32(0, 25);
{
core::rect < s32 > rect(0, 0, 100, 20);
rect += topleft + v2s32(offset.X, offset.Y);
Environment->addStaticText(wgettext("Right"),
rect, false, true, this, -1);
//t->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_UPPERLEFT);
}
{
core::rect < s32 > rect(0, 0, 100, 30);
rect += topleft + v2s32(offset.X + 105, offset.Y - 5);
this->right = Environment->addButton(rect, this,
GUI_ID_KEY_RIGHT_BUTTON,
wgettext(key_right.name()));
}
offset += v2s32(0, 25);
{
core::rect < s32 > rect(0, 0, 100, 20);
rect += topleft + v2s32(offset.X, offset.Y);
Environment->addStaticText(wgettext("Use"),
rect, false, true, this, -1);
//t->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_UPPERLEFT);
}
{
core::rect < s32 > rect(0, 0, 100, 30);
rect += topleft + v2s32(offset.X + 105, offset.Y - 5);
this->use = Environment->addButton(rect, this, GUI_ID_KEY_USE_BUTTON,
wgettext(key_use.name()));
}
offset += v2s32(0, 25);
{
core::rect < s32 > rect(0, 0, 100, 20);
rect += topleft + v2s32(offset.X, offset.Y);
Environment->addStaticText(wgettext("Sneak"),
rect, false, true, this, -1);
//t->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_UPPERLEFT);
}
{
core::rect < s32 > rect(0, 0, 100, 30);
rect += topleft + v2s32(offset.X + 105, offset.Y - 5);
this->sneak = Environment->addButton(rect, this,
GUI_ID_KEY_SNEAK_BUTTON,
wgettext(key_sneak.name()));
}
offset += v2s32(0, 25);
{
core::rect < s32 > rect(0, 0, 100, 20);
rect += topleft + v2s32(offset.X, offset.Y);
Environment->addStaticText(wgettext("Jump"), rect, false, true, this, -1);
//t->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_UPPERLEFT);
}
{
core::rect < s32 > rect(0, 0, 100, 30);
rect += topleft + v2s32(offset.X + 105, offset.Y - 5);
this->jump = Environment->addButton(rect, this, GUI_ID_KEY_JUMP_BUTTON,
wgettext(key_jump.name()));
}
offset += v2s32(0, 25);
{
core::rect < s32 > rect(0, 0, 100, 20);
rect += topleft + v2s32(offset.X, offset.Y);
Environment->addStaticText(wgettext("Drop"), rect, false, true, this, -1);
//t->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_UPPERLEFT);
}
{
core::rect < s32 > rect(0, 0, 100, 30);
rect += topleft + v2s32(offset.X + 105, offset.Y - 5);
this->dropbtn = Environment->addButton(rect, this, GUI_ID_KEY_DROP_BUTTON,
wgettext(key_drop.name()));
}
offset += v2s32(0, 25);
{
core::rect < s32 > rect(0, 0, 100, 20);
rect += topleft + v2s32(offset.X, offset.Y);
Environment->addStaticText(wgettext("Inventory"),
rect, false, true, this, -1);
//t->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_UPPERLEFT);
}
{
core::rect < s32 > rect(0, 0, 100, 30);
rect += topleft + v2s32(offset.X + 105, offset.Y - 5);
this->inventory = Environment->addButton(rect, this,
GUI_ID_KEY_INVENTORY_BUTTON,
wgettext(key_inventory.name()));
}
offset += v2s32(0, 25);
{
core::rect < s32 > rect(0, 0, 100, 20);
rect += topleft + v2s32(offset.X, offset.Y);
Environment->addStaticText(wgettext("Chat"), rect, false, true, this, -1);
//t->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_UPPERLEFT);
}
{
core::rect < s32 > rect(0, 0, 100, 30);
rect += topleft + v2s32(offset.X + 105, offset.Y - 5);
this->chat = Environment->addButton(rect, this, GUI_ID_KEY_CHAT_BUTTON,
wgettext(key_chat.name()));
}
offset += v2s32(0, 25);
{
core::rect < s32 > rect(0, 0, 100, 20);
rect += topleft + v2s32(offset.X, offset.Y);
Environment->addStaticText(wgettext("Command"), rect, false, true, this, -1);
//t->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_UPPERLEFT);
}
{
core::rect < s32 > rect(0, 0, 100, 30);
rect += topleft + v2s32(offset.X + 105, offset.Y - 5);
this->cmd = Environment->addButton(rect, this, GUI_ID_KEY_CMD_BUTTON,
wgettext(key_cmd.name()));
}
offset += v2s32(0, 25);
{
core::rect < s32 > rect(0, 0, 100, 20);
rect += topleft + v2s32(offset.X, offset.Y);
Environment->addStaticText(wgettext("Console"), rect, false, true, this, -1);
//t->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_UPPERLEFT);
}
{
core::rect < s32 > rect(0, 0, 100, 30);
rect += topleft + v2s32(offset.X + 105, offset.Y - 5);
this->console = Environment->addButton(rect, this, GUI_ID_KEY_CONSOLE_BUTTON,
wgettext(key_console.name()));
}
//next col
offset = v2s32(250, 60);
{
core::rect < s32 > rect(0, 0, 100, 20);
rect += topleft + v2s32(offset.X, offset.Y);
Environment->addStaticText(wgettext("Toggle fly"),
rect, false, true, this, -1);
//t->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_UPPERLEFT);
}
{
core::rect < s32 > rect(0, 0, 100, 30);
rect += topleft + v2s32(offset.X + 105, offset.Y - 5);
this->fly = Environment->addButton(rect, this, GUI_ID_KEY_FLY_BUTTON,
wgettext(key_fly.name()));
}
offset += v2s32(0, 25);
{
core::rect < s32 > rect(0, 0, 100, 20);
rect += topleft + v2s32(offset.X, offset.Y);
Environment->addStaticText(wgettext("Toggle fast"),
rect, false, true, this, -1);
//t->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_UPPERLEFT);
}
{
core::rect < s32 > rect(0, 0, 100, 30);
rect += topleft + v2s32(offset.X + 105, offset.Y - 5);
this->fast = Environment->addButton(rect, this, GUI_ID_KEY_FAST_BUTTON,
wgettext(key_fast.name()));
}
offset += v2s32(0, 25);
{
core::rect < s32 > rect(0, 0, 100, 20);
rect += topleft + v2s32(offset.X, offset.Y);
Environment->addStaticText(wgettext("Range select"),
rect, false, true, this, -1);
//t->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_UPPERLEFT);
}
{
core::rect < s32 > rect(0, 0, 100, 30);
rect += topleft + v2s32(offset.X + 105, offset.Y - 5);
this->range = Environment->addButton(rect, this,
GUI_ID_KEY_RANGE_BUTTON,
wgettext(key_range.name()));
}
offset += v2s32(0, 25);
{
core::rect < s32 > rect(0, 0, 100, 20);
rect += topleft + v2s32(offset.X, offset.Y);
Environment->addStaticText(wgettext("Print stacks"),
rect, false, true, this, -1);
//t->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_UPPERLEFT);
}
{
core::rect < s32 > rect(0, 0, 100, 30);
rect += topleft + v2s32(offset.X + 105, offset.Y - 5);
this->dump = Environment->addButton(rect, this, GUI_ID_KEY_DUMP_BUTTON,
wgettext(key_dump.name()));
}
{ {
core::rect < s32 > rect(0, 0, 100, 30); core::rect < s32 > rect(0, 0, 100, 30);
rect += topleft + v2s32(size.X - 100 - 20, size.Y - 40); rect += topleft + v2s32(size.X - 100 - 20, size.Y - 40);
Environment->addButton(rect, this, GUI_ID_BACK_BUTTON, Environment->addButton(rect, this, GUI_ID_BACK_BUTTON,
wgettext("Save")); wgettext("Save"));
} }
{ {
core::rect < s32 > rect(0, 0, 100, 30); core::rect < s32 > rect(0, 0, 100, 30);
rect += topleft + v2s32(size.X - 100 - 20 - 100 - 20, size.Y - 40); rect += topleft + v2s32(size.X - 100 - 20 - 100 - 20, size.Y - 40);
Environment->addButton(rect, this, GUI_ID_ABORT_BUTTON, Environment->addButton(rect, this, GUI_ID_ABORT_BUTTON,
wgettext("Cancel")); wgettext("Cancel"));
} }
changeCtype("C"); changeCtype("C");
} }
void GUIKeyChangeMenu::drawMenu() void GUIKeyChangeMenu::drawMenu()
@ -396,102 +186,32 @@ void GUIKeyChangeMenu::drawMenu()
bool GUIKeyChangeMenu::acceptInput() bool GUIKeyChangeMenu::acceptInput()
{ {
g_settings->set("keymap_forward", key_forward.sym()); for(size_t i = 0; i < key_settings.size(); i++)
g_settings->set("keymap_backward", key_backward.sym()); {
g_settings->set("keymap_left", key_left.sym()); key_setting *k = key_settings.at(i);
g_settings->set("keymap_right", key_right.sym()); g_settings->set(k->setting_name, k->key.sym());
g_settings->set("keymap_jump", key_jump.sym()); }
g_settings->set("keymap_sneak", key_sneak.sym()); {
g_settings->set("keymap_drop", key_drop.sym()); gui::IGUIElement *e = getElementFromId(GUI_ID_CB_AUX1_DESCENDS);
g_settings->set("keymap_inventory", key_inventory.sym()); if(e != NULL && e->getType() == gui::EGUIET_CHECK_BOX)
g_settings->set("keymap_chat", key_chat.sym()); g_settings->setBool("aux1_descends", ((gui::IGUICheckBox*)e)->isChecked());
g_settings->set("keymap_cmd", key_cmd.sym()); }
g_settings->set("keymap_console", key_console.sym());
g_settings->set("keymap_rangeselect", key_range.sym());
g_settings->set("keymap_freemove", key_fly.sym());
g_settings->set("keymap_fastmove", key_fast.sym());
g_settings->set("keymap_special1", key_use.sym());
g_settings->set("keymap_print_debug_stacks", key_dump.sym());
clearKeyCache(); clearKeyCache();
return true; return true;
} }
void GUIKeyChangeMenu::init_keys()
{
key_forward = getKeySetting("keymap_forward");
key_backward = getKeySetting("keymap_backward");
key_left = getKeySetting("keymap_left");
key_right = getKeySetting("keymap_right");
key_jump = getKeySetting("keymap_jump");
key_sneak = getKeySetting("keymap_sneak");
key_drop = getKeySetting("keymap_drop");
key_inventory = getKeySetting("keymap_inventory");
key_chat = getKeySetting("keymap_chat");
key_cmd = getKeySetting("keymap_cmd");
key_console = getKeySetting("keymap_console");
key_range = getKeySetting("keymap_rangeselect");
key_fly = getKeySetting("keymap_freemove");
key_fast = getKeySetting("keymap_fastmove");
key_use = getKeySetting("keymap_special1");
key_dump = getKeySetting("keymap_print_debug_stacks");
}
bool GUIKeyChangeMenu::resetMenu() bool GUIKeyChangeMenu::resetMenu()
{ {
if (activeKey >= 0) if (activeKey >= 0)
{ {
switch (activeKey) for(size_t i = 0; i < key_settings.size(); i++)
{ {
case GUI_ID_KEY_FORWARD_BUTTON: key_setting *k = key_settings.at(i);
this->forward->setText( if(k->id == activeKey)
wgettext(key_forward.name())); {
break; k->button->setText(wgettext(k->key.name()));
case GUI_ID_KEY_BACKWARD_BUTTON: break;
this->backward->setText( }
wgettext(key_backward.name()));
break;
case GUI_ID_KEY_LEFT_BUTTON:
this->left->setText(wgettext(key_left.name()));
break;
case GUI_ID_KEY_RIGHT_BUTTON:
this->right->setText(wgettext(key_right.name()));
break;
case GUI_ID_KEY_JUMP_BUTTON:
this->jump->setText(wgettext(key_jump.name()));
break;
case GUI_ID_KEY_SNEAK_BUTTON:
this->sneak->setText(wgettext(key_sneak.name()));
break;
case GUI_ID_KEY_DROP_BUTTON:
this->dropbtn->setText(wgettext(key_drop.name()));
break;
case GUI_ID_KEY_INVENTORY_BUTTON:
this->inventory->setText(
wgettext(key_inventory.name()));
break;
case GUI_ID_KEY_CHAT_BUTTON:
this->chat->setText(wgettext(key_chat.name()));
break;
case GUI_ID_KEY_CMD_BUTTON:
this->cmd->setText(wgettext(key_cmd.name()));
break;
case GUI_ID_KEY_CONSOLE_BUTTON:
this->console->setText(wgettext(key_console.name()));
break;
case GUI_ID_KEY_RANGE_BUTTON:
this->range->setText(wgettext(key_range.name()));
break;
case GUI_ID_KEY_FLY_BUTTON:
this->fly->setText(wgettext(key_fly.name()));
break;
case GUI_ID_KEY_FAST_BUTTON:
this->fast->setText(wgettext(key_fast.name()));
break;
case GUI_ID_KEY_USE_BUTTON:
this->use->setText(wgettext(key_use.name()));
break;
case GUI_ID_KEY_DUMP_BUTTON:
this->dump->setText(wgettext(key_dump.name()));
break;
} }
activeKey = -1; activeKey = -1;
return false; return false;
@ -501,104 +221,72 @@ bool GUIKeyChangeMenu::resetMenu()
bool GUIKeyChangeMenu::OnEvent(const SEvent& event) bool GUIKeyChangeMenu::OnEvent(const SEvent& event)
{ {
if (event.EventType == EET_KEY_INPUT_EVENT && activeKey >= 0 if (event.EventType == EET_KEY_INPUT_EVENT && activeKey >= 0
&& event.KeyInput.PressedDown) && event.KeyInput.PressedDown)
{ {
changeCtype(""); changeCtype("");
KeyPress kp(event.KeyInput); bool prefer_character = shift_down;
KeyPress kp(event.KeyInput, prefer_character);
bool shift_went_down = false;
if(!shift_down &&
(event.KeyInput.Key == irr::KEY_SHIFT ||
event.KeyInput.Key == irr::KEY_LSHIFT ||
event.KeyInput.Key == irr::KEY_RSHIFT))
shift_went_down = true;
if (activeKey == GUI_ID_KEY_FORWARD_BUTTON) // Remove Key already in use message
if(this->key_used_text)
{ {
this->forward->setText(wgettext(kp.name())); this->key_used_text->remove();
this->key_forward = kp; this->key_used_text = NULL;
} }
else if (activeKey == GUI_ID_KEY_BACKWARD_BUTTON) // Display Key already in use message
if (std::find(this->key_used.begin(), this->key_used.end(), kp) != this->key_used.end())
{ {
this->backward->setText(wgettext(kp.name())); core::rect < s32 > rect(0, 0, 600, 40);
this->key_backward = kp; rect += v2s32(0, 0) + v2s32(25, 30);
this->key_used_text = Environment->addStaticText(wgettext("Key already in use"),
rect, false, true, this, -1);
//infostream << "Key already in use" << std::endl;
} }
else if (activeKey == GUI_ID_KEY_LEFT_BUTTON)
// But go on
{ {
this->left->setText(wgettext(kp.name())); key_setting *k=NULL;
this->key_left = kp; for(size_t i = 0; i < key_settings.size(); i++)
{
if(key_settings.at(i)->id == activeKey)
{
k = key_settings.at(i);
break;
}
}
assert(k);
k->key = kp;
k->button->setText(wgettext(k->key.name()));
this->key_used.push_back(kp);
changeCtype("C");
// Allow characters made with shift
if(shift_went_down){
shift_down = true;
return false;
}else{
activeKey = -1;
return true;
}
} }
else if (activeKey == GUI_ID_KEY_RIGHT_BUTTON)
{
this->right->setText(wgettext(kp.name()));
this->key_right = kp;
}
else if (activeKey == GUI_ID_KEY_JUMP_BUTTON)
{
this->jump->setText(wgettext(kp.name()));
this->key_jump = kp;
}
else if (activeKey == GUI_ID_KEY_SNEAK_BUTTON)
{
this->sneak->setText(wgettext(kp.name()));
this->key_sneak = kp;
}
else if (activeKey == GUI_ID_KEY_DROP_BUTTON)
{
this->dropbtn->setText(wgettext(kp.name()));
this->key_drop = kp;
}
else if (activeKey == GUI_ID_KEY_INVENTORY_BUTTON)
{
this->inventory->setText(wgettext(kp.name()));
this->key_inventory = kp;
}
else if (activeKey == GUI_ID_KEY_CHAT_BUTTON)
{
this->chat->setText(wgettext(kp.name()));
this->key_chat = kp;
}
else if (activeKey == GUI_ID_KEY_CMD_BUTTON)
{
this->cmd->setText(wgettext(kp.name()));
this->key_cmd = kp;
}
else if (activeKey == GUI_ID_KEY_CONSOLE_BUTTON)
{
this->console->setText(wgettext(kp.name()));
this->key_console = kp;
}
else if (activeKey == GUI_ID_KEY_RANGE_BUTTON)
{
this->range->setText(wgettext(kp.name()));
this->key_range = kp;
}
else if (activeKey == GUI_ID_KEY_FLY_BUTTON)
{
this->fly->setText(wgettext(kp.name()));
this->key_fly = kp;
}
else if (activeKey == GUI_ID_KEY_FAST_BUTTON)
{
this->fast->setText(wgettext(kp.name()));
this->key_fast = kp;
}
else if (activeKey == GUI_ID_KEY_USE_BUTTON)
{
this->use->setText(wgettext(kp.name()));
this->key_use = kp;
}
else if (activeKey == GUI_ID_KEY_DUMP_BUTTON)
{
this->dump->setText(wgettext(kp.name()));
this->key_dump = kp;
}
changeCtype("C");
activeKey = -1;
return true;
} }
if (event.EventType == EET_GUI_EVENT) if (event.EventType == EET_GUI_EVENT)
{ {
if (event.GUIEvent.EventType == gui::EGET_ELEMENT_FOCUS_LOST if (event.GUIEvent.EventType == gui::EGET_ELEMENT_FOCUS_LOST
&& isVisible()) && isVisible())
{ {
if (!canTakeFocus(event.GUIEvent.Element)) if (!canTakeFocus(event.GUIEvent.Element))
{ {
dstream << "GUIMainMenu: Not allowing focus change." dstream << "GUIMainMenu: Not allowing focus change."
<< std::endl; << std::endl;
// Returning true disables focus change // Returning true disables focus change
return true; return true;
} }
@ -606,106 +294,74 @@ bool GUIKeyChangeMenu::OnEvent(const SEvent& event)
if (event.GUIEvent.EventType == gui::EGET_BUTTON_CLICKED) if (event.GUIEvent.EventType == gui::EGET_BUTTON_CLICKED)
{ {
if(event.GUIEvent.Caller->getID() != GUI_ID_BACK_BUTTON && if(event.GUIEvent.Caller->getID() != GUI_ID_BACK_BUTTON &&
event.GUIEvent.Caller->getID() != GUI_ID_ABORT_BUTTON) event.GUIEvent.Caller->getID() != GUI_ID_ABORT_BUTTON)
{ {
changeCtype(""); changeCtype("");
} }
switch (event.GUIEvent.Caller->getID()) switch (event.GUIEvent.Caller->getID())
{ {
case GUI_ID_BACK_BUTTON: //back case GUI_ID_BACK_BUTTON: //back
acceptInput(); acceptInput();
quitMenu(); quitMenu();
return true; return true;
case GUI_ID_ABORT_BUTTON: //abort case GUI_ID_ABORT_BUTTON: //abort
quitMenu(); quitMenu();
return true; return true;
case GUI_ID_KEY_FORWARD_BUTTON: default:
resetMenu(); key_setting *k = NULL;
activeKey = event.GUIEvent.Caller->getID(); for(size_t i = 0; i < key_settings.size(); i++)
this->forward->setText(wgettext("press Key")); {
break; if(key_settings.at(i)->id == event.GUIEvent.Caller->getID())
case GUI_ID_KEY_BACKWARD_BUTTON: {
resetMenu(); k = key_settings.at(i);
activeKey = event.GUIEvent.Caller->getID(); break;
this->backward->setText(wgettext("press Key")); }
break; }
case GUI_ID_KEY_LEFT_BUTTON: assert(k);
resetMenu();
activeKey = event.GUIEvent.Caller->getID(); resetMenu();
this->left->setText(wgettext("press Key")); shift_down = false;
break; activeKey = event.GUIEvent.Caller->getID();
case GUI_ID_KEY_RIGHT_BUTTON: k->button->setText(wgettext("press key"));
resetMenu(); this->key_used.erase(std::remove(this->key_used.begin(),
activeKey = event.GUIEvent.Caller->getID(); this->key_used.end(), k->key), this->key_used.end());
this->right->setText(wgettext("press Key")); break;
break;
case GUI_ID_KEY_USE_BUTTON:
resetMenu();
activeKey = event.GUIEvent.Caller->getID();
this->use->setText(wgettext("press Key"));
break;
case GUI_ID_KEY_FLY_BUTTON:
resetMenu();
activeKey = event.GUIEvent.Caller->getID();
this->fly->setText(wgettext("press Key"));
break;
case GUI_ID_KEY_FAST_BUTTON:
resetMenu();
activeKey = event.GUIEvent.Caller->getID();
this->fast->setText(wgettext("press Key"));
break;
case GUI_ID_KEY_JUMP_BUTTON:
resetMenu();
activeKey = event.GUIEvent.Caller->getID();
this->jump->setText(wgettext("press Key"));
break;
case GUI_ID_KEY_DROP_BUTTON:
resetMenu();
activeKey = event.GUIEvent.Caller->getID();
this->dropbtn->setText(wgettext("press Key"));
break;
case GUI_ID_KEY_CHAT_BUTTON:
resetMenu();
activeKey = event.GUIEvent.Caller->getID();
this->chat->setText(wgettext("press Key"));
break;
case GUI_ID_KEY_CMD_BUTTON:
resetMenu();
activeKey = event.GUIEvent.Caller->getID();
this->cmd->setText(wgettext("press Key"));
break;
case GUI_ID_KEY_CONSOLE_BUTTON:
resetMenu();
activeKey = event.GUIEvent.Caller->getID();
this->console->setText(wgettext("press Key"));
break;
case GUI_ID_KEY_SNEAK_BUTTON:
resetMenu();
activeKey = event.GUIEvent.Caller->getID();
this->sneak->setText(wgettext("press Key"));
break;
case GUI_ID_KEY_INVENTORY_BUTTON:
resetMenu();
activeKey = event.GUIEvent.Caller->getID();
this->inventory->setText(wgettext("press Key"));
break;
case GUI_ID_KEY_DUMP_BUTTON:
resetMenu();
activeKey = event.GUIEvent.Caller->getID();
this->dump->setText(wgettext("press Key"));
break;
case GUI_ID_KEY_RANGE_BUTTON:
resetMenu();
activeKey = event.GUIEvent.Caller->getID();
this->range->setText(wgettext("press Key"));
break;
} }
Environment->setFocus(this);
//Buttons //Buttons
changeCtype("C"); changeCtype("C");
} }
} }
return Parent ? Parent->OnEvent(event) : false; return Parent ? Parent->OnEvent(event) : false;
} }
void GUIKeyChangeMenu::add_key(int id, std::string button_name, std::string setting_name)
{
key_setting *k = new key_setting;
k->id = id;
k->button_name = wgettext(button_name.c_str());
k->setting_name = setting_name;
k->key = getKeySetting(k->setting_name.c_str());
key_settings.push_back(k);
}
void GUIKeyChangeMenu::init_keys()
{
this->add_key(GUI_ID_KEY_FORWARD_BUTTON, "Forward", "keymap_forward");
this->add_key(GUI_ID_KEY_BACKWARD_BUTTON, "Backward", "keymap_backward");
this->add_key(GUI_ID_KEY_LEFT_BUTTON, "Left", "keymap_left");
this->add_key(GUI_ID_KEY_RIGHT_BUTTON, "Right", "keymap_right");
this->add_key(GUI_ID_KEY_USE_BUTTON, "Use", "keymap_special1");
this->add_key(GUI_ID_KEY_JUMP_BUTTON, "Jump", "keymap_jump");
this->add_key(GUI_ID_KEY_SNEAK_BUTTON, "Sneak", "keymap_sneak");
this->add_key(GUI_ID_KEY_DROP_BUTTON, "Drop", "keymap_drop");
this->add_key(GUI_ID_KEY_INVENTORY_BUTTON, "Inventory", "keymap_inventory");
this->add_key(GUI_ID_KEY_CHAT_BUTTON, "Chat", "keymap_chat");
this->add_key(GUI_ID_KEY_CMD_BUTTON, "Command", "keymap_cmd");
this->add_key(GUI_ID_KEY_CONSOLE_BUTTON, "Console", "keymap_console");
this->add_key(GUI_ID_KEY_FLY_BUTTON, "Toggle fly", "keymap_freemove");
this->add_key(GUI_ID_KEY_FAST_BUTTON, "Toggle fast", "keymap_fastmove");
this->add_key(GUI_ID_KEY_RANGE_BUTTON, "Range select", "keymap_rangeselect");
this->add_key(GUI_ID_KEY_DUMP_BUTTON, "Print stacks", "keymap_print_debug_stacks");
}

View File

@ -28,6 +28,16 @@
#include "gettext.h" #include "gettext.h"
#include "keycode.h" #include "keycode.h"
#include <string> #include <string>
#include <vector>
typedef struct {
int id;
wchar_t *button_name;
KeyPress key;
std::string setting_name;
gui::IGUIButton *button;
} key_setting;
class GUIKeyChangeMenu: public GUIModalMenu class GUIKeyChangeMenu: public GUIModalMenu
{ {
@ -54,40 +64,15 @@ private:
bool resetMenu(); bool resetMenu();
gui::IGUIButton *forward; void add_key(int id, std::string setting_name, std::string button_name);
gui::IGUIButton *backward;
gui::IGUIButton *left;
gui::IGUIButton *right;
gui::IGUIButton *use;
gui::IGUIButton *sneak;
gui::IGUIButton *jump;
gui::IGUIButton *dropbtn;
gui::IGUIButton *inventory;
gui::IGUIButton *fly;
gui::IGUIButton *fast;
gui::IGUIButton *range;
gui::IGUIButton *dump;
gui::IGUIButton *chat;
gui::IGUIButton *cmd;
gui::IGUIButton *console;
bool shift_down;
s32 activeKey; s32 activeKey;
KeyPress key_forward;
KeyPress key_backward; std::vector<KeyPress> key_used;
KeyPress key_left; gui::IGUIStaticText *key_used_text;
KeyPress key_right; std::vector<key_setting *> key_settings;
KeyPress key_use;
KeyPress key_sneak;
KeyPress key_jump;
KeyPress key_drop;
KeyPress key_inventory;
KeyPress key_fly;
KeyPress key_fast;
KeyPress key_range;
KeyPress key_chat;
KeyPress key_cmd;
KeyPress key_console;
KeyPress key_dump;
}; };
#endif #endif

View File

@ -39,6 +39,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "tile.h" // getTexturePath #include "tile.h" // getTexturePath
#include "filesys.h" #include "filesys.h"
#include "util/string.h" #include "util/string.h"
#include "subgame.h"
struct CreateWorldDestMainMenu : public CreateWorldDest struct CreateWorldDestMainMenu : public CreateWorldDest
{ {
@ -47,6 +48,22 @@ struct CreateWorldDestMainMenu : public CreateWorldDest
{} {}
void accepted(std::wstring name, std::string gameid) void accepted(std::wstring name, std::string gameid)
{ {
std::string name_narrow = wide_to_narrow(name);
if(!string_allowed_blacklist(name_narrow, WORLDNAME_BLACKLISTED_CHARS))
{
m_menu->displayMessageMenu(wgettext("Cannot create world: Name contains invalid characters"));
return;
}
std::vector<WorldSpec> worlds = getAvailableWorlds();
for(std::vector<WorldSpec>::iterator i = worlds.begin();
i != worlds.end(); i++)
{
if((*i).name == name_narrow)
{
m_menu->displayMessageMenu(wgettext("Cannot create world: A world by this name already exists"));
return;
}
}
m_menu->createNewWorld(name, gameid); m_menu->createNewWorld(name, gameid);
} }
GUIMainMenu *m_menu; GUIMainMenu *m_menu;
@ -934,3 +951,7 @@ int GUIMainMenu::getTab()
return TAB_SINGLEPLAYER; // Default return TAB_SINGLEPLAYER; // Default
} }
void GUIMainMenu::displayMessageMenu(std::wstring msg)
{
(new GUIMessageMenu(env, parent, -1, menumgr, msg))->drop();
}

View File

@ -92,6 +92,7 @@ public:
void createNewWorld(std::wstring name, std::string gameid); void createNewWorld(std::wstring name, std::string gameid);
void deleteWorld(const std::vector<std::string> &paths); void deleteWorld(const std::vector<std::string> &paths);
int getTab(); int getTab();
void displayMessageMenu(std::wstring msg);
private: private:
MainMenuData *m_data; MainMenuData *m_data;

View File

@ -431,6 +431,7 @@ InventoryList::InventoryList(std::string name, u32 size, IItemDefManager *itemde
{ {
m_name = name; m_name = name;
m_size = size; m_size = size;
m_width = 0;
m_itemdef = itemdef; m_itemdef = itemdef;
clearItems(); clearItems();
//m_dirty = false; //m_dirty = false;
@ -459,6 +460,11 @@ void InventoryList::setSize(u32 newsize)
m_size = newsize; m_size = newsize;
} }
void InventoryList::setWidth(u32 newwidth)
{
m_width = newwidth;
}
void InventoryList::setName(const std::string &name) void InventoryList::setName(const std::string &name)
{ {
m_name = name; m_name = name;
@ -468,6 +474,8 @@ void InventoryList::serialize(std::ostream &os) const
{ {
//os.imbue(std::locale("C")); //os.imbue(std::locale("C"));
os<<"Width "<<m_width<<"\n";
for(u32 i=0; i<m_items.size(); i++) for(u32 i=0; i<m_items.size(); i++)
{ {
const ItemStack &item = m_items[i]; const ItemStack &item = m_items[i];
@ -492,6 +500,7 @@ void InventoryList::deSerialize(std::istream &is)
clearItems(); clearItems();
u32 item_i = 0; u32 item_i = 0;
m_width = 0;
for(;;) for(;;)
{ {
@ -513,6 +522,12 @@ void InventoryList::deSerialize(std::istream &is)
{ {
break; break;
} }
else if(name == "Width")
{
iss >> m_width;
if (iss.fail())
throw SerializationError("incorrect width property");
}
else if(name == "Item") else if(name == "Item")
{ {
if(item_i > getSize() - 1) if(item_i > getSize() - 1)
@ -527,10 +542,6 @@ void InventoryList::deSerialize(std::istream &is)
throw SerializationError("too many items"); throw SerializationError("too many items");
m_items[item_i++].clear(); m_items[item_i++].clear();
} }
else
{
throw SerializationError("Unknown inventory identifier");
}
} }
} }
@ -543,6 +554,7 @@ InventoryList & InventoryList::operator = (const InventoryList &other)
{ {
m_items = other.m_items; m_items = other.m_items;
m_size = other.m_size; m_size = other.m_size;
m_width = other.m_width;
m_name = other.m_name; m_name = other.m_name;
m_itemdef = other.m_itemdef; m_itemdef = other.m_itemdef;
//setDirty(true); //setDirty(true);
@ -560,6 +572,11 @@ u32 InventoryList::getSize() const
return m_items.size(); return m_items.size();
} }
u32 InventoryList::getWidth() const
{
return m_width;
}
u32 InventoryList::getUsedSlots() const u32 InventoryList::getUsedSlots() const
{ {
u32 num = 0; u32 num = 0;
@ -886,10 +903,6 @@ void Inventory::deSerialize(std::istream &is)
m_lists.push_back(list); m_lists.push_back(list);
} }
else
{
throw SerializationError("Unknown inventory identifier");
}
} }
} }

View File

@ -176,6 +176,7 @@ public:
~InventoryList(); ~InventoryList();
void clearItems(); void clearItems();
void setSize(u32 newsize); void setSize(u32 newsize);
void setWidth(u32 newWidth);
void setName(const std::string &name); void setName(const std::string &name);
void serialize(std::ostream &os) const; void serialize(std::ostream &os) const;
void deSerialize(std::istream &is); void deSerialize(std::istream &is);
@ -185,6 +186,7 @@ public:
const std::string &getName() const; const std::string &getName() const;
u32 getSize() const; u32 getSize() const;
u32 getWidth() const;
// Count used slots // Count used slots
u32 getUsedSlots() const; u32 getUsedSlots() const;
u32 getFreeSlots() const; u32 getFreeSlots() const;
@ -240,7 +242,7 @@ public:
private: private:
std::vector<ItemStack> m_items; std::vector<ItemStack> m_items;
u32 m_size; u32 m_size, m_width;
std::string m_name; std::string m_name;
IItemDefManager *m_itemdef; IItemDefManager *m_itemdef;
}; };

View File

@ -332,6 +332,18 @@ void IMoveAction::apply(InventoryManager *mgr, ServerActiveObject *player, IGame
// If source is infinite, reset it's stack // If source is infinite, reset it's stack
if(src_can_take_count == -1){ if(src_can_take_count == -1){
// If destination stack is of different type and there are leftover
// items, attempt to put the leftover items to a different place in the
// destination inventory.
// The client-side GUI will try to guess if this happens.
if(from_stack_was.name != to_stack_was.name){
for(u32 i=0; i<list_to->getSize(); i++){
if(list_to->getItem(i).empty()){
list_to->changeItem(i, to_stack_was);
break;
}
}
}
list_from->deleteItem(from_i); list_from->deleteItem(from_i);
list_from->addItem(from_i, from_stack_was); list_from->addItem(from_i, from_stack_was);
} }
@ -339,6 +351,8 @@ void IMoveAction::apply(InventoryManager *mgr, ServerActiveObject *player, IGame
if(dst_can_put_count == -1){ if(dst_can_put_count == -1){
list_to->deleteItem(to_i); list_to->deleteItem(to_i);
list_to->addItem(to_i, to_stack_was); list_to->addItem(to_i, to_stack_was);
list_from->deleteItem(from_i);
list_from->addItem(from_i, from_stack_was);
list_from->takeItem(from_i, count); list_from->takeItem(from_i, count);
} }
@ -769,18 +783,16 @@ bool getCraftingResult(Inventory *inv, ItemStack& result,
result.clear(); result.clear();
// TODO: Allow different sizes of crafting grids
// Get the InventoryList in which we will operate // Get the InventoryList in which we will operate
InventoryList *clist = inv->getList("craft"); InventoryList *clist = inv->getList("craft");
if(!clist || clist->getSize() != 9) if(!clist)
return false; return false;
// Mangle crafting grid to an another format // Mangle crafting grid to an another format
CraftInput ci; CraftInput ci;
ci.method = CRAFT_METHOD_NORMAL; ci.method = CRAFT_METHOD_NORMAL;
ci.width = 3; ci.width = clist->getWidth() ? clist->getWidth() : 3;
for(u16 i=0; i<9; i++) for(u16 i=0; i<clist->getSize(); i++)
ci.items.push_back(clist->getItem(i)); ci.items.push_back(clist->getItem(i));
// Find out what is crafted and add it to result item slot // Find out what is crafted and add it to result item slot
@ -793,7 +805,7 @@ bool getCraftingResult(Inventory *inv, ItemStack& result,
if(found && decrementInput) if(found && decrementInput)
{ {
// CraftInput has been changed, apply changes in clist // CraftInput has been changed, apply changes in clist
for(u16 i=0; i<9; i++) for(u16 i=0; i<clist->getSize(); i++)
{ {
clist->changeItem(i, ci.items[i]); clist->changeItem(i, ci.items[i]);
} }

View File

@ -66,6 +66,29 @@ struct InventoryLocation
name = name_; name = name_;
} }
bool operator==(const InventoryLocation &other) const
{
if(type != other.type)
return false;
switch(type){
case UNDEFINED:
return false;
case CURRENT_PLAYER:
return true;
case PLAYER:
return (name == other.name);
case NODEMETA:
return (p == other.p);
case DETACHED:
return (name == other.name);
}
return false;
}
bool operator!=(const InventoryLocation &other) const
{
return !(*this == other);
}
void applyCurrentPlayer(const std::string &name_) void applyCurrentPlayer(const std::string &name_)
{ {
if(type == CURRENT_PLAYER) if(type == CURRENT_PLAYER)

View File

@ -21,6 +21,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "main.h" // For g_settings #include "main.h" // For g_settings
#include "exceptions.h" #include "exceptions.h"
#include "settings.h" #include "settings.h"
#include "log.h"
#include "hex.h"
class UnknownKeycode : public BaseException class UnknownKeycode : public BaseException
{ {
@ -286,16 +288,30 @@ KeyPress::KeyPress(const char *name)
m_name = name[0]; m_name = name[0];
} }
KeyPress::KeyPress(const irr::SEvent::SKeyInput &in) KeyPress::KeyPress(const irr::SEvent::SKeyInput &in, bool prefer_character)
{ {
Key = in.Key; Key = in.Key;
Char = in.Char; Char = in.Char;
if(prefer_character){
m_name.resize(MB_CUR_MAX+1, '\0');
int written = wctomb(&m_name[0], Char);
if(written > 0){
infostream<<"KeyPress: Preferring character for "<<m_name<<std::endl;
Key = irr::KEY_KEY_CODES_COUNT;
return;
}
}
if (valid_kcode(Key)) { if (valid_kcode(Key)) {
m_name = KeyNames[Key]; m_name = KeyNames[Key];
} else { } else {
m_name.resize(MB_CUR_MAX+1, '\0'); m_name.resize(MB_CUR_MAX+1, '\0');
int written = wctomb(&m_name[0], Char); int written = wctomb(&m_name[0], Char);
assert (written >= 0 && "unexpected multibyte character"); if(written < 0){
std::string hexstr = hex_encode((const char*)&Char, sizeof(Char));
errorstream<<"KeyPress: Unexpected multibyte character "<<hexstr<<std::endl;
}
} }
} }

View File

@ -33,7 +33,7 @@ public:
KeyPress(); KeyPress();
KeyPress(const char *name); KeyPress(const char *name);
KeyPress(const irr::SEvent::SKeyInput &in); KeyPress(const irr::SEvent::SKeyInput &in, bool prefer_character=false);
bool operator==(const KeyPress &o) const bool operator==(const KeyPress &o) const
{ {

View File

@ -38,7 +38,8 @@ LocalPlayer::LocalPlayer(IGameDef *gamedef):
m_sneak_node_exists(false), m_sneak_node_exists(false),
m_old_node_below(32767,32767,32767), m_old_node_below(32767,32767,32767),
m_old_node_below_type("air"), m_old_node_below_type("air"),
m_need_to_get_new_sneak_node(true) m_need_to_get_new_sneak_node(true),
m_can_jump(false)
{ {
// Initialize hp to 0, so that no hearts will be shown if server // Initialize hp to 0, so that no hearts will be shown if server
// doesn't support health points // doesn't support health points
@ -149,13 +150,16 @@ void LocalPlayer::move(f32 dtime, Map &map, f32 pos_max_d,
position.X = rangelim(position.X, lwn_f.X-maxd, lwn_f.X+maxd); position.X = rangelim(position.X, lwn_f.X-maxd, lwn_f.X+maxd);
position.Z = rangelim(position.Z, lwn_f.Z-maxd, lwn_f.Z+maxd); position.Z = rangelim(position.Z, lwn_f.Z-maxd, lwn_f.Z+maxd);
f32 min_y = lwn_f.Y + 0.5*BS; if(!is_climbing)
if(position.Y < min_y)
{ {
position.Y = min_y; f32 min_y = lwn_f.Y + 0.5*BS;
if(position.Y < min_y)
{
position.Y = min_y;
if(m_speed.Y < 0) if(m_speed.Y < 0)
m_speed.Y = 0; m_speed.Y = 0;
}
} }
} }
@ -188,7 +192,7 @@ void LocalPlayer::move(f32 dtime, Map &map, f32 pos_max_d,
bool touching_ground_was = touching_ground; bool touching_ground_was = touching_ground;
touching_ground = result.touching_ground; touching_ground = result.touching_ground;
bool standing_on_unloaded = result.standing_on_unloaded; //bool standing_on_unloaded = result.standing_on_unloaded;
/* /*
Check the nodes under the player to see from which node the Check the nodes under the player to see from which node the
@ -281,18 +285,25 @@ void LocalPlayer::move(f32 dtime, Map &map, f32 pos_max_d,
/* /*
Report collisions Report collisions
*/ */
bool bouncy_jump = false;
if(collision_info) if(collision_info)
{ {
// Report fall collision for(size_t i=0; i<result.collisions.size(); i++){
if(old_speed.Y < m_speed.Y - 0.1 && !standing_on_unloaded) const CollisionInfo &info = result.collisions[i];
{
CollisionInfo info;
info.t = COLLISION_FALL;
info.speed = m_speed.Y - old_speed.Y;
collision_info->push_back(info); collision_info->push_back(info);
if(info.new_speed.Y - info.old_speed.Y > 0.1*BS &&
info.bouncy)
bouncy_jump = true;
} }
} }
if(bouncy_jump && control.jump){
m_speed.Y += 6.5*BS;
touching_ground = false;
MtEvent *e = new SimpleTriggerEvent("PlayerJump");
m_gamedef->event()->put(e);
}
if(!touching_ground_was && touching_ground){ if(!touching_ground_was && touching_ground){
MtEvent *e = new SimpleTriggerEvent("PlayerRegainGround"); MtEvent *e = new SimpleTriggerEvent("PlayerRegainGround");
m_gamedef->event()->put(e); m_gamedef->event()->put(e);
@ -314,6 +325,15 @@ void LocalPlayer::move(f32 dtime, Map &map, f32 pos_max_d,
*/ */
m_old_node_below = floatToInt(position - v3f(0,BS/2,0), BS); m_old_node_below = floatToInt(position - v3f(0,BS/2,0), BS);
m_old_node_below_type = nodemgr->get(map.getNodeNoEx(m_old_node_below)).name; m_old_node_below_type = nodemgr->get(map.getNodeNoEx(m_old_node_below)).name;
/*
Check properties of the node on which the player is standing
*/
const ContentFeatures &f = nodemgr->get(map.getNodeNoEx(getStandingNodePos()));
// Determine if jumping is possible
m_can_jump = touching_ground;
if(itemgroup_get(f.groups, "disable_jump"))
m_can_jump = false;
} }
void LocalPlayer::move(f32 dtime, Map &map, f32 pos_max_d) void LocalPlayer::move(f32 dtime, Map &map, f32 pos_max_d)
@ -359,31 +379,70 @@ void LocalPlayer::applyControl(float dtime)
if(free_move && fast_move) if(free_move && fast_move)
superspeed = true; superspeed = true;
// Auxiliary button 1 (E) // Old descend control
if(control.aux1) if(g_settings->getBool("aux1_descends"))
{ {
if(free_move) // Auxiliary button 1 (E)
if(control.aux1)
{ {
// In free movement mode, aux1 descends if(free_move)
v3f speed = getSpeed(); {
if(fast_move) // In free movement mode, aux1 descends
speed.Y = -20*BS; v3f speed = getSpeed();
if(fast_move)
speed.Y = -20*BS;
else
speed.Y = -walkspeed_max;
setSpeed(speed);
}
else if(is_climbing)
{
v3f speed = getSpeed();
speed.Y = -3*BS;
setSpeed(speed);
}
else else
speed.Y = -walkspeed_max; {
setSpeed(speed); // If not free movement but fast is allowed, aux1 is
// "Turbo button"
if(fast_move)
superspeed = true;
}
} }
else if(is_climbing) }
// New minecraft-like descend control
else
{
// Auxiliary button 1 (E)
if(control.aux1)
{ {
v3f speed = getSpeed(); if(!free_move && !is_climbing)
speed.Y = -3*BS; {
setSpeed(speed); // If not free movement but fast is allowed, aux1 is
// "Turbo button"
if(fast_move)
superspeed = true;
}
} }
else
if(control.sneak)
{ {
// If not free movement but fast is allowed, aux1 is if(free_move)
// "Turbo button" {
if(fast_move) // In free movement mode, sneak descends
superspeed = true; v3f speed = getSpeed();
if(fast_move)
speed.Y = -20*BS;
else
speed.Y = -walkspeed_max;
setSpeed(speed);
}
else if(is_climbing)
{
v3f speed = getSpeed();
speed.Y = -3*BS;
setSpeed(speed);
}
} }
} }
@ -420,7 +479,7 @@ void LocalPlayer::applyControl(float dtime)
speed.Y = walkspeed_max; speed.Y = walkspeed_max;
setSpeed(speed); setSpeed(speed);
} }
else if(touching_ground) else if(m_can_jump)
{ {
/* /*
NOTE: The d value in move() affects jump height by NOTE: The d value in move() affects jump height by

View File

@ -101,6 +101,7 @@ private:
std::string m_old_node_below_type; std::string m_old_node_below_type;
// Whether recalculation of the sneak node is needed // Whether recalculation of the sneak node is needed
bool m_need_to_get_new_sneak_node; bool m_need_to_get_new_sneak_node;
bool m_can_jump;
}; };
#endif #endif

View File

@ -1468,8 +1468,8 @@ void Map::timerUpdate(float dtime, float unload_timeout,
MapBlock *block = (*i); MapBlock *block = (*i);
block->incrementUsageTimer(dtime); block->incrementUsageTimer(dtime);
if(block->getUsageTimer() > unload_timeout) if(block->refGet() == 0 && block->getUsageTimer() > unload_timeout)
{ {
v3s16 p = block->getPos(); v3s16 p = block->getPos();

View File

@ -56,7 +56,8 @@ MapBlock::MapBlock(Map *parent, v3s16 pos, IGameDef *gamedef, bool dummy):
m_generated(false), m_generated(false),
m_timestamp(BLOCK_TIMESTAMP_UNDEFINED), m_timestamp(BLOCK_TIMESTAMP_UNDEFINED),
m_disk_timestamp(BLOCK_TIMESTAMP_UNDEFINED), m_disk_timestamp(BLOCK_TIMESTAMP_UNDEFINED),
m_usage_timer(0) m_usage_timer(0),
m_refcount(0)
{ {
data = NULL; data = NULL;
if(dummy == false) if(dummy == false)

View File

@ -430,6 +430,22 @@ public:
{ {
return m_usage_timer; return m_usage_timer;
} }
/*
See m_refcount
*/
void refGrab()
{
m_refcount++;
}
void refDrop()
{
m_refcount--;
}
int refGet()
{
return m_refcount;
}
/* /*
Node Timers Node Timers
@ -566,6 +582,12 @@ private:
Map will unload the block when this reaches a timeout. Map will unload the block when this reaches a timeout.
*/ */
float m_usage_timer; float m_usage_timer;
/*
Reference count; currently used for determining if this block is in
the list of blocks to be drawn.
*/
int m_refcount;
}; };
inline bool blockpos_over_limit(v3s16 p) inline bool blockpos_over_limit(v3s16 p)

View File

@ -1297,7 +1297,9 @@ BiomeType get_biome(u64 seed, v2s16 p2d)
double d = noise2d_perlin( double d = noise2d_perlin(
0.6+(float)p2d.X/250, 0.2+(float)p2d.Y/250, 0.6+(float)p2d.X/250, 0.2+(float)p2d.Y/250,
seed+9130, 3, 0.50); seed+9130, 3, 0.50);
if(d > 0.35) if(d > 0.45)
return BT_DESERT;
if(d > 0.35 && (noise2d( p2d.X, p2d.Y, int(seed) ) + 1.0) > ( 0.45 - d ) * 20.0 )
return BT_DESERT; return BT_DESERT;
return BT_NORMAL; return BT_NORMAL;
}; };
@ -1759,8 +1761,9 @@ void make_block(BlockMakeData *data)
vmanip.m_data[i] = airnode; vmanip.m_data[i] = airnode;
} }
} else { } else {
// Don't replace air or water or lava // Don't replace air or water or lava or ignore
if(vmanip.m_data[i].getContent() == CONTENT_AIR || if(vmanip.m_data[i].getContent() == CONTENT_IGNORE ||
vmanip.m_data[i].getContent() == CONTENT_AIR ||
vmanip.m_data[i].getContent() == c_water_source || vmanip.m_data[i].getContent() == c_water_source ||
vmanip.m_data[i].getContent() == c_lava_source) vmanip.m_data[i].getContent() == c_lava_source)
continue; continue;

View File

@ -556,8 +556,6 @@ public:
f->solidness = 0; f->solidness = 0;
} else { } else {
f->solidness = 1; f->solidness = 1;
if(f->alpha == 255)
f->solidness = 2;
f->backface_culling = false; f->backface_culling = false;
} }
break; break;

View File

@ -45,7 +45,8 @@ Player::Player(IGameDef *gamedef):
updateName("<not set>"); updateName("<not set>");
inventory.clear(); inventory.clear();
inventory.addList("main", PLAYER_INVENTORY_SIZE); inventory.addList("main", PLAYER_INVENTORY_SIZE);
inventory.addList("craft", 9); InventoryList *craft = inventory.addList("craft", 9);
craft->setWidth(3);
inventory.addList("craftpreview", 1); inventory.addList("craftpreview", 1);
inventory.addList("craftresult", 1); inventory.addList("craftresult", 1);

View File

@ -1847,6 +1847,20 @@ private:
return 1; return 1;
} }
// get_width(self, listname)
static int l_get_width(lua_State *L)
{
InvRef *ref = checkobject(L, 1);
const char *listname = luaL_checkstring(L, 2);
InventoryList *list = getlist(L, ref, listname);
if(list){
lua_pushinteger(L, list->getWidth());
} else {
lua_pushinteger(L, 0);
}
return 1;
}
// set_size(self, listname, size) // set_size(self, listname, size)
static int l_set_size(lua_State *L) static int l_set_size(lua_State *L)
{ {
@ -1869,6 +1883,23 @@ private:
return 0; return 0;
} }
// set_width(self, listname, size)
static int l_set_width(lua_State *L)
{
InvRef *ref = checkobject(L, 1);
const char *listname = luaL_checkstring(L, 2);
int newwidth = luaL_checknumber(L, 3);
Inventory *inv = getinv(L, ref);
InventoryList *list = inv->getList(listname);
if(list){
list->setWidth(newwidth);
} else {
return 0;
}
reportInventoryChange(L, ref);
return 0;
}
// get_stack(self, listname, i) -> itemstack // get_stack(self, listname, i) -> itemstack
static int l_get_stack(lua_State *L) static int l_get_stack(lua_State *L)
{ {
@ -2062,6 +2093,8 @@ const luaL_reg InvRef::methods[] = {
method(InvRef, is_empty), method(InvRef, is_empty),
method(InvRef, get_size), method(InvRef, get_size),
method(InvRef, set_size), method(InvRef, set_size),
method(InvRef, get_width),
method(InvRef, set_width),
method(InvRef, get_stack), method(InvRef, get_stack),
method(InvRef, set_stack), method(InvRef, set_stack),
method(InvRef, get_list), method(InvRef, get_list),

View File

@ -442,9 +442,12 @@ void RemoteClient::GetNextBlocks(Server *server, float dtime,
m_nearest_unsent_reset_timer += dtime; m_nearest_unsent_reset_timer += dtime;
if(m_nothing_to_send_pause_timer >= 0) if(m_nothing_to_send_pause_timer >= 0)
{
return; return;
}
Player *player = server->m_env->getPlayer(peer_id);
// This can happen sometimes; clients and players are not in perfect sync.
if(player == NULL)
return;
// Won't send anything if already sending // Won't send anything if already sending
if(m_blocks_sending.size() >= g_settings->getU16 if(m_blocks_sending.size() >= g_settings->getU16
@ -456,10 +459,6 @@ void RemoteClient::GetNextBlocks(Server *server, float dtime,
//TimeTaker timer("RemoteClient::GetNextBlocks"); //TimeTaker timer("RemoteClient::GetNextBlocks");
Player *player = server->m_env->getPlayer(peer_id);
assert(player != NULL);
v3f playerpos = player->getPosition(); v3f playerpos = player->getPosition();
v3f playerspeed = player->getSpeed(); v3f playerspeed = player->getSpeed();
v3f playerspeeddir(0,0,0); v3f playerspeeddir(0,0,0);

View File

@ -30,7 +30,7 @@ with this program; ifnot, write to the Free Software Foundation, Inc.,
#elif defined(__APPLE__) #elif defined(__APPLE__)
#include <OpenAL/al.h> #include <OpenAL/al.h>
#include <OpenAL/alc.h> #include <OpenAL/alc.h>
#include <OpenAL/alext.h> //#include <OpenAL/alext.h>
#else #else
#include <AL/al.h> #include <AL/al.h>
#include <AL/alc.h> #include <AL/alc.h>

View File

@ -24,6 +24,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include <set> #include <set>
#include <vector> #include <vector>
#define WORLDNAME_BLACKLISTED_CHARS "/\\"
struct SubgameSpec struct SubgameSpec
{ {
std::string id; // "" = game does not exist std::string id; // "" = game does not exist

View File

@ -698,6 +698,7 @@ struct TestInventory: public TestBase
{ {
std::string serialized_inventory = std::string serialized_inventory =
"List 0 32\n" "List 0 32\n"
"Width 3\n"
"Empty\n" "Empty\n"
"Empty\n" "Empty\n"
"Empty\n" "Empty\n"
@ -735,6 +736,7 @@ struct TestInventory: public TestBase
std::string serialized_inventory_2 = std::string serialized_inventory_2 =
"List main 32\n" "List main 32\n"
"Width 5\n"
"Empty\n" "Empty\n"
"Empty\n" "Empty\n"
"Empty\n" "Empty\n"
@ -778,6 +780,8 @@ struct TestInventory: public TestBase
inv.getList("0")->setName("main"); inv.getList("0")->setName("main");
UASSERT(!inv.getList("0")); UASSERT(!inv.getList("0"));
UASSERT(inv.getList("main")); UASSERT(inv.getList("main"));
UASSERT(inv.getList("main")->getWidth() == 3);
inv.getList("main")->setWidth(5);
std::ostringstream inv_os(std::ios::binary); std::ostringstream inv_os(std::ios::binary);
inv.serialize(inv_os); inv.serialize(inv_os);
UASSERT(inv_os.str() == serialized_inventory_2); UASSERT(inv_os.str() == serialized_inventory_2);

View File

@ -1216,10 +1216,11 @@ bool generate_image(std::string part_of_name, video::IImage *& baseimg,
// Position to copy the blitted from in the blitted image // Position to copy the blitted from in the blitted image
core::position2d<s32> pos_from(0,0); core::position2d<s32> pos_from(0,0);
// Blit // Blit
image->copyToWithAlpha(baseimg, pos_to, /*image->copyToWithAlpha(baseimg, pos_to,
core::rect<s32>(pos_from, dim), core::rect<s32>(pos_from, dim),
video::SColor(255,255,255,255), video::SColor(255,255,255,255),
NULL); NULL);*/
blit_with_alpha(image, baseimg, pos_from, pos_to, dim);
// Drop image // Drop image
image->drop(); image->drop();
} }
@ -1360,7 +1361,11 @@ bool generate_image(std::string part_of_name, video::IImage *& baseimg,
u32 h0 = stoi(sf.next(":")); u32 h0 = stoi(sf.next(":"));
infostream<<"combined w="<<w0<<" h="<<h0<<std::endl; infostream<<"combined w="<<w0<<" h="<<h0<<std::endl;
core::dimension2d<u32> dim(w0,h0); core::dimension2d<u32> dim(w0,h0);
baseimg = driver->createImage(video::ECF_A8R8G8B8, dim); if(baseimg == NULL)
{
baseimg = driver->createImage(video::ECF_A8R8G8B8, dim);
baseimg->fill(video::SColor(0,0,0,0));
}
while(sf.atend() == false) while(sf.atend() == false)
{ {
u32 x = stoi(sf.next(",")); u32 x = stoi(sf.next(","));
@ -1380,10 +1385,11 @@ bool generate_image(std::string part_of_name, video::IImage *& baseimg,
driver->createImage(video::ECF_A8R8G8B8, dim); driver->createImage(video::ECF_A8R8G8B8, dim);
img->copyTo(img2); img->copyTo(img2);
img->drop(); img->drop();
img2->copyToWithAlpha(baseimg, pos_base, /*img2->copyToWithAlpha(baseimg, pos_base,
core::rect<s32>(v2s32(0,0), dim), core::rect<s32>(v2s32(0,0), dim),
video::SColor(255,255,255,255), video::SColor(255,255,255,255),
NULL); NULL);*/
blit_with_alpha(img2, baseimg, v2s32(0,0), pos_base, dim);
img2->drop(); img2->drop();
} }
else else

View File

@ -242,6 +242,29 @@ inline bool string_allowed(const std::string &s, const std::string &allowed_char
return true; return true;
} }
/*
Checks if a string contains no blacklisted characters (opposite
function of string_allowed())
*/
inline bool string_allowed_blacklist(const std::string & s, const std::string & blacklisted_chars)
{
for(unsigned int i = 0; i < s.length(); i++)
{
bool invalid = false;
for(unsigned int j = 0; j < blacklisted_chars.length(); j++)
{
if(s[i] == blacklisted_chars[j])
{
invalid = true;
break;
}
}
if(invalid)
return false;
}
return true;
}
/* /*
Forcefully wraps string into rows using \n Forcefully wraps string into rows using \n
(no word wrap, used for showing paths in gui) (no word wrap, used for showing paths in gui)