diff --git a/data/junglegrass.png b/data/junglegrass.png new file mode 100644 index 000000000..eea87c07c Binary files /dev/null and b/data/junglegrass.png differ diff --git a/data/jungletree.png b/data/jungletree.png new file mode 100644 index 000000000..ccd20ac74 Binary files /dev/null and b/data/jungletree.png differ diff --git a/data/jungletree_top.png b/data/jungletree_top.png new file mode 100644 index 000000000..2a9b51373 Binary files /dev/null and b/data/jungletree_top.png differ diff --git a/po/en/minetest.pot b/po/en/minetest.pot new file mode 100644 index 000000000..8fbac78d0 --- /dev/null +++ b/po/en/minetest.pot @@ -0,0 +1,113 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2011-07-24 11:32+0300\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=CHARSET\n" +"Content-Transfer-Encoding: 8bit\n" + +#: src/guiMainMenu.cpp:180 +msgid "Name/Password" +msgstr "" + +#: src/guiMainMenu.cpp:203 +msgid "Address/Port" +msgstr "" + +#: src/guiMainMenu.cpp:223 +msgid "Leave address blank to start a local server." +msgstr "" + +#: src/guiMainMenu.cpp:230 +msgid "Fancy trees" +msgstr "" + +#: src/guiMainMenu.cpp:236 +msgid "Smooth Lighting" +msgstr "" + +#: src/guiMainMenu.cpp:244 +msgid "Start Game / Connect" +msgstr "" + +#: src/guiMainMenu.cpp:253 +msgid "Change keys" +msgstr "" + +#: src/guiMainMenu.cpp:276 +msgid "Creative Mode" +msgstr "" + +#: src/guiMainMenu.cpp:282 +msgid "Enable Damage" +msgstr "" + +#: src/guiMainMenu.cpp:290 +msgid "Delete map" +msgstr "" + +#: src/guiMessageMenu.cpp:93 src/guiTextInputMenu.cpp:111 +msgid "Proceed" +msgstr "" + +#: src/guiPasswordChange.cpp:102 +msgid "Old Password" +msgstr "" + +#: src/guiPasswordChange.cpp:117 +msgid "New Password" +msgstr "" + +#: src/guiPasswordChange.cpp:131 +msgid "Confirm Password" +msgstr "" + +#: src/guiPasswordChange.cpp:146 +msgid "Change" +msgstr "" + +#: src/guiPasswordChange.cpp:155 +msgid "Passwords do not match!" +msgstr "" + +#: src/guiPauseMenu.cpp:110 +msgid "Continue" +msgstr "" + +#: src/guiPauseMenu.cpp:117 +msgid "Change Password" +msgstr "" + +#: src/guiPauseMenu.cpp:124 +msgid "Disconnect" +msgstr "" + +#: src/guiPauseMenu.cpp:131 +msgid "Exit to OS" +msgstr "" + +#: src/guiPauseMenu.cpp:138 +msgid "" +"Keys:\n" +"- WASD: Walk\n" +"- Mouse left: dig blocks\n" +"- Mouse right: place blocks\n" +"- Mouse wheel: select item\n" +"- 0...9: select item\n" +"- Shift: sneak\n" +"- R: Toggle viewing all loaded chunks\n" +"- I: Inventory menu\n" +"- ESC: This menu\n" +"- T: Chat\n" +msgstr "" diff --git a/src/collision.cpp b/src/collision.cpp index 01d546284..3d322cf0c 100644 --- a/src/collision.cpp +++ b/src/collision.cpp @@ -78,7 +78,8 @@ collisionMoveResult collisionMoveSimple(Map *map, f32 pos_max_d, { try{ // Object collides into walkable nodes - if(content_walkable(map->getNode(v3s16(x,y,z)).d) == false) + MapNode n = map->getNode(v3s16(x,y,z)); + if(content_features(n).walkable == false) continue; } catch(InvalidPositionException &e) diff --git a/src/content_craft.cpp b/src/content_craft.cpp index 069e68300..b5a1dc776 100644 --- a/src/content_craft.cpp +++ b/src/content_craft.cpp @@ -21,6 +21,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "inventory.h" #include "content_mapnode.h" #include "player.h" +#include "mapnode.h" // For content_t /* items: actually *items[9] @@ -446,7 +447,7 @@ void craft_set_creative_inventory(Player *player) */ // CONTENT_IGNORE-terminated list - u8 material_items[] = { + content_t material_items[] = { CONTENT_TORCH, CONTENT_COBBLE, CONTENT_MUD, @@ -472,7 +473,7 @@ void craft_set_creative_inventory(Player *player) CONTENT_IGNORE }; - u8 *mip = material_items; + content_t *mip = material_items; for(u16 i=0; i +#include "mapnode.h" // For content_t class InventoryItem; class ServerActiveObject; class ServerEnvironment; -bool item_material_is_cookable(u8 content); -InventoryItem* item_material_create_cook_result(u8 content); +bool item_material_is_cookable(content_t content); +InventoryItem* item_material_create_cook_result(content_t content); std::string item_craft_get_image_name(const std::string &subname); ServerActiveObject* item_craft_create_object(const std::string &subname, diff --git a/src/content_mapblock.cpp b/src/content_mapblock.cpp index d8bf71dc0..3044c8b35 100644 --- a/src/content_mapblock.cpp +++ b/src/content_mapblock.cpp @@ -21,6 +21,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "content_mapnode.h" #include "main.h" // For g_settings and g_texturesource #include "mineral.h" +#include "mapblock_mesh.h" // For MapBlock_LightColor() #ifndef SERVER // Create a cuboid. @@ -198,6 +199,17 @@ void mapblock_mesh_generate_special(MeshMakeData *data, AtlasPointer pa_papyrus = g_texturesource->getTexture( g_texturesource->getTextureId("papyrus.png")); material_papyrus.setTexture(0, pa_papyrus.atlas); + + // junglegrass material + video::SMaterial material_junglegrass; + material_junglegrass.setFlag(video::EMF_LIGHTING, false); + material_junglegrass.setFlag(video::EMF_BILINEAR_FILTER, false); + material_junglegrass.setFlag(video::EMF_FOG_ENABLE, true); + material_junglegrass.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF; + AtlasPointer pa_junglegrass = g_texturesource->getTexture( + g_texturesource->getTextureId("junglegrass.png")); + material_junglegrass.setTexture(0, pa_junglegrass.atlas); + for(s16 z=0; zm_daynight_ratio)); - video::SColor c(255,l,l,l); + video::SColor c = MapBlock_LightColor(255, l); float d = (float)BS/16; // Wall at X+ of node @@ -287,7 +299,7 @@ void mapblock_mesh_generate_special(MeshMakeData *data, video::S3DVertex(BS/2-d,BS/2,-BS/2, 0,0,0, c, 0,0), }; - v3s16 dir = unpackDir(n.dir); + v3s16 dir = unpackDir(n.param2); for(s32 i=0; i<4; i++) { @@ -327,26 +339,26 @@ void mapblock_mesh_generate_special(MeshMakeData *data, /* Add flowing water to mesh */ - else if(n.d == CONTENT_WATER) + else if(n.getContent() == CONTENT_WATER) { bool top_is_water = false; MapNode ntop = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(x,y+1,z)); - if(ntop.d == CONTENT_WATER || ntop.d == CONTENT_WATERSOURCE) + if(ntop.getContent() == CONTENT_WATER || ntop.getContent() == CONTENT_WATERSOURCE) top_is_water = true; u8 l = 0; // Use the light of the node on top if possible - if(content_features(ntop.d).param_type == CPT_LIGHT) + if(content_features(ntop).param_type == CPT_LIGHT) l = decode_light(ntop.getLightBlend(data->m_daynight_ratio)); // Otherwise use the light of this node (the water) else l = decode_light(n.getLightBlend(data->m_daynight_ratio)); - video::SColor c(WATER_ALPHA,l,l,l); + video::SColor c = MapBlock_LightColor(WATER_ALPHA, l); // Neighbor water levels (key = relative position) // Includes current node core::map neighbor_levels; - core::map neighbor_contents; + core::map neighbor_contents; core::map neighbor_flags; const u8 neighborflag_top_is_water = 0x01; v3s16 neighbor_dirs[9] = { @@ -368,14 +380,14 @@ void mapblock_mesh_generate_special(MeshMakeData *data, // Check neighbor v3s16 p2 = p + neighbor_dirs[i]; MapNode n2 = data->m_vmanip.getNodeNoEx(blockpos_nodes + p2); - if(n2.d != CONTENT_IGNORE) + if(n2.getContent() != CONTENT_IGNORE) { - content = n2.d; + content = n2.getContent(); - if(n2.d == CONTENT_WATERSOURCE) + if(n2.getContent() == CONTENT_WATERSOURCE) level = (-0.5+node_water_level) * BS; - else if(n2.d == CONTENT_WATER) - level = (-0.5 + ((float)(n2.param2 & LIQUID_LEVEL_MASK) + 0.5) / 8.0 + else if(n2.getContent() == CONTENT_WATER) + level = (-0.5 + ((float)n2.param2 + 0.5) / 8.0 * node_water_level) * BS; // Check node above neighbor. @@ -383,7 +395,7 @@ void mapblock_mesh_generate_special(MeshMakeData *data, // doesn't exist p2.Y += 1; n2 = data->m_vmanip.getNodeNoEx(blockpos_nodes + p2); - if(n2.d == CONTENT_WATERSOURCE || n2.d == CONTENT_WATER) + if(n2.getContent() == CONTENT_WATERSOURCE || n2.getContent() == CONTENT_WATER) flags |= neighborflag_top_is_water; } @@ -591,14 +603,14 @@ void mapblock_mesh_generate_special(MeshMakeData *data, /* Add water sources to mesh if using new style */ - else if(n.d == CONTENT_WATERSOURCE && new_style_water) + else if(n.getContent() == CONTENT_WATERSOURCE && new_style_water) { //bool top_is_water = false; bool top_is_air = false; MapNode n = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(x,y+1,z)); - /*if(n.d == CONTENT_WATER || n.d == CONTENT_WATERSOURCE) + /*if(n.getContent() == CONTENT_WATER || n.getContent() == CONTENT_WATERSOURCE) top_is_water = true;*/ - if(n.d == CONTENT_AIR) + if(n.getContent() == CONTENT_AIR) top_is_air = true; /*if(top_is_water == true) @@ -607,7 +619,7 @@ void mapblock_mesh_generate_special(MeshMakeData *data, continue; u8 l = decode_light(n.getLightBlend(data->m_daynight_ratio)); - video::SColor c(WATER_ALPHA,l,l,l); + video::SColor c = MapBlock_LightColor(WATER_ALPHA, l); video::S3DVertex vertices[4] = { @@ -638,11 +650,11 @@ void mapblock_mesh_generate_special(MeshMakeData *data, /* Add leaves if using new style */ - else if(n.d == CONTENT_LEAVES && new_style_leaves) + else if(n.getContent() == CONTENT_LEAVES && new_style_leaves) { /*u8 l = decode_light(n.getLightBlend(data->m_daynight_ratio));*/ u8 l = decode_light(undiminish_light(n.getLightBlend(data->m_daynight_ratio))); - video::SColor c(255,l,l,l); + video::SColor c = MapBlock_LightColor(255, l); for(u32 j=0; j<6; j++) { @@ -706,10 +718,10 @@ void mapblock_mesh_generate_special(MeshMakeData *data, /* Add glass */ - else if(n.d == CONTENT_GLASS) + else if(n.getContent() == CONTENT_GLASS) { u8 l = decode_light(undiminish_light(n.getLightBlend(data->m_daynight_ratio))); - video::SColor c(255,l,l,l); + video::SColor c = MapBlock_LightColor(255, l); for(u32 j=0; j<6; j++) { @@ -769,10 +781,10 @@ void mapblock_mesh_generate_special(MeshMakeData *data, /* Add fence */ - else if(n.d == CONTENT_FENCE) + else if(n.getContent() == CONTENT_FENCE) { u8 l = decode_light(undiminish_light(n.getLightBlend(data->m_daynight_ratio))); - video::SColor c(255,l,l,l); + video::SColor c = MapBlock_LightColor(255, l); const f32 post_rad=(f32)BS/10; const f32 bar_rad=(f32)BS/20; @@ -795,7 +807,7 @@ void mapblock_mesh_generate_special(MeshMakeData *data, v3s16 p2 = p; p2.X++; MapNode n2 = data->m_vmanip.getNodeNoEx(blockpos_nodes + p2); - if(n2.d == CONTENT_FENCE) + if(n2.getContent() == CONTENT_FENCE) { pos = intToFloat(p+blockpos_nodes, BS); pos.X += BS/2; @@ -821,7 +833,7 @@ void mapblock_mesh_generate_special(MeshMakeData *data, p2 = p; p2.Z++; n2 = data->m_vmanip.getNodeNoEx(blockpos_nodes + p2); - if(n2.d == CONTENT_FENCE) + if(n2.getContent() == CONTENT_FENCE) { pos = intToFloat(p+blockpos_nodes, BS); pos.Z += BS/2; @@ -848,7 +860,7 @@ void mapblock_mesh_generate_special(MeshMakeData *data, /* Add stones with minerals if stone is invisible */ - else if(n.d == CONTENT_STONE && invisible_stone && n.getMineral() != MINERAL_NONE) + else if(n.getContent() == CONTENT_STONE && invisible_stone && n.getMineral() != MINERAL_NONE) { for(u32 j=0; j<6; j++) { @@ -856,19 +868,20 @@ void mapblock_mesh_generate_special(MeshMakeData *data, v3s16 dir = g_6dirs[j]; /*u8 l = 0; MapNode n2 = data->m_vmanip.getNodeNoEx(blockpos_nodes + dir); - if(content_features(n2.d).param_type == CPT_LIGHT) + if(content_features(n2).param_type == CPT_LIGHT) l = decode_light(n2.getLightBlend(data->m_daynight_ratio)); else l = 255;*/ u8 l = 255; - video::SColor c(255,l,l,l); + video::SColor c = MapBlock_LightColor(255, l); // Get the right texture TileSpec ts = n.getTile(dir); AtlasPointer ap = ts.texture; material_general.setTexture(0, ap.atlas); + video::S3DVertex vertices[4] = - { + { /*video::S3DVertex(-BS/2,-BS/2,BS/2, 0,0,0, c, 0,1), video::S3DVertex(BS/2,-BS/2,BS/2, 0,0,0, c, 1,1), video::S3DVertex(BS/2,BS/2,BS/2, 0,0,0, c, 1,0), @@ -916,10 +929,10 @@ void mapblock_mesh_generate_special(MeshMakeData *data, } } #endif - else if(n.d == CONTENT_PAPYRUS) + else if(n.getContent() == CONTENT_PAPYRUS) { u8 l = decode_light(undiminish_light(n.getLightBlend(data->m_daynight_ratio))); - video::SColor c(255,l,l,l); + video::SColor c = MapBlock_LightColor(255, l); for(u32 j=0; j<4; j++) { @@ -966,10 +979,61 @@ void mapblock_mesh_generate_special(MeshMakeData *data, collector.append(material_papyrus, vertices, 4, indices, 6); } } - else if(n.d == CONTENT_RAIL) + else if(n.getContent() == CONTENT_JUNGLEGRASS) + { + u8 l = decode_light(undiminish_light(n.getLightBlend(data->m_daynight_ratio))); + video::SColor c = MapBlock_LightColor(255, l); + + for(u32 j=0; j<4; j++) + { + video::S3DVertex vertices[4] = + { + video::S3DVertex(-BS/2,-BS/2,0, 0,0,0, c, + pa_papyrus.x0(), pa_papyrus.y1()), + video::S3DVertex(BS/2,-BS/2,0, 0,0,0, c, + pa_papyrus.x1(), pa_papyrus.y1()), + video::S3DVertex(BS/2,BS/1,0, 0,0,0, c, + pa_papyrus.x1(), pa_papyrus.y0()), + video::S3DVertex(-BS/2,BS/1,0, 0,0,0, c, + pa_papyrus.x0(), pa_papyrus.y0()), + }; + + if(j == 0) + { + for(u16 i=0; i<4; i++) + vertices[i].Pos.rotateXZBy(45); + } + else if(j == 1) + { + for(u16 i=0; i<4; i++) + vertices[i].Pos.rotateXZBy(-45); + } + else if(j == 2) + { + for(u16 i=0; i<4; i++) + vertices[i].Pos.rotateXZBy(135); + } + else if(j == 3) + { + for(u16 i=0; i<4; i++) + vertices[i].Pos.rotateXZBy(-135); + } + + for(u16 i=0; i<4; i++) + { + vertices[i].Pos *= 1.3; + vertices[i].Pos += intToFloat(p + blockpos_nodes, BS); + } + + u16 indices[] = {0,1,2,2,3,0}; + // Add to mesh collector + collector.append(material_junglegrass, vertices, 4, indices, 6); + } + } + else if(n.getContent() == CONTENT_RAIL) { u8 l = decode_light(n.getLightBlend(data->m_daynight_ratio)); - video::SColor c(255,l,l,l); + video::SColor c = MapBlock_LightColor(255, l); bool is_rail_x [] = { false, false }; /* x-1, x+1 */ bool is_rail_z [] = { false, false }; /* z-1, z+1 */ @@ -979,13 +1043,13 @@ void mapblock_mesh_generate_special(MeshMakeData *data, MapNode n_minus_z = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(x,y,z-1)); MapNode n_plus_z = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(x,y,z+1)); - if(n_minus_x.d == CONTENT_RAIL) + if(n_minus_x.getContent() == CONTENT_RAIL) is_rail_x[0] = true; - if(n_plus_x.d == CONTENT_RAIL) + if(n_plus_x.getContent() == CONTENT_RAIL) is_rail_x[1] = true; - if(n_minus_z.d == CONTENT_RAIL) + if(n_minus_z.getContent() == CONTENT_RAIL) is_rail_z[0] = true; - if(n_plus_z.d == CONTENT_RAIL) + if(n_plus_z.getContent() == CONTENT_RAIL) is_rail_z[1] = true; float d = (float)BS/16; diff --git a/src/content_mapnode.cpp b/src/content_mapnode.cpp index 79e10fd61..db036ebd9 100644 --- a/src/content_mapnode.cpp +++ b/src/content_mapnode.cpp @@ -31,6 +31,65 @@ void setStoneLikeDiggingProperties(DiggingPropertiesList &list, float toughness) void setDirtLikeDiggingProperties(DiggingPropertiesList &list, float toughness); void setWoodLikeDiggingProperties(DiggingPropertiesList &list, float toughness); +content_t trans_table_19[][2] = { + {CONTENT_GRASS, 1}, + {CONTENT_TREE, 4}, + {CONTENT_LEAVES, 5}, + {CONTENT_GRASS_FOOTSTEPS, 6}, + {CONTENT_MESE, 7}, + {CONTENT_MUD, 8}, + {CONTENT_CLOUD, 10}, + {CONTENT_COALSTONE, 11}, + {CONTENT_WOOD, 12}, + {CONTENT_SAND, 13}, + {CONTENT_COBBLE, 18}, + {CONTENT_STEEL, 19}, + {CONTENT_GLASS, 20}, + {CONTENT_MOSSYCOBBLE, 22}, + {CONTENT_GRAVEL, 23}, + {CONTENT_SANDSTONE, 24}, + {CONTENT_CACTUS, 25}, + {CONTENT_BRICK, 26}, + {CONTENT_CLAY, 27}, + {CONTENT_PAPYRUS, 28}, + {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; idug_item = std::string("MaterialItem ")+itos(i)+" 1"; setWoodLikeDiggingProperties(f->digging_properties, 1.0); + i = CONTENT_JUNGLETREE; + f = &content_features(i); + f->setAllTextures("jungletree.png"); + f->setTexture(0, "jungletree_top.png"); + f->setTexture(1, "jungletree_top.png"); + f->param_type = CPT_MINERAL; + //f->is_ground_content = true; + f->dug_item = std::string("MaterialItem ")+itos(i)+" 1"; + setWoodLikeDiggingProperties(f->digging_properties, 1.0); + + i = CONTENT_JUNGLEGRASS; + f = &content_features(i); + f->setInventoryTexture("junglegrass.png"); + f->light_propagates = true; + f->param_type = CPT_LIGHT; + //f->is_ground_content = true; + f->air_equivalent = false; // grass grows underneath + f->dug_item = std::string("MaterialItem ")+itos(i)+" 1"; + f->solidness = 0; // drawn separately, makes no faces + f->walkable = false; + setWoodLikeDiggingProperties(f->digging_properties, 0.10); + i = CONTENT_LEAVES; f = &content_features(i); f->light_propagates = true; //f->param_type = CPT_MINERAL; f->param_type = CPT_LIGHT; - f->is_ground_content = true; + //f->is_ground_content = true; if(new_style_leaves) { f->solidness = 0; // drawn separately, makes no faces @@ -191,6 +272,7 @@ void content_mapnode_init() i = CONTENT_GLASS; f = &content_features(i); f->light_propagates = true; + f->sunlight_propagates = true; f->param_type = CPT_LIGHT; f->is_ground_content = true; f->dug_item = std::string("MaterialItem ")+itos(i)+" 1"; diff --git a/src/content_mapnode.h b/src/content_mapnode.h index e53624c21..5fdbf45f3 100644 --- a/src/content_mapnode.h +++ b/src/content_mapnode.h @@ -20,43 +20,57 @@ with this program; if not, write to the Free Software Foundation, Inc., #ifndef CONTENT_MAPNODE_HEADER #define CONTENT_MAPNODE_HEADER +#include "mapnode.h" + void content_mapnode_init(); +MapNode mapnode_translate_from_internal(MapNode n_from, u8 version); +MapNode mapnode_translate_to_internal(MapNode n_from, u8 version); + /* Node content type IDs + Ranges: */ + +// 0x000...0x07f (0...127): param2 is fully usable +// 126 and 127 are reserved. +// Use these sparingly, only when the extra space in param2 might be needed. #define CONTENT_STONE 0 -#define CONTENT_GRASS 1 #define CONTENT_WATER 2 #define CONTENT_TORCH 3 -#define CONTENT_TREE 4 -#define CONTENT_LEAVES 5 -#define CONTENT_GRASS_FOOTSTEPS 6 -#define CONTENT_MESE 7 -#define CONTENT_MUD 8 #define CONTENT_WATERSOURCE 9 -// Pretty much useless, clouds won't be drawn this way -#define CONTENT_CLOUD 10 -#define CONTENT_COALSTONE 11 -#define CONTENT_WOOD 12 -#define CONTENT_SAND 13 #define CONTENT_SIGN_WALL 14 #define CONTENT_CHEST 15 #define CONTENT_FURNACE 16 -//#define CONTENT_WORKBENCH 17 -#define CONTENT_COBBLE 18 -#define CONTENT_STEEL 19 -#define CONTENT_GLASS 20 #define CONTENT_FENCE 21 -#define CONTENT_MOSSYCOBBLE 22 -#define CONTENT_GRAVEL 23 -#define CONTENT_SANDSTONE 24 -#define CONTENT_CACTUS 25 -#define CONTENT_BRICK 26 -#define CONTENT_CLAY 27 -#define CONTENT_PAPYRUS 28 -#define CONTENT_BOOKSHELF 29 #define CONTENT_RAIL 30 +// 0x800...0xfff (2048...4095): higher 4 bytes of param2 are not usable +#define CONTENT_GRASS 0x800 //1 +#define CONTENT_TREE 0x801 //4 +#define CONTENT_LEAVES 0x802 //5 +#define CONTENT_GRASS_FOOTSTEPS 0x803 //6 +#define CONTENT_MESE 0x804 //7 +#define CONTENT_MUD 0x805 //8 +// Pretty much useless, clouds won't be drawn this way +#define CONTENT_CLOUD 0x806 //10 +#define CONTENT_COALSTONE 0x807 //11 +#define CONTENT_WOOD 0x808 //12 +#define CONTENT_SAND 0x809 //13 +#define CONTENT_COBBLE 0x80a //18 +#define CONTENT_STEEL 0x80b //19 +#define CONTENT_GLASS 0x80c //20 +#define CONTENT_MOSSYCOBBLE 0x80d //22 +#define CONTENT_GRAVEL 0x80e //23 +#define CONTENT_SANDSTONE 0x80f //24 +#define CONTENT_CACTUS 0x810 //25 +#define CONTENT_BRICK 0x811 //26 +#define CONTENT_CLAY 0x812 //27 +#define CONTENT_PAPYRUS 0x813 //28 +#define CONTENT_BOOKSHELF 0x814 //29 +#define CONTENT_JUNGLETREE 0x815 +#define CONTENT_JUNGLEGRASS 0x816 + + #endif diff --git a/src/defaultsettings.cpp b/src/defaultsettings.cpp index cbc78ad3f..8326a0f0d 100644 --- a/src/defaultsettings.cpp +++ b/src/defaultsettings.cpp @@ -51,7 +51,7 @@ void set_default_settings() g_settings.setDefault("wanted_fps", "30"); g_settings.setDefault("fps_max", "60"); g_settings.setDefault("viewing_range_nodes_max", "300"); - g_settings.setDefault("viewing_range_nodes_min", "25"); + g_settings.setDefault("viewing_range_nodes_min", "15"); g_settings.setDefault("screenW", "800"); g_settings.setDefault("screenH", "600"); g_settings.setDefault("address", ""); diff --git a/src/environment.cpp b/src/environment.cpp index df41dc63f..d72369620 100644 --- a/src/environment.cpp +++ b/src/environment.cpp @@ -543,11 +543,11 @@ void spawnRandomObjects(MapBlock *block) { v3s16 p(x0,y0,z0); MapNode n = block->getNodeNoEx(p); - if(n.d == CONTENT_IGNORE) + if(n.getContent() == CONTENT_IGNORE) continue; - if(content_features(n.d).liquid_type != LIQUID_NONE) + if(content_features(n).liquid_type != LIQUID_NONE) continue; - if(content_features(n.d).walkable) + if(content_features(n).walkable) { last_node_walkable = true; continue; @@ -555,7 +555,7 @@ void spawnRandomObjects(MapBlock *block) if(last_node_walkable) { // If block contains light information - if(content_features(n.d).param_type == CPT_LIGHT) + if(content_features(n).param_type == CPT_LIGHT) { if(n.getLight(LIGHTBANK_DAY) <= 5) { @@ -624,15 +624,15 @@ void ServerEnvironment::activateBlock(MapBlock *block, u32 additional_dtime) #if 1 // Test something: // Convert all mud under proper day lighting to grass - if(n.d == CONTENT_MUD) + if(n.getContent() == CONTENT_MUD) { if(dtime_s > 300) { MapNode n_top = block->getNodeNoEx(p0+v3s16(0,1,0)); - if(content_features(n_top.d).air_equivalent && + if(content_features(n_top).air_equivalent && n_top.getLight(LIGHTBANK_DAY) >= 13) { - n.d = CONTENT_GRASS; + n.setContent(CONTENT_GRASS); m_map->addNodeWithEvent(p, n); } } @@ -686,9 +686,9 @@ void ServerEnvironment::step(float dtime) v3s16 bottompos = floatToInt(playerpos + v3f(0,-BS/4,0), BS); try{ MapNode n = m_map->getNode(bottompos); - if(n.d == CONTENT_GRASS) + if(n.getContent() == CONTENT_GRASS) { - n.d = CONTENT_GRASS_FOOTSTEPS; + n.setContent(CONTENT_GRASS_FOOTSTEPS); m_map->setNode(bottompos, n); } } @@ -859,15 +859,15 @@ void ServerEnvironment::step(float dtime) Test something: Convert mud under proper lighting to grass */ - if(n.d == CONTENT_MUD) + if(n.getContent() == CONTENT_MUD) { if(myrand()%20 == 0) { - MapNode n_top = block->getNodeNoEx(p0+v3s16(0,1,0)); - if(content_features(n_top.d).air_equivalent && + MapNode n_top = m_map->getNodeNoEx(p+v3s16(0,1,0)); + if(content_features(n_top).air_equivalent && n_top.getLightBlend(getDayNightRatio()) >= 13) { - n.d = CONTENT_GRASS; + n.setContent(CONTENT_GRASS); m_map->addNodeWithEvent(p, n); } } @@ -875,15 +875,14 @@ void ServerEnvironment::step(float dtime) /* Convert grass into mud if under something else than air */ - else if(n.d == CONTENT_GRASS) + else if(n.getContent() == CONTENT_GRASS) { //if(myrand()%20 == 0) { - MapNode n_top = block->getNodeNoEx(p0+v3s16(0,1,0)); - if(n_top.d != CONTENT_AIR - && n_top.d != CONTENT_IGNORE) + MapNode n_top = m_map->getNodeNoEx(p+v3s16(0,1,0)); + if(content_features(n_top).air_equivalent == false) { - n.d = CONTENT_MUD; + n.setContent(CONTENT_MUD); m_map->addNodeWithEvent(p, n); } } @@ -1633,9 +1632,9 @@ void ClientEnvironment::step(float dtime) v3s16 bottompos = floatToInt(playerpos + v3f(0,-BS/4,0), BS); try{ MapNode n = m_map->getNode(bottompos); - if(n.d == CONTENT_GRASS) + if(n.getContent() == CONTENT_GRASS) { - n.d = CONTENT_GRASS_FOOTSTEPS; + n.setContent(CONTENT_GRASS_FOOTSTEPS); m_map->setNode(bottompos, n); // Update mesh on client if(m_map->mapType() == MAPTYPE_CLIENT) @@ -1874,7 +1873,7 @@ void ClientEnvironment::drawPostFx(video::IVideoDriver* driver, v3f camera_pos) v3f pos_f = camera_pos; v3s16 p_nodes = floatToInt(pos_f, BS); MapNode n = m_map->getNodeNoEx(p_nodes); - if(n.d == CONTENT_WATER || n.d == CONTENT_WATERSOURCE) + if(n.getContent() == CONTENT_WATER || n.getContent() == CONTENT_WATERSOURCE) { v2u32 ss = driver->getScreenSize(); core::rect rect(0,0, ss.X, ss.Y); diff --git a/src/game.cpp b/src/game.cpp index 0f858e879..b3069d6f9 100644 --- a/src/game.cpp +++ b/src/game.cpp @@ -417,7 +417,7 @@ void getPointedNode(Client *client, v3f player_position, try { n = client->getNode(v3s16(x,y,z)); - if(content_pointable(n.d) == false) + if(content_pointable(n.getContent()) == false) continue; } catch(InvalidPositionException &e) @@ -442,9 +442,9 @@ void getPointedNode(Client *client, v3f player_position, /* Meta-objects */ - if(n.d == CONTENT_TORCH) + if(n.getContent() == CONTENT_TORCH) { - v3s16 dir = unpackDir(n.dir); + v3s16 dir = unpackDir(n.param2); v3f dir_f = v3f(dir.X, dir.Y, dir.Z); dir_f *= BS/2 - BS/6 - BS/20; v3f cpf = npf + dir_f; @@ -489,9 +489,9 @@ void getPointedNode(Client *client, v3f player_position, } } } - else if(n.d == CONTENT_SIGN_WALL) + else if(n.getContent() == CONTENT_SIGN_WALL) { - v3s16 dir = unpackDir(n.dir); + v3s16 dir = unpackDir(n.param2); v3f dir_f = v3f(dir.X, dir.Y, dir.Z); dir_f *= BS/2 - BS/6 - BS/20; v3f cpf = npf + dir_f; @@ -538,9 +538,9 @@ void getPointedNode(Client *client, v3f player_position, } } } - else if(n.d == CONTENT_RAIL) + else if(n.getContent() == CONTENT_RAIL) { - v3s16 dir = unpackDir(n.dir); + v3s16 dir = unpackDir(n.param0); v3f dir_f = v3f(dir.X, dir.Y, dir.Z); dir_f *= BS/2 - BS/6 - BS/20; v3f cpf = npf + dir_f; @@ -1038,9 +1038,9 @@ void the_game( //bool screensize_changed = screensize != last_screensize; // Resize hotbar - if(screensize.Y <= 600) + if(screensize.Y <= 800) hotbar_imagesize = 32; - else if(screensize.Y <= 1024) + else if(screensize.Y <= 1280) hotbar_imagesize = 48; else hotbar_imagesize = 64; @@ -1759,7 +1759,7 @@ void the_game( } // Get digging properties for material and tool - u8 material = n.d; + content_t material = n.getContent(); DiggingProperties prop = getDiggingProperties(material, toolname); diff --git a/src/inventory.cpp b/src/inventory.cpp index fec51a759..7ef7f0138 100644 --- a/src/inventory.cpp +++ b/src/inventory.cpp @@ -61,7 +61,7 @@ InventoryItem* InventoryItem::deSerialize(std::istream &is) is>>material; u16 count; is>>count; - if(material > 255) + if(material > MAX_CONTENT) throw SerializationError("Too large material number"); return new MaterialItem(material, count); } diff --git a/src/inventory.h b/src/inventory.h index 07d81a3f7..5c64f89bb 100644 --- a/src/inventory.h +++ b/src/inventory.h @@ -30,8 +30,8 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "common_irrlicht.h" #include "debug.h" #include "mapblockobject.h" -// For g_materials -#include "main.h" +#include "main.h" // For g_materials +#include "mapnode.h" // For content_t #define QUANTITY_ITEM_MAX_COUNT 99 @@ -113,7 +113,7 @@ protected: class MaterialItem : public InventoryItem { public: - MaterialItem(u8 content, u16 count): + MaterialItem(content_t content, u16 count): InventoryItem(count) { m_content = content; @@ -175,12 +175,12 @@ public: /* Special methods */ - u8 getMaterial() + content_t getMaterial() { return m_content; } private: - u8 m_content; + content_t m_content; }; //TODO: Remove diff --git a/src/main.cpp b/src/main.cpp index bcca60d95..da17b84bc 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,1652 +1,1682 @@ -/* -Minetest-c55 -Copyright (C) 2010-2011 celeron55, Perttu Ahola - -This program is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 2 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License along -with this program; if not, write to the Free Software Foundation, Inc., -51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -*/ - -/* -=============================== NOTES ============================== -NOTE: Things starting with TODO are sometimes only suggestions. - -NOTE: iostream.imbue(std::locale("C")) is very slow -NOTE: Global locale is now set at initialization - -NOTE: If VBO (EHM_STATIC) is used, remember to explicitly free the - hardware buffer (it is not freed automatically) - -NOTE: A random to-do list saved here as documentation: -A list of "active blocks" in which stuff happens. (+=done) - + Add a never-resetted game timer to the server - + Add a timestamp value to blocks - + The simple rule: All blocks near some player are "active" - - Do stuff in real time in active blocks - + Handle objects - - Grow grass, delete leaves without a tree - - Spawn some mobs based on some rules - - Transform cobble to mossy cobble near water - - Run a custom script - - ...And all kinds of other dynamic stuff - + Keep track of when a block becomes active and becomes inactive - + When a block goes inactive: - + Store objects statically to block - + Store timer value as the timestamp - + When a block goes active: - + Create active objects out of static objects - - Simulate the results of what would have happened if it would have - been active for all the time - - Grow a lot of grass and so on - + Initially it is fine to send information about every active object - to every player. Eventually it should be modified to only send info - about the nearest ones. - + This was left to be done by the old system and it sends only the - nearest ones. - -Old, wild and random suggestions that probably won't be done: -------------------------------------------------------------- - -SUGG: If player is on ground, mainly fetch ground-level blocks - -SUGG: Expose Connection's seqnums and ACKs to server and client. - - This enables saving many packets and making a faster connection - - This also enables server to check if client has received the - most recent block sent, for example. -SUGG: Add a sane bandwidth throttling system to Connection - -SUGG: More fine-grained control of client's dumping of blocks from - memory - - ...What does this mean in the first place? - -SUGG: A map editing mode (similar to dedicated server mode) - -SUGG: Transfer more blocks in a single packet -SUGG: A blockdata combiner class, to which blocks are added and at - destruction it sends all the stuff in as few packets as possible. -SUGG: Make a PACKET_COMBINED which contains many subpackets. Utilize - it by sending more stuff in a single packet. - - Add a packet queue to RemoteClient, from which packets will be - combined with object data packets - - This is not exactly trivial: the object data packets are - sometimes very big by themselves - - This might not give much network performance gain though. - -SUGG: Precalculate lighting translation table at runtime (at startup) - - This is not doable because it is currently hand-made and not - based on some mathematical function. - - Note: This has been changing lately - -SUGG: A version number to blocks, which increments when the block is - modified (node add/remove, water update, lighting update) - - This can then be used to make sure the most recent version of - a block has been sent to client, for example - -SUGG: Make the amount of blocks sending to client and the total - amount of blocks dynamically limited. Transferring blocks is the - main network eater of this system, so it is the one that has - to be throttled so that RTTs stay low. - -SUGG: Meshes of blocks could be split into 6 meshes facing into - different directions and then only those drawn that need to be - -SUGG: Background music based on cellular automata? - http://www.earslap.com/projectslab/otomata - -SUGG: Simple light color information to air - -SUGG: Server-side objects could be moved based on nodes to enable very - lightweight operation and simple AI - - Not practical; client would still need to show smooth movement. - -SUGG: Make a system for pregenerating quick information for mapblocks, so - that the client can show them as cubes before they are actually sent - or even generated. - -SUGG: Erosion simulation at map generation time - - This might be plausible if larger areas of map were pregenerated - without lighting (which is slow) - - Simulate water flows, which would carve out dirt fast and - then turn stone into gravel and sand and relocate it. - - How about relocating minerals, too? Coal and gold in - downstream sand and gravel would be kind of cool - - This would need a better way of handling minerals, mainly - to have mineral content as a separate field. the first - parameter field is free for this. - - Simulate rock falling from cliffs when water has removed - enough solid rock from the bottom - -SUGG: For non-mapgen FarMesh: Add a per-sector database to store surface - stuff as simple flags/values - - Light? - - A building? - And at some point make the server send this data to the client too, - instead of referring to the noise functions - - Ground height - - Surface ground type - - Trees? - -Gaming ideas: -------------- - -- Aim for something like controlling a single dwarf in Dwarf Fortress -- The player could go faster by a crafting a boat, or riding an animal -- Random NPC traders. what else? - -Game content: -------------- - -- When furnace is destroyed, move items to player's inventory -- Add lots of stuff -- Glass blocks -- Growing grass, decaying leaves - - This can be done in the active blocks I guess. - - Lots of stuff can be done in the active blocks. - - Uh, is there an active block list somewhere? I think not. Add it. -- Breaking weak structures - - This can probably be accomplished in the same way as grass -- Player health points - - When player dies, throw items on map (needs better item-on-map - implementation) -- Cobble to get mossy if near water -- More slots in furnace source list, so that multiple ingredients - are possible. -- Keys to chests? - -- The Treasure Guard; a big monster with a hammer - - The hammer does great damage, shakes the ground and removes a block - - You can drop on top of it, and have some time to attack there - before he shakes you off - -- Maybe the difficulty could come from monsters getting tougher in - far-away places, and the player starting to need something from - there when time goes by. - - The player would have some of that stuff at the beginning, and - would need new supplies of it when it runs out - -- A bomb -- A spread-items-on-map routine for the bomb, and for dying players - -- Fighting: - - Proper sword swing simulation - - Player should get damage from colliding to a wall at high speed - -Documentation: --------------- - -Build system / running: ------------------------ - -Networking and serialization: ------------------------------ - -SUGG: Fix address to be ipv6 compatible - -User Interface: ---------------- - -Graphics: ---------- - -SUGG: Combine MapBlock's face caches to so big pieces that VBO - can be used - - That is >500 vertices - - This is not easy; all the MapBlocks close to the player would - still need to be drawn separately and combining the blocks - would have to happen in a background thread - -SUGG: Make fetching sector's blocks more efficient when rendering - sectors that have very large amounts of blocks (on client) - - Is this necessary at all? - -SUGG: Draw cubes in inventory directly with 3D drawing commands, so that - animating them is easier. - -SUGG: Option for enabling proper alpha channel for textures - -TODO: Flowing water animation - -TODO: A setting for enabling bilinear filtering for textures - -TODO: Better control of draw_control.wanted_max_blocks - -TODO: Further investigate the use of GPU lighting in addition to the - current one - -TODO: Artificial (night) light could be more yellow colored than sunlight. - - This is technically doable. - - Also the actual colors of the textures could be made less colorful - in the dark but it's a bit more difficult. - -SUGG: Somehow make the night less colorful - -TODO: Occlusion culling - - At the same time, move some of the renderMap() block choosing code - to the same place as where the new culling happens. - - Shoot some rays per frame and when ready, make a new list of - blocks for usage of renderMap and give it a new pointer to it. - -Configuration: --------------- - -Client: -------- - -TODO: Untie client network operations from framerate - - Needs some input queues or something - - This won't give much performance boost because calculating block - meshes takes so long - -SUGG: Make morning and evening transition more smooth and maybe shorter - -TODO: Don't update all meshes always on single node changes, but - check which ones should be updated - - implement Map::updateNodeMeshes() and the usage of it - - It will give almost always a 4x boost in mesh update performance. - -- A weapon engine - -- Tool/weapon visualization - -FIXME: When disconnected to the menu, memory is not freed properly - -TODO: Investigate how much the mesh generator thread gets used when - transferring map data - -Server: -------- - -SUGG: Make an option to the server to disable building and digging near - the starting position - -FIXME: Server sometimes goes into some infinite PeerNotFoundException loop - -* Fix the problem with the server constantly saving one or a few - blocks? List the first saved block, maybe it explains. - - It is probably caused by oscillating water - - TODO: Investigate if this still happens (this is a very old one) -* Make a small history check to transformLiquids to detect and log - continuous oscillations, in such detail that they can be fixed. - -FIXME: The new optimized map sending doesn't sometimes send enough blocks - from big caves and such -FIXME: Block send distance configuration does not take effect for some reason - -Environment: ------------- - -TODO: Add proper hooks to when adding and removing active blocks - -TODO: Finish the ActiveBlockModifier stuff and use it for something - -Objects: --------- - -TODO: Get rid of MapBlockObjects and use only ActiveObjects - - Skipping the MapBlockObject data is nasty - there is no "total - length" stored; have to make a SkipMBOs function which contains - enough of the current code to skip them properly. - -SUGG: MovingObject::move and Player::move are basically the same. - combine them. - - NOTE: This is a bit tricky because player has the sneaking ability - - NOTE: Player::move is more up-to-date. - - NOTE: There is a simple move implementation now in collision.{h,cpp} - - NOTE: MovingObject will be deleted (MapBlockObject) - -TODO: Add a long step function to objects that is called with the time - difference when block activates - -Map: ----- - -TODO: Mineral and ground material properties - - This way mineral ground toughness can be calculated with just - some formula, as well as tool strengths - - There are TODOs in appropriate files: material.h, content_mapnode.h - -TODO: Flowing water to actually contain flow direction information - - There is a space for this - it just has to be implemented. - -TODO: Consider smoothening cave floors after generating them - -Misc. stuff: ------------- -TODO: Make sure server handles removing grass when a block is placed (etc) - - The client should not do it by itself - - NOTE: I think nobody does it currently... -TODO: Block cube placement around player's head -TODO: Protocol version field -TODO: Think about using same bits for material for fences and doors, for - example -TODO: Move mineral to param2, increment map serialization version, add - conversion - -TODO: Restart irrlicht completely when coming back to main menu from game. - - This gets rid of everything that is stored in irrlicht's caches. - -TODO: Merge bahamada's audio stuff (clean patch available) - -TODO: Merge key configuration menu (no clean patch available) - -Making it more portable: ------------------------- - -Stuff to do before release: ---------------------------- - -Fixes to the current release: ------------------------------ - -Stuff to do after release: ---------------------------- - -Doing currently: ----------------- - -====================================================================== - -*/ - -#ifdef NDEBUG - #ifdef _WIN32 - #pragma message ("Disabling unit tests") - #else - #warning "Disabling unit tests" - #endif - // Disable unit tests - #define ENABLE_TESTS 0 -#else - // Enable unit tests - #define ENABLE_TESTS 1 -#endif - -#ifdef _MSC_VER - #pragma comment(lib, "Irrlicht.lib") - //#pragma comment(lib, "jthread.lib") - #pragma comment(lib, "zlibwapi.lib") - #pragma comment(lib, "Shell32.lib") - // This would get rid of the console window - //#pragma comment(linker, "/subsystem:windows /ENTRY:mainCRTStartup") -#endif - -#include -#include -#include -#include "main.h" -#include "common_irrlicht.h" -#include "debug.h" -#include "test.h" -#include "server.h" -#include "constants.h" -#include "porting.h" -#include "gettime.h" -#include "guiMessageMenu.h" -#include "filesys.h" -#include "config.h" -#include "guiMainMenu.h" -#include "mineral.h" -#include "materials.h" -#include "game.h" -#include "keycode.h" -#include "tile.h" - -#include "gettext.h" - -// This makes textures -ITextureSource *g_texturesource = NULL; - -/* - Settings. - These are loaded from the config file. -*/ - -Settings g_settings; -// This is located in defaultsettings.cpp -extern void set_default_settings(); - -// Global profiler -Profiler g_profiler; - -/* - Random stuff -*/ - -/* - GUI Stuff -*/ - -gui::IGUIEnvironment* guienv = NULL; -gui::IGUIStaticText *guiroot = NULL; - -MainMenuManager g_menumgr; - -bool noMenuActive() -{ - return (g_menumgr.menuCount() == 0); -} - -// Passed to menus to allow disconnecting and exiting - -MainGameCallback *g_gamecallback = NULL; - -/* - Debug streams -*/ - -// Connection -std::ostream *dout_con_ptr = &dummyout; -std::ostream *derr_con_ptr = &dstream_no_stderr; -//std::ostream *dout_con_ptr = &dstream_no_stderr; -//std::ostream *derr_con_ptr = &dstream_no_stderr; -//std::ostream *dout_con_ptr = &dstream; -//std::ostream *derr_con_ptr = &dstream; - -// Server -std::ostream *dout_server_ptr = &dstream; -std::ostream *derr_server_ptr = &dstream; - -// Client -std::ostream *dout_client_ptr = &dstream; -std::ostream *derr_client_ptr = &dstream; - -/* - gettime.h implementation -*/ - -// A small helper class -class TimeGetter -{ -public: - virtual u32 getTime() = 0; -}; - -// A precise irrlicht one -class IrrlichtTimeGetter: public TimeGetter -{ -public: - IrrlichtTimeGetter(IrrlichtDevice *device): - m_device(device) - {} - u32 getTime() - { - if(m_device == NULL) - return 0; - return m_device->getTimer()->getRealTime(); - } -private: - IrrlichtDevice *m_device; -}; -// Not so precise one which works without irrlicht -class SimpleTimeGetter: public TimeGetter -{ -public: - u32 getTime() - { - return porting::getTimeMs(); - } -}; - -// A pointer to a global instance of the time getter -// TODO: why? -TimeGetter *g_timegetter = NULL; - -u32 getTimeMs() -{ - if(g_timegetter == NULL) - return 0; - return g_timegetter->getTime(); -} - -/* - Event handler for Irrlicht - - NOTE: Everything possible should be moved out from here, - probably to InputHandler and the_game -*/ - -class MyEventReceiver : public IEventReceiver -{ -public: - // This is the one method that we have to implement - virtual bool OnEvent(const SEvent& event) - { - /* - React to nothing here if a menu is active - */ - if(noMenuActive() == false) - { - return false; - } - - // Remember whether each key is down or up - if(event.EventType == irr::EET_KEY_INPUT_EVENT) - { - keyIsDown[event.KeyInput.Key] = event.KeyInput.PressedDown; - - if(event.KeyInput.PressedDown) - keyWasDown[event.KeyInput.Key] = true; - } - - if(event.EventType == irr::EET_MOUSE_INPUT_EVENT) - { - if(noMenuActive() == false) - { - left_active = false; - middle_active = false; - right_active = false; - } - else - { - //dstream<<"MyEventReceiver: mouse input"<IsKeyDown(keyCode); - } - virtual bool wasKeyDown(EKEY_CODE keyCode) - { - return m_receiver->WasKeyDown(keyCode); - } - virtual v2s32 getMousePos() - { - return m_device->getCursorControl()->getPosition(); - } - virtual void setMousePos(s32 x, s32 y) - { - m_device->getCursorControl()->setPosition(x, y); - } - - virtual bool getLeftState() - { - return m_receiver->left_active; - } - virtual bool getRightState() - { - return m_receiver->right_active; - } - - virtual bool getLeftClicked() - { - return m_receiver->leftclicked; - } - virtual bool getRightClicked() - { - return m_receiver->rightclicked; - } - virtual void resetLeftClicked() - { - m_receiver->leftclicked = false; - } - virtual void resetRightClicked() - { - m_receiver->rightclicked = false; - } - - virtual bool getLeftReleased() - { - return m_receiver->leftreleased; - } - virtual bool getRightReleased() - { - return m_receiver->rightreleased; - } - virtual void resetLeftReleased() - { - m_receiver->leftreleased = false; - } - virtual void resetRightReleased() - { - m_receiver->rightreleased = false; - } - - virtual s32 getMouseWheel() - { - return m_receiver->getMouseWheel(); - } - - void clear() - { - m_receiver->clearInput(); - } -private: - IrrlichtDevice *m_device; - MyEventReceiver *m_receiver; -}; - -class RandomInputHandler : public InputHandler -{ -public: - RandomInputHandler() - { - leftdown = false; - rightdown = false; - leftclicked = false; - rightclicked = false; - leftreleased = false; - rightreleased = false; - for(u32 i=0; i map1; - tempf = -324; - const s16 ii=300; - for(s16 y=0; y=0; y--){ - for(s16 x=0; x screensize = driver->getScreenSize(); - - video::ITexture *bgtexture = - driver->getTexture(getTexturePath("mud.png").c_str()); - if(bgtexture) - { - s32 texturesize = 128; - s32 tiled_y = screensize.Height / texturesize + 1; - s32 tiled_x = screensize.Width / texturesize + 1; - - for(s32 y=0; y rect(0,0,texturesize,texturesize); - rect += v2s32(x*texturesize, y*texturesize); - driver->draw2DImage(bgtexture, rect, - core::rect(core::position2d(0,0), - core::dimension2di(bgtexture->getSize())), - NULL, NULL, true); - } - } - - video::ITexture *logotexture = - driver->getTexture(getTexturePath("menulogo.png").c_str()); - if(logotexture) - { - v2s32 logosize(logotexture->getOriginalSize().Width, - logotexture->getOriginalSize().Height); - logosize *= 4; - - video::SColor bgcolor(255,50,50,50); - core::rect bgrect(0, screensize.Height-logosize.Y-20, - screensize.Width, screensize.Height); - driver->draw2DRectangle(bgcolor, bgrect, NULL); - - core::rect rect(0,0,logosize.X,logosize.Y); - rect += v2s32(screensize.Width/2,screensize.Height-10-logosize.Y); - rect -= v2s32(logosize.X/2, 0); - driver->draw2DImage(logotexture, rect, - core::rect(core::position2d(0,0), - core::dimension2di(logotexture->getSize())), - NULL, NULL, true); - } -} - -int main(int argc, char *argv[]) -{ - /* - Initialization - */ - - // Set locale. This is for forcing '.' as the decimal point. - std::locale::global(std::locale("C")); - // This enables printing all characters in bitmap font - setlocale(LC_CTYPE, "en_US"); - /* - Parse command line - */ - - // List all allowed options - core::map allowed_options; - allowed_options.insert("help", ValueSpec(VALUETYPE_FLAG)); - allowed_options.insert("server", ValueSpec(VALUETYPE_FLAG, - "Run server directly")); - allowed_options.insert("config", ValueSpec(VALUETYPE_STRING, - "Load configuration from specified file")); - allowed_options.insert("port", ValueSpec(VALUETYPE_STRING)); - allowed_options.insert("address", ValueSpec(VALUETYPE_STRING)); - allowed_options.insert("random-input", ValueSpec(VALUETYPE_FLAG)); - allowed_options.insert("disable-unittests", ValueSpec(VALUETYPE_FLAG)); - allowed_options.insert("enable-unittests", ValueSpec(VALUETYPE_FLAG)); - allowed_options.insert("map-dir", ValueSpec(VALUETYPE_STRING)); -#ifdef _WIN32 - allowed_options.insert("dstream-on-stderr", ValueSpec(VALUETYPE_FLAG)); -#endif - allowed_options.insert("speedtests", ValueSpec(VALUETYPE_FLAG)); - - Settings cmd_args; - - bool ret = cmd_args.parseCommandLine(argc, argv, allowed_options); - - if(ret == false || cmd_args.getFlag("help")) - { - dstream<<"Allowed options:"<::Iterator - i = allowed_options.getIterator(); - i.atEnd() == false; i++) - { - dstream<<" --"<getKey(); - if(i.getNode()->getValue().type == VALUETYPE_FLAG) - { - } - else - { - dstream<<" "; - } - dstream<getValue().help != NULL) - { - dstream<<" "<getValue().help - < filenames; - filenames.push_back(porting::path_userdata + "/minetest.conf"); -#ifdef RUN_IN_PLACE - filenames.push_back(porting::path_userdata + "/../minetest.conf"); -#endif - - for(u32 i=0; i(screenW, screenH), - 16, fullscreen, false, false, &receiver); - - if (device == 0) - return 1; // could not create selected driver. - - // Set device in game parameters - device = device; +/* +Minetest-c55 +Copyright (C) 2010-2011 celeron55, Perttu Ahola + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +/* +=============================== NOTES ============================== +NOTE: Things starting with TODO are sometimes only suggestions. + +NOTE: iostream.imbue(std::locale("C")) is very slow +NOTE: Global locale is now set at initialization + +NOTE: If VBO (EHM_STATIC) is used, remember to explicitly free the + hardware buffer (it is not freed automatically) + +NOTE: A random to-do list saved here as documentation: +A list of "active blocks" in which stuff happens. (+=done) + + Add a never-resetted game timer to the server + + Add a timestamp value to blocks + + The simple rule: All blocks near some player are "active" + - Do stuff in real time in active blocks + + Handle objects + - Grow grass, delete leaves without a tree + - Spawn some mobs based on some rules + - Transform cobble to mossy cobble near water + - Run a custom script + - ...And all kinds of other dynamic stuff + + Keep track of when a block becomes active and becomes inactive + + When a block goes inactive: + + Store objects statically to block + + Store timer value as the timestamp + + When a block goes active: + + Create active objects out of static objects + - Simulate the results of what would have happened if it would have + been active for all the time + - Grow a lot of grass and so on + + Initially it is fine to send information about every active object + to every player. Eventually it should be modified to only send info + about the nearest ones. + + This was left to be done by the old system and it sends only the + nearest ones. + +Vim conversion regexpes for moving to extended content type storage: +%s/\(\.\|->\)d \([!=]=\)/\1getContent() \2/g +%s/content_features(\([^.]*\)\.d)/content_features(\1)/g +%s/\(\.\|->\)d = \([^;]*\);/\1setContent(\2);/g +%s/\(getNodeNoExNoEmerge([^)]*)\)\.d/\1.getContent()/g +%s/\(getNodeNoExNoEmerge(.*)\)\.d/\1.getContent()/g +%s/\.d;/.getContent();/g +%s/\(content_liquid\|content_flowing_liquid\|make_liquid_flowing\|content_pointable\)(\([^.]*\).d)/\1(\2.getContent())/g +Other things to note: +- node.d = node.param0 (only in raw serialization; use getContent() otherwise) +- node.param = node.param1 +- node.dir = node.param2 +- content_walkable(node.d) etc should be changed to + content_features(node).walkable etc +- Also check for lines that store the result of getContent to a 8-bit + variable and fix them (result of getContent() must be stored in + content_t, which is 16-bit) + +NOTE: Seeds in 1260:6c77e7dbfd29: +5721858502589302589: + Spawns you on a small sand island with a surface dungeon +2983455799928051958: + Enormous jungle + a surface dungeon at ~(250,0,0) + +Old, wild and random suggestions that probably won't be done: +------------------------------------------------------------- + +SUGG: If player is on ground, mainly fetch ground-level blocks + +SUGG: Expose Connection's seqnums and ACKs to server and client. + - This enables saving many packets and making a faster connection + - This also enables server to check if client has received the + most recent block sent, for example. +SUGG: Add a sane bandwidth throttling system to Connection + +SUGG: More fine-grained control of client's dumping of blocks from + memory + - ...What does this mean in the first place? + +SUGG: A map editing mode (similar to dedicated server mode) + +SUGG: Transfer more blocks in a single packet +SUGG: A blockdata combiner class, to which blocks are added and at + destruction it sends all the stuff in as few packets as possible. +SUGG: Make a PACKET_COMBINED which contains many subpackets. Utilize + it by sending more stuff in a single packet. + - Add a packet queue to RemoteClient, from which packets will be + combined with object data packets + - This is not exactly trivial: the object data packets are + sometimes very big by themselves + - This might not give much network performance gain though. + +SUGG: Precalculate lighting translation table at runtime (at startup) + - This is not doable because it is currently hand-made and not + based on some mathematical function. + - Note: This has been changing lately + +SUGG: A version number to blocks, which increments when the block is + modified (node add/remove, water update, lighting update) + - This can then be used to make sure the most recent version of + a block has been sent to client, for example + +SUGG: Make the amount of blocks sending to client and the total + amount of blocks dynamically limited. Transferring blocks is the + main network eater of this system, so it is the one that has + to be throttled so that RTTs stay low. + +SUGG: Meshes of blocks could be split into 6 meshes facing into + different directions and then only those drawn that need to be + +SUGG: Background music based on cellular automata? + http://www.earslap.com/projectslab/otomata + +SUGG: Simple light color information to air + +SUGG: Server-side objects could be moved based on nodes to enable very + lightweight operation and simple AI + - Not practical; client would still need to show smooth movement. + +SUGG: Make a system for pregenerating quick information for mapblocks, so + that the client can show them as cubes before they are actually sent + or even generated. + +SUGG: Erosion simulation at map generation time + - This might be plausible if larger areas of map were pregenerated + without lighting (which is slow) + - Simulate water flows, which would carve out dirt fast and + then turn stone into gravel and sand and relocate it. + - How about relocating minerals, too? Coal and gold in + downstream sand and gravel would be kind of cool + - This would need a better way of handling minerals, mainly + to have mineral content as a separate field. the first + parameter field is free for this. + - Simulate rock falling from cliffs when water has removed + enough solid rock from the bottom + +SUGG: For non-mapgen FarMesh: Add a per-sector database to store surface + stuff as simple flags/values + - Light? + - A building? + And at some point make the server send this data to the client too, + instead of referring to the noise functions + - Ground height + - Surface ground type + - Trees? + +Gaming ideas: +------------- + +- Aim for something like controlling a single dwarf in Dwarf Fortress +- The player could go faster by a crafting a boat, or riding an animal +- Random NPC traders. what else? + +Game content: +------------- + +- When furnace is destroyed, move items to player's inventory +- Add lots of stuff +- Glass blocks +- Growing grass, decaying leaves + - This can be done in the active blocks I guess. + - Lots of stuff can be done in the active blocks. + - Uh, is there an active block list somewhere? I think not. Add it. +- Breaking weak structures + - This can probably be accomplished in the same way as grass +- Player health points + - When player dies, throw items on map (needs better item-on-map + implementation) +- Cobble to get mossy if near water +- More slots in furnace source list, so that multiple ingredients + are possible. +- Keys to chests? + +- The Treasure Guard; a big monster with a hammer + - The hammer does great damage, shakes the ground and removes a block + - You can drop on top of it, and have some time to attack there + before he shakes you off + +- Maybe the difficulty could come from monsters getting tougher in + far-away places, and the player starting to need something from + there when time goes by. + - The player would have some of that stuff at the beginning, and + would need new supplies of it when it runs out + +- A bomb +- A spread-items-on-map routine for the bomb, and for dying players + +- Fighting: + - Proper sword swing simulation + - Player should get damage from colliding to a wall at high speed + +Documentation: +-------------- + +Build system / running: +----------------------- + +Networking and serialization: +----------------------------- + +SUGG: Fix address to be ipv6 compatible + +User Interface: +--------------- + +Graphics: +--------- + +SUGG: Combine MapBlock's face caches to so big pieces that VBO + can be used + - That is >500 vertices + - This is not easy; all the MapBlocks close to the player would + still need to be drawn separately and combining the blocks + would have to happen in a background thread + +SUGG: Make fetching sector's blocks more efficient when rendering + sectors that have very large amounts of blocks (on client) + - Is this necessary at all? + +SUGG: Draw cubes in inventory directly with 3D drawing commands, so that + animating them is easier. + +SUGG: Option for enabling proper alpha channel for textures + +TODO: Flowing water animation + +TODO: A setting for enabling bilinear filtering for textures + +TODO: Better control of draw_control.wanted_max_blocks + +TODO: Further investigate the use of GPU lighting in addition to the + current one + +TODO: Artificial (night) light could be more yellow colored than sunlight. + - This is technically doable. + - Also the actual colors of the textures could be made less colorful + in the dark but it's a bit more difficult. + +SUGG: Somehow make the night less colorful + +TODO: Occlusion culling + - At the same time, move some of the renderMap() block choosing code + to the same place as where the new culling happens. + - Shoot some rays per frame and when ready, make a new list of + blocks for usage of renderMap and give it a new pointer to it. + +Configuration: +-------------- + +Client: +------- + +TODO: Untie client network operations from framerate + - Needs some input queues or something + - This won't give much performance boost because calculating block + meshes takes so long + +SUGG: Make morning and evening transition more smooth and maybe shorter + +TODO: Don't update all meshes always on single node changes, but + check which ones should be updated + - implement Map::updateNodeMeshes() and the usage of it + - It will give almost always a 4x boost in mesh update performance. + +- A weapon engine + +- Tool/weapon visualization + +FIXME: When disconnected to the menu, memory is not freed properly + +TODO: Investigate how much the mesh generator thread gets used when + transferring map data + +Server: +------- + +SUGG: Make an option to the server to disable building and digging near + the starting position + +FIXME: Server sometimes goes into some infinite PeerNotFoundException loop + +* Fix the problem with the server constantly saving one or a few + blocks? List the first saved block, maybe it explains. + - It is probably caused by oscillating water + - TODO: Investigate if this still happens (this is a very old one) +* Make a small history check to transformLiquids to detect and log + continuous oscillations, in such detail that they can be fixed. + +FIXME: The new optimized map sending doesn't sometimes send enough blocks + from big caves and such +FIXME: Block send distance configuration does not take effect for some reason + +Environment: +------------ + +TODO: Add proper hooks to when adding and removing active blocks + +TODO: Finish the ActiveBlockModifier stuff and use it for something + +Objects: +-------- + +TODO: Get rid of MapBlockObjects and use only ActiveObjects + - Skipping the MapBlockObject data is nasty - there is no "total + length" stored; have to make a SkipMBOs function which contains + enough of the current code to skip them properly. + +SUGG: MovingObject::move and Player::move are basically the same. + combine them. + - NOTE: This is a bit tricky because player has the sneaking ability + - NOTE: Player::move is more up-to-date. + - NOTE: There is a simple move implementation now in collision.{h,cpp} + - NOTE: MovingObject will be deleted (MapBlockObject) + +TODO: Add a long step function to objects that is called with the time + difference when block activates + +Map: +---- + +TODO: Mineral and ground material properties + - This way mineral ground toughness can be calculated with just + some formula, as well as tool strengths. Sounds too. + - There are TODOs in appropriate files: material.h, content_mapnode.h + +TODO: Flowing water to actually contain flow direction information + - There is a space for this - it just has to be implemented. + +TODO: Consider smoothening cave floors after generating them + +TODO: Fix make_tree, make_* to use seed-position-consistent pseudorandom + - delta also + +Misc. stuff: +------------ +TODO: Make sure server handles removing grass when a block is placed (etc) + - The client should not do it by itself + - NOTE: I think nobody does it currently... +TODO: Block cube placement around player's head +TODO: Protocol version field +TODO: Think about using same bits for material for fences and doors, for + example +TODO: Move mineral to param2, increment map serialization version, add + conversion + +SUGG: Restart irrlicht completely when coming back to main menu from game. + - This gets rid of everything that is stored in irrlicht's caches. + - This might be needed for texture pack selection in menu + +TODO: Merge bahamada's audio stuff (clean patch available) + +TODO: Move content_features to mapnode_content_features.{h,cpp} or so + +Making it more portable: +------------------------ + +Stuff to do before release: +--------------------------- + +Fixes to the current release: +----------------------------- + +Stuff to do after release: +--------------------------- + +Doing currently: +---------------- + +====================================================================== + +*/ + +#ifdef NDEBUG + #ifdef _WIN32 + #pragma message ("Disabling unit tests") + #else + #warning "Disabling unit tests" + #endif + // Disable unit tests + #define ENABLE_TESTS 0 +#else + // Enable unit tests + #define ENABLE_TESTS 1 +#endif + +#ifdef _MSC_VER + #pragma comment(lib, "Irrlicht.lib") + //#pragma comment(lib, "jthread.lib") + #pragma comment(lib, "zlibwapi.lib") + #pragma comment(lib, "Shell32.lib") + // This would get rid of the console window + //#pragma comment(linker, "/subsystem:windows /ENTRY:mainCRTStartup") +#endif + +#include +#include +#include +#include "main.h" +#include "common_irrlicht.h" +#include "debug.h" +#include "test.h" +#include "server.h" +#include "constants.h" +#include "porting.h" +#include "gettime.h" +#include "guiMessageMenu.h" +#include "filesys.h" +#include "config.h" +#include "guiMainMenu.h" +#include "mineral.h" +#include "materials.h" +#include "game.h" +#include "keycode.h" +#include "tile.h" + +#include "gettext.h" + +// This makes textures +ITextureSource *g_texturesource = NULL; + +/* + Settings. + These are loaded from the config file. +*/ + +Settings g_settings; +// This is located in defaultsettings.cpp +extern void set_default_settings(); + +// Global profiler +Profiler g_profiler; + +/* + Random stuff +*/ + +/* + GUI Stuff +*/ + +gui::IGUIEnvironment* guienv = NULL; +gui::IGUIStaticText *guiroot = NULL; + +MainMenuManager g_menumgr; + +bool noMenuActive() +{ + return (g_menumgr.menuCount() == 0); +} + +// Passed to menus to allow disconnecting and exiting + +MainGameCallback *g_gamecallback = NULL; + +/* + Debug streams +*/ + +// Connection +std::ostream *dout_con_ptr = &dummyout; +std::ostream *derr_con_ptr = &dstream_no_stderr; +//std::ostream *dout_con_ptr = &dstream_no_stderr; +//std::ostream *derr_con_ptr = &dstream_no_stderr; +//std::ostream *dout_con_ptr = &dstream; +//std::ostream *derr_con_ptr = &dstream; + +// Server +std::ostream *dout_server_ptr = &dstream; +std::ostream *derr_server_ptr = &dstream; + +// Client +std::ostream *dout_client_ptr = &dstream; +std::ostream *derr_client_ptr = &dstream; + +/* + gettime.h implementation +*/ + +// A small helper class +class TimeGetter +{ +public: + virtual u32 getTime() = 0; +}; + +// A precise irrlicht one +class IrrlichtTimeGetter: public TimeGetter +{ +public: + IrrlichtTimeGetter(IrrlichtDevice *device): + m_device(device) + {} + u32 getTime() + { + if(m_device == NULL) + return 0; + return m_device->getTimer()->getRealTime(); + } +private: + IrrlichtDevice *m_device; +}; +// Not so precise one which works without irrlicht +class SimpleTimeGetter: public TimeGetter +{ +public: + u32 getTime() + { + return porting::getTimeMs(); + } +}; + +// A pointer to a global instance of the time getter +// TODO: why? +TimeGetter *g_timegetter = NULL; + +u32 getTimeMs() +{ + if(g_timegetter == NULL) + return 0; + return g_timegetter->getTime(); +} + +/* + Event handler for Irrlicht + + NOTE: Everything possible should be moved out from here, + probably to InputHandler and the_game +*/ + +class MyEventReceiver : public IEventReceiver +{ +public: + // This is the one method that we have to implement + virtual bool OnEvent(const SEvent& event) + { + /* + React to nothing here if a menu is active + */ + if(noMenuActive() == false) + { + return false; + } + + // Remember whether each key is down or up + if(event.EventType == irr::EET_KEY_INPUT_EVENT) + { + keyIsDown[event.KeyInput.Key] = event.KeyInput.PressedDown; + + if(event.KeyInput.PressedDown) + keyWasDown[event.KeyInput.Key] = true; + } + + if(event.EventType == irr::EET_MOUSE_INPUT_EVENT) + { + if(noMenuActive() == false) + { + left_active = false; + middle_active = false; + right_active = false; + } + else + { + //dstream<<"MyEventReceiver: mouse input"<IsKeyDown(keyCode); + } + virtual bool wasKeyDown(EKEY_CODE keyCode) + { + return m_receiver->WasKeyDown(keyCode); + } + virtual v2s32 getMousePos() + { + return m_device->getCursorControl()->getPosition(); + } + virtual void setMousePos(s32 x, s32 y) + { + m_device->getCursorControl()->setPosition(x, y); + } + + virtual bool getLeftState() + { + return m_receiver->left_active; + } + virtual bool getRightState() + { + return m_receiver->right_active; + } + + virtual bool getLeftClicked() + { + return m_receiver->leftclicked; + } + virtual bool getRightClicked() + { + return m_receiver->rightclicked; + } + virtual void resetLeftClicked() + { + m_receiver->leftclicked = false; + } + virtual void resetRightClicked() + { + m_receiver->rightclicked = false; + } + + virtual bool getLeftReleased() + { + return m_receiver->leftreleased; + } + virtual bool getRightReleased() + { + return m_receiver->rightreleased; + } + virtual void resetLeftReleased() + { + m_receiver->leftreleased = false; + } + virtual void resetRightReleased() + { + m_receiver->rightreleased = false; + } + + virtual s32 getMouseWheel() + { + return m_receiver->getMouseWheel(); + } + + void clear() + { + m_receiver->clearInput(); + } +private: + IrrlichtDevice *m_device; + MyEventReceiver *m_receiver; +}; + +class RandomInputHandler : public InputHandler +{ +public: + RandomInputHandler() + { + leftdown = false; + rightdown = false; + leftclicked = false; + rightclicked = false; + leftreleased = false; + rightreleased = false; + for(u32 i=0; i map1; + tempf = -324; + const s16 ii=300; + for(s16 y=0; y=0; y--){ + for(s16 x=0; x screensize = driver->getScreenSize(); + + video::ITexture *bgtexture = + driver->getTexture(getTexturePath("mud.png").c_str()); + if(bgtexture) + { + s32 texturesize = 128; + s32 tiled_y = screensize.Height / texturesize + 1; + s32 tiled_x = screensize.Width / texturesize + 1; + + for(s32 y=0; y rect(0,0,texturesize,texturesize); + rect += v2s32(x*texturesize, y*texturesize); + driver->draw2DImage(bgtexture, rect, + core::rect(core::position2d(0,0), + core::dimension2di(bgtexture->getSize())), + NULL, NULL, true); + } + } + + video::ITexture *logotexture = + driver->getTexture(getTexturePath("menulogo.png").c_str()); + if(logotexture) + { + v2s32 logosize(logotexture->getOriginalSize().Width, + logotexture->getOriginalSize().Height); + logosize *= 4; + + video::SColor bgcolor(255,50,50,50); + core::rect bgrect(0, screensize.Height-logosize.Y-20, + screensize.Width, screensize.Height); + driver->draw2DRectangle(bgcolor, bgrect, NULL); + + core::rect rect(0,0,logosize.X,logosize.Y); + rect += v2s32(screensize.Width/2,screensize.Height-10-logosize.Y); + rect -= v2s32(logosize.X/2, 0); + driver->draw2DImage(logotexture, rect, + core::rect(core::position2d(0,0), + core::dimension2di(logotexture->getSize())), + NULL, NULL, true); + } +} + +int main(int argc, char *argv[]) +{ + /* + Initialization + */ + + // Set locale. This is for forcing '.' as the decimal point. + std::locale::global(std::locale("C")); + // This enables printing all characters in bitmap font + setlocale(LC_CTYPE, "en_US"); + + /* + Parse command line + */ + + // List all allowed options + core::map allowed_options; + allowed_options.insert("help", ValueSpec(VALUETYPE_FLAG)); + allowed_options.insert("server", ValueSpec(VALUETYPE_FLAG, + "Run server directly")); + allowed_options.insert("config", ValueSpec(VALUETYPE_STRING, + "Load configuration from specified file")); + allowed_options.insert("port", ValueSpec(VALUETYPE_STRING)); + allowed_options.insert("address", ValueSpec(VALUETYPE_STRING)); + allowed_options.insert("random-input", ValueSpec(VALUETYPE_FLAG)); + allowed_options.insert("disable-unittests", ValueSpec(VALUETYPE_FLAG)); + allowed_options.insert("enable-unittests", ValueSpec(VALUETYPE_FLAG)); + allowed_options.insert("map-dir", ValueSpec(VALUETYPE_STRING)); +#ifdef _WIN32 + allowed_options.insert("dstream-on-stderr", ValueSpec(VALUETYPE_FLAG)); +#endif + allowed_options.insert("speedtests", ValueSpec(VALUETYPE_FLAG)); + + Settings cmd_args; + + bool ret = cmd_args.parseCommandLine(argc, argv, allowed_options); + + if(ret == false || cmd_args.getFlag("help")) + { + dstream<<"Allowed options:"<::Iterator + i = allowed_options.getIterator(); + i.atEnd() == false; i++) + { + dstream<<" --"<getKey(); + if(i.getNode()->getValue().type == VALUETYPE_FLAG) + { + } + else + { + dstream<<" "; + } + dstream<getValue().help != NULL) + { + dstream<<" "<getValue().help + < filenames; + filenames.push_back(porting::path_userdata + "/minetest.conf"); +#ifdef RUN_IN_PLACE + filenames.push_back(porting::path_userdata + "/../minetest.conf"); +#endif + + for(u32 i=0; i(screenW, screenH), + 16, fullscreen, false, false, &receiver); + + if (device == 0) + return 1; // could not create selected driver. + + // Set device in game parameters + device = device; // Set the window caption device->setWindowCaption(L"Minetest [Main Menu]"); - - // Create time getter - g_timegetter = new IrrlichtTimeGetter(device); - - // Create game callback for menus - g_gamecallback = new MainGameCallback(device); - - // Create texture source - g_texturesource = new TextureSource(device); - - /* - Speed tests (done after irrlicht is loaded to get timer) - */ - if(cmd_args.getFlag("speedtests")) - { - dstream<<"Running speed tests"<setResizable(true); - - bool random_input = g_settings.getBool("random_input") - || cmd_args.getFlag("random-input"); - InputHandler *input = NULL; - if(random_input) - input = new RandomInputHandler(); - else - input = new RealInputHandler(device, &receiver); - - /* - Continue initialization - */ - - //video::IVideoDriver* driver = device->getVideoDriver(); - - /* - This changes the minimum allowed number of vertices in a VBO. - Default is 500. - */ - //driver->setMinHardwareBufferVertexCount(50); - - scene::ISceneManager* smgr = device->getSceneManager(); - - guienv = device->getGUIEnvironment(); - gui::IGUISkin* skin = guienv->getSkin(); - gui::IGUIFont* font = guienv->getFont(getTexturePath("fontlucida.png").c_str()); - if(font) - skin->setFont(font); - else - dstream<<"WARNING: Font file was not found." - " Using default font."<getFont(); - assert(font); - - u32 text_height = font->getDimension(L"Hello, world!").Height; - dstream<<"text_height="<setColor(gui::EGDC_BUTTON_TEXT, video::SColor(255,0,0,0)); - skin->setColor(gui::EGDC_BUTTON_TEXT, video::SColor(255,255,255,255)); - //skin->setColor(gui::EGDC_3D_HIGH_LIGHT, video::SColor(0,0,0,0)); - //skin->setColor(gui::EGDC_3D_SHADOW, video::SColor(0,0,0,0)); - skin->setColor(gui::EGDC_3D_HIGH_LIGHT, video::SColor(255,0,0,0)); - skin->setColor(gui::EGDC_3D_SHADOW, video::SColor(255,0,0,0)); - - /* - Preload some textures and stuff - */ - - init_mapnode(); // Second call with g_texturesource set - init_mineral(); - - /* - GUI stuff - */ - - /* - If an error occurs, this is set to something and the - menu-game loop is restarted. It is then displayed before - the menu. - */ - std::wstring error_message = L""; - - // The password entered during the menu screen, - std::string password; - - /* - Menu-game loop - */ - while(device->run() && kill == false) - { - - // This is used for catching disconnects - try - { - - /* - Clear everything from the GUIEnvironment - */ - guienv->clear(); - - /* - We need some kind of a root node to be able to add - custom gui elements directly on the screen. - Otherwise they won't be automatically drawn. - */ - guiroot = guienv->addStaticText(L"", - core::rect(0, 0, 10000, 10000)); - - /* - Out-of-game menu loop. - - Loop quits when menu returns proper parameters. - */ - while(kill == false) - { - // Cursor can be non-visible when coming from the game - device->getCursorControl()->setVisible(true); - // Some stuff are left to scene manager when coming from the game - // (map at least?) - smgr->clear(); - // Reset or hide the debug gui texts - /*guitext->setText(L"Minetest-c55"); - guitext2->setVisible(false); - guitext_info->setVisible(false); - guitext_chat->setVisible(false);*/ - - // Initialize menu data - MainMenuData menudata; - menudata.address = narrow_to_wide(address); - menudata.name = narrow_to_wide(playername); - menudata.port = narrow_to_wide(itos(port)); - menudata.fancy_trees = g_settings.getBool("new_style_leaves"); - menudata.smooth_lighting = g_settings.getBool("smooth_lighting"); - menudata.creative_mode = g_settings.getBool("creative_mode"); - menudata.enable_damage = g_settings.getBool("enable_damage"); - - GUIMainMenu *menu = - new GUIMainMenu(guienv, guiroot, -1, - &g_menumgr, &menudata, g_gamecallback); - menu->allowFocusRemoval(true); - - if(error_message != L"") - { - dstream<<"WARNING: error_message = " - <drop(); - error_message = L""; - } - - video::IVideoDriver* driver = device->getVideoDriver(); - - dstream<<"Created main menu"<run() && kill == false) - { - if(menu->getStatus() == true) - break; - - //driver->beginScene(true, true, video::SColor(255,0,0,0)); - driver->beginScene(true, true, video::SColor(255,128,128,128)); - - drawMenuBackground(driver); - - guienv->drawAll(); - - driver->endScene(); - - // On some computers framerate doesn't seem to be - // automatically limited - sleep_ms(25); - } - - // Break out of menu-game loop to shut down cleanly - if(device->run() == false || kill == true) - break; - - dstream<<"Dropping main menu"<drop(); - - // Delete map if requested - if(menudata.delete_map) - { - bool r = fs::RecursiveDeleteContent(map_dir); - if(r == false) - error_message = L"Delete failed"; - continue; - } - - playername = wide_to_narrow(menudata.name); - - password = translatePassword(playername, menudata.password); - - address = wide_to_narrow(menudata.address); - int newport = stoi(wide_to_narrow(menudata.port)); - if(newport != 0) - port = newport; - g_settings.set("new_style_leaves", itos(menudata.fancy_trees)); - g_settings.set("smooth_lighting", itos(menudata.smooth_lighting)); - g_settings.set("creative_mode", itos(menudata.creative_mode)); - g_settings.set("enable_damage", itos(menudata.enable_damage)); - - // NOTE: These are now checked server side; no need to do it - // here, so let's not do it here. - /*// Check for valid parameters, restart menu if invalid. - if(playername == "") - { - error_message = L"Name required."; - continue; - } - // Check that name has only valid chars - if(string_allowed(playername, PLAYERNAME_ALLOWED_CHARS)==false) - { - error_message = L"Characters allowed: " - +narrow_to_wide(PLAYERNAME_ALLOWED_CHARS); - continue; - }*/ - - // Save settings - g_settings.set("name", playername); - g_settings.set("address", address); - g_settings.set("port", itos(port)); - // Update configuration file - if(configpath != "") - g_settings.updateConfigFile(configpath.c_str()); - - // Continue to game - break; - } - - // Break out of menu-game loop to shut down cleanly - if(device->run() == false) - break; - - // Initialize mapnode again to enable changed graphics settings - init_mapnode(); - - /* - Run game - */ - the_game( - kill, - random_input, - input, - device, - font, - map_dir, - playername, - password, - address, - port, - error_message - ); - - } //try - catch(con::PeerNotFoundException &e) - { - dstream<drop(); - - END_DEBUG_EXCEPTION_HANDLER - - debugstreams_deinit(); - - return 0; -} - -//END + + // Create time getter + g_timegetter = new IrrlichtTimeGetter(device); + + // Create game callback for menus + g_gamecallback = new MainGameCallback(device); + + // Create texture source + g_texturesource = new TextureSource(device); + + /* + Speed tests (done after irrlicht is loaded to get timer) + */ + if(cmd_args.getFlag("speedtests")) + { + dstream<<"Running speed tests"<setResizable(true); + + bool random_input = g_settings.getBool("random_input") + || cmd_args.getFlag("random-input"); + InputHandler *input = NULL; + if(random_input) + input = new RandomInputHandler(); + else + input = new RealInputHandler(device, &receiver); + + /* + Continue initialization + */ + + //video::IVideoDriver* driver = device->getVideoDriver(); + + /* + This changes the minimum allowed number of vertices in a VBO. + Default is 500. + */ + //driver->setMinHardwareBufferVertexCount(50); + + scene::ISceneManager* smgr = device->getSceneManager(); + + guienv = device->getGUIEnvironment(); + gui::IGUISkin* skin = guienv->getSkin(); + gui::IGUIFont* font = guienv->getFont(getTexturePath("fontlucida.png").c_str()); + if(font) + skin->setFont(font); + else + dstream<<"WARNING: Font file was not found." + " Using default font."<getFont(); + assert(font); + + u32 text_height = font->getDimension(L"Hello, world!").Height; + dstream<<"text_height="<setColor(gui::EGDC_BUTTON_TEXT, video::SColor(255,0,0,0)); + skin->setColor(gui::EGDC_BUTTON_TEXT, video::SColor(255,255,255,255)); + //skin->setColor(gui::EGDC_3D_HIGH_LIGHT, video::SColor(0,0,0,0)); + //skin->setColor(gui::EGDC_3D_SHADOW, video::SColor(0,0,0,0)); + skin->setColor(gui::EGDC_3D_HIGH_LIGHT, video::SColor(255,0,0,0)); + skin->setColor(gui::EGDC_3D_SHADOW, video::SColor(255,0,0,0)); + + /* + Preload some textures and stuff + */ + + init_mapnode(); // Second call with g_texturesource set + init_mineral(); + + /* + GUI stuff + */ + + /* + If an error occurs, this is set to something and the + menu-game loop is restarted. It is then displayed before + the menu. + */ + std::wstring error_message = L""; + + // The password entered during the menu screen, + std::string password; + + /* + Menu-game loop + */ + while(device->run() && kill == false) + { + + // This is used for catching disconnects + try + { + + /* + Clear everything from the GUIEnvironment + */ + guienv->clear(); + + /* + We need some kind of a root node to be able to add + custom gui elements directly on the screen. + Otherwise they won't be automatically drawn. + */ + guiroot = guienv->addStaticText(L"", + core::rect(0, 0, 10000, 10000)); + + /* + Out-of-game menu loop. + + Loop quits when menu returns proper parameters. + */ + while(kill == false) + { + // Cursor can be non-visible when coming from the game + device->getCursorControl()->setVisible(true); + // Some stuff are left to scene manager when coming from the game + // (map at least?) + smgr->clear(); + // Reset or hide the debug gui texts + /*guitext->setText(L"Minetest-c55"); + guitext2->setVisible(false); + guitext_info->setVisible(false); + guitext_chat->setVisible(false);*/ + + // Initialize menu data + MainMenuData menudata; + menudata.address = narrow_to_wide(address); + menudata.name = narrow_to_wide(playername); + menudata.port = narrow_to_wide(itos(port)); + menudata.fancy_trees = g_settings.getBool("new_style_leaves"); + menudata.smooth_lighting = g_settings.getBool("smooth_lighting"); + menudata.creative_mode = g_settings.getBool("creative_mode"); + menudata.enable_damage = g_settings.getBool("enable_damage"); + + GUIMainMenu *menu = + new GUIMainMenu(guienv, guiroot, -1, + &g_menumgr, &menudata, g_gamecallback); + menu->allowFocusRemoval(true); + + if(error_message != L"") + { + dstream<<"WARNING: error_message = " + <drop(); + error_message = L""; + } + + video::IVideoDriver* driver = device->getVideoDriver(); + + dstream<<"Created main menu"<run() && kill == false) + { + if(menu->getStatus() == true) + break; + + //driver->beginScene(true, true, video::SColor(255,0,0,0)); + driver->beginScene(true, true, video::SColor(255,128,128,128)); + + drawMenuBackground(driver); + + guienv->drawAll(); + + driver->endScene(); + + // On some computers framerate doesn't seem to be + // automatically limited + sleep_ms(25); + } + + // Break out of menu-game loop to shut down cleanly + if(device->run() == false || kill == true) + break; + + dstream<<"Dropping main menu"<drop(); + + // Delete map if requested + if(menudata.delete_map) + { + bool r = fs::RecursiveDeleteContent(map_dir); + if(r == false) + error_message = L"Delete failed"; + continue; + } + + playername = wide_to_narrow(menudata.name); + + password = translatePassword(playername, menudata.password); + + address = wide_to_narrow(menudata.address); + int newport = stoi(wide_to_narrow(menudata.port)); + if(newport != 0) + port = newport; + g_settings.set("new_style_leaves", itos(menudata.fancy_trees)); + g_settings.set("smooth_lighting", itos(menudata.smooth_lighting)); + g_settings.set("creative_mode", itos(menudata.creative_mode)); + g_settings.set("enable_damage", itos(menudata.enable_damage)); + + // NOTE: These are now checked server side; no need to do it + // here, so let's not do it here. + /*// Check for valid parameters, restart menu if invalid. + if(playername == "") + { + error_message = L"Name required."; + continue; + } + // Check that name has only valid chars + if(string_allowed(playername, PLAYERNAME_ALLOWED_CHARS)==false) + { + error_message = L"Characters allowed: " + +narrow_to_wide(PLAYERNAME_ALLOWED_CHARS); + continue; + }*/ + + // Save settings + g_settings.set("name", playername); + g_settings.set("address", address); + g_settings.set("port", itos(port)); + // Update configuration file + if(configpath != "") + g_settings.updateConfigFile(configpath.c_str()); + + // Continue to game + break; + } + + // Break out of menu-game loop to shut down cleanly + if(device->run() == false) + break; + + // Initialize mapnode again to enable changed graphics settings + init_mapnode(); + + /* + Run game + */ + the_game( + kill, + random_input, + input, + device, + font, + map_dir, + playername, + password, + address, + port, + error_message + ); + + } //try + catch(con::PeerNotFoundException &e) + { + dstream<drop(); + + END_DEBUG_EXCEPTION_HANDLER + + debugstreams_deinit(); + + return 0; +} + +//END + diff --git a/src/map.cpp b/src/map.cpp index e1769b8ef..092ce97fd 100644 --- a/src/map.cpp +++ b/src/map.cpp @@ -630,9 +630,9 @@ s16 Map::propagateSunlight(v3s16 start, else { /*// Turn mud into grass - if(n.d == CONTENT_MUD) + if(n.getContent() == CONTENT_MUD) { - n.d = CONTENT_GRASS; + n.setContent(CONTENT_GRASS); block->setNode(relpos, n); modified_blocks.insert(blockpos, block); }*/ @@ -920,15 +920,15 @@ void Map::addNodeAndUpdate(v3s16 p, MapNode n, /* If the new node is solid and there is grass below, change it to mud */ - if(content_features(n.d).walkable == true) + if(content_features(n).walkable == true) { try{ MapNode bottomnode = getNode(bottompos); - if(bottomnode.d == CONTENT_GRASS - || bottomnode.d == CONTENT_GRASS_FOOTSTEPS) + if(bottomnode.getContent() == CONTENT_GRASS + || bottomnode.getContent() == CONTENT_GRASS_FOOTSTEPS) { - bottomnode.d = CONTENT_MUD; + bottomnode.setContent(CONTENT_MUD); setNode(bottompos, bottomnode); } } @@ -943,9 +943,9 @@ void Map::addNodeAndUpdate(v3s16 p, MapNode n, If the new node is mud and it is under sunlight, change it to grass */ - if(n.d == CONTENT_MUD && node_under_sunlight) + if(n.getContent() == CONTENT_MUD && node_under_sunlight) { - n.d = CONTENT_GRASS; + n.setContent(CONTENT_GRASS); } #endif @@ -986,7 +986,7 @@ void Map::addNodeAndUpdate(v3s16 p, MapNode n, If node lets sunlight through and is under sunlight, it has sunlight too. */ - if(node_under_sunlight && content_features(n.d).sunlight_propagates) + if(node_under_sunlight && content_features(n).sunlight_propagates) { n.setLight(LIGHTBANK_DAY, LIGHT_SUN); } @@ -1001,7 +1001,7 @@ void Map::addNodeAndUpdate(v3s16 p, MapNode n, Add intial metadata */ - NodeMetadata *meta_proto = content_features(n.d).initial_metadata; + NodeMetadata *meta_proto = content_features(n).initial_metadata; if(meta_proto) { NodeMetadata *meta = meta_proto->clone(); @@ -1015,7 +1015,7 @@ void Map::addNodeAndUpdate(v3s16 p, MapNode n, TODO: This could be optimized by mass-unlighting instead of looping */ - if(node_under_sunlight && !content_features(n.d).sunlight_propagates) + if(node_under_sunlight && !content_features(n).sunlight_propagates) { s16 y = p.Y - 1; for(;; y--){ @@ -1086,7 +1086,7 @@ void Map::addNodeAndUpdate(v3s16 p, MapNode n, v3s16 p2 = p + dirs[i]; MapNode n2 = getNode(p2); - if(content_liquid(n2.d) || n2.d == CONTENT_AIR) + if(content_liquid(n2.getContent())) { m_transforming_liquid.push_back(p2); } @@ -1111,7 +1111,7 @@ void Map::removeNodeAndUpdate(v3s16 p, v3s16 toppos = p + v3s16(0,1,0); // Node will be replaced with this - u8 replace_material = CONTENT_AIR; + content_t replace_material = CONTENT_AIR; /* If there is a node at top and it doesn't have sunlight, @@ -1158,7 +1158,7 @@ void Map::removeNodeAndUpdate(v3s16 p, */ MapNode n; - n.d = replace_material; + n.setContent(replace_material); setNode(p, n); for(s32 i=0; i<2; i++) @@ -1260,7 +1260,7 @@ void Map::removeNodeAndUpdate(v3s16 p, v3s16 p2 = p + dirs[i]; MapNode n2 = getNode(p2); - if(content_liquid(n2.d) || n2.d == CONTENT_AIR) + if(content_liquid(n2.getContent())) { m_transforming_liquid.push_back(p2); } @@ -1576,20 +1576,20 @@ void Map::transformLiquids(core::map & modified_blocks) */ s8 liquid_level = -1; u8 liquid_kind = CONTENT_IGNORE; - LiquidType liquid_type = content_features(n0.d).liquid_type; + LiquidType liquid_type = content_features(n0.getContent()).liquid_type; switch (liquid_type) { case LIQUID_SOURCE: liquid_level = 8; - liquid_kind = content_features(n0.d).liquid_alternative_flowing; + liquid_kind = content_features(n0.getContent()).liquid_alternative_flowing; break; case LIQUID_FLOWING: liquid_level = (n0.param2 & LIQUID_LEVEL_MASK); - liquid_kind = n0.d; + liquid_kind = n0.getContent(); break; case LIQUID_NONE: // if this is an air node, it *could* be transformed into a liquid. otherwise, // continue with the next node. - if (n0.d != CONTENT_AIR) + if (n0.getContent() != CONTENT_AIR) continue; liquid_kind = CONTENT_AIR; break; @@ -1627,9 +1627,9 @@ void Map::transformLiquids(core::map & modified_blocks) } v3s16 npos = p0 + dirs[i]; NodeNeighbor nb = {getNodeNoEx(npos), nt, npos}; - switch (content_features(nb.n.d).liquid_type) { + switch (content_features(nb.n.getContent()).liquid_type) { case LIQUID_NONE: - if (nb.n.d == CONTENT_AIR) { + if (nb.n.getContent() == CONTENT_AIR) { airs[num_airs++] = nb; // if the current node happens to be a flowing node, it will start to flow down here. if (nb.t == NEIGHBOR_LOWER) @@ -1641,8 +1641,8 @@ void Map::transformLiquids(core::map & modified_blocks) case LIQUID_SOURCE: // if this node is not (yet) of a liquid type, choose the first liquid type we encounter if (liquid_kind == CONTENT_AIR) - liquid_kind = content_features(nb.n.d).liquid_alternative_flowing; - if (content_features(nb.n.d).liquid_alternative_flowing !=liquid_kind) { + liquid_kind = content_features(nb.n.getContent()).liquid_alternative_flowing; + if (content_features(nb.n.getContent()).liquid_alternative_flowing !=liquid_kind) { neutrals[num_neutrals++] = nb; } else { sources[num_sources++] = nb; @@ -1651,8 +1651,8 @@ void Map::transformLiquids(core::map & modified_blocks) case LIQUID_FLOWING: // if this node is not (yet) of a liquid type, choose the first liquid type we encounter if (liquid_kind == CONTENT_AIR) - liquid_kind = content_features(nb.n.d).liquid_alternative_flowing; - if (content_features(nb.n.d).liquid_alternative_flowing != liquid_kind) { + liquid_kind = content_features(nb.n.getContent()).liquid_alternative_flowing; + if (content_features(nb.n.getContent()).liquid_alternative_flowing != liquid_kind) { neutrals[num_neutrals++] = nb; } else { flows[num_flows++] = nb; @@ -1722,7 +1722,7 @@ void Map::transformLiquids(core::map & modified_blocks) /* check if anything has changed. if not, just continue with the next node. */ - if (new_node_content == n0.d && (content_features(n0.d).liquid_type != LIQUID_FLOWING || + if (new_node_content == n0.getContent() && (content_features(n0.getContent()).liquid_type != LIQUID_FLOWING || ((n0.param2 & LIQUID_LEVEL_MASK) == (u8)new_node_level && ((n0.param2 & LIQUID_FLOW_DOWN_MASK) == LIQUID_FLOW_DOWN_MASK) == flowing_down))) @@ -1733,8 +1733,8 @@ void Map::transformLiquids(core::map & modified_blocks) update the current node */ bool flow_down_enabled = (flowing_down && ((n0.param2 & LIQUID_FLOW_DOWN_MASK) != LIQUID_FLOW_DOWN_MASK)); - n0.d = new_node_content; - if (content_features(n0.d).liquid_type == LIQUID_FLOWING) { + n0.setContent(new_node_content); + if (content_features(n0.getContent()).liquid_type == LIQUID_FLOWING) { // set level to last 3 bits, flowing down bit to 4th bit n0.param2 = (flowing_down ? LIQUID_FLOW_DOWN_MASK : 0x00) | (new_node_level & LIQUID_LEVEL_MASK); } else { @@ -1749,7 +1749,7 @@ void Map::transformLiquids(core::map & modified_blocks) /* enqueue neighbors for update if neccessary */ - switch (content_features(n0.d).liquid_type) { + switch (content_features(n0.getContent()).liquid_type) { case LIQUID_SOURCE: // make sure source flows into all neighboring nodes for (u16 i = 0; i < num_flows; i++) @@ -2003,8 +2003,10 @@ ServerMap::~ServerMap() void ServerMap::initBlockMake(mapgen::BlockMakeData *data, v3s16 blockpos) { - /*dstream<<"initBlockMake(): ("<seed, p); + block->setIsUnderground(ug); + } // Lighting will not be valid after make_chunk is called block->setLightingExpired(true); // Lighting will be calculated //block->setLightingExpired(false); - - /* - Block gets sunlight if this is true. - - This should be set to true when the top side of a block - is completely exposed to the sky. - */ - block->setIsUnderground(false); } } } @@ -2128,10 +2133,14 @@ MapBlock* ServerMap::finishBlockMake(mapgen::BlockMakeData *data, assert(block); /* - Set is_underground flag for lighting with sunlight - */ + Set is_underground flag for lighting with sunlight. + + Refer to map generator heuristics. + + NOTE: This is done in initChunkMake + */ + //block->setIsUnderground(mapgen::block_is_underground(data->seed, blockpos)); - block->setIsUnderground(mapgen::block_is_underground(data->seed, blockpos)); /* Add sunlight to central block. @@ -2162,6 +2171,13 @@ MapBlock* ServerMap::finishBlockMake(mapgen::BlockMakeData *data, #if 1 // Center block lighting_update_blocks.insert(block->getPos(), block); + + /*{ + s16 x = 0; + s16 z = 0; + v3s16 p = block->getPos()+v3s16(x,1,z); + lighting_update_blocks[p] = getBlockNoCreateNoEx(p); + }*/ #endif #if 0 // All modified blocks @@ -2178,8 +2194,28 @@ MapBlock* ServerMap::finishBlockMake(mapgen::BlockMakeData *data, lighting_update_blocks.insert(i.getNode()->getKey(), i.getNode()->getValue()); } + /*// Also force-add all the upmost blocks for proper sunlight + for(s16 x=-1; x<=1; x++) + for(s16 z=-1; z<=1; z++) + { + v3s16 p = block->getPos()+v3s16(x,1,z); + lighting_update_blocks[p] = getBlockNoCreateNoEx(p); + }*/ #endif updateLighting(lighting_update_blocks, changed_blocks); + + /* + Set lighting to non-expired state in all of them. + This is cheating, but it is not fast enough if all of them + would actually be updated. + */ + for(s16 x=-1; x<=1; x++) + for(s16 y=-1; y<=1; y++) + for(s16 z=-1; z<=1; z++) + { + v3s16 p = block->getPos()+v3s16(x,y,z); + getBlockNoCreateNoEx(p)->setLightingExpired(false); + } if(enable_mapgen_debug_info == false) t.stop(true); // Hide output @@ -2221,7 +2257,26 @@ MapBlock* ServerMap::finishBlockMake(mapgen::BlockMakeData *data, /*dstream<<"finishBlockMake() done for ("<getPos()+v3s16(x,y,z); + MapBlock *block = getBlockNoCreateNoEx(p); + char spos[20]; + snprintf(spos, 20, "(%2d,%2d,%2d)", x, y, z); + dstream<<"Generated "<getNode(p); - if(n.d == CONTENT_IGNORE) + if(n.getContent() == CONTENT_IGNORE) { dstream<<"CONTENT_IGNORE at " <<"("<setNode(v3s16(x0,y0,z0), n); } } @@ -2470,7 +2525,7 @@ MapBlock * ServerMap::emergeBlock(v3s16 p, bool allow_generate) { MapBlock *block = getBlockNoCreateNoEx(p); - if(block) + if(block && block->isDummy() == false) return block; } @@ -2667,19 +2722,19 @@ s16 ServerMap::findGroundLevel(v2s16 p2d) for(; p.Y>min; p.Y--) { MapNode n = getNodeNoEx(p); - if(n.d != CONTENT_IGNORE) + if(n.getContent() != CONTENT_IGNORE) break; } if(p.Y == min) goto plan_b; // If this node is not air, go to plan b - if(getNodeNoEx(p).d != CONTENT_AIR) + if(getNodeNoEx(p).getContent() != CONTENT_AIR) goto plan_b; // Search existing walkable and return it for(; p.Y>min; p.Y--) { MapNode n = getNodeNoEx(p); - if(content_walkable(n.d) && n.d != CONTENT_IGNORE) + if(content_walkable(n.d) && n.getContent() != CONTENT_IGNORE) return p.Y; } @@ -4072,10 +4127,16 @@ void ManualMapVoxelManipulator::blitBackAll( i = m_loaded_blocks.getIterator(); i.atEnd() == false; i++) { + v3s16 p = i.getNode()->getKey(); bool existed = i.getNode()->getValue(); if(existed == false) + { + // The Great Bug was found using this + /*dstream<<"ManualMapVoxelManipulator::blitBackAll: " + <<"Inexistent ("<getKey(); + } MapBlock *block = m_map->getBlockNoCreateNoEx(p); if(block == NULL) { diff --git a/src/mapblock.cpp b/src/mapblock.cpp index 647a17756..44ca75ff0 100644 --- a/src/mapblock.cpp +++ b/src/mapblock.cpp @@ -242,7 +242,12 @@ bool MapBlock::propagateSunlight(core::map & light_sources, // Check if node above block has sunlight try{ MapNode n = getNodeParent(v3s16(x, MAP_BLOCKSIZE, z)); - if(n.d == CONTENT_IGNORE || n.getLight(LIGHTBANK_DAY) != LIGHT_SUN) + if(n.getContent() == CONTENT_IGNORE) + { + // Trust heuristics + no_sunlight = is_underground; + } + else if(n.getLight(LIGHTBANK_DAY) != LIGHT_SUN) { no_sunlight = true; } @@ -260,8 +265,8 @@ bool MapBlock::propagateSunlight(core::map & light_sources, else { MapNode n = getNode(v3s16(x, MAP_BLOCKSIZE-1, z)); - //if(n.d == CONTENT_WATER || n.d == CONTENT_WATERSOURCE) - if(content_features(n.d).sunlight_propagates == false) + //if(n.getContent() == CONTENT_WATER || n.getContent() == CONTENT_WATERSOURCE) + if(content_features(n).sunlight_propagates == false) { no_sunlight = true; } @@ -322,14 +327,14 @@ bool MapBlock::propagateSunlight(core::map & light_sources, bool upper_is_air = false; try { - if(getNodeParent(pos+v3s16(0,1,0)).d == CONTENT_AIR) + if(getNodeParent(pos+v3s16(0,1,0)).getContent() == CONTENT_AIR) upper_is_air = true; } catch(InvalidPositionException &e) { } // Turn mud into grass - if(upper_is_air && n.d == CONTENT_MUD + if(upper_is_air && n.getContent() == CONTENT_MUD && current_light == LIGHT_SUN) { n.d = CONTENT_GRASS; @@ -473,7 +478,7 @@ void MapBlock::updateDayNightDiff() for(u32 i=0; i=0; y--) { - //if(is_ground_content(getNodeRef(p2d.X, y, p2d.Y).d)) - if(content_features(getNodeRef(p2d.X, y, p2d.Y).d).walkable) + MapNode n = getNodeRef(p2d.X, y, p2d.Y); + if(content_features(n).walkable) { if(y == MAP_BLOCKSIZE-1) return -2; @@ -560,7 +565,7 @@ void MapBlock::serialize(std::ostream &os, u8 version) SharedBuffer materialdata(nodecount); for(u32 i=0; i lightdata(nodecount); for(u32 i=0; igetPos(); + char spos[20]; + snprintf(spos, 20, "(%2d,%2d,%2d), ", p.X, p.Y, p.Z); + desc<getModified()) + { + case MOD_STATE_CLEAN: + desc<<"CLEAN, "; + break; + case MOD_STATE_WRITE_AT_UNLOAD: + desc<<"WRITE_AT_UNLOAD, "; + break; + case MOD_STATE_WRITE_NEEDED: + desc<<"WRITE_NEEDED, "; + break; + default: + desc<<"unknown getModified()="+itos(block->getModified())+", "; + } + + if(block->isGenerated()) + desc<<"is_gen [X], "; + else + desc<<"is_gen [ ], "; + + if(block->getIsUnderground()) + desc<<"is_ug [X], "; + else + desc<<"is_ug [ ], "; + +#ifndef SERVER + if(block->getMeshExpired()) + desc<<"mesh_exp [X], "; + else + desc<<"mesh_exp [ ], "; +#endif + + if(block->getLightingExpired()) + desc<<"lighting_exp [X], "; + else + desc<<"lighting_exp [ ], "; + + if(block->isDummy()) + { + desc<<"Dummy, "; + } + else + { + // We'll just define the numbers here, don't want to include + // content_mapnode.h + const content_t content_water = 2; + const content_t content_watersource = 9; + const content_t content_tree = 0x801; + const content_t content_leaves = 0x802; + const content_t content_jungletree = 0x815; + + bool full_ignore = true; + bool some_ignore = false; + bool full_air = true; + bool some_air = false; + bool trees = false; + bool water = false; + for(s16 z0=0; z0getNode(p); + content_t c = n.getContent(); + if(c == CONTENT_IGNORE) + some_ignore = true; + else + full_ignore = false; + if(c == CONTENT_AIR) + some_air = true; + else + full_air = false; + if(c == content_tree || c == content_jungletree + || c == content_leaves) + trees = true; + if(c == content_water + || c == content_watersource) + water = true; + } + + desc<<"content {"; + + std::ostringstream ss; + + if(full_ignore) + ss<<"IGNORE (full), "; + else if(some_ignore) + ss<<"IGNORE, "; + + if(full_air) + ss<<"AIR (full), "; + else if(some_air) + ss<<"AIR, "; + + if(trees) + ss<<"trees, "; + if(water) + ss<<"water, "; + + if(ss.str().size()>=2) + desc< lim) + return video::SColor(alpha,light,light,light); + else + return video::SColor(alpha,light,light,MYMAX(0, + pow((float)light/lim, power)*lim)); +#endif } struct FastFace @@ -198,7 +213,7 @@ void makeFastFace(TileSpec tile, u8 li0, u8 li1, u8 li2, u8 li3, v3f p, float w = tile.texture.size.X; float h = tile.texture.size.Y; - /*video::SColor c = lightColor(alpha, li); + /*video::SColor c = MapBlock_LightColor(alpha, li); face.vertices[0] = video::S3DVertex(vertex_pos[0], v3f(0,1,0), c, core::vector2d(x0+w*abs_scale, y0+h)); @@ -210,16 +225,16 @@ void makeFastFace(TileSpec tile, u8 li0, u8 li1, u8 li2, u8 li3, v3f p, core::vector2d(x0+w*abs_scale, y0));*/ face.vertices[0] = video::S3DVertex(vertex_pos[0], v3f(0,1,0), - lightColor(alpha, li0), + MapBlock_LightColor(alpha, li0), core::vector2d(x0+w*abs_scale, y0+h)); face.vertices[1] = video::S3DVertex(vertex_pos[1], v3f(0,1,0), - lightColor(alpha, li1), + MapBlock_LightColor(alpha, li1), core::vector2d(x0, y0+h)); face.vertices[2] = video::S3DVertex(vertex_pos[2], v3f(0,1,0), - lightColor(alpha, li2), + MapBlock_LightColor(alpha, li2), core::vector2d(x0, y0)); face.vertices[3] = video::S3DVertex(vertex_pos[3], v3f(0,1,0), - lightColor(alpha, li3), + MapBlock_LightColor(alpha, li3), core::vector2d(x0+w*abs_scale, y0)); face.tile = tile; @@ -285,7 +300,7 @@ TileSpec getNodeTile(MapNode mn, v3s16 p, v3s16 face_dir, return spec; } -u8 getNodeContent(v3s16 p, MapNode mn, NodeModMap &temp_mods) +content_t getNodeContent(v3s16 p, MapNode mn, NodeModMap &temp_mods) { /* Check temporary modifications on this node @@ -320,7 +335,7 @@ u8 getNodeContent(v3s16 p, MapNode mn, NodeModMap &temp_mods) } } - return mn.d; + return mn.getContent(); } v3s16 dirs8[8] = { @@ -343,16 +358,16 @@ u8 getSmoothLight(v3s16 p, VoxelManipulator &vmanip, u32 daynight_ratio) for(u32 i=0; i<8; i++) { MapNode n = vmanip.getNodeNoEx(p - dirs8[i]); - if(content_features(n.d).param_type == CPT_LIGHT + if(content_features(n).param_type == CPT_LIGHT // Fast-style leaves look better this way - && content_features(n.d).solidness != 2) + && content_features(n).solidness != 2) { light += decode_light(n.getLightBlend(daynight_ratio)); light_count++; } else { - if(n.d != CONTENT_IGNORE) + if(n.getContent() != CONTENT_IGNORE) ambient_occlusion++; } } @@ -408,8 +423,8 @@ void getTileInfo( TileSpec tile1 = getNodeTile(n1, p + face_dir, -face_dir, temp_mods); // This is hackish - u8 content0 = getNodeContent(p, n0, temp_mods); - u8 content1 = getNodeContent(p + face_dir, n1, temp_mods); + content_t content0 = getNodeContent(p, n0, temp_mods); + content_t content1 = getNodeContent(p + face_dir, n1, temp_mods); u8 mf = face_contents(content0, content1); if(mf == 0) diff --git a/src/mapblock_mesh.h b/src/mapblock_mesh.h index 591172bc9..d43c19a25 100644 --- a/src/mapblock_mesh.h +++ b/src/mapblock_mesh.h @@ -121,6 +121,9 @@ private: core::array m_prebuffers; }; +// Helper functions +video::SColor MapBlock_LightColor(u8 alpha, u8 light); + class MapBlock; struct MeshMakeData @@ -137,6 +140,7 @@ struct MeshMakeData void fill(u32 daynight_ratio, MapBlock *block); }; +// This is the highest-level function in here scene::SMesh* makeMapBlockMesh(MeshMakeData *data); #endif diff --git a/src/mapblockobject.cpp b/src/mapblockobject.cpp index ab1c20267..071a14b0c 100644 --- a/src/mapblockobject.cpp +++ b/src/mapblockobject.cpp @@ -161,8 +161,8 @@ void MovingObject::move(float dtime, v3f acceleration) for(s16 x = oldpos_i.X - 1; x <= oldpos_i.X + 1; x++) { try{ - if(content_walkable(m_block->getNodeParent(v3s16(x,y,z)).d) - == false) + MapNode n = m_block->getNodeParent(v3s16(x,y,z)); + if(content_features(n).walkable == false) continue; } catch(InvalidPositionException &e) diff --git a/src/mapgen.cpp b/src/mapgen.cpp index 8dda93c96..0ba7f91ab 100644 --- a/src/mapgen.cpp +++ b/src/mapgen.cpp @@ -67,8 +67,8 @@ static s16 find_ground_level_clever(VoxelManipulator &vmanip, v2s16 p2d) { MapNode &n = vmanip.m_data[i]; if(content_walkable(n.d) - && n.d != CONTENT_TREE - && n.d != CONTENT_LEAVES) + && n.getContent() != CONTENT_TREE + && n.getContent() != CONTENT_LEAVES) break; vmanip.m_area.add_y(em, i, -1); @@ -143,8 +143,94 @@ static void make_tree(VoxelManipulator &vmanip, v3s16 p0) if(vmanip.m_area.contains(p) == false) continue; u32 vi = vmanip.m_area.index(p); - if(vmanip.m_data[vi].d != CONTENT_AIR - && vmanip.m_data[vi].d != CONTENT_IGNORE) + if(vmanip.m_data[vi].getContent() != CONTENT_AIR + && vmanip.m_data[vi].getContent() != CONTENT_IGNORE) + continue; + u32 i = leaves_a.index(x,y,z); + if(leaves_d[i] == 1) + vmanip.m_data[vi] = leavesnode; + } +} + +static void make_jungletree(VoxelManipulator &vmanip, v3s16 p0) +{ + MapNode treenode(CONTENT_JUNGLETREE); + MapNode leavesnode(CONTENT_LEAVES); + + for(s16 x=-1; x<=1; x++) + for(s16 z=-1; z<=1; z++) + { + if(myrand_range(0, 2) == 0) + continue; + v3s16 p1 = p0 + v3s16(x,0,z); + v3s16 p2 = p0 + v3s16(x,-1,z); + if(vmanip.m_area.contains(p2) + && vmanip.m_data[vmanip.m_area.index(p2)] == CONTENT_AIR) + vmanip.m_data[vmanip.m_area.index(p2)] = treenode; + else if(vmanip.m_area.contains(p1)) + vmanip.m_data[vmanip.m_area.index(p1)] = treenode; + } + + s16 trunk_h = myrand_range(8, 12); + v3s16 p1 = p0; + for(s16 ii=0; ii leaves_d(new u8[leaves_a.getVolume()]); + Buffer leaves_d(leaves_a.getVolume()); + for(s32 i=0; i 1.0) + noise = 1.0; + return noise; +} + #if 0 double randomstone_amount_2d(u64 seed, v2s16 p) { @@ -1273,11 +1372,11 @@ void add_random_objects(MapBlock *block) { v3s16 p(x0,y0,z0); MapNode n = block->getNodeNoEx(p); - if(n.d == CONTENT_IGNORE) + if(n.getContent() == CONTENT_IGNORE) continue; - if(content_features(n.d).liquid_type != LIQUID_NONE) + if(content_features(n).liquid_type != LIQUID_NONE) continue; - if(content_features(n.d).walkable) + if(content_features(n).walkable) { last_node_walkable = true; continue; @@ -1285,7 +1384,7 @@ void add_random_objects(MapBlock *block) if(last_node_walkable) { // If block contains light information - if(content_features(n.d).param_type == CPT_LIGHT) + if(content_features(n).param_type == CPT_LIGHT) { if(n.getLight(LIGHTBANK_DAY) <= 3) { @@ -1392,7 +1491,7 @@ void make_block(BlockMakeData *data) for(s16 y=node_min.Y; y<=node_max.Y; y++) { // Only modify places that have no content - if(vmanip.m_data[i].d == CONTENT_IGNORE) + if(vmanip.m_data[i].getContent() == CONTENT_IGNORE) { if(y <= WATER_LEVEL) vmanip.m_data[i] = MapNode(CONTENT_WATERSOURCE); @@ -1498,7 +1597,7 @@ void make_block(BlockMakeData *data) for(s16 y=node_min.Y; y<=node_max.Y; y++) { // Only modify places that have no content - if(vmanip.m_data[i].d == CONTENT_IGNORE) + if(vmanip.m_data[i].getContent() == CONTENT_IGNORE) { // First priority: make air and water. // This avoids caves inside water. @@ -1543,7 +1642,7 @@ void make_block(BlockMakeData *data) { v3s16 p = v3s16(x,y,z) + g_27dirs[i]; u32 vi = vmanip.m_area.index(p); - if(vmanip.m_data[vi].d == CONTENT_STONE) + if(vmanip.m_data[vi].getContent() == CONTENT_STONE) if(mineralrandom.next()%8 == 0) vmanip.m_data[vi] = MapNode(CONTENT_MESE); } @@ -1584,13 +1683,13 @@ void make_block(BlockMakeData *data) { }*/ - if(new_content.d != CONTENT_IGNORE) + if(new_content.getContent() != CONTENT_IGNORE) { for(u16 i=0; i<27; i++) { v3s16 p = v3s16(x,y,z) + g_27dirs[i]; u32 vi = vmanip.m_area.index(p); - if(vmanip.m_data[vi].d == base_content) + if(vmanip.m_data[vi].getContent() == base_content) { if(mineralrandom.next()%sparseness == 0) vmanip.m_data[vi] = new_content; @@ -1621,7 +1720,7 @@ void make_block(BlockMakeData *data) { v3s16 p = v3s16(x,y,z) + g_27dirs[i]; u32 vi = vmanip.m_area.index(p); - if(vmanip.m_data[vi].d == CONTENT_STONE) + if(vmanip.m_data[vi].getContent() == CONTENT_STONE) if(mineralrandom.next()%8 == 0) vmanip.m_data[vi] = MapNode(CONTENT_STONE, MINERAL_COAL); } @@ -1647,7 +1746,7 @@ void make_block(BlockMakeData *data) { v3s16 p = v3s16(x,y,z) + g_27dirs[i]; u32 vi = vmanip.m_area.index(p); - if(vmanip.m_data[vi].d == CONTENT_STONE) + if(vmanip.m_data[vi].getContent() == CONTENT_STONE) if(mineralrandom.next()%8 == 0) vmanip.m_data[vi] = MapNode(CONTENT_STONE, MINERAL_IRON); } @@ -1670,7 +1769,7 @@ void make_block(BlockMakeData *data) u32 i = vmanip.m_area.index(v3s16(p2d.X, node_max.Y, p2d.Y)); for(s16 y=node_max.Y; y>=node_min.Y; y--) { - if(vmanip.m_data[i].d == CONTENT_STONE) + if(vmanip.m_data[i].getContent() == CONTENT_STONE) { if(noisebuf_ground_crumbleness.get(x,y,z) > 1.3) { @@ -1722,9 +1821,9 @@ void make_block(BlockMakeData *data) u32 i = vmanip.m_area.index(v3s16(p2d.X, full_node_max.Y, p2d.Y)); for(s16 y=full_node_max.Y; y>=full_node_min.Y; y--) { - if(vmanip.m_data[i].d == CONTENT_AIR) + if(vmanip.m_data[i].getContent() == CONTENT_AIR) vmanip.m_flags[i] |= VMANIP_FLAG_DUNGEON_PRESERVE; - else if(vmanip.m_data[i].d == CONTENT_WATERSOURCE) + else if(vmanip.m_data[i].getContent() == CONTENT_WATERSOURCE) vmanip.m_flags[i] |= VMANIP_FLAG_DUNGEON_PRESERVE; data->vmanip->m_area.add_y(em, i, -1); } @@ -1755,17 +1854,17 @@ void make_block(BlockMakeData *data) double d = noise3d_perlin((float)x/2.5, (float)y/2.5,(float)z/2.5, blockseed, 2, 1.4); - if(vmanip.m_data[i].d == CONTENT_COBBLE) + if(vmanip.m_data[i].getContent() == CONTENT_COBBLE) { if(d < wetness/3.0) { - vmanip.m_data[i].d = CONTENT_MOSSYCOBBLE; + vmanip.m_data[i].setContent(CONTENT_MOSSYCOBBLE); } } /*else if(vmanip.m_flags[i] & VMANIP_FLAG_DUNGEON_INSIDE) { if(wetness > 1.2) - vmanip.m_data[i].d = CONTENT_MUD; + vmanip.m_data[i].setContent(CONTENT_MUD); }*/ data->vmanip->m_area.add_y(em, i, -1); } @@ -1791,7 +1890,7 @@ void make_block(BlockMakeData *data) { if(water_found == false) { - if(vmanip.m_data[i].d == CONTENT_WATERSOURCE) + if(vmanip.m_data[i].getContent() == CONTENT_WATERSOURCE) { v3s16 p = v3s16(p2d.X, y, p2d.Y); data->transforming_liquid.push_back(p); @@ -1803,7 +1902,7 @@ void make_block(BlockMakeData *data) // This can be done because water_found can only // turn to true and end up here after going through // a single block. - if(vmanip.m_data[i+1].d != CONTENT_WATERSOURCE) + if(vmanip.m_data[i+1].getContent() != CONTENT_WATERSOURCE) { v3s16 p = v3s16(p2d.X, y+1, p2d.Y); data->transforming_liquid.push_back(p); @@ -1846,16 +1945,16 @@ void make_block(BlockMakeData *data) u32 i = vmanip.m_area.index(v3s16(p2d.X, start_y, p2d.Y)); for(s16 y=start_y; y>=node_min.Y-3; y--) { - if(vmanip.m_data[i].d == CONTENT_WATERSOURCE) + if(vmanip.m_data[i].getContent() == CONTENT_WATERSOURCE) water_detected = true; - if(vmanip.m_data[i].d == CONTENT_AIR) + if(vmanip.m_data[i].getContent() == CONTENT_AIR) air_detected = true; - if((vmanip.m_data[i].d == CONTENT_STONE - || vmanip.m_data[i].d == CONTENT_GRASS - || vmanip.m_data[i].d == CONTENT_MUD - || vmanip.m_data[i].d == CONTENT_SAND - || vmanip.m_data[i].d == CONTENT_GRAVEL + if((vmanip.m_data[i].getContent() == CONTENT_STONE + || vmanip.m_data[i].getContent() == CONTENT_GRASS + || vmanip.m_data[i].getContent() == CONTENT_MUD + || vmanip.m_data[i].getContent() == CONTENT_SAND + || vmanip.m_data[i].getContent() == CONTENT_GRAVEL ) && (air_detected || water_detected)) { if(current_depth == 0 && y <= WATER_LEVEL+2 @@ -1890,8 +1989,8 @@ void make_block(BlockMakeData *data) } else { - if(vmanip.m_data[i].d == CONTENT_MUD - || vmanip.m_data[i].d == CONTENT_GRASS) + if(vmanip.m_data[i].getContent() == CONTENT_MUD + || vmanip.m_data[i].getContent() == CONTENT_GRASS) vmanip.m_data[i] = MapNode(CONTENT_STONE); } @@ -1909,11 +2008,19 @@ void make_block(BlockMakeData *data) } /* - Add trees + Calculate some stuff */ + float surface_humidity = surface_humidity_2d(data->seed, p2d_center); + bool is_jungle = surface_humidity > 0.75; // Amount of trees u32 tree_count = block_area_nodes * tree_amount_2d(data->seed, p2d_center); + if(is_jungle) + tree_count *= 5; + + /* + Add trees + */ PseudoRandom treerandom(blockseed); // Put trees in random places on part of division for(u32 i=0; ivmanip->m_area.index(p); MapNode *n = &data->vmanip->m_data[i]; - if(n->d != CONTENT_AIR && n->d != CONTENT_WATERSOURCE && n->d != CONTENT_IGNORE) + if(n->getContent() != CONTENT_AIR && n->getContent() != CONTENT_WATERSOURCE && n->getContent() != CONTENT_IGNORE) { found = true; break; @@ -1952,23 +2059,27 @@ void make_block(BlockMakeData *data) u32 i = data->vmanip->m_area.index(p); MapNode *n = &data->vmanip->m_data[i]; - if(n->d != CONTENT_MUD && n->d != CONTENT_GRASS && n->d != CONTENT_SAND) + if(n->getContent() != CONTENT_MUD && n->getContent() != CONTENT_GRASS && n->getContent() != CONTENT_SAND) continue; // Papyrus grows only on mud and in water - if(n->d == CONTENT_MUD && y <= WATER_LEVEL) + if(n->getContent() == CONTENT_MUD && y <= WATER_LEVEL) { p.Y++; make_papyrus(vmanip, p); } // Trees grow only on mud and grass, on land - else if((n->d == CONTENT_MUD || n->d == CONTENT_GRASS) && y > WATER_LEVEL + 2) + else if((n->getContent() == CONTENT_MUD || n->getContent() == CONTENT_GRASS) && y > WATER_LEVEL + 2) { p.Y++; - make_tree(vmanip, p); + //if(surface_humidity_2d(data->seed, v2s16(x, y)) < 0.5) + if(is_jungle == false) + make_tree(vmanip, p); + else + make_jungletree(vmanip, p); } // Cactii grow only on sand, on land - else if(n->d == CONTENT_SAND && y > WATER_LEVEL + 2) + else if(n->getContent() == CONTENT_SAND && y > WATER_LEVEL + 2) { p.Y++; make_cactus(vmanip, p); @@ -1976,6 +2087,54 @@ void make_block(BlockMakeData *data) } } + /* + Add jungle grass + */ + if(is_jungle) + { + PseudoRandom grassrandom(blockseed); + for(u32 i=0; iseed, v2s16(x,z), 4); + if(y < WATER_LEVEL) + continue; + if(y < node_min.Y || y > node_max.Y) + continue; + /* + Find exact ground level + */ + v3s16 p(x,y+6,z); + bool found = false; + for(; p.Y >= y-6; p.Y--) + { + u32 i = data->vmanip->m_area.index(p); + MapNode *n = &data->vmanip->m_data[i]; + if(content_features(*n).is_ground_content + || n->getContent() == CONTENT_JUNGLETREE) + { + found = true; + break; + } + } + // If not found, handle next one + if(found == false) + continue; + p.Y++; + if(vmanip.m_area.contains(p) == false) + continue; + if(vmanip.m_data[vmanip.m_area.index(p)].getContent() != CONTENT_AIR) + continue; + /*p.Y--; + if(vmanip.m_area.contains(p)) + vmanip.m_data[vmanip.m_area.index(p)] = CONTENT_MUD; + p.Y++;*/ + if(vmanip.m_area.contains(p)) + vmanip.m_data[vmanip.m_area.index(p)] = CONTENT_JUNGLEGRASS; + } + } + #if 0 /* Add some kind of random stones @@ -2000,7 +2159,7 @@ void make_block(BlockMakeData *data) /*{ u32 i = data->vmanip->m_area.index(v3s16(p)); MapNode *n = &data->vmanip->m_data[i]; - if(n->d != CONTENT_MUD && n->d != CONTENT_GRASS) + if(n->getContent() != CONTENT_MUD && n->getContent() != CONTENT_GRASS) continue; }*/ // Will be placed one higher @@ -2035,7 +2194,7 @@ void make_block(BlockMakeData *data) /*{ u32 i = data->vmanip->m_area.index(v3s16(p)); MapNode *n = &data->vmanip->m_data[i]; - if(n->d != CONTENT_MUD && n->d != CONTENT_GRASS) + if(n->getContent() != CONTENT_MUD && n->getContent() != CONTENT_GRASS) continue; }*/ // Will be placed one lower diff --git a/src/mapnode.cpp b/src/mapnode.cpp index 1e9b64989..c9f85c303 100644 --- a/src/mapnode.cpp +++ b/src/mapnode.cpp @@ -30,8 +30,6 @@ with this program; if not, write to the Free Software Foundation, Inc., ContentFeatures::~ContentFeatures() { - /*if(translate_to) - delete translate_to;*/ if(initial_metadata) delete initial_metadata; } @@ -83,12 +81,16 @@ void ContentFeatures::setInventoryTextureCube(std::string top, inventory_texture = g_texturesource->getTextureRaw(imgname_full); } -struct ContentFeatures g_content_features[256]; +struct ContentFeatures g_content_features[MAX_CONTENT+1]; -ContentFeatures & content_features(u8 i) +ContentFeatures & content_features(content_t i) { return g_content_features[i]; } +ContentFeatures & content_features(MapNode &n) +{ + return content_features(n.getContent()); +} /* See mapnode.h for description. @@ -128,7 +130,7 @@ void init_mapnode() initial_material_type = MATERIAL_ALPHA_SIMPLE; else initial_material_type = MATERIAL_ALPHA_NONE;*/ - for(u16 i=0; i<256; i++) + for(u16 i=0; i 0) - param = 0; + param1 = 0; else - param = source[1]; + param1 = source[1]; } else if(version <= 9) { - d = source[0]; - param = source[1]; + param0 = source[0]; + param1 = source[1]; } else { - d = source[0]; - param = source[1]; + param0 = source[0]; + param1 = source[1]; param2 = source[2]; } - // Convert from old version to new + // Convert special values from old version to new if(version <= 18) { // In these versions, CONTENT_IGNORE and CONTENT_AIR // are 255 and 254 - if(d == 255) - d = CONTENT_IGNORE; - else if(d == 254) - d = CONTENT_AIR; + if(param0 == 255) + param0 = CONTENT_IGNORE; + else if(param0 == 254) + param0 = CONTENT_AIR; } // version 19 is fucked up with sometimes the old values and sometimes not if(version == 19) { - if(d == 255) - d = CONTENT_IGNORE; - else if(d == 254) - d = CONTENT_AIR; + if(param0 == 255) + param0 = CONTENT_IGNORE; + else if(param0 == 254) + param0 = CONTENT_AIR; } + + // Translate to our known version + *this = mapnode_translate_to_internal(*this, version); } /* diff --git a/src/mapnode.h b/src/mapnode.h index 33128049a..3cca985bc 100644 --- a/src/mapnode.h +++ b/src/mapnode.h @@ -32,16 +32,15 @@ with this program; if not, write to the Free Software Foundation, Inc., /* Naming scheme: - Material = irrlicht's Material class - - Content = (u8) content of a node + - Content = (content_t) content of a node - Tile = TileSpec at some side of a node of some content type -*/ -/* - Ranges: + Content ranges: 0x000...0x07f: param2 is fully usable 0x800...0xfff: param2 lower 4 bytes are free */ typedef u16 content_t; +#define MAX_CONTENT 0xfff /* Initializes all kind of stuff in here. @@ -102,10 +101,7 @@ class NodeMetadata; struct ContentFeatures { - // If non-NULL, content is translated to this when deserialized - //MapNode *translate_to; - - // Type of MapNode::param + // Type of MapNode::param1 ContentParamType param_type; /* @@ -119,7 +115,8 @@ struct ContentFeatures TileSpec tiles[6]; video::ITexture *inventory_texture; - + + // True for all ground-like things like stone and mud, false for eg. trees bool is_ground_content; bool light_propagates; bool sunlight_propagates; @@ -150,10 +147,10 @@ struct ContentFeatures NodeMetadata *initial_metadata; // If the content is liquid, this is the flowing version of the liquid. - // If content is flowing liquid, this is the same content. - u8 liquid_alternative_flowing; + // If content is liquid, this is the same content. + content_t liquid_alternative_flowing; // If the content is liquid, this is the source version of the liquid. - u8 liquid_alternative_source; + content_t liquid_alternative_source; // Amount of light the node emits u8 light_source; @@ -165,7 +162,6 @@ struct ContentFeatures void reset() { - //translate_to = NULL; param_type = CPT_NONE; inventory_texture = NULL; is_ground_content = false; @@ -230,8 +226,8 @@ struct ContentFeatures /* Call this to access the ContentFeature list */ -ContentFeatures & content_features(u8 i); - +ContentFeatures & content_features(content_t i); +ContentFeatures & content_features(MapNode &n); /* Here is a bunch of DEPRECATED functions. @@ -242,7 +238,7 @@ ContentFeatures & content_features(u8 i); in param. NOTE: Don't use, use "content_features(m).whatever" instead */ -inline bool light_propagates_content(u8 m) +inline bool light_propagates_content(content_t m) { return content_features(m).light_propagates; } @@ -251,7 +247,7 @@ inline bool light_propagates_content(u8 m) NOTE: It doesn't seem to go through torches regardlessly of this NOTE: Don't use, use "content_features(m).whatever" instead */ -inline bool sunlight_propagates_content(u8 m) +inline bool sunlight_propagates_content(content_t m) { return content_features(m).sunlight_propagates; } @@ -263,35 +259,35 @@ inline bool sunlight_propagates_content(u8 m) 2: Opaque NOTE: Don't use, use "content_features(m).whatever" instead */ -inline u8 content_solidness(u8 m) +inline u8 content_solidness(content_t m) { return content_features(m).solidness; } // Objects collide with walkable contents // NOTE: Don't use, use "content_features(m).whatever" instead -inline bool content_walkable(u8 m) +inline bool content_walkable(content_t m) { return content_features(m).walkable; } // NOTE: Don't use, use "content_features(m).whatever" instead -inline bool content_liquid(u8 m) +inline bool content_liquid(content_t m) { return content_features(m).liquid_type != LIQUID_NONE; } // NOTE: Don't use, use "content_features(m).whatever" instead -inline bool content_flowing_liquid(u8 m) +inline bool content_flowing_liquid(content_t m) { return content_features(m).liquid_type == LIQUID_FLOWING; } // NOTE: Don't use, use "content_features(m).whatever" instead -inline bool content_liquid_source(u8 m) +inline bool content_liquid_source(content_t m) { return content_features(m).liquid_type == LIQUID_SOURCE; } // CONTENT_WATER || CONTENT_WATERSOURCE -> CONTENT_WATER // CONTENT_LAVA || CONTENT_LAVASOURCE -> CONTENT_LAVA // NOTE: Don't use, use "content_features(m).whatever" instead -inline u8 make_liquid_flowing(u8 m) +inline content_t make_liquid_flowing(content_t m) { u8 c = content_features(m).liquid_alternative_flowing; assert(c != CONTENT_IGNORE); @@ -299,17 +295,17 @@ inline u8 make_liquid_flowing(u8 m) } // Pointable contents can be pointed to in the map // NOTE: Don't use, use "content_features(m).whatever" instead -inline bool content_pointable(u8 m) +inline bool content_pointable(content_t m) { return content_features(m).pointable; } // NOTE: Don't use, use "content_features(m).whatever" instead -inline bool content_diggable(u8 m) +inline bool content_diggable(content_t m) { return content_features(m).diggable; } // NOTE: Don't use, use "content_features(m).whatever" instead -inline bool content_buildable_to(u8 m) +inline bool content_buildable_to(content_t m) { return content_features(m).buildable_to; } @@ -321,7 +317,7 @@ inline bool content_buildable_to(u8 m) 1: Face uses m1's content 2: Face uses m2's content */ -inline u8 face_contents(u8 m1, u8 m2) +inline u8 face_contents(content_t m1, content_t m2) { if(m1 == CONTENT_IGNORE || m2 == CONTENT_IGNORE) return 0; @@ -425,7 +421,7 @@ struct MapNode union { u8 param0; - u8 d; + //u8 d; }; /* @@ -440,17 +436,18 @@ struct MapNode union { u8 param1; - s8 param; + //s8 param; }; /* The second parameter. Initialized to 0. E.g. direction for torches and flowing water. + If param0 >= 0x80, bits 0xf0 of this is extended content type data */ union { u8 param2; - u8 dir; + //u8 dir; }; MapNode(const MapNode & n) @@ -458,28 +455,44 @@ struct MapNode *this = n; } - MapNode(u8 data=CONTENT_AIR, u8 a_param=0, u8 a_param2=0) + MapNode(content_t content=CONTENT_AIR, u8 a_param1=0, u8 a_param2=0) { - d = data; - param = a_param; + //param0 = a_param0; + param1 = a_param1; param2 = a_param2; + // Set after other params because this needs to override part of param2 + setContent(content); } bool operator==(const MapNode &other) { - return (d == other.d - && param == other.param + return (param0 == other.param0 + && param1 == other.param1 && param2 == other.param2); } // To be used everywhere content_t getContent() { - return d; + if(param0 < 0x80) + return param0; + else + return (param0<<4) + (param2>>4); } void setContent(content_t c) { - d = c; + if(c < 0x80) + { + if(param0 >= 0x80) + param2 &= ~(0xf0); + param0 = c; + } + else + { + param0 = c>>4; + param2 &= ~(0xf0); + param2 |= (c&0x0f)<<4; + } } /* @@ -487,19 +500,19 @@ struct MapNode */ bool light_propagates() { - return light_propagates_content(d); + return light_propagates_content(getContent()); } bool sunlight_propagates() { - return sunlight_propagates_content(d); + return sunlight_propagates_content(getContent()); } u8 solidness() { - return content_solidness(d); + return content_solidness(getContent()); } u8 light_source() { - return content_features(d).light_source; + return content_features(*this).light_source; } u8 getLightBanksWithSource() @@ -507,10 +520,10 @@ struct MapNode // Select the brightest of [light source, propagated light] u8 lightday = 0; u8 lightnight = 0; - if(content_features(d).param_type == CPT_LIGHT) + if(content_features(*this).param_type == CPT_LIGHT) { - lightday = param & 0x0f; - lightnight = (param>>4)&0x0f; + lightday = param1 & 0x0f; + lightnight = (param1>>4)&0x0f; } if(light_source() > lightday) lightday = light_source(); @@ -523,12 +536,12 @@ struct MapNode { // Select the brightest of [light source, propagated light] u8 light = 0; - if(content_features(d).param_type == CPT_LIGHT) + if(content_features(*this).param_type == CPT_LIGHT) { if(bank == LIGHTBANK_DAY) - light = param & 0x0f; + light = param1 & 0x0f; else if(bank == LIGHTBANK_NIGHT) - light = (param>>4)&0x0f; + light = (param1>>4)&0x0f; else assert(0); } @@ -566,17 +579,17 @@ struct MapNode void setLight(enum LightBank bank, u8 a_light) { // If node doesn't contain light data, ignore this - if(content_features(d).param_type != CPT_LIGHT) + if(content_features(*this).param_type != CPT_LIGHT) return; if(bank == LIGHTBANK_DAY) { - param &= 0xf0; - param |= a_light & 0x0f; + param1 &= 0xf0; + param1 |= a_light & 0x0f; } else if(bank == LIGHTBANK_NIGHT) { - param &= 0x0f; - param |= (a_light & 0x0f)<<4; + param1 &= 0x0f; + param1 |= (a_light & 0x0f)<<4; } else assert(0); diff --git a/src/materials.cpp b/src/materials.cpp index e3a24b9e3..24f300724 100644 --- a/src/materials.cpp +++ b/src/materials.cpp @@ -3,12 +3,12 @@ // NOTE: DEPRECATED -DiggingPropertiesList * getDiggingPropertiesList(u8 content) +DiggingPropertiesList * getDiggingPropertiesList(u16 content) { return &content_features(content).digging_properties; } -DiggingProperties getDiggingProperties(u8 content, const std::string &tool) +DiggingProperties getDiggingProperties(u16 content, const std::string &tool) { DiggingPropertiesList *mprop = getDiggingPropertiesList(content); if(mprop == NULL) diff --git a/src/materials.h b/src/materials.h index f061ecbfa..1439df194 100644 --- a/src/materials.h +++ b/src/materials.h @@ -97,7 +97,7 @@ private: }; // For getting the default properties, set tool="" -DiggingProperties getDiggingProperties(u8 material, const std::string &tool); +DiggingProperties getDiggingProperties(u16 material, const std::string &tool); #endif diff --git a/src/player.cpp b/src/player.cpp index 6bacb088d..d52d6b88f 100644 --- a/src/player.cpp +++ b/src/player.cpp @@ -342,13 +342,13 @@ void LocalPlayer::move(f32 dtime, Map &map, f32 pos_max_d, if(in_water) { v3s16 pp = floatToInt(position + v3f(0,BS*0.1,0), BS); - in_water = content_liquid(map.getNode(pp).d); + in_water = content_liquid(map.getNode(pp).getContent()); } // If not in water, the threshold of going in is at lower y else { v3s16 pp = floatToInt(position + v3f(0,BS*0.5,0), BS); - in_water = content_liquid(map.getNode(pp).d); + in_water = content_liquid(map.getNode(pp).getContent()); } } catch(InvalidPositionException &e) @@ -361,7 +361,7 @@ void LocalPlayer::move(f32 dtime, Map &map, f32 pos_max_d, */ try{ v3s16 pp = floatToInt(position + v3f(0,0,0), BS); - in_water_stable = content_liquid(map.getNode(pp).d); + in_water_stable = content_liquid(map.getNode(pp).getContent()); } catch(InvalidPositionException &e) { @@ -470,7 +470,7 @@ void LocalPlayer::move(f32 dtime, Map &map, f32 pos_max_d, { try{ // Player collides into walkable nodes - if(content_walkable(map.getNode(v3s16(x,y,z)).d) == false) + if(content_walkable(map.getNode(v3s16(x,y,z)).getContent()) == false) continue; } catch(InvalidPositionException &e) @@ -633,10 +633,10 @@ void LocalPlayer::move(f32 dtime, Map &map, f32 pos_max_d, try{ // The node to be sneaked on has to be walkable - if(content_walkable(map.getNode(p).d) == false) + if(content_walkable(map.getNode(p).getContent()) == false) continue; // And the node above it has to be nonwalkable - if(content_walkable(map.getNode(p+v3s16(0,1,0)).d) == true) + if(content_walkable(map.getNode(p+v3s16(0,1,0)).getContent()) == true) continue; } catch(InvalidPositionException &e) diff --git a/src/serialization.h b/src/serialization.h index 974ae95d8..d93e61892 100644 --- a/src/serialization.h +++ b/src/serialization.h @@ -55,11 +55,12 @@ with this program; if not, write to the Free Software Foundation, Inc., 17: MapBlocks contain timestamp 18: new generator (not really necessary, but it's there) 19: new content type handling + 20: many existing content types translated to extended ones */ // This represents an uninitialized or invalid format #define SER_FMT_VER_INVALID 255 // Highest supported serialization version -#define SER_FMT_VER_HIGHEST 19 +#define SER_FMT_VER_HIGHEST 20 // Lowest supported serialization version #define SER_FMT_VER_LOWEST 0 diff --git a/src/server.cpp b/src/server.cpp index c2433e1af..e2e6ce46b 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -105,10 +105,10 @@ void * EmergeThread::Thread() DSTACK(__FUNCTION_NAME); - //bool debug=false; - BEGIN_DEBUG_EXCEPTION_HANDLER + bool enable_mapgen_debug_info = g_settings.getBool("enable_mapgen_debug_info"); + /* Get block info from queue, emerge them and send them to clients. @@ -155,7 +155,7 @@ void * EmergeThread::Thread() Also decrement the emerge queue count in clients. */ - bool optional = true; + bool only_from_disk = true; { core::map::Iterator i; @@ -166,14 +166,15 @@ void * EmergeThread::Thread() // Check flags u8 flags = i.getNode()->getValue(); if((flags & BLOCK_EMERGE_FLAG_FROMDISK) == false) - optional = false; + only_from_disk = false; } } - - /*dstream<<"EmergeThread: p=" - <<"("<isGenerated() == false) + { + if(enable_mapgen_debug_info) + dstream<<"EmergeThread: generating"< modified_blocks; - u8 material = CONTENT_IGNORE; + content_t material = CONTENT_IGNORE; u8 mineral = MINERAL_NONE; bool cannot_remove_node = false; @@ -2505,7 +2512,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) // Get mineral mineral = n.getMineral(); // Get material at position - material = n.d; + material = n.getContent(); // If not yet cancelled if(cannot_remove_node == false) { @@ -2705,7 +2712,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) <<" because privileges are "<getMaterial(); + n.setContent(mitem->getMaterial()); // Calculate direction for wall mounted stuff - if(content_features(n.d).wall_mounted) - n.dir = packDir(p_under - p_over); + if(content_features(n).wall_mounted) + n.param2 = packDir(p_under - p_over); // Calculate the direction for furnaces and chests and stuff - if(content_features(n.d).param_type == CPT_FACEDIR_SIMPLE) + if(content_features(n).param_type == CPT_FACEDIR_SIMPLE) { v3f playerpos = player->getPosition(); v3f blockpos = intToFloat(p_over, BS) - playerpos; diff --git a/src/test.cpp b/src/test.cpp index 0487f1436..3ff4dc807 100644 --- a/src/test.cpp +++ b/src/test.cpp @@ -219,14 +219,14 @@ struct TestMapNode MapNode n; // Default values - assert(n.d == CONTENT_AIR); + assert(n.getContent() == CONTENT_AIR); assert(n.getLight(LIGHTBANK_DAY) == 0); assert(n.getLight(LIGHTBANK_NIGHT) == 0); // Transparency - n.d = CONTENT_AIR; + n.setContent(CONTENT_AIR); assert(n.light_propagates() == true); - n.d = CONTENT_STONE; + n.setContent(CONTENT_STONE); assert(n.light_propagates() == false); } }; @@ -284,7 +284,7 @@ struct TestVoxelManipulator v.print(dstream); - assert(v.getNode(v3s16(-1,0,-1)).d == 2); + assert(v.getNode(v3s16(-1,0,-1)).getContent() == 2); dstream<<"*** Reading from inexistent (0,0,-1) ***"< light_sources; @@ -611,7 +611,7 @@ struct TestMapBlock for(u16 y=0; y @@ -194,7 +194,7 @@ void TextureSource::processQueue() u32 TextureSource::getTextureId(const std::string &name) { - //dstream<<"INFO: getTextureId(): name="< result_queue; @@ -226,8 +226,8 @@ u32 TextureSource::getTextureId(const std::string &name) // Throw a request in m_get_texture_queue.add(name, 0, 0, &result_queue); - dstream<<"INFO: Waiting for texture from main thread, name=" - <getValue(); } } - dstream<<"INFO: getTextureIdDirect(): name="<getVideoDriver(); assert(driver); @@ -708,8 +710,9 @@ video::IImage* generate_image_from_scratch(std::string name, { // Construct base name base_image_name = name.substr(0, last_separator_position); - dstream<<"INFO: generate_image_from_scratch(): Calling itself recursively" - " to get base image, name="<createImageFromFile(path.c_str()); if(image == NULL) { - dstream<<"WARNING: Could not load image \""< dim(2,2); @@ -782,7 +785,7 @@ bool generate_image(std::string part_of_name, video::IImage *& baseimg, // If base image is NULL, load as base. if(baseimg == NULL) { - dstream<<"INFO: Setting "< dim = image->getDimension(); //core::dimension2d dim(16,16); @@ -817,7 +820,7 @@ bool generate_image(std::string part_of_name, video::IImage *& baseimg, { // A special texture modification - dstream<<"INFO: getTextureIdDirect(): generating special " + dstream<<"INFO: generate_image(): generating special " <<"modification \""<createImageFromFile(path.c_str()); if(image == NULL) { - dstream<<"WARNING: getTextureIdDirect(): Loading path \"" + dstream<<"WARNING: generate_image(): Loading path \"" <queryFeature(video::EVDF_RENDER_TO_TARGET) == false) { - dstream<<"WARNING: getTextureIdDirect(): EVDF_RENDER_TO_TARGET" + dstream<<"WARNING: generate_image(): EVDF_RENDER_TO_TARGET" " not supported. Creating fallback image"< 500: @@ -283,7 +286,7 @@ for n in range(len(xlist)): for (x, z) in reversed(pixellist): for y in reversed(range(16)): datapos = x + y * 16 + z * 256 - if(ord(mapdata[datapos]) != 254 and ord(mapdata[datapos]) in colors): + if(not data_is_air(ord(mapdata[datapos])) and ord(mapdata[datapos]) in colors): if(ord(mapdata[datapos]) == 2 or ord(mapdata[datapos]) == 9): water[(x, z)] += 1 # Add dummy stuff for drawing sea without seabed @@ -293,7 +296,7 @@ for n in range(len(xlist)): # Memorize information on the type and height of the block and for drawing the picture. stuff[(chunkxpos + x, chunkzpos + z)] = (chunkypos + y, ord(mapdata[datapos]), water[(x, z)]) break - elif(ord(mapdata[datapos]) != 254 and ord(mapdata[datapos]) not in colors): + elif(not data_is_air(ord(mapdata[datapos])) and ord(mapdata[datapos]) not in colors): print "strange block: " + xhex + "/" + zhex + "/" + yhex + " x: " + str(x) + " y: " + str(y) + " z: " + str(z) + " palikka: " + str(ord(mapdata[datapos])) # After finding all the pixels in the sector, we can move on to the next sector without having to continue the Y axis. @@ -324,7 +327,7 @@ for n in range(len(xlist)): for (x, z) in reversed(pixellist): for y in reversed(range(16)): datapos = x + y * 16 + z * 256 - if(ord(mapdata[datapos]) != 254 and ord(mapdata[datapos]) in colors): + if(not data_is_air(ord(mapdata[datapos])) and ord(mapdata[datapos]) in colors): if(ord(mapdata[datapos]) == 2 or ord(mapdata[datapos]) == 9): water[(x, z)] += 1 # Add dummy stuff for drawing sea without seabed @@ -334,7 +337,7 @@ for n in range(len(xlist)): # Memorize information on the type and height of the block and for drawing the picture. stuff[(chunkxpos + x, chunkzpos + z)] = (chunkypos + y, ord(mapdata[datapos]), water[(x, z)]) break - elif(ord(mapdata[datapos]) != 254 and ord(mapdata[datapos]) not in colors): + elif(not data_is_air(ord(mapdata[datapos])) and ord(mapdata[datapos]) not in colors): print "outo palikka: " + xhex + "/" + zhex + "/" + yhex + " x: " + str(x) + " y: " + str(y) + " z: " + str(z) + " palikka: " + str(ord(mapdata[datapos])) # After finding all the pixels in the sector, we can move on to the next sector without having to continue the Y axis.