Merge remote-tracking branch 'origin/master' into portals
1
.gitignore
vendored
@ -48,6 +48,7 @@ world_nether
|
||||
CMakeFiles/
|
||||
cmake_install.cmake
|
||||
CMakeCache.txt
|
||||
CTestTestfile.cmake
|
||||
Makefile
|
||||
|
||||
*.a
|
||||
|
14
.travis.yml
@ -2,8 +2,15 @@ language: cpp
|
||||
compiler:
|
||||
- gcc
|
||||
- clang
|
||||
|
||||
before_install:
|
||||
- if [ "$TRAVIS_MCSERVER_BUILD_TYPE" == "COVERAGE" ]; then sudo pip install cpp_coveralls; fi
|
||||
|
||||
# Build MCServer
|
||||
script: cmake . -DBUILD_TOOLS=1 -DSELF_TEST=1 && make -j 2 && cd MCServer/ && (echo stop | $MCSERVER_PATH)
|
||||
script: ./CIbuild.sh
|
||||
|
||||
after_success:
|
||||
- ./uploadCoverage.sh
|
||||
|
||||
env:
|
||||
- TRAVIS_MCSERVER_BUILD_TYPE=RELEASE MCSERVER_PATH=./MCServer
|
||||
@ -11,6 +18,11 @@ env:
|
||||
- TRAVIS_MCSERVER_BUILD_TYPE=RELEASE TRAVIS_MCSERVER_FORCE32=1 MCSERVER_PATH=./MCServer
|
||||
- TRAVIS_MCSERVER_BUILD_TYPE=DEBUG TRAVIS_MCSERVER_FORCE32=1 MCSERVER_PATH=./MCServer_debug
|
||||
|
||||
matrix:
|
||||
include:
|
||||
- compiler: gcc
|
||||
env: TRAVIS_MCSERVER_BUILD_TYPE=COVERAGE MCSERVER_PATH=./MCServer
|
||||
|
||||
# Notification Settings
|
||||
notifications:
|
||||
email:
|
||||
|
11
CIbuild.sh
Executable file
@ -0,0 +1,11 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -e
|
||||
|
||||
cmake . -DBUILD_TOOLS=1 -DSELF_TEST=1;
|
||||
make -j 2;
|
||||
make -j 2 test;
|
||||
cd MCServer/;
|
||||
if [ "$TRAVIS_MCSERVER_BUILD_TYPE" != "COVERAGE" ]
|
||||
then echo stop | $MCSERVER_PATH;
|
||||
fi
|
@ -1,4 +1,4 @@
|
||||
cmake_minimum_required (VERSION 2.6)
|
||||
cmake_minimum_required (VERSION 2.8.2)
|
||||
|
||||
# Without this, the MSVC variable isn't defined for MSVC builds ( http://www.cmake.org/pipermail/cmake/2011-November/047130.html )
|
||||
enable_language(CXX C)
|
||||
@ -14,6 +14,10 @@ if(DEFINED ENV{TRAVIS_MCSERVER_FORCE32})
|
||||
set(FORCE32 $ENV{TRAVIS_MCSERVER_FORCE32})
|
||||
endif()
|
||||
|
||||
if(DEFINED ENV{TRAVIS_BUILD_WITH_COVERAGE})
|
||||
set(BUILD_WITH_COVERAGE $ENV{TRAVIS_BUILD_WITH_COVERAGE})
|
||||
endif()
|
||||
|
||||
# This has to be done before any flags have been set up.
|
||||
if(${BUILD_TOOLS})
|
||||
add_subdirectory(Tools/MCADefrag/)
|
||||
@ -69,3 +73,8 @@ set_exe_flags()
|
||||
|
||||
add_subdirectory (src)
|
||||
|
||||
if(${SELF_TEST})
|
||||
enable_testing()
|
||||
add_subdirectory (tests)
|
||||
endif()
|
||||
|
||||
|
@ -10,6 +10,11 @@ return
|
||||
Params =
|
||||
{
|
||||
{ Name = "ProjectileEntity", Type = "{{cProjectileEntity}}", Notes = "The projectile that hit an entity." },
|
||||
{ Name = "BlockX", Type = "number", Notes = "The X-coord where the projectile hit." },
|
||||
{ Name = "BlockY", Type = "number", Notes = "The Y-coord where the projectile hit." },
|
||||
{ Name = "BlockZ", Type = "number", Notes = "The Z-coord where the projectile hit." },
|
||||
{ Name = "BlockFace", Type = "number", Notes = "The side of the block where the projectile hit." },
|
||||
{ Name = "BlockHitPos", Type = "Vector3d", Notes = "The exact position where the projectile hit." },
|
||||
},
|
||||
Returns = [[
|
||||
If the function returns false or no value, the next plugin's callback is called. If the function
|
||||
|
@ -29,7 +29,8 @@ function Initialize(Plugin)
|
||||
PM:AddHook(cPluginManager.HOOK_WORLD_TICK, OnWorldTick);
|
||||
PM:AddHook(cPluginManager.HOOK_PLUGINS_LOADED, OnPluginsLoaded);
|
||||
PM:AddHook(cPluginManager.HOOK_PLUGIN_MESSAGE, OnPluginMessage);
|
||||
PM:AddHook(cPluginManager.HOOK_PLAYER_JOINED, OnPlayerJoined)
|
||||
PM:AddHook(cPluginManager.HOOK_PLAYER_JOINED, OnPlayerJoined);
|
||||
PM:AddHook(cPluginManager.HOOK_PROJECTILE_HIT_BLOCK, OnProjectileHitBlock);
|
||||
|
||||
-- _X: Disabled so that the normal operation doesn't interfere with anything
|
||||
-- PM:AddHook(cPluginManager.HOOK_CHUNK_GENERATED, OnChunkGenerated);
|
||||
@ -1379,3 +1380,14 @@ end
|
||||
|
||||
|
||||
|
||||
|
||||
function OnProjectileHitBlock(a_Projectile, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_BlockHitPos)
|
||||
local BlockX, BlockY, BlockZ = AddFaceDirection(a_BlockX, a_BlockY, a_BlockZ, a_BlockFace)
|
||||
local World = a_Projectile:GetWorld()
|
||||
|
||||
World:SetBlock(BlockX, BlockY, BlockZ, E_BLOCK_FIRE, 0)
|
||||
end
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -1,32 +1,41 @@
|
||||
macro (add_flags_lnk FLAGS)
|
||||
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${FLAGS}")
|
||||
set(CMAKE_EXE_LINKER_FLAGS_DEBUG "${CMAKE_EXE_LINKER_FLAGS_DEBUG} ${FLAGS}")
|
||||
set(CMAKE_EXE_LINKER_FLAGS_RELEASE "${CMAKE_EXE_LINKER_FLAGS_RELEASE} ${FLAGS}")
|
||||
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} ${FLAGS}")
|
||||
set(CMAKE_SHARED_LINKER_FLAGS_DEBUG "${CMAKE_SHARED_LINKER_FLAGS_DEBUG} ${FLAGS}")
|
||||
set(CMAKE_SHARED_LINKER_FLAGS_RELEASE "${CMAKE_SHARED_LINKER_FLAGS_RELEASE} ${FLAGS}")
|
||||
set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} ${FLAGS}")
|
||||
set(CMAKE_MODULE_LINKER_FLAGS_DEBUG "${CMAKE_MODULE_LINKER_FLAGS_DEBUG} ${FLAGS}")
|
||||
set(CMAKE_MODULE_LINKER_FLAGS_RELEASE "${CMAKE_MODULE_LINKER_FLAGS_RELEASE} ${FLAGS}")
|
||||
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${FLAGS}")
|
||||
set(CMAKE_EXE_LINKER_FLAGS_DEBUG "${CMAKE_EXE_LINKER_FLAGS_DEBUG} ${FLAGS}")
|
||||
set(CMAKE_EXE_LINKER_FLAGS_COVERAGE "${CMAKE_EXE_LINKER_FLAGS_COVERAGE} ${FLAGS}")
|
||||
set(CMAKE_EXE_LINKER_FLAGS_RELEASE "${CMAKE_EXE_LINKER_FLAGS_RELEASE} ${FLAGS}")
|
||||
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} ${FLAGS}")
|
||||
set(CMAKE_SHARED_LINKER_FLAGS_DEBUG "${CMAKE_SHARED_LINKER_FLAGS_DEBUG} ${FLAGS}")
|
||||
set(CMAKE_SHARED_LINKER_FLAGS_COVERAGE "${CMAKE_SHARED_LINKER_FLAGS_COVERAGE} ${FLAGS}")
|
||||
set(CMAKE_SHARED_LINKER_FLAGS_RELEASE "${CMAKE_SHARED_LINKER_FLAGS_RELEASE} ${FLAGS}")
|
||||
set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} ${FLAGS}")
|
||||
set(CMAKE_MODULE_LINKER_FLAGS_DEBUG "${CMAKE_MODULE_LINKER_FLAGS_DEBUG} ${FLAGS}")
|
||||
set(CMAKE_MODULE_LINKER_FLAGS_COVERAGE "${CMAKE_MODULE_LINKER_FLAGS_COVERAGE} ${FLAGS}")
|
||||
set(CMAKE_MODULE_LINKER_FLAGS_RELEASE "${CMAKE_MODULE_LINKER_FLAGS_RELEASE} ${FLAGS}")
|
||||
endmacro()
|
||||
|
||||
macro(add_flags_cxx FLAGS)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${FLAGS}")
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${FLAGS}")
|
||||
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} ${FLAGS}")
|
||||
set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} ${FLAGS}")
|
||||
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} ${FLAGS}")
|
||||
set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} ${FLAGS}")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${FLAGS}")
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${FLAGS}")
|
||||
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} ${FLAGS}")
|
||||
set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} ${FLAGS}")
|
||||
set(CMAKE_CXX_FLAGS_COVERAGE "${CMAKE_CXX_FLAGS_COVERAGE} ${FLAGS}")
|
||||
set(CMAKE_C_FLAGS_COVERAGE "${CMAKE_C_FLAGS_COVERAGE} ${FLAGS}")
|
||||
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} ${FLAGS}")
|
||||
set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} ${FLAGS}")
|
||||
endmacro()
|
||||
|
||||
|
||||
macro(set_flags)
|
||||
# Add the preprocessor macros used for distinguishing between debug and release builds (CMake does this automatically for MSVC):
|
||||
if (NOT MSVC)
|
||||
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -D_DEBUG")
|
||||
set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -D_DEBUG")
|
||||
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -DNDEBUG")
|
||||
set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -DNDEBUG")
|
||||
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/lib/cmake-coverage/")
|
||||
include(CodeCoverage)
|
||||
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -D_DEBUG")
|
||||
set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -D_DEBUG")
|
||||
set(CMAKE_CXX_FLAGS_COVERAGE "${CMAKE_CXX_FLAGS_COVERAGE} -D_DEBUG")
|
||||
set(CMAKE_C_FLAGS_COVERAGE "${CMAKE_C_FLAGS_COVERAGE} -D_DEBUG")
|
||||
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -DNDEBUG")
|
||||
set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -DNDEBUG")
|
||||
endif()
|
||||
|
||||
if(MSVC)
|
||||
@ -42,9 +51,10 @@ macro(set_flags)
|
||||
elseif(APPLE)
|
||||
#on os x clang adds pthread for us but we need to add it for gcc
|
||||
if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
|
||||
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -std=c++11")
|
||||
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -std=c++11")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
|
||||
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -std=c++11")
|
||||
set(CMAKE_CXX_FLAGS_COVERAGE "${CMAKE_CXX_FLAGS_COVERAGE} -std=c++11")
|
||||
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -std=c++11")
|
||||
add_flags_cxx("-stdlib=libc++")
|
||||
add_flags_lnk("-stdlib=libc++")
|
||||
else()
|
||||
@ -57,6 +67,7 @@ macro(set_flags)
|
||||
if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
|
||||
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -std=c++11")
|
||||
set(CMAKE_CXX_FLAGS_COVERAGE "${CMAKE_CXX_FLAGS_COVERAGE} -std=c++11")
|
||||
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -std=c++11")
|
||||
endif()
|
||||
|
||||
@ -97,10 +108,12 @@ macro(set_lib_flags)
|
||||
string(REPLACE "/W3" "" CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG}")
|
||||
string(REPLACE "/W3" "" CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG}")
|
||||
else()
|
||||
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -w")
|
||||
set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -w")
|
||||
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -w")
|
||||
set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -w")
|
||||
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -w")
|
||||
set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -w")
|
||||
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -w")
|
||||
set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -w")
|
||||
set(CMAKE_CXX_FLAGS_COVERAGE "${CMAKE_CXX_FLAGS_COVERAGE} -w")
|
||||
set(CMAKE_C_FLAGS_COVERAGE "${CMAKE_C_FLAGS_COVERAGE} -w")
|
||||
endif()
|
||||
|
||||
# On Unix we use two dynamic loading libraries dl and ltdl.
|
||||
@ -128,7 +141,7 @@ macro(enable_profile)
|
||||
|
||||
# Declare the profiling configurations:
|
||||
SET(CMAKE_CXX_FLAGS_DEBUGPROFILE
|
||||
"${CMAKE_CXX_FLAGS_DEBUG} ${PCXX_ROFILING}"
|
||||
"${CMAKE_CXX_FLAGS_DEBUG} ${CXX_PROFILING}"
|
||||
CACHE STRING "Flags used by the C++ compiler during profile builds."
|
||||
FORCE )
|
||||
SET(CMAKE_C_FLAGS_DEBUGPROFILE
|
||||
@ -171,7 +184,11 @@ macro(enable_profile)
|
||||
CMAKE_EXE_LINKER_FLAGS_RELEASEPROFILE
|
||||
CMAKE_SHARED_LINKER_FLAGS_RELEASEPROFILE )
|
||||
# The configuration types need to be set after their respective c/cxx/linker flags and before the project directive
|
||||
set(CMAKE_CONFIGURATION_TYPES "Debug;Release;DebugProfile;ReleaseProfile" CACHE STRING "" FORCE)
|
||||
if(MSVC)
|
||||
set(CMAKE_CONFIGURATION_TYPES "Debug;Release;DebugProfile;ReleaseProfile" CACHE STRING "" FORCE)
|
||||
else()
|
||||
set(CMAKE_CONFIGURATION_TYPES "Debug;Release;DebugProfile;ReleaseProfile;Coverage" CACHE STRING "" FORCE)
|
||||
endif()
|
||||
endmacro()
|
||||
|
||||
macro(set_exe_flags)
|
||||
@ -180,10 +197,12 @@ macro(set_exe_flags)
|
||||
# We do not do that for MSVC since MSVC produces an awful lot of warnings for its own STL headers;
|
||||
# the important warnings are turned on using #pragma in Globals.h
|
||||
if (NOT MSVC)
|
||||
string(REPLACE "-w" "" CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE}")
|
||||
string(REPLACE "-w" "" CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE}")
|
||||
string(REPLACE "-w" "" CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG}")
|
||||
string(REPLACE "-w" "" CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG}")
|
||||
string(REPLACE "-w" "" CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE}")
|
||||
string(REPLACE "-w" "" CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE}")
|
||||
string(REPLACE "-w" "" CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG}")
|
||||
string(REPLACE "-w" "" CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG}")
|
||||
string(REPLACE "-w" "" CMAKE_CXX_FLAGS_COVERAGE "${CMAKE_CXX_FLAGS_COVERAGE}")
|
||||
string(REPLACE "-w" "" CMAKE_C_FLAGS_COVERAGE "${CMAKE_C_FLAGS_COVERAGE}")
|
||||
add_flags_cxx("-Wall -Wextra -Wno-unused-parameter -Wno-error=switch")
|
||||
|
||||
# we support non-IEEE 754 fpus so can make no guarentees about error
|
||||
|
@ -1,32 +1,53 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 10.00
|
||||
# Visual C++ Express 2008
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "AnvilStats", "AnvilStats.vcproj", "{CF996A5E-0A86-4004-9710-682B06B5AEBA}"
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio Express 2013 for Windows Desktop
|
||||
VisualStudioVersion = 12.0.21005.1
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "AnvilStats", "AnvilStats.vcxproj", "{CF996A5E-0A86-4004-9710-682B06B5AEBA}"
|
||||
ProjectSection(ProjectDependencies) = postProject
|
||||
{EA9D50FD-937A-4EF5-8C37-5F4175AF4FEA} = {EA9D50FD-937A-4EF5-8C37-5F4175AF4FEA}
|
||||
{B61007AC-B557-4B67-A765-E468C0C3A821} = {B61007AC-B557-4B67-A765-E468C0C3A821}
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "zlib", "..\..\VC2008\zlib.vcproj", "{EA9D50FD-937A-4EF5-8C37-5F4175AF4FEA}"
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "zlib", "..\..\lib\zlib\zlib.vcxproj", "{B61007AC-B557-4B67-A765-E468C0C3A821}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Win32 = Debug|Win32
|
||||
DebugProfile|Win32 = DebugProfile|Win32
|
||||
MinSizeRel|Win32 = MinSizeRel|Win32
|
||||
Release profiled|Win32 = Release profiled|Win32
|
||||
Release|Win32 = Release|Win32
|
||||
ReleaseProfile|Win32 = ReleaseProfile|Win32
|
||||
RelWithDebInfo|Win32 = RelWithDebInfo|Win32
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{CF996A5E-0A86-4004-9710-682B06B5AEBA}.Debug|Win32.ActiveCfg = Debug|Win32
|
||||
{CF996A5E-0A86-4004-9710-682B06B5AEBA}.Debug|Win32.Build.0 = Debug|Win32
|
||||
{CF996A5E-0A86-4004-9710-682B06B5AEBA}.DebugProfile|Win32.ActiveCfg = Debug|Win32
|
||||
{CF996A5E-0A86-4004-9710-682B06B5AEBA}.DebugProfile|Win32.Build.0 = Debug|Win32
|
||||
{CF996A5E-0A86-4004-9710-682B06B5AEBA}.MinSizeRel|Win32.ActiveCfg = Release|Win32
|
||||
{CF996A5E-0A86-4004-9710-682B06B5AEBA}.MinSizeRel|Win32.Build.0 = Release|Win32
|
||||
{CF996A5E-0A86-4004-9710-682B06B5AEBA}.Release profiled|Win32.ActiveCfg = Release profiled|Win32
|
||||
{CF996A5E-0A86-4004-9710-682B06B5AEBA}.Release profiled|Win32.Build.0 = Release profiled|Win32
|
||||
{CF996A5E-0A86-4004-9710-682B06B5AEBA}.Release|Win32.ActiveCfg = Release|Win32
|
||||
{CF996A5E-0A86-4004-9710-682B06B5AEBA}.Release|Win32.Build.0 = Release|Win32
|
||||
{EA9D50FD-937A-4EF5-8C37-5F4175AF4FEA}.Debug|Win32.ActiveCfg = Debug|Win32
|
||||
{EA9D50FD-937A-4EF5-8C37-5F4175AF4FEA}.Debug|Win32.Build.0 = Debug|Win32
|
||||
{EA9D50FD-937A-4EF5-8C37-5F4175AF4FEA}.Release profiled|Win32.ActiveCfg = Release profiled|Win32
|
||||
{EA9D50FD-937A-4EF5-8C37-5F4175AF4FEA}.Release profiled|Win32.Build.0 = Release profiled|Win32
|
||||
{EA9D50FD-937A-4EF5-8C37-5F4175AF4FEA}.Release|Win32.ActiveCfg = Release|Win32
|
||||
{EA9D50FD-937A-4EF5-8C37-5F4175AF4FEA}.Release|Win32.Build.0 = Release|Win32
|
||||
{CF996A5E-0A86-4004-9710-682B06B5AEBA}.ReleaseProfile|Win32.ActiveCfg = Release|Win32
|
||||
{CF996A5E-0A86-4004-9710-682B06B5AEBA}.ReleaseProfile|Win32.Build.0 = Release|Win32
|
||||
{CF996A5E-0A86-4004-9710-682B06B5AEBA}.RelWithDebInfo|Win32.ActiveCfg = Release|Win32
|
||||
{CF996A5E-0A86-4004-9710-682B06B5AEBA}.RelWithDebInfo|Win32.Build.0 = Release|Win32
|
||||
{B61007AC-B557-4B67-A765-E468C0C3A821}.Debug|Win32.ActiveCfg = Debug|Win32
|
||||
{B61007AC-B557-4B67-A765-E468C0C3A821}.Debug|Win32.Build.0 = Debug|Win32
|
||||
{B61007AC-B557-4B67-A765-E468C0C3A821}.DebugProfile|Win32.ActiveCfg = DebugProfile|Win32
|
||||
{B61007AC-B557-4B67-A765-E468C0C3A821}.DebugProfile|Win32.Build.0 = DebugProfile|Win32
|
||||
{B61007AC-B557-4B67-A765-E468C0C3A821}.MinSizeRel|Win32.ActiveCfg = MinSizeRel|Win32
|
||||
{B61007AC-B557-4B67-A765-E468C0C3A821}.MinSizeRel|Win32.Build.0 = MinSizeRel|Win32
|
||||
{B61007AC-B557-4B67-A765-E468C0C3A821}.Release profiled|Win32.ActiveCfg = Release|Win32
|
||||
{B61007AC-B557-4B67-A765-E468C0C3A821}.Release profiled|Win32.Build.0 = Release|Win32
|
||||
{B61007AC-B557-4B67-A765-E468C0C3A821}.Release|Win32.ActiveCfg = Release|Win32
|
||||
{B61007AC-B557-4B67-A765-E468C0C3A821}.Release|Win32.Build.0 = Release|Win32
|
||||
{B61007AC-B557-4B67-A765-E468C0C3A821}.ReleaseProfile|Win32.ActiveCfg = ReleaseProfile|Win32
|
||||
{B61007AC-B557-4B67-A765-E468C0C3A821}.ReleaseProfile|Win32.Build.0 = ReleaseProfile|Win32
|
||||
{B61007AC-B557-4B67-A765-E468C0C3A821}.RelWithDebInfo|Win32.ActiveCfg = RelWithDebInfo|Win32
|
||||
{B61007AC-B557-4B67-A765-E468C0C3A821}.RelWithDebInfo|Win32.Build.0 = RelWithDebInfo|Win32
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
|
@ -5,38 +5,7 @@
|
||||
|
||||
#include "Globals.h"
|
||||
#include "BiomeMap.h"
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
static const int g_BiomePalette[] =
|
||||
{
|
||||
// ARGB:
|
||||
0xff0000ff, /* Ocean */
|
||||
0xff00cf3f, /* Plains */
|
||||
0xffffff00, /* Desert */
|
||||
0xff7f7f7f, /* Extreme Hills */
|
||||
0xff00cf00, /* Forest */
|
||||
0xff007f3f, /* Taiga */
|
||||
0xff3f7f00, /* Swampland */
|
||||
0xff003fff, /* River */
|
||||
0xff7f0000, /* Hell */
|
||||
0xff007fff, /* Sky */
|
||||
0xff3f3fff, /* Frozen Ocean */
|
||||
0xff3f3fff, /* Frozen River */
|
||||
0xff7fffcf, /* Ice Plains */
|
||||
0xff3fcf7f, /* Ice Mountains */
|
||||
0xffcf00cf, /* Mushroom Island */
|
||||
0xff7f00ff, /* Mushroom Island Shore */
|
||||
0xffffff3f, /* Beach */
|
||||
0xffcfcf00, /* Desert Hills */
|
||||
0xff00cf3f, /* Forest Hills */
|
||||
0xff006f1f, /* Taiga Hills */
|
||||
0xff7f8f7f, /* Extreme Hills Edge */
|
||||
0xff004f00, /* Jungle */
|
||||
0xff003f00, /* Jungle Hills */
|
||||
} ;
|
||||
#include "../BiomeVisualiser/BiomeColors.h"
|
||||
|
||||
|
||||
|
||||
@ -139,7 +108,7 @@ void cBiomeMap::StartNewRegion(int a_RegionX, int a_RegionZ)
|
||||
unsigned char * BiomeRow = (unsigned char *)m_Biomes + z * 512;
|
||||
for (int x = 0; x < 512; x++)
|
||||
{
|
||||
RowData[x] = g_BiomePalette[BiomeRow[x]];
|
||||
RowData[x] = g_BiomeColors[BiomeRow[x]];
|
||||
}
|
||||
f.Write(RowData, sizeof(RowData));
|
||||
} // for z
|
||||
|
@ -41,7 +41,7 @@ protected:
|
||||
virtual bool OnDecompressedData(const char * a_DecompressedNBT, int a_DataSize) override { return false; }
|
||||
virtual bool OnRealCoords(int a_ChunkX, int a_ChunkZ) override { return false; }
|
||||
virtual bool OnLastUpdate(Int64 a_LastUpdate) override { return false; }
|
||||
virtual bool OnTerrainPopulated(bool a_Populated) override { return !a_Populated; } // If not populated, we don't want it!
|
||||
virtual bool OnTerrainPopulated(bool a_Populated) override { return false; } // We don't care about "populated", the biomes are the same
|
||||
virtual bool OnBiomes(const unsigned char * a_BiomeData) override;
|
||||
|
||||
void StartNewRegion(int a_RegionX, int a_RegionZ);
|
||||
|
@ -24,6 +24,15 @@
|
||||
#define ALIGN_8
|
||||
#define ALIGN_16
|
||||
|
||||
#define FORMATSTRING(formatIndex, va_argsIndex)
|
||||
|
||||
// MSVC has its own custom version of zu format
|
||||
#define SIZE_T_FMT "%Iu"
|
||||
#define SIZE_T_FMT_PRECISION(x) "%" #x "Iu"
|
||||
#define SIZE_T_FMT_HEX "%Ix"
|
||||
|
||||
#define NORETURN __declspec(noreturn)
|
||||
|
||||
#elif defined(__GNUC__)
|
||||
|
||||
// TODO: Can GCC explicitly mark classes as abstract (no instances can be created)?
|
||||
@ -40,6 +49,14 @@
|
||||
// Some portability macros :)
|
||||
#define stricmp strcasecmp
|
||||
|
||||
#define FORMATSTRING(formatIndex, va_argsIndex) __attribute__((format (printf, formatIndex, va_argsIndex)))
|
||||
|
||||
#define SIZE_T_FMT "%zu"
|
||||
#define SIZE_T_FMT_PRECISION(x) "%" #x "zu"
|
||||
#define SIZE_T_FMT_HEX "%zx"
|
||||
|
||||
#define NORETURN __attribute((__noreturn__))
|
||||
|
||||
#else
|
||||
|
||||
#error "You are using an unsupported compiler, you might need to #define some stuff here for your compiler"
|
||||
@ -194,6 +211,8 @@ typedef unsigned short UInt16;
|
||||
/// Faster than (int)floorf((float)x / (float)div)
|
||||
#define FAST_FLOOR_DIV( x, div ) ( (x) < 0 ? (((int)x / div) - 1) : ((int)x / div) )
|
||||
|
||||
#define TOLUA_TEMPLATE_BIND(...)
|
||||
|
||||
// Own version of assert() that writes failed assertions to the log for review
|
||||
#ifdef _DEBUG
|
||||
#define ASSERT( x ) ( !!(x) || ( LOGERROR("Assertion failed: %s, file %s, line %i", #x, __FILE__, __LINE__ ), assert(0), 0 ) )
|
||||
@ -204,6 +223,8 @@ typedef unsigned short UInt16;
|
||||
// Pretty much the same as ASSERT() but stays in Release builds
|
||||
#define VERIFY( x ) ( !!(x) || ( LOGERROR("Verification failed: %s, file %s, line %i", #x, __FILE__, __LINE__ ), exit(1), 0 ) )
|
||||
|
||||
typedef unsigned char Byte;
|
||||
|
||||
|
||||
|
||||
|
||||
@ -227,3 +248,4 @@ public:
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -109,7 +109,7 @@ bool cSpringStats::OnSectionsFinished(void)
|
||||
int Base = BaseY + z * 16;
|
||||
for (int x = 1; x < 15; x++)
|
||||
{
|
||||
if (cChunkDef::GetNibble(m_BlockMetas, Base + x) != 0)
|
||||
if (cChunkDef::GetNibble(m_BlockMetas, x, y, z) != 0)
|
||||
{
|
||||
// Not a source block
|
||||
continue;
|
||||
|
319
docs/Generator.html
Normal file
@ -0,0 +1,319 @@
|
||||
<html>
|
||||
<head>
|
||||
<title>Generating terrain in MCServer</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Generating terrain in MCServer</h1>
|
||||
<p>This article explains the principles behind the terrain generator in MCServer. It is not strictly
|
||||
specific to MCServer, though, it can be viewed as a generic guide to various terrain-generating algorithms,
|
||||
with specific implementation notes regarding MCServer.</p>
|
||||
|
||||
<p>Contents:
|
||||
<ul>
|
||||
<li><a href="#preface">Preface: How it's done in real life</a></li>
|
||||
<li><a href="#expectedprops">Expected properties</a></li>
|
||||
<li><a href="#reversingflow">Reversing the flow</a></li>
|
||||
<li><a href="#composablegen">The ComposableGenerator pipeline</a></li>
|
||||
<li><a href="#coherentnoise">Using coherent noise</a></li>
|
||||
<li><a href="#biomegen">Generating biomes</a></li>
|
||||
<li><a href="#heightgen">Terrain height</a></li>
|
||||
<li><a href="#compositiongen">Terrain composition</a></li>
|
||||
<li><a href="#finishgen">Finishers</a></li>
|
||||
</ul>
|
||||
</p>
|
||||
|
||||
|
||||
<hr />
|
||||
|
||||
<a name="preface"><h2>Preface: How it's done in real life</h2></a>
|
||||
<p>The nature has many complicated geological, physical and biological processes working on all scales from
|
||||
microscopic to planet-wide scale, that have shaped the terrain into what we see today. The tectonic plates
|
||||
collide, push mountain ranges up and ocean trenches down. Erosion dulls the sharp shapes. Plantlife takes
|
||||
over to further change the overall look of the world.</p>
|
||||
|
||||
<p>Generally speaking, the processes take what's there and change it. Unlike computer generating, which
|
||||
usually creates a finished terrain from scratch, or maybe with only a few iterations. It would be unfeasible
|
||||
for software to emulate all the natural processes in enough detail to provide world generation for a game,
|
||||
mainly because in the nature everything interacts with everything. If a mountain range rises, it changes the
|
||||
way that the precipitation is carried by the wind to the lands beyond the mountains, thus changing the
|
||||
erosion rate there and the vegetation type. </p>
|
||||
|
||||
|
||||
<hr />
|
||||
|
||||
<a name="expectedprops"><h2>Expected properties</h2></a>
|
||||
<p>For a MineCraft-like game terrain generator we need the generator to have several properties:
|
||||
<ul>
|
||||
<li>The generator must be able to generate terrain in small chunks. This means it must be possible to
|
||||
generate each of the chunks separately, without dependencies on the neighboring chunks. Note that this
|
||||
doesn't mean chunks cannot coordinate together, it means that "a tree in one chunk cannot ask if there's
|
||||
a building in the neighbor chunk", simply because the neighbor chunk may not be generated yet.</li>
|
||||
<li>The generated chunk needs to be the same if re-generated. This property is not exactly required, but it
|
||||
makes available several techniques that wouldn't be possible otherwise.</li>
|
||||
<li>The generator needs to be reasonably fast. For a server application this means at least some 20 chunks
|
||||
per second for chunks close to each other, and 5 chunks per second for distant chunks. The reason for this
|
||||
distinction will be discussed later.</li>
|
||||
</ul>
|
||||
</p>
|
||||
|
||||
|
||||
<hr />
|
||||
|
||||
<a name="reversingflow"><h2>Reversing the flow</h2></a>
|
||||
<p>As already mentioned, the nature works basically by generating raw terrain composition, then "applying"
|
||||
erosion, vegetation and finally this leads to biomes being formed. Let's now try a somewhat inverse
|
||||
approach: First generate biomes, then fit them with appropriate terrain, and finally cover in vegetation
|
||||
and all the other stuff.</p>
|
||||
|
||||
<p>Splitting the parts like this suddenly makes it possible to create a generator with the required
|
||||
properties. We can generate a reasonable biome map chunk-wise, independently of all the other data. Once we
|
||||
have the biomes, we can compose the terrain for the chunk by using the biome data for the chunk, and
|
||||
possibly even for neighboring chunks. Note that we're not breaking the first property, the biomes can be
|
||||
generated separately so a neighboring chunk's biome map can be generated without the need for the entire
|
||||
neighboring chunk to be present. Similarly, once we have the terrain composition for a chunk, we can
|
||||
generate all the vegetation and structures in it, and those can again use the terrain composition in
|
||||
neighboring chunks.</p>
|
||||
|
||||
|
||||
<hr />
|
||||
|
||||
<a name="composablegen"><h2>The ComposableGenerator pipeline</h2></a>
|
||||
<p>This leads us directly to the main pipeline that is used for generating terrain in MCServer. For
|
||||
technical reasons, the terrain composition step is further subdivided into Height generation and Composition
|
||||
generation, and the structures are really called Finishers. For each chunk the generator generates, in this
|
||||
sequence:
|
||||
<ul>
|
||||
<li>Biomes</li>
|
||||
<li>Terrain height</li>
|
||||
<li>Terrain composition</li>
|
||||
<li>Finishers</li>
|
||||
</ul>
|
||||
</p>
|
||||
|
||||
<img src="img/biomes.jpg" />
|
||||
<img src="img/terrainheight.jpg" />
|
||||
<img src="img/terraincomposition.jpg" />
|
||||
<img src="img/finishers.jpg" />
|
||||
<p>The beautiful thing about this is that the individual components can be changed independently. You can
|
||||
have 5 biome generators and 3 height generators and you can let the users mix'n'match.
|
||||
</p>
|
||||
|
||||
|
||||
<hr />
|
||||
|
||||
<a name="coherentnoise"><h2>Using coherent noise for the generation</h2></a>
|
||||
<p>For a great tutorial on coherent noise, see the <a href="http://libnoise.sourceforge.net/">LibNoise
|
||||
documentation</a>.</p>
|
||||
<p>Coherent noise is a type of noise that has three important properties that we can use to our advantage:
|
||||
<ul>
|
||||
<li>The noise is smooth</li>
|
||||
<li>The noise is algorithmically generated, which means that the same data is generated when the same
|
||||
parameters are given to the noise functions.</li>
|
||||
<li>The noise can be seamlessly extended in any direction</li>
|
||||
</ul></p>
|
||||
|
||||
<p>We'll be mostly using Perlin noise in this article. It is the easiest one to visualise and use and is one
|
||||
of the most useful kinds of coherent noises. Here's an example of a Perlin noise generated in 2 dimensions:</p>
|
||||
<img src="img/perlin.jpg" />
|
||||
|
||||
<p>It comes only naturally that such a 2D noise can be used as a terrain height map directly:</p>
|
||||
<img src="img/perlinheightmap.jpg" />
|
||||
|
||||
<p>However, this is not the only use for this noise, and 2 dimensions is not the limit - this noise can be
|
||||
generated for any number of dimensions.</p>
|
||||
|
||||
|
||||
|
||||
<hr />
|
||||
|
||||
<a name="biomegen"><h2>Generating biomes</h2></a>
|
||||
<p>The easiest way to generate biomes is to not generate them at all - simply assign a single constant biome
|
||||
to everywhere. And indeed there are times when this kind of "generator" is useful - for the MineCraft's Flat
|
||||
world type, or for testing purposes, or for tematic maps. In MCServer, this is exactly what the Constant
|
||||
biome generator does.</p>
|
||||
|
||||
<p>Of course, there are more interesting test scenarios for which multiple biomes must be generated as easy
|
||||
as possible. For these special needs, there's a CheckerBoard biome generator. As the name suggests, it
|
||||
generates a grid of alternating biomes.</p>
|
||||
|
||||
<h3>Voronoi diagram</h3>
|
||||
<p>Those two generators were more of a technicality, we need to make something more interesting if we're
|
||||
going for a natural look. The Voronoi generator is the first step towards such a change. Recall that a
|
||||
<a href="http://en.wikipedia.org/wiki/Voronoi_diagram">Voronoi diagram</a> is a construct that creates a
|
||||
set of areas where each point in an area is closer to the appropriate seed of the area than the seeds of any
|
||||
other area:</p>
|
||||
<img src="img/voronoi.png" />
|
||||
|
||||
<p>To generate biomes using this approach, you select random "seeds", assign a biome to each one, and then
|
||||
for each "column" of the world you find the seed that is the nearest to that column, and use that seed's
|
||||
biome.</p>
|
||||
|
||||
<p>The overall shape of a Voronoi diagram is governed by the placement of the seeds. In extreme cases, a
|
||||
seed could affect the entire diagram, which is what we don't want - we need our locality, so that we can
|
||||
generate a chunk's worth of biome data. We also don't want the too much irregular diagrams that are produced
|
||||
when the seeds are in small clusters. We need our seeds to come in random, yet somewhat uniform fashion.</p>
|
||||
|
||||
<p>Luckily, we have just the tool: Grid with jitter. Originally used in antialiasing techniques, they can be
|
||||
successfully applied as a source of the seeds for a Voronoi diagram. Simply take a regular 2D grid of seeds
|
||||
with the grid distance being N, and move each seed along the X and Y axis by a random distance, usually in
|
||||
the range [-N / 2, +N / 2]:</p>
|
||||
<img src="img/jittergrid.jpg" />
|
||||
|
||||
<p>Such a grid is the ideal seed source for a Voronoi biome generator, because not
|
||||
only are the Voronoi cells "reasonable", but the seed placement's effect on the diagram is localized - each
|
||||
pixel in the diagram depends on at most 4 x 4 seeds around it. In the following picture, the seed for the
|
||||
requested point (blue) must be within the indicated circle. Even the second-nearest seed, which we will need
|
||||
later, is inside that circle.</p>
|
||||
<img src="img/jittergridlocality.jpg" />
|
||||
|
||||
<p>Calculating the jitter for each cell can be done easily by using a 2D Perlin noise for each coord. We
|
||||
calculate the noise's value at [X, Z], which gives us a number in the range [-1; 1]. We then multiply the
|
||||
number by N / 2, this gives us the required range of [-N / 2, +N / 2]. Adding this number to the X coord
|
||||
gives us the seed's X position. We use another Perlin noise and the same calculation for the Z coord of the
|
||||
seed.</p>
|
||||
|
||||
<p>Here's an example of a biome map generated using the Voronoi + jitter grid, as implemented by the Voronoi
|
||||
biome generator in MCServer:</p>
|
||||
<img src="img/voronoijitterbiomes.png" />
|
||||
|
||||
<h3>Distorted Voronoi</h3>
|
||||
<p>The biomes are starting to look interesting, but now they have straight-line borders, which looks rather
|
||||
weird and the players will most likely notice very soon. We need to somehow distort the borders to make them
|
||||
look more natural. By far the easiest way to achieve that is to use a little trick: When the generator is
|
||||
asked for the biome at column [X, Z], instead of calculating the Voronoi biome for column [X, Z], we first
|
||||
calculate a random offset for each coord, and add it to the coordinates. So the generator actually responds
|
||||
with the biome for [X + rndX, Z + rndZ].</p>
|
||||
|
||||
<p>In order to keep the property that generating for the second time gives us the same result, we need the
|
||||
"random offset" to be replicatable - same output for the same input. This is where we use yet another Perlin
|
||||
noise - just like with the jitter for the Voronoi grid, we add a value from a separate noise to each
|
||||
coordinate before sending the coordinates down to the Voronoi generator:</p>
|
||||
<code>
|
||||
DistortedVoronoiBiome(X, Z) := VoronoiBiome(X + PerlinX(X, Z), Z + PerlinZ(X, Z))
|
||||
</code>
|
||||
|
||||
<p>The following image shows the effects of the change, as generated by MCServer's DistortedVoronoi biome
|
||||
generator. It is actually using the very same Voronoi map as the previous image, the only change has been
|
||||
the addition of the distortion:</p>
|
||||
<img src="img/distortedvoronoibiomes.png" />
|
||||
|
||||
<p>As you can see, this already looks reasonable enough, it could be considered natural biomes, if it
|
||||
weren't for several drawbacks:
|
||||
<ul>
|
||||
<li>There's no way to limit the neighbors. A desert biome can neighbor a tundra biome. </li>
|
||||
<li>All the biomes are considered equal. There's no way to make oceans larger. A mushroom biome is
|
||||
generated right next to other land biomes.</li>
|
||||
</ul></p>
|
||||
|
||||
<h3>Adding relativity</h3>
|
||||
<p>Our next goal is to remove the first defect of the distorted Voronoi generator: unrelated biomes
|
||||
generating next to each other. It is highly unlikely to find a jungle biome next to a desert biome, so we
|
||||
want to have as few of those borders as possible. We could further improve on the selection of
|
||||
biome-to-seed in the Voronoi generator. Or we can try a completely different idea altogether.</p>
|
||||
|
||||
<p>Recall how we talked about the nature, where the biomes are formed by the specific conditions of a place.
|
||||
What if we could make a similar dependency, but without the terrain? It turns out this is possible rather
|
||||
easily - instead of depending on the terrain, we choose two completely artificial measures. Let's call them
|
||||
Temperature and Humidity. If we knew the temperature of the place, we know what set of biomes are possible
|
||||
for such temperatures - we won't place deserts in the cold and tundra in the hot anymore. Similarly, the
|
||||
humidity will help us sort out the desert vs jungle issue. But how do we get a temperature and humidity?
|
||||
Once again, the Perlin noise comes to the rescue. We can use a simple 2D Perlin noise as the temperature
|
||||
map, and another one as the humidity map.</p>
|
||||
|
||||
<p>What we need next is a decision of what biome to generate in certain temperature and humidity
|
||||
combinations. The fastest way for a computer is to have a 2D array, where the temperature is one dimension
|
||||
and humidity the other, and the values in the array specify the biome to generate:</p>
|
||||
<img src="img/temperaturehumiditydecisionsimple.jpg" />
|
||||
|
||||
<p>We can even "misuse" the above diagram to include the hill variants of the biomes and have those hills
|
||||
neighbor each other properly, simply by declaring some of the decision diagram's parts as hills:</p>
|
||||
<img src="img/temperaturehumiditydecisionhills.jpg" />
|
||||
|
||||
<p>The problem with this approach is that there are biomes that should not depend on temperature or
|
||||
humidity, they generate across all of their values. Biomes like Oceans, Rivers and Mushroom. We could
|
||||
either add them somewhere into the decision diagram, or we can make the generator use a multi-step decision:
|
||||
<ul>
|
||||
<li>Decide whether the point is in the ocean, land or mushroom</li>
|
||||
<li>If it's land, decide if it's real land or river.</li>
|
||||
<li>If it's real land, use a TemperatureHumidity approach to generate land-biomes</li>
|
||||
</ul>
|
||||
</p>
|
||||
|
||||
<p>This is the approach implemented in MCServer's MultiStepMap biome generator. It generates biome maps like
|
||||
this:</p>
|
||||
<img src="img/multistepmapbiomes.png" />
|
||||
|
||||
<p>To decide whether the point is in the ocean, land or mushroom, the generator first chooses seeds in a grid
|
||||
that will be later fed to a DistortedVoronoi algorithm, the seeds get the "ocean" and "land" values. Then it
|
||||
considers all the "ocean" seeds that are surrounded by 8 other "ocean" seeds and turns a random few of them
|
||||
into "mushroom". This special seed processing makes the mushroom biomes mostly surrounded by ocean. The
|
||||
following image shows an example seeds grid that the generator might consider, only the two framed cells are
|
||||
allowed to change into mushroom. L = land, O = ocean:</p>
|
||||
<img src="img/multistepmapgrid.jpg" />
|
||||
|
||||
<p>Next, the generator calculates the DistortedVoronoi for the seeds. For the areas that are calculated as
|
||||
mushroom, the distance to the nearest-seed is used to further shrink the mushroom biome and then to
|
||||
distinguish between mushroom and mushroom-shore (image depicts a Voronoi cell for illustration purposes, it
|
||||
works similarly with DistortedVoronoi). O = ocean, M = mushroom, MS = mushroom shore:</p>
|
||||
<img src="img/multistepmapdistance.jpg" />
|
||||
|
||||
<a name="perlinrivers">
|
||||
<p>The rivers are added only to the areas that have been previously marked as land. A simple 2D Perlin noise
|
||||
is used as the base, where its value is between 0 and a configured threshold value, a river is created. This
|
||||
creates the rivers in a closed-loop-like shapes, occasionally splitting two branches off:</p>
|
||||
<img src="img/perlinrivers1.jpg" />
|
||||
<img src="img/perlinrivers2.jpg" />
|
||||
<img src="img/perlinrivers3.jpg" />
|
||||
</a>
|
||||
|
||||
<p>For the leftover land biomes, the two Perlin noises, representing temperature and humidity, are used to
|
||||
generate the biomes, as described earlier. Additionally, the temperature map is used to turn the Ocean biome
|
||||
into FrozenOcean, and the River biome into FrozenRiver, wherever the temperature drops below a threshold.</p>
|
||||
|
||||
<h3>Two-level Voronoi</h3>
|
||||
<p>The 1.7 MineCraft update brought a completely new terrain generation, which has sparked renewed interest
|
||||
in the biome generation. A new, potentially simpler way of generating biomes was found, the two-level
|
||||
DistortedVoronoi generator.</p>
|
||||
|
||||
<p>The main idea behind it all is that we create large areas of similar biomes. There are several groups of
|
||||
related biomes that can be generated near each other: Desert biomes, Ice biomes, Forest biomes, Mesa biomes.
|
||||
Technically, the Ocean biomes were added as yet another group, so that the oceans will generate in
|
||||
approximately the size of the larger areas, too.</p>
|
||||
|
||||
<p>For each column a DistortedVoronoi is used to select, which large area to use. This in turn results in
|
||||
the list of biomes from which to choose. Another DistortedVoronoi, this time with a smaller grid size, is
|
||||
used to select one biome out of that list. Additionally, the smaller DistortedVoronoi calculates not only
|
||||
the nearest seed's distance, but also the distance to the second-nearest seed; the ratio between these two
|
||||
is used as an indicator whether the column is in the "inside" or on the "outskirt" of the smaller Voronoi
|
||||
cell. This allows us to give certain biomes an "edge" biome - the Mushroom biome has a MushroomShore edge,
|
||||
the ExtremeHills biome have an ExtremeHillsEdge biome on the edge, etc.</p>
|
||||
|
||||
<p>The images below illustrate the process with regular Voronoi diagrams, for clarity purposes. The real
|
||||
generator uses distortion before querying the small areas.</p>
|
||||
<img src="img/twolevellargeareas.jpg" /><br />
|
||||
<img src="img/twolevelsmallgrid.jpg" /><br />
|
||||
<img src="img/twolevelsmallareas.jpg" /><br />
|
||||
|
||||
<p>The following image shows an example output of a TwoLevel biome generator in MCServer:</p>
|
||||
<img src="img/twolevelbiomes.png" />
|
||||
|
||||
<p>Note that rivers are currently not implemented in this generator in MCServer, but they could be added
|
||||
using the same approach as in MultiStepMap - by using a thresholded 2D Perlin noise.</p>
|
||||
|
||||
|
||||
<hr />
|
||||
|
||||
<a name="heightgen"><h2>Terrain height</h2></a>
|
||||
|
||||
|
||||
<hr />
|
||||
|
||||
<a name="compositiongen"><h2>Terrain composition</h2></a>
|
||||
|
||||
|
||||
<hr />
|
||||
|
||||
<a name="finishgen"><h2>Finishers</h2></a>
|
||||
|
||||
</body>
|
||||
</html>
|
BIN
docs/img/biomes.jpg
Normal file
After Width: | Height: | Size: 12 KiB |
BIN
docs/img/distortedvoronoibiomes.png
Normal file
After Width: | Height: | Size: 5.9 KiB |
BIN
docs/img/finishers.jpg
Normal file
After Width: | Height: | Size: 14 KiB |
BIN
docs/img/jittergrid.jpg
Normal file
After Width: | Height: | Size: 18 KiB |
BIN
docs/img/jittergridlocality.jpg
Normal file
After Width: | Height: | Size: 15 KiB |
BIN
docs/img/multistepmapbiomes.png
Normal file
After Width: | Height: | Size: 11 KiB |
BIN
docs/img/multistepmapdistance.jpg
Normal file
After Width: | Height: | Size: 16 KiB |
BIN
docs/img/multistepmapgrid.jpg
Normal file
After Width: | Height: | Size: 22 KiB |
BIN
docs/img/perlin.jpg
Normal file
After Width: | Height: | Size: 24 KiB |
BIN
docs/img/perlinheightmap.jpg
Normal file
After Width: | Height: | Size: 52 KiB |
BIN
docs/img/perlinrivers1.jpg
Normal file
After Width: | Height: | Size: 20 KiB |
BIN
docs/img/perlinrivers2.jpg
Normal file
After Width: | Height: | Size: 28 KiB |
BIN
docs/img/perlinrivers3.jpg
Normal file
After Width: | Height: | Size: 28 KiB |
BIN
docs/img/temperaturehumiditydecisionhills.jpg
Normal file
After Width: | Height: | Size: 32 KiB |
BIN
docs/img/temperaturehumiditydecisionsimple.jpg
Normal file
After Width: | Height: | Size: 18 KiB |
BIN
docs/img/terraincomposition.jpg
Normal file
After Width: | Height: | Size: 15 KiB |
BIN
docs/img/terrainheight.jpg
Normal file
After Width: | Height: | Size: 11 KiB |
BIN
docs/img/twolevelbiomes.png
Normal file
After Width: | Height: | Size: 33 KiB |
BIN
docs/img/twolevellargeareas.jpg
Normal file
After Width: | Height: | Size: 17 KiB |
BIN
docs/img/twolevelsmallareas.jpg
Normal file
After Width: | Height: | Size: 23 KiB |
BIN
docs/img/twolevelsmallgrid.jpg
Normal file
After Width: | Height: | Size: 38 KiB |
BIN
docs/img/voronoi.png
Normal file
After Width: | Height: | Size: 19 KiB |
BIN
docs/img/voronoijitterbiomes.png
Normal file
After Width: | Height: | Size: 4.2 KiB |
160
lib/cmake-coverage/CodeCoverage.cmake
Normal file
@ -0,0 +1,160 @@
|
||||
#
|
||||
# 2012-01-31, Lars Bilke
|
||||
# - Enable Code Coverage
|
||||
#
|
||||
# 2013-09-17, Joakim Söderberg
|
||||
# - Added support for Clang.
|
||||
# - Some additional usage instructions.
|
||||
#
|
||||
# USAGE:
|
||||
# 1. Copy this file into your cmake modules path.
|
||||
#
|
||||
# 2. Add the following line to your CMakeLists.txt:
|
||||
# INCLUDE(CodeCoverage)
|
||||
#
|
||||
# 3. Set compiler flags to turn off optimization and enable coverage:
|
||||
# SET(CMAKE_CXX_FLAGS "-g -O0 -fprofile-arcs -ftest-coverage")
|
||||
# SET(CMAKE_C_FLAGS "-g -O0 -fprofile-arcs -ftest-coverage")
|
||||
#
|
||||
# 3. Use the function SETUP_TARGET_FOR_COVERAGE to create a custom make target
|
||||
# which runs your test executable and produces a lcov code coverage report:
|
||||
# Example:
|
||||
# SETUP_TARGET_FOR_COVERAGE(
|
||||
# my_coverage_target # Name for custom target.
|
||||
# test_driver # Name of the test driver executable that runs the tests.
|
||||
# # NOTE! This should always have a ZERO as exit code
|
||||
# # otherwise the coverage generation will not complete.
|
||||
# coverage # Name of output directory.
|
||||
# )
|
||||
#
|
||||
# 4. Build a Debug build:
|
||||
# cmake -DCMAKE_BUILD_TYPE=Debug ..
|
||||
# make
|
||||
# make my_coverage_target
|
||||
#
|
||||
#
|
||||
|
||||
# Check prereqs
|
||||
FIND_PROGRAM( GCOV_PATH gcov )
|
||||
FIND_PROGRAM( LCOV_PATH lcov )
|
||||
FIND_PROGRAM( GENHTML_PATH genhtml )
|
||||
FIND_PROGRAM( GCOVR_PATH gcovr PATHS ${CMAKE_SOURCE_DIR}/tests)
|
||||
|
||||
IF(NOT GCOV_PATH)
|
||||
MESSAGE(FATAL_ERROR "gcov not found! Aborting...")
|
||||
ENDIF() # NOT GCOV_PATH
|
||||
|
||||
IF(NOT CMAKE_COMPILER_IS_GNUCXX)
|
||||
# Clang version 3.0.0 and greater now supports gcov as well.
|
||||
MESSAGE(WARNING "Compiler is not GNU gcc! Clang Version 3.0.0 and greater supports gcov as well, but older versions don't.")
|
||||
|
||||
IF(NOT "${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang")
|
||||
MESSAGE(FATAL_ERROR "Compiler is not GNU gcc! Aborting...")
|
||||
ENDIF()
|
||||
ENDIF() # NOT CMAKE_COMPILER_IS_GNUCXX
|
||||
|
||||
SET(CMAKE_CXX_FLAGS_COVERAGE
|
||||
"-g -O0 --coverage -fprofile-arcs -ftest-coverage"
|
||||
CACHE STRING "Flags used by the C++ compiler during coverage builds."
|
||||
FORCE )
|
||||
SET(CMAKE_C_FLAGS_COVERAGE
|
||||
"-g -O0 --coverage -fprofile-arcs -ftest-coverage"
|
||||
CACHE STRING "Flags used by the C compiler during coverage builds."
|
||||
FORCE )
|
||||
SET(CMAKE_EXE_LINKER_FLAGS_COVERAGE
|
||||
""
|
||||
CACHE STRING "Flags used for linking binaries during coverage builds."
|
||||
FORCE )
|
||||
SET(CMAKE_SHARED_LINKER_FLAGS_COVERAGE
|
||||
""
|
||||
CACHE STRING "Flags used by the shared libraries linker during coverage builds."
|
||||
FORCE )
|
||||
MARK_AS_ADVANCED(
|
||||
CMAKE_CXX_FLAGS_COVERAGE
|
||||
CMAKE_C_FLAGS_COVERAGE
|
||||
CMAKE_EXE_LINKER_FLAGS_COVERAGE
|
||||
CMAKE_SHARED_LINKER_FLAGS_COVERAGE )
|
||||
|
||||
IF ( NOT (CMAKE_BUILD_TYPE STREQUAL "Debug" OR CMAKE_BUILD_TYPE STREQUAL "Coverage"))
|
||||
MESSAGE( WARNING "Code coverage results with an optimized (non-Debug) build may be misleading" )
|
||||
ENDIF() # NOT CMAKE_BUILD_TYPE STREQUAL "Debug"
|
||||
|
||||
|
||||
# Param _targetname The name of new the custom make target
|
||||
# Param _testrunner The name of the target which runs the tests.
|
||||
# MUST return ZERO always, even on errors.
|
||||
# If not, no coverage report will be created!
|
||||
# Param _outputname lcov output is generated as _outputname.info
|
||||
# HTML report is generated in _outputname/index.html
|
||||
# Optional fourth parameter is passed as arguments to _testrunner
|
||||
# Pass them in list form, e.g.: "-j;2" for -j 2
|
||||
FUNCTION(SETUP_TARGET_FOR_COVERAGE _targetname _testrunner _outputname)
|
||||
|
||||
IF(NOT LCOV_PATH)
|
||||
MESSAGE(FATAL_ERROR "lcov not found! Aborting...")
|
||||
ENDIF() # NOT LCOV_PATH
|
||||
|
||||
IF(NOT GENHTML_PATH)
|
||||
MESSAGE(FATAL_ERROR "genhtml not found! Aborting...")
|
||||
ENDIF() # NOT GENHTML_PATH
|
||||
|
||||
# Setup target
|
||||
ADD_CUSTOM_TARGET(${_targetname}
|
||||
|
||||
# Cleanup lcov
|
||||
${LCOV_PATH} --directory . --zerocounters
|
||||
|
||||
# Run tests
|
||||
COMMAND ${_testrunner} ${ARGV3}
|
||||
|
||||
# Capturing lcov counters and generating report
|
||||
COMMAND ${LCOV_PATH} --directory . --capture --output-file ${_outputname}.info
|
||||
COMMAND ${LCOV_PATH} --remove ${_outputname}.info 'tests/*' '/usr/*' --output-file ${_outputname}.info.cleaned
|
||||
COMMAND ${GENHTML_PATH} -o ${_outputname} ${_outputname}.info.cleaned
|
||||
COMMAND ${CMAKE_COMMAND} -E remove ${_outputname}.info ${_outputname}.info.cleaned
|
||||
|
||||
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
|
||||
COMMENT "Resetting code coverage counters to zero.\nProcessing code coverage counters and generating report."
|
||||
)
|
||||
|
||||
# Show info where to find the report
|
||||
ADD_CUSTOM_COMMAND(TARGET ${_targetname} POST_BUILD
|
||||
COMMAND ;
|
||||
COMMENT "Open ./${_outputname}/index.html in your browser to view the coverage report."
|
||||
)
|
||||
|
||||
ENDFUNCTION() # SETUP_TARGET_FOR_COVERAGE
|
||||
|
||||
# Param _targetname The name of new the custom make target
|
||||
# Param _testrunner The name of the target which runs the tests
|
||||
# Param _outputname cobertura output is generated as _outputname.xml
|
||||
# Optional fourth parameter is passed as arguments to _testrunner
|
||||
# Pass them in list form, e.g.: "-j;2" for -j 2
|
||||
FUNCTION(SETUP_TARGET_FOR_COVERAGE_COBERTURA _targetname _testrunner _outputname)
|
||||
|
||||
IF(NOT PYTHON_EXECUTABLE)
|
||||
MESSAGE(FATAL_ERROR "Python not found! Aborting...")
|
||||
ENDIF() # NOT PYTHON_EXECUTABLE
|
||||
|
||||
IF(NOT GCOVR_PATH)
|
||||
MESSAGE(FATAL_ERROR "gcovr not found! Aborting...")
|
||||
ENDIF() # NOT GCOVR_PATH
|
||||
|
||||
ADD_CUSTOM_TARGET(${_targetname}
|
||||
|
||||
# Run tests
|
||||
${_testrunner} ${ARGV3}
|
||||
|
||||
# Running gcovr
|
||||
COMMAND ${GCOVR_PATH} -x -r ${CMAKE_SOURCE_DIR} -e '${CMAKE_SOURCE_DIR}/tests/' -o ${_outputname}.xml
|
||||
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
|
||||
COMMENT "Running gcovr to produce Cobertura code coverage report."
|
||||
)
|
||||
|
||||
# Show info where to find the report
|
||||
ADD_CUSTOM_COMMAND(TARGET ${_targetname} POST_BUILD
|
||||
COMMAND ;
|
||||
COMMENT "Cobertura code coverage report saved in ${_outputname}.xml."
|
||||
)
|
||||
|
||||
ENDFUNCTION() # SETUP_TARGET_FOR_COVERAGE_COBERTURA
|
23
lib/cmake-coverage/LICENSE
Normal file
@ -0,0 +1,23 @@
|
||||
Boost Software License - Version 1.0 - August 17th, 2003
|
||||
|
||||
Permission is hereby granted, free of charge, to any person or organization
|
||||
obtaining a copy of the software and accompanying documentation covered by
|
||||
this license (the "Software") to use, reproduce, display, distribute,
|
||||
execute, and transmit the Software, and to prepare derivative works of the
|
||||
Software, and to permit third-parties to whom the Software is furnished to
|
||||
do so, all subject to the following:
|
||||
|
||||
The copyright notices in the Software and this entire statement, including
|
||||
the above license grant, this restriction and the following disclaimer,
|
||||
must be included in all copies of the Software, in whole or in part, and
|
||||
all derivative works of the Software, unless such copies or derivative
|
||||
works are solely in the form of machine-executable object code generated by
|
||||
a source language processor.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
||||
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
||||
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
||||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
@ -623,7 +623,7 @@ Reader::decodeDouble( Token &token )
|
||||
const int bufferSize = 32;
|
||||
int count;
|
||||
int length = int(token.end_ - token.start_);
|
||||
if ( length <= bufferSize )
|
||||
if ( length < bufferSize )
|
||||
{
|
||||
Char buffer[bufferSize];
|
||||
memcpy( buffer, token.start_, length );
|
||||
|
@ -1,5 +1,11 @@
|
||||
|
||||
if(NOT TARGET polarssl)
|
||||
message("including polarssl")
|
||||
add_subdirectory(${CMAKE_CURRENT_LIST_DIR}/polarssl/ ${CMAKE_CURRENT_BINARY_DIR}/lib/polarssl EXCLUDE_FROM_ALL )
|
||||
if (SELF_TEST)
|
||||
set(ENABLE_TESTING OFF CACHE BOOL "Disable tests")
|
||||
set(ENABLE_PROGRAMS OFF CACHE BOOL "Disable programs")
|
||||
add_subdirectory(${CMAKE_CURRENT_LIST_DIR}/polarssl/ ${CMAKE_CURRENT_BINARY_DIR}/lib/polarssl)
|
||||
else()
|
||||
add_subdirectory(${CMAKE_CURRENT_LIST_DIR}/polarssl/ ${CMAKE_CURRENT_BINARY_DIR}/lib/polarssl EXCLUDE_FROM_ALL)
|
||||
endif()
|
||||
endif()
|
||||
|
@ -372,11 +372,11 @@ void cLuaState::Push(const AStringVector & a_Vector)
|
||||
|
||||
|
||||
|
||||
void cLuaState::PushUserType(void * a_Object, const char * a_Type)
|
||||
void cLuaState::Push(const cCraftingGrid * a_Grid)
|
||||
{
|
||||
ASSERT(IsValid());
|
||||
|
||||
tolua_pushusertype(m_LuaState, a_Object, a_Type);
|
||||
tolua_pushusertype(m_LuaState, (void *)a_Grid, "cCraftingGrid");
|
||||
m_NumCurrentFunctionArgs += 1;
|
||||
}
|
||||
|
||||
@ -384,23 +384,11 @@ void cLuaState::PushUserType(void * a_Object, const char * a_Type)
|
||||
|
||||
|
||||
|
||||
void cLuaState::Push(int a_Value)
|
||||
void cLuaState::Push(const cCraftingRecipe * a_Recipe)
|
||||
{
|
||||
ASSERT(IsValid());
|
||||
|
||||
tolua_pushnumber(m_LuaState, a_Value);
|
||||
m_NumCurrentFunctionArgs += 1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cLuaState::Push(double a_Value)
|
||||
{
|
||||
ASSERT(IsValid());
|
||||
|
||||
tolua_pushnumber(m_LuaState, a_Value);
|
||||
tolua_pushusertype(m_LuaState, (void *)a_Recipe, "cCraftingRecipe");
|
||||
m_NumCurrentFunctionArgs += 1;
|
||||
}
|
||||
|
||||
@ -420,35 +408,11 @@ void cLuaState::Push(const char * a_Value)
|
||||
|
||||
|
||||
|
||||
void cLuaState::Push(bool a_Value)
|
||||
void cLuaState::Push(const cItems & a_Items)
|
||||
{
|
||||
ASSERT(IsValid());
|
||||
|
||||
tolua_pushboolean(m_LuaState, a_Value ? 1 : 0);
|
||||
m_NumCurrentFunctionArgs += 1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cLuaState::Push(cWorld * a_World)
|
||||
{
|
||||
ASSERT(IsValid());
|
||||
|
||||
tolua_pushusertype(m_LuaState, a_World, "cWorld");
|
||||
m_NumCurrentFunctionArgs += 1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cLuaState::Push(cPlayer * a_Player)
|
||||
{
|
||||
ASSERT(IsValid());
|
||||
|
||||
tolua_pushusertype(m_LuaState, a_Player, "cPlayer");
|
||||
tolua_pushusertype(m_LuaState, (void *)&a_Items, "cItems");
|
||||
m_NumCurrentFunctionArgs += 1;
|
||||
}
|
||||
|
||||
@ -468,6 +432,90 @@ void cLuaState::Push(const cPlayer * a_Player)
|
||||
|
||||
|
||||
|
||||
void cLuaState::Push(const HTTPRequest * a_Request)
|
||||
{
|
||||
ASSERT(IsValid());
|
||||
|
||||
tolua_pushusertype(m_LuaState, (void *)a_Request, "HTTPRequest");
|
||||
m_NumCurrentFunctionArgs += 1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cLuaState::Push(const HTTPTemplateRequest * a_Request)
|
||||
{
|
||||
ASSERT(IsValid());
|
||||
|
||||
tolua_pushusertype(m_LuaState, (void *)a_Request, "HTTPTemplateRequest");
|
||||
m_NumCurrentFunctionArgs += 1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cLuaState::Push(const Vector3d & a_Vector)
|
||||
{
|
||||
ASSERT(IsValid());
|
||||
|
||||
tolua_pushusertype(m_LuaState, (void *)&a_Vector, "Vector3d");
|
||||
m_NumCurrentFunctionArgs += 1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cLuaState::Push(bool a_Value)
|
||||
{
|
||||
ASSERT(IsValid());
|
||||
|
||||
tolua_pushboolean(m_LuaState, a_Value ? 1 : 0);
|
||||
m_NumCurrentFunctionArgs += 1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cLuaState::Push(cBlockEntity * a_BlockEntity)
|
||||
{
|
||||
ASSERT(IsValid());
|
||||
|
||||
tolua_pushusertype(m_LuaState, a_BlockEntity, "cBlockEntity");
|
||||
m_NumCurrentFunctionArgs += 1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cLuaState::Push(cChunkDesc * a_ChunkDesc)
|
||||
{
|
||||
ASSERT(IsValid());
|
||||
|
||||
tolua_pushusertype(m_LuaState, a_ChunkDesc, "cChunkDesc");
|
||||
m_NumCurrentFunctionArgs += 1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cLuaState::Push(cClientHandle * a_Client)
|
||||
{
|
||||
ASSERT(IsValid());
|
||||
|
||||
tolua_pushusertype(m_LuaState, a_Client, "cClientHandle");
|
||||
m_NumCurrentFunctionArgs += 1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cLuaState::Push(cEntity * a_Entity)
|
||||
{
|
||||
ASSERT(IsValid());
|
||||
@ -480,23 +528,11 @@ void cLuaState::Push(cEntity * a_Entity)
|
||||
|
||||
|
||||
|
||||
void cLuaState::Push(cProjectileEntity * a_ProjectileEntity)
|
||||
void cLuaState::Push(cHopperEntity * a_Hopper)
|
||||
{
|
||||
ASSERT(IsValid());
|
||||
|
||||
tolua_pushusertype(m_LuaState, a_ProjectileEntity, "cProjectileEntity");
|
||||
m_NumCurrentFunctionArgs += 1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cLuaState::Push(cMonster * a_Monster)
|
||||
{
|
||||
ASSERT(IsValid());
|
||||
|
||||
tolua_pushusertype(m_LuaState, a_Monster, "cMonster");
|
||||
tolua_pushusertype(m_LuaState, a_Hopper, "cHopperEntity");
|
||||
m_NumCurrentFunctionArgs += 1;
|
||||
}
|
||||
|
||||
@ -528,23 +564,11 @@ void cLuaState::Push(cItems * a_Items)
|
||||
|
||||
|
||||
|
||||
void cLuaState::Push(const cItems & a_Items)
|
||||
void cLuaState::Push(cMonster * a_Monster)
|
||||
{
|
||||
ASSERT(IsValid());
|
||||
|
||||
tolua_pushusertype(m_LuaState, (void *)&a_Items, "cItems");
|
||||
m_NumCurrentFunctionArgs += 1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cLuaState::Push(cClientHandle * a_Client)
|
||||
{
|
||||
ASSERT(IsValid());
|
||||
|
||||
tolua_pushusertype(m_LuaState, a_Client, "cClientHandle");
|
||||
tolua_pushusertype(m_LuaState, a_Monster, "cMonster");
|
||||
m_NumCurrentFunctionArgs += 1;
|
||||
}
|
||||
|
||||
@ -564,59 +588,11 @@ void cLuaState::Push(cPickup * a_Pickup)
|
||||
|
||||
|
||||
|
||||
void cLuaState::Push(cChunkDesc * a_ChunkDesc)
|
||||
void cLuaState::Push(cPlayer * a_Player)
|
||||
{
|
||||
ASSERT(IsValid());
|
||||
|
||||
tolua_pushusertype(m_LuaState, a_ChunkDesc, "cChunkDesc");
|
||||
m_NumCurrentFunctionArgs += 1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cLuaState::Push(const cCraftingGrid * a_Grid)
|
||||
{
|
||||
ASSERT(IsValid());
|
||||
|
||||
tolua_pushusertype(m_LuaState, (void *)a_Grid, "cCraftingGrid");
|
||||
m_NumCurrentFunctionArgs += 1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cLuaState::Push(const cCraftingRecipe * a_Recipe)
|
||||
{
|
||||
ASSERT(IsValid());
|
||||
|
||||
tolua_pushusertype(m_LuaState, (void *)a_Recipe, "cCraftingRecipe");
|
||||
m_NumCurrentFunctionArgs += 1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cLuaState::Push(TakeDamageInfo * a_TDI)
|
||||
{
|
||||
ASSERT(IsValid());
|
||||
|
||||
tolua_pushusertype(m_LuaState, a_TDI, "TakeDamageInfo");
|
||||
m_NumCurrentFunctionArgs += 1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cLuaState::Push(cWindow * a_Window)
|
||||
{
|
||||
ASSERT(IsValid());
|
||||
|
||||
tolua_pushusertype(m_LuaState, a_Window, "cWindow");
|
||||
tolua_pushusertype(m_LuaState, a_Player, "cPlayer");
|
||||
m_NumCurrentFunctionArgs += 1;
|
||||
}
|
||||
|
||||
@ -636,35 +612,11 @@ void cLuaState::Push(cPluginLua * a_Plugin)
|
||||
|
||||
|
||||
|
||||
void cLuaState::Push(const HTTPRequest * a_Request)
|
||||
void cLuaState::Push(cProjectileEntity * a_ProjectileEntity)
|
||||
{
|
||||
ASSERT(IsValid());
|
||||
|
||||
tolua_pushusertype(m_LuaState, (void *)a_Request, "HTTPRequest");
|
||||
m_NumCurrentFunctionArgs += 1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cLuaState::Push(cWebAdmin * a_WebAdmin)
|
||||
{
|
||||
ASSERT(IsValid());
|
||||
|
||||
tolua_pushusertype(m_LuaState, a_WebAdmin, "cWebAdmin");
|
||||
m_NumCurrentFunctionArgs += 1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cLuaState::Push(const HTTPTemplateRequest * a_Request)
|
||||
{
|
||||
ASSERT(IsValid());
|
||||
|
||||
tolua_pushusertype(m_LuaState, (void *)a_Request, "HTTPTemplateRequest");
|
||||
tolua_pushusertype(m_LuaState, a_ProjectileEntity, "cProjectileEntity");
|
||||
m_NumCurrentFunctionArgs += 1;
|
||||
}
|
||||
|
||||
@ -684,6 +636,78 @@ void cLuaState::Push(cTNTEntity * a_TNTEntity)
|
||||
|
||||
|
||||
|
||||
void cLuaState::Push(cWebAdmin * a_WebAdmin)
|
||||
{
|
||||
ASSERT(IsValid());
|
||||
|
||||
tolua_pushusertype(m_LuaState, a_WebAdmin, "cWebAdmin");
|
||||
m_NumCurrentFunctionArgs += 1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cLuaState::Push(cWindow * a_Window)
|
||||
{
|
||||
ASSERT(IsValid());
|
||||
|
||||
tolua_pushusertype(m_LuaState, a_Window, "cWindow");
|
||||
m_NumCurrentFunctionArgs += 1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cLuaState::Push(cWorld * a_World)
|
||||
{
|
||||
ASSERT(IsValid());
|
||||
|
||||
tolua_pushusertype(m_LuaState, a_World, "cWorld");
|
||||
m_NumCurrentFunctionArgs += 1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cLuaState::Push(double a_Value)
|
||||
{
|
||||
ASSERT(IsValid());
|
||||
|
||||
tolua_pushnumber(m_LuaState, a_Value);
|
||||
m_NumCurrentFunctionArgs += 1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cLuaState::Push(int a_Value)
|
||||
{
|
||||
ASSERT(IsValid());
|
||||
|
||||
tolua_pushnumber(m_LuaState, a_Value);
|
||||
m_NumCurrentFunctionArgs += 1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cLuaState::Push(TakeDamageInfo * a_TDI)
|
||||
{
|
||||
ASSERT(IsValid());
|
||||
|
||||
tolua_pushusertype(m_LuaState, a_TDI, "TakeDamageInfo");
|
||||
m_NumCurrentFunctionArgs += 1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cLuaState::Push(Vector3i * a_Vector)
|
||||
{
|
||||
ASSERT(IsValid());
|
||||
@ -696,6 +720,18 @@ void cLuaState::Push(Vector3i * a_Vector)
|
||||
|
||||
|
||||
|
||||
void cLuaState::Push(Vector3d * a_Vector)
|
||||
{
|
||||
ASSERT(IsValid());
|
||||
|
||||
tolua_pushusertype(m_LuaState, a_Vector, "Vector3d");
|
||||
m_NumCurrentFunctionArgs += 1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cLuaState::Push(void * a_Ptr)
|
||||
{
|
||||
UNUSED(a_Ptr);
|
||||
@ -715,23 +751,12 @@ void cLuaState::Push(void * a_Ptr)
|
||||
|
||||
|
||||
|
||||
void cLuaState::Push(cHopperEntity * a_Hopper)
|
||||
|
||||
void cLuaState::PushUserType(void * a_Object, const char * a_Type)
|
||||
{
|
||||
ASSERT(IsValid());
|
||||
|
||||
tolua_pushusertype(m_LuaState, a_Hopper, "cHopperEntity");
|
||||
m_NumCurrentFunctionArgs += 1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cLuaState::Push(cBlockEntity * a_BlockEntity)
|
||||
{
|
||||
ASSERT(IsValid());
|
||||
|
||||
tolua_pushusertype(m_LuaState, a_BlockEntity, "cBlockEntity");
|
||||
tolua_pushusertype(m_LuaState, a_Object, a_Type);
|
||||
m_NumCurrentFunctionArgs += 1;
|
||||
}
|
||||
|
||||
|
@ -173,38 +173,42 @@ public:
|
||||
/** Returns true if a_FunctionName is a valid Lua function that can be called */
|
||||
bool HasFunction(const char * a_FunctionName);
|
||||
|
||||
// Push a value onto the stack
|
||||
// Push a const value onto the stack (keep alpha-sorted):
|
||||
void Push(const AString & a_String);
|
||||
void Push(const AStringVector & a_Vector);
|
||||
void Push(int a_Value);
|
||||
void Push(double a_Value);
|
||||
void Push(const char * a_Value);
|
||||
void Push(bool a_Value);
|
||||
void Push(cWorld * a_World);
|
||||
void Push(cPlayer * a_Player);
|
||||
void Push(const cPlayer * a_Player);
|
||||
void Push(cEntity * a_Entity);
|
||||
void Push(cProjectileEntity * a_ProjectileEntity);
|
||||
void Push(cMonster * a_Monster);
|
||||
void Push(cItem * a_Item);
|
||||
void Push(cItems * a_Items);
|
||||
void Push(const cItems & a_Items);
|
||||
void Push(cClientHandle * a_ClientHandle);
|
||||
void Push(cPickup * a_Pickup);
|
||||
void Push(cChunkDesc * a_ChunkDesc);
|
||||
void Push(const cCraftingGrid * a_Grid);
|
||||
void Push(const cCraftingRecipe * a_Recipe);
|
||||
void Push(TakeDamageInfo * a_TDI);
|
||||
void Push(cWindow * a_Window);
|
||||
void Push(cPluginLua * a_Plugin);
|
||||
void Push(const char * a_Value);
|
||||
void Push(const cItems & a_Items);
|
||||
void Push(const cPlayer * a_Player);
|
||||
void Push(const HTTPRequest * a_Request);
|
||||
void Push(cWebAdmin * a_WebAdmin);
|
||||
void Push(const HTTPTemplateRequest * a_Request);
|
||||
void Push(const Vector3d & a_Vector);
|
||||
|
||||
// Push a value onto the stack (keep alpha-sorted):
|
||||
void Push(bool a_Value);
|
||||
void Push(cBlockEntity * a_BlockEntity);
|
||||
void Push(cChunkDesc * a_ChunkDesc);
|
||||
void Push(cClientHandle * a_ClientHandle);
|
||||
void Push(cEntity * a_Entity);
|
||||
void Push(cHopperEntity * a_Hopper);
|
||||
void Push(cItem * a_Item);
|
||||
void Push(cItems * a_Items);
|
||||
void Push(cMonster * a_Monster);
|
||||
void Push(cPickup * a_Pickup);
|
||||
void Push(cPlayer * a_Player);
|
||||
void Push(cPluginLua * a_Plugin);
|
||||
void Push(cProjectileEntity * a_ProjectileEntity);
|
||||
void Push(cTNTEntity * a_TNTEntity);
|
||||
void Push(cWebAdmin * a_WebAdmin);
|
||||
void Push(cWindow * a_Window);
|
||||
void Push(cWorld * a_World);
|
||||
void Push(double a_Value);
|
||||
void Push(int a_Value);
|
||||
void Push(TakeDamageInfo * a_TDI);
|
||||
void Push(Vector3d * a_Vector);
|
||||
void Push(Vector3i * a_Vector);
|
||||
void Push(void * a_Ptr);
|
||||
void Push(cHopperEntity * a_Hopper);
|
||||
void Push(cBlockEntity * a_BlockEntity);
|
||||
|
||||
/** Retrieve value at a_StackPos, if it is a valid bool. If not, a_Value is unchanged */
|
||||
void GetStackValue(int a_StackPos, bool & a_Value);
|
||||
|
@ -2864,8 +2864,8 @@ static int tolua_cCompositeChat_SetMessageType(lua_State * tolua_S)
|
||||
}
|
||||
|
||||
// Set the type:
|
||||
int MessageType;
|
||||
L.GetStackValue(1, MessageType);
|
||||
int MessageType = mtCustom;
|
||||
L.GetStackValue(2, MessageType);
|
||||
self->SetMessageType((eMessageType)MessageType);
|
||||
|
||||
// Cut away everything from the stack except for the cCompositeChat instance; return that:
|
||||
|
@ -90,7 +90,7 @@ public:
|
||||
virtual bool OnPluginsLoaded (void) = 0;
|
||||
virtual bool OnPostCrafting (const cPlayer * a_Player, const cCraftingGrid * a_Grid, cCraftingRecipe * a_Recipe) = 0;
|
||||
virtual bool OnPreCrafting (const cPlayer * a_Player, const cCraftingGrid * a_Grid, cCraftingRecipe * a_Recipe) = 0;
|
||||
virtual bool OnProjectileHitBlock (cProjectileEntity & a_Projectile) = 0;
|
||||
virtual bool OnProjectileHitBlock (cProjectileEntity & a_Projectile, int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_Face, const Vector3d & a_BlockHitPos) = 0;
|
||||
virtual bool OnProjectileHitEntity (cProjectileEntity & a_Projectile, cEntity & a_HitEntity) = 0;
|
||||
virtual bool OnSpawnedEntity (cWorld & a_World, cEntity & a_Entity) = 0;
|
||||
virtual bool OnSpawnedMonster (cWorld & a_World, cMonster & a_Monster) = 0;
|
||||
|
@ -1113,14 +1113,14 @@ bool cPluginLua::OnPreCrafting(const cPlayer * a_Player, const cCraftingGrid * a
|
||||
|
||||
|
||||
|
||||
bool cPluginLua::OnProjectileHitBlock(cProjectileEntity & a_Projectile)
|
||||
bool cPluginLua::OnProjectileHitBlock(cProjectileEntity & a_Projectile, int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_Face, const Vector3d & a_BlockHitPos)
|
||||
{
|
||||
cCSLock Lock(m_CriticalSection);
|
||||
bool res = false;
|
||||
cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_PROJECTILE_HIT_BLOCK];
|
||||
for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr)
|
||||
{
|
||||
m_LuaState.Call((int)(**itr), &a_Projectile, cLuaState::Return, res);
|
||||
m_LuaState.Call((int)(**itr), &a_Projectile, a_BlockX, a_BlockY, a_BlockZ, a_Face, a_BlockHitPos, cLuaState::Return, res);
|
||||
if (res)
|
||||
{
|
||||
return true;
|
||||
|
@ -113,7 +113,7 @@ public:
|
||||
virtual bool OnPluginsLoaded (void) override;
|
||||
virtual bool OnPostCrafting (const cPlayer * a_Player, const cCraftingGrid * a_Grid, cCraftingRecipe * a_Recipe) override;
|
||||
virtual bool OnPreCrafting (const cPlayer * a_Player, const cCraftingGrid * a_Grid, cCraftingRecipe * a_Recipe) override;
|
||||
virtual bool OnProjectileHitBlock (cProjectileEntity & a_Projectile) override;
|
||||
virtual bool OnProjectileHitBlock (cProjectileEntity & a_Projectile, int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_Face, const Vector3d & a_BlockHitPos) override;
|
||||
virtual bool OnProjectileHitEntity (cProjectileEntity & a_Projectile, cEntity & a_HitEntity) override;
|
||||
virtual bool OnSpawnedEntity (cWorld & a_World, cEntity & a_Entity) override;
|
||||
virtual bool OnSpawnedMonster (cWorld & a_World, cMonster & a_Monster) override;
|
||||
|
@ -1155,7 +1155,7 @@ bool cPluginManager::CallHookPreCrafting(const cPlayer * a_Player, const cCrafti
|
||||
|
||||
|
||||
|
||||
bool cPluginManager::CallHookProjectileHitBlock(cProjectileEntity & a_Projectile)
|
||||
bool cPluginManager::CallHookProjectileHitBlock(cProjectileEntity & a_Projectile, int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_Face, const Vector3d & a_BlockHitPos)
|
||||
{
|
||||
HookMap::iterator Plugins = m_Hooks.find(HOOK_PROJECTILE_HIT_BLOCK);
|
||||
if (Plugins == m_Hooks.end())
|
||||
@ -1164,7 +1164,7 @@ bool cPluginManager::CallHookProjectileHitBlock(cProjectileEntity & a_Projectile
|
||||
}
|
||||
for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr)
|
||||
{
|
||||
if ((*itr)->OnProjectileHitBlock(a_Projectile))
|
||||
if ((*itr)->OnProjectileHitBlock(a_Projectile, a_BlockX, a_BlockY, a_BlockZ, a_Face, a_BlockHitPos))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
@ -206,7 +206,7 @@ public: // tolua_export
|
||||
bool CallHookPluginsLoaded (void);
|
||||
bool CallHookPostCrafting (const cPlayer * a_Player, const cCraftingGrid * a_Grid, cCraftingRecipe * a_Recipe);
|
||||
bool CallHookPreCrafting (const cPlayer * a_Player, const cCraftingGrid * a_Grid, cCraftingRecipe * a_Recipe);
|
||||
bool CallHookProjectileHitBlock (cProjectileEntity & a_Projectile);
|
||||
bool CallHookProjectileHitBlock (cProjectileEntity & a_Projectile, int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_Face, const Vector3d & a_BlockHitPos);
|
||||
bool CallHookProjectileHitEntity (cProjectileEntity & a_Projectile, cEntity & a_HitEntity);
|
||||
bool CallHookSpawnedEntity (cWorld & a_World, cEntity & a_Entity);
|
||||
bool CallHookSpawnedMonster (cWorld & a_World, cMonster & a_Monster);
|
||||
|
@ -10,7 +10,7 @@
|
||||
#pragma once
|
||||
|
||||
|
||||
|
||||
#include "StringUtils.h"
|
||||
|
||||
|
||||
// tolua_begin
|
||||
|
@ -9,7 +9,7 @@
|
||||
#include "OSSupport/GZipFile.h"
|
||||
#include "Blocks/BlockHandler.h"
|
||||
#include "Cuboid.h"
|
||||
|
||||
#include "ChunkData.h"
|
||||
|
||||
|
||||
|
||||
@ -1835,18 +1835,12 @@ bool cBlockArea::cChunkReader::Coords(int a_ChunkX, int a_ChunkZ)
|
||||
|
||||
|
||||
|
||||
void cBlockArea::cChunkReader::BlockTypes(const BLOCKTYPE * a_BlockTypes)
|
||||
void cBlockArea::cChunkReader::ChunkData(const cChunkData & a_BlockBuffer)
|
||||
{
|
||||
if (m_Area.m_BlockTypes == NULL)
|
||||
{
|
||||
// Don't want BlockTypes
|
||||
return;
|
||||
}
|
||||
|
||||
int SizeY = m_Area.m_Size.y;
|
||||
int MinY = m_Origin.y;
|
||||
|
||||
// SizeX, SizeZ are the dmensions of the block data to copy from the current chunk (size of the geometric union)
|
||||
|
||||
// SizeX, SizeZ are the dimensions of the block data to copy from the current chunk (size of the geometric union)
|
||||
// OffX, OffZ are the offsets of the current chunk data from the area origin
|
||||
// BaseX, BaseZ are the offsets of the area data within the current chunk from the chunk borders
|
||||
int SizeX = cChunkDef::Width;
|
||||
@ -1884,67 +1878,91 @@ void cBlockArea::cChunkReader::BlockTypes(const BLOCKTYPE * a_BlockTypes)
|
||||
{
|
||||
SizeZ -= (m_CurrentChunkZ + 1) * cChunkDef::Width - (m_Origin.z + m_Area.m_Size.z);
|
||||
}
|
||||
|
||||
for (int y = 0; y < SizeY; y++)
|
||||
|
||||
// Copy the blocktypes:
|
||||
if (m_Area.m_BlockTypes != NULL)
|
||||
{
|
||||
int ChunkY = MinY + y;
|
||||
int AreaY = y;
|
||||
for (int z = 0; z < SizeZ; z++)
|
||||
for (int y = 0; y < SizeY; y++)
|
||||
{
|
||||
int ChunkZ = BaseZ + z;
|
||||
int AreaZ = OffZ + z;
|
||||
for (int x = 0; x < SizeX; x++)
|
||||
int InChunkY = MinY + y;
|
||||
int AreaY = y;
|
||||
for (int z = 0; z < SizeZ; z++)
|
||||
{
|
||||
int ChunkX = BaseX + x;
|
||||
int AreaX = OffX + x;
|
||||
m_Area.m_BlockTypes[m_Area.MakeIndex(AreaX, AreaY, AreaZ)] = cChunkDef::GetBlock(a_BlockTypes, ChunkX, ChunkY, ChunkZ);
|
||||
} // for x
|
||||
} // for z
|
||||
} // for y
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cBlockArea::cChunkReader::BlockMeta(const NIBBLETYPE * a_BlockMetas)
|
||||
{
|
||||
if (m_Area.m_BlockMetas == NULL)
|
||||
{
|
||||
// Don't want metas
|
||||
return;
|
||||
int InChunkZ = BaseZ + z;
|
||||
int AreaZ = OffZ + z;
|
||||
for (int x = 0; x < SizeX; x++)
|
||||
{
|
||||
int InChunkX = BaseX + x;
|
||||
int AreaX = OffX + x;
|
||||
m_Area.m_BlockTypes[m_Area.MakeIndex(AreaX, AreaY, AreaZ)] = a_BlockBuffer.GetBlock(InChunkX, InChunkY, InChunkZ);
|
||||
} // for x
|
||||
} // for z
|
||||
} // for y
|
||||
}
|
||||
CopyNibbles(m_Area.m_BlockMetas, a_BlockMetas);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cBlockArea::cChunkReader::BlockLight(const NIBBLETYPE * a_BlockLight)
|
||||
{
|
||||
if (m_Area.m_BlockLight == NULL)
|
||||
// Copy the block metas:
|
||||
if (m_Area.m_BlockMetas != NULL)
|
||||
{
|
||||
// Don't want light
|
||||
return;
|
||||
for (int y = 0; y < SizeY; y++)
|
||||
{
|
||||
int InChunkY = MinY + y;
|
||||
int AreaY = y;
|
||||
for (int z = 0; z < SizeZ; z++)
|
||||
{
|
||||
int InChunkZ = BaseZ + z;
|
||||
int AreaZ = OffZ + z;
|
||||
for (int x = 0; x < SizeX; x++)
|
||||
{
|
||||
int InChunkX = BaseX + x;
|
||||
int AreaX = OffX + x;
|
||||
m_Area.m_BlockMetas[m_Area.MakeIndex(AreaX, AreaY, AreaZ)] = a_BlockBuffer.GetMeta(InChunkX, InChunkY, InChunkZ);
|
||||
} // for x
|
||||
} // for z
|
||||
} // for y
|
||||
}
|
||||
CopyNibbles(m_Area.m_BlockLight, a_BlockLight);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cBlockArea::cChunkReader::BlockSkyLight(const NIBBLETYPE * a_BlockSkyLight)
|
||||
{
|
||||
if (m_Area.m_BlockSkyLight == NULL)
|
||||
// Copy the blocklight:
|
||||
if (m_Area.m_BlockLight != NULL)
|
||||
{
|
||||
// Don't want skylight
|
||||
return;
|
||||
for (int y = 0; y < SizeY; y++)
|
||||
{
|
||||
int InChunkY = MinY + y;
|
||||
int AreaY = y;
|
||||
for (int z = 0; z < SizeZ; z++)
|
||||
{
|
||||
int InChunkZ = BaseZ + z;
|
||||
int AreaZ = OffZ + z;
|
||||
for (int x = 0; x < SizeX; x++)
|
||||
{
|
||||
int InChunkX = BaseX + x;
|
||||
int AreaX = OffX + x;
|
||||
m_Area.m_BlockLight[m_Area.MakeIndex(AreaX, AreaY, AreaZ)] = a_BlockBuffer.GetBlockLight(InChunkX, InChunkY, InChunkZ);
|
||||
} // for x
|
||||
} // for z
|
||||
} // for y
|
||||
}
|
||||
CopyNibbles(m_Area.m_BlockSkyLight, a_BlockSkyLight);
|
||||
}
|
||||
|
||||
// Copy the skylight:
|
||||
if (m_Area.m_BlockSkyLight != NULL)
|
||||
{
|
||||
for (int y = 0; y < SizeY; y++)
|
||||
{
|
||||
int InChunkY = MinY + y;
|
||||
int AreaY = y;
|
||||
for (int z = 0; z < SizeZ; z++)
|
||||
{
|
||||
int InChunkZ = BaseZ + z;
|
||||
int AreaZ = OffZ + z;
|
||||
for (int x = 0; x < SizeX; x++)
|
||||
{
|
||||
int InChunkX = BaseX + x;
|
||||
int AreaX = OffX + x;
|
||||
m_Area.m_BlockSkyLight[m_Area.MakeIndex(AreaX, AreaY, AreaZ)] = a_BlockBuffer.GetSkyLight(InChunkX, InChunkY, InChunkZ);
|
||||
} // for x
|
||||
} // for z
|
||||
} // for y
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
@ -14,6 +14,7 @@
|
||||
|
||||
#include "ForEachChunkProvider.h"
|
||||
#include "Vector3.h"
|
||||
#include "ChunkDataCallback.h"
|
||||
|
||||
|
||||
|
||||
@ -316,11 +317,8 @@ protected:
|
||||
void CopyNibbles(NIBBLETYPE * a_AreaDst, const NIBBLETYPE * a_ChunkSrc);
|
||||
|
||||
// cChunkDataCallback overrides:
|
||||
virtual bool Coords (int a_ChunkX, int a_ChunkZ) override;
|
||||
virtual void BlockTypes (const BLOCKTYPE * a_BlockTypes) override;
|
||||
virtual void BlockMeta (const NIBBLETYPE * a_BlockMetas) override;
|
||||
virtual void BlockLight (const NIBBLETYPE * a_BlockLight) override;
|
||||
virtual void BlockSkyLight(const NIBBLETYPE * a_BlockSkyLight) override;
|
||||
virtual bool Coords(int a_ChunkX, int a_ChunkZ) override;
|
||||
virtual void ChunkData(const cChunkData & a_BlockTypes) override;
|
||||
} ;
|
||||
|
||||
typedef NIBBLETYPE * NIBBLEARRAY;
|
||||
|
@ -5,7 +5,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../Piston.h"
|
||||
#include "../Blocks/BlockPiston.h"
|
||||
#include "MetaRotator.h"
|
||||
|
||||
|
||||
@ -32,7 +32,7 @@ public:
|
||||
a_BlockType = m_BlockType;
|
||||
|
||||
// FIXME: Do not use cPiston class for dispenser placement!
|
||||
a_BlockMeta = cPiston::RotationPitchToMetaData(a_Player->GetYaw(), a_Player->GetPitch());
|
||||
a_BlockMeta = cBlockPistonHandler::RotationPitchToMetaData(a_Player->GetYaw(), a_Player->GetPitch());
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -3,7 +3,7 @@
|
||||
|
||||
#include "BlockEntity.h"
|
||||
#include "../World.h"
|
||||
#include "../Piston.h"
|
||||
#include "../Blocks/BlockPiston.h"
|
||||
#include "MetaRotator.h"
|
||||
|
||||
|
||||
@ -35,7 +35,7 @@ public:
|
||||
a_BlockType = m_BlockType;
|
||||
|
||||
// FIXME: Do not use cPiston class for furnace placement!
|
||||
a_BlockMeta = cPiston::RotationPitchToMetaData(a_Player->GetYaw(), 0);
|
||||
a_BlockMeta = cBlockPistonHandler::RotationPitchToMetaData(a_Player->GetYaw(), 0);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -68,7 +68,7 @@ public:
|
||||
public:
|
||||
cPlayerCallback(const Vector3f & a_Pos) : m_Pos(a_Pos) {}
|
||||
|
||||
} PlayerCallback(Vector3f(a_BlockX, a_BlockY, a_BlockZ));
|
||||
} PlayerCallback(Vector3f((float)a_BlockX, (float)a_BlockY, (float)a_BlockZ));
|
||||
|
||||
a_World->DoWithMobHeadAt(a_BlockX, a_BlockY, a_BlockZ, CallbackA);
|
||||
|
||||
|
@ -4,14 +4,14 @@
|
||||
#include "../Item.h"
|
||||
#include "../World.h"
|
||||
#include "../Entities/Player.h"
|
||||
#include "../Piston.h"
|
||||
#include "BlockInServerPluginInterface.h"
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#define AddPistonDir(x, y, z, dir, amount) \
|
||||
switch (dir) \
|
||||
switch (dir & 0x07) \
|
||||
{ \
|
||||
case 0: (y) -= (amount); break; \
|
||||
case 1: (y) += (amount); break; \
|
||||
@ -19,8 +19,16 @@
|
||||
case 3: (z) += (amount); break; \
|
||||
case 4: (x) -= (amount); break; \
|
||||
case 5: (x) += (amount); break; \
|
||||
default: \
|
||||
{ \
|
||||
LOGWARNING("%s: invalid direction %d, ignoring", __FUNCTION__, dir & 0x07); \
|
||||
break; \
|
||||
} \
|
||||
}
|
||||
|
||||
#define PISTON_TICK_DELAY 1
|
||||
#define PISTON_MAX_PUSH_DISTANCE 12
|
||||
|
||||
|
||||
|
||||
|
||||
@ -40,7 +48,7 @@ void cBlockPistonHandler::OnDestroyed(cChunkInterface & a_ChunkInterface, cWorld
|
||||
int newX = a_BlockX;
|
||||
int newY = a_BlockY;
|
||||
int newZ = a_BlockZ;
|
||||
AddPistonDir(newX, newY, newZ, OldMeta & ~(8), 1);
|
||||
AddPistonDir(newX, newY, newZ, OldMeta, 1);
|
||||
|
||||
if (a_ChunkInterface.GetBlock(newX, newY, newZ) == E_BLOCK_PISTON_EXTENSION)
|
||||
{
|
||||
@ -60,7 +68,7 @@ bool cBlockPistonHandler::GetPlacementBlockTypeMeta(
|
||||
)
|
||||
{
|
||||
a_BlockType = m_BlockType;
|
||||
a_BlockMeta = cPiston::RotationPitchToMetaData(a_Player->GetYaw(), a_Player->GetPitch());
|
||||
a_BlockMeta = RotationPitchToMetaData(a_Player->GetYaw(), a_Player->GetPitch());
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -68,6 +76,165 @@ bool cBlockPistonHandler::GetPlacementBlockTypeMeta(
|
||||
|
||||
|
||||
|
||||
int cBlockPistonHandler::FirstPassthroughBlock(int a_PistonX, int a_PistonY, int a_PistonZ, NIBBLETYPE pistonmeta, cWorld * a_World)
|
||||
{
|
||||
// Examine each of the 12 blocks ahead of the piston:
|
||||
for (int ret = 0; ret < PISTON_MAX_PUSH_DISTANCE; ret++)
|
||||
{
|
||||
BLOCKTYPE currBlock;
|
||||
NIBBLETYPE currMeta;
|
||||
AddPistonDir(a_PistonX, a_PistonY, a_PistonZ, pistonmeta, 1);
|
||||
a_World->GetBlockTypeMeta(a_PistonX, a_PistonY, a_PistonZ, currBlock, currMeta);
|
||||
if (CanBreakPush(currBlock))
|
||||
{
|
||||
// This block breaks when pushed, extend up to here
|
||||
return ret;
|
||||
}
|
||||
if (!CanPush(currBlock, currMeta))
|
||||
{
|
||||
// This block cannot be pushed at all, the piston can't extend
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
// There is no space for the blocks to move, piston can't extend
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cBlockPistonHandler::ExtendPiston(int a_BlockX, int a_BlockY, int a_BlockZ, cWorld * a_World)
|
||||
{
|
||||
BLOCKTYPE pistonBlock;
|
||||
NIBBLETYPE pistonMeta;
|
||||
a_World->GetBlockTypeMeta(a_BlockX, a_BlockY, a_BlockZ, pistonBlock, pistonMeta);
|
||||
|
||||
if (IsExtended(pistonMeta))
|
||||
{
|
||||
// Already extended, bail out
|
||||
return;
|
||||
}
|
||||
|
||||
int dist = FirstPassthroughBlock(a_BlockX, a_BlockY, a_BlockZ, pistonMeta, a_World);
|
||||
if (dist < 0)
|
||||
{
|
||||
// FirstPassthroughBlock says piston can't push anything, bail out
|
||||
return;
|
||||
}
|
||||
|
||||
a_World->BroadcastBlockAction(a_BlockX, a_BlockY, a_BlockZ, 0, pistonMeta, pistonBlock);
|
||||
a_World->BroadcastSoundEffect("tile.piston.out", a_BlockX * 8, a_BlockY * 8, a_BlockZ * 8, 0.5f, 0.7f);
|
||||
|
||||
// Drop the breakable block in the line, if appropriate:
|
||||
AddPistonDir(a_BlockX, a_BlockY, a_BlockZ, pistonMeta, dist + 1); // "a_Block" now at the breakable / empty block
|
||||
BLOCKTYPE currBlock;
|
||||
NIBBLETYPE currMeta;
|
||||
a_World->GetBlockTypeMeta(a_BlockX, a_BlockY, a_BlockZ, currBlock, currMeta);
|
||||
if (currBlock != E_BLOCK_AIR)
|
||||
{
|
||||
cBlockHandler * Handler = BlockHandler(currBlock);
|
||||
if (Handler->DoesDropOnUnsuitable())
|
||||
{
|
||||
cChunkInterface ChunkInterface(a_World->GetChunkMap());
|
||||
cBlockInServerPluginInterface PluginInterface(*a_World);
|
||||
Handler->DropBlock(ChunkInterface, *a_World, PluginInterface, NULL, a_BlockX, a_BlockY, a_BlockZ);
|
||||
}
|
||||
}
|
||||
|
||||
// Push blocks, from the furthest to the nearest:
|
||||
int oldx = a_BlockX, oldy = a_BlockY, oldz = a_BlockZ;
|
||||
NIBBLETYPE currBlockMeta;
|
||||
std::vector<Vector3i> ScheduledBlocks;
|
||||
ScheduledBlocks.reserve(PISTON_MAX_PUSH_DISTANCE);
|
||||
|
||||
for (int i = dist + 1; i > 1; i--)
|
||||
{
|
||||
AddPistonDir(a_BlockX, a_BlockY, a_BlockZ, pistonMeta, -1);
|
||||
a_World->GetBlockTypeMeta(a_BlockX, a_BlockY, a_BlockZ, currBlock, currBlockMeta);
|
||||
a_World->SetBlock(oldx, oldy, oldz, currBlock, currBlockMeta, false);
|
||||
ScheduledBlocks.push_back(Vector3i(oldx, oldy, oldz));
|
||||
oldx = a_BlockX;
|
||||
oldy = a_BlockY;
|
||||
oldz = a_BlockZ;
|
||||
}
|
||||
|
||||
int extx = a_BlockX;
|
||||
int exty = a_BlockY;
|
||||
int extz = a_BlockZ;
|
||||
ScheduledBlocks.push_back(Vector3i(extx, exty, extz));
|
||||
AddPistonDir(a_BlockX, a_BlockY, a_BlockZ, pistonMeta, -1);
|
||||
// "a_Block" now at piston body, "ext" at future extension
|
||||
|
||||
a_World->SetBlock(a_BlockX, a_BlockY, a_BlockZ, pistonBlock, pistonMeta | 0x8);
|
||||
a_World->SetBlock(extx, exty, extz, E_BLOCK_PISTON_EXTENSION, pistonMeta | (IsSticky(pistonBlock) ? 8 : 0), false);
|
||||
a_World->ScheduleTask(PISTON_TICK_DELAY, new cWorld::cTaskSendBlockToAllPlayers(ScheduledBlocks));
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cBlockPistonHandler::RetractPiston(int a_BlockX, int a_BlockY, int a_BlockZ, cWorld * a_World)
|
||||
{
|
||||
BLOCKTYPE pistonBlock;
|
||||
NIBBLETYPE pistonMeta;
|
||||
a_World->GetBlockTypeMeta(a_BlockX, a_BlockY, a_BlockZ, pistonBlock, pistonMeta);
|
||||
|
||||
if (!IsExtended(pistonMeta))
|
||||
{
|
||||
// Already retracted, bail out
|
||||
return;
|
||||
}
|
||||
|
||||
// Check the extension:
|
||||
AddPistonDir(a_BlockX, a_BlockY, a_BlockZ, pistonMeta, 1);
|
||||
if (a_World->GetBlock(a_BlockX, a_BlockY, a_BlockZ) != E_BLOCK_PISTON_EXTENSION)
|
||||
{
|
||||
LOGD("%s: Piston without an extension - still extending, or just in an invalid state?", __FUNCTION__);
|
||||
return;
|
||||
}
|
||||
|
||||
AddPistonDir(a_BlockX, a_BlockY, a_BlockZ, pistonMeta, -1);
|
||||
a_World->SetBlock(a_BlockX, a_BlockY, a_BlockZ, pistonBlock, pistonMeta & ~(8));
|
||||
a_World->BroadcastBlockAction(a_BlockX, a_BlockY, a_BlockZ, 1, pistonMeta & ~(8), pistonBlock);
|
||||
a_World->BroadcastSoundEffect("tile.piston.in", a_BlockX * 8, a_BlockY * 8, a_BlockZ * 8, 0.5f, 0.7f);
|
||||
AddPistonDir(a_BlockX, a_BlockY, a_BlockZ, pistonMeta, 1);
|
||||
|
||||
// Retract the extension, pull block if appropriate
|
||||
if (IsSticky(pistonBlock))
|
||||
{
|
||||
int tempx = a_BlockX, tempy = a_BlockY, tempz = a_BlockZ;
|
||||
AddPistonDir(tempx, tempy, tempz, pistonMeta, 1);
|
||||
BLOCKTYPE tempBlock;
|
||||
NIBBLETYPE tempMeta;
|
||||
a_World->GetBlockTypeMeta(tempx, tempy, tempz, tempBlock, tempMeta);
|
||||
if (CanPull(tempBlock, tempMeta))
|
||||
{
|
||||
// Pull the block
|
||||
a_World->SetBlock(a_BlockX, a_BlockY, a_BlockZ, tempBlock, tempMeta, false);
|
||||
a_World->SetBlock(tempx, tempy, tempz, E_BLOCK_AIR, 0, false);
|
||||
|
||||
std::vector<Vector3i> ScheduledBlocks;
|
||||
ScheduledBlocks.push_back(Vector3i(a_BlockX, a_BlockY, a_BlockZ));
|
||||
ScheduledBlocks.push_back(Vector3i(tempx, tempy, tempz));
|
||||
a_World->ScheduleTask(PISTON_TICK_DELAY + 1, new cWorld::cTaskSendBlockToAllPlayers(ScheduledBlocks));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Retract without pulling
|
||||
a_World->SetBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_AIR, 0, false);
|
||||
|
||||
std::vector<Vector3i> ScheduledBlocks;
|
||||
ScheduledBlocks.push_back(Vector3i(a_BlockX, a_BlockY, a_BlockZ));
|
||||
a_World->ScheduleTask(PISTON_TICK_DELAY + 1, new cWorld::cTaskSendBlockToAllPlayers(ScheduledBlocks));
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// cBlockPistonHeadHandler:
|
||||
|
||||
@ -87,7 +254,7 @@ void cBlockPistonHeadHandler::OnDestroyedByPlayer(cChunkInterface & a_ChunkInter
|
||||
int newX = a_BlockX;
|
||||
int newY = a_BlockY;
|
||||
int newZ = a_BlockZ;
|
||||
AddPistonDir(newX, newY, newZ, OldMeta & ~(8), -1);
|
||||
AddPistonDir(newX, newY, newZ, OldMeta, -1);
|
||||
|
||||
BLOCKTYPE Block = a_ChunkInterface.GetBlock(newX, newY, newZ);
|
||||
if ((Block == E_BLOCK_STICKY_PISTON) || (Block == E_BLOCK_PISTON))
|
||||
|
@ -21,6 +21,138 @@ public:
|
||||
int a_CursorX, int a_CursorY, int a_CursorZ,
|
||||
BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta
|
||||
) override;
|
||||
|
||||
static NIBBLETYPE RotationPitchToMetaData(double a_Rotation, double a_Pitch)
|
||||
{
|
||||
if (a_Pitch >= 50)
|
||||
{
|
||||
return 0x1;
|
||||
}
|
||||
else if (a_Pitch <= -50)
|
||||
{
|
||||
return 0x0;
|
||||
}
|
||||
else
|
||||
{
|
||||
a_Rotation += 90 + 45; // So its not aligned with axis
|
||||
|
||||
if (a_Rotation > 360)
|
||||
{
|
||||
a_Rotation -= 360;
|
||||
}
|
||||
if ((a_Rotation >= 0) && (a_Rotation < 90))
|
||||
{
|
||||
return 0x4;
|
||||
}
|
||||
else if ((a_Rotation >= 180) && (a_Rotation < 270))
|
||||
{
|
||||
return 0x5;
|
||||
}
|
||||
else if ((a_Rotation >= 90) && (a_Rotation < 180))
|
||||
{
|
||||
return 0x2;
|
||||
}
|
||||
else
|
||||
{
|
||||
return 0x3;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static eBlockFace MetaDataToDirection(NIBBLETYPE a_MetaData)
|
||||
{
|
||||
switch (a_MetaData)
|
||||
{
|
||||
case 0x0: return BLOCK_FACE_YM;
|
||||
case 0x1: return BLOCK_FACE_YP;
|
||||
case 0x2: return BLOCK_FACE_ZM;
|
||||
case 0x3: return BLOCK_FACE_ZP;
|
||||
case 0x4: return BLOCK_FACE_XM;
|
||||
case 0x5: return BLOCK_FACE_XP;
|
||||
default:
|
||||
{
|
||||
ASSERT(!"Invalid Metadata");
|
||||
return BLOCK_FACE_NONE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void ExtendPiston(int a_BlockX, int a_BlockY, int a_BlockZ, cWorld * a_World);
|
||||
static void RetractPiston(int a_BlockX, int a_BlockY, int a_BlockZ, cWorld * a_World);
|
||||
|
||||
private:
|
||||
|
||||
/// Returns true if the piston (specified by blocktype) is a sticky piston
|
||||
static inline bool IsSticky(BLOCKTYPE a_BlockType) { return (a_BlockType == E_BLOCK_STICKY_PISTON); }
|
||||
|
||||
/// Returns true if the piston (with the specified meta) is extended
|
||||
static inline bool IsExtended(NIBBLETYPE a_PistonMeta) { return ((a_PistonMeta & 0x8) != 0x0); }
|
||||
|
||||
/// Returns true if the specified block can be pushed by a piston (and left intact)
|
||||
static inline bool CanPush(BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta)
|
||||
{
|
||||
switch (a_BlockType)
|
||||
{
|
||||
case E_BLOCK_ANVIL:
|
||||
case E_BLOCK_BED:
|
||||
case E_BLOCK_BEDROCK:
|
||||
case E_BLOCK_BREWING_STAND:
|
||||
case E_BLOCK_CHEST:
|
||||
case E_BLOCK_COMMAND_BLOCK:
|
||||
case E_BLOCK_DISPENSER:
|
||||
case E_BLOCK_DROPPER:
|
||||
case E_BLOCK_ENCHANTMENT_TABLE:
|
||||
case E_BLOCK_END_PORTAL:
|
||||
case E_BLOCK_END_PORTAL_FRAME:
|
||||
case E_BLOCK_FURNACE:
|
||||
case E_BLOCK_LIT_FURNACE:
|
||||
case E_BLOCK_HOPPER:
|
||||
case E_BLOCK_JUKEBOX:
|
||||
case E_BLOCK_MOB_SPAWNER:
|
||||
case E_BLOCK_NETHER_PORTAL:
|
||||
case E_BLOCK_NOTE_BLOCK:
|
||||
case E_BLOCK_OBSIDIAN:
|
||||
case E_BLOCK_PISTON_EXTENSION:
|
||||
{
|
||||
return false;
|
||||
}
|
||||
case E_BLOCK_STICKY_PISTON:
|
||||
case E_BLOCK_PISTON:
|
||||
{
|
||||
// A piston can only be pushed if retracted:
|
||||
return !IsExtended(a_BlockMeta);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/// Returns true if the specified block can be pushed by a piston and broken / replaced
|
||||
static inline bool CanBreakPush(BLOCKTYPE a_BlockType) { return cBlockInfo::IsPistonBreakable(a_BlockType); }
|
||||
|
||||
/// Returns true if the specified block can be pulled by a sticky piston
|
||||
static inline bool CanPull(BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta)
|
||||
{
|
||||
switch (a_BlockType)
|
||||
{
|
||||
case E_BLOCK_LAVA:
|
||||
case E_BLOCK_STATIONARY_LAVA:
|
||||
case E_BLOCK_STATIONARY_WATER:
|
||||
case E_BLOCK_WATER:
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (CanBreakPush(a_BlockType))
|
||||
{
|
||||
return false; // CanBreakPush returns true, but we need false to prevent pulling
|
||||
}
|
||||
|
||||
return CanPush(a_BlockType, a_BlockMeta);
|
||||
}
|
||||
|
||||
/// Returns how many blocks the piston has to push (where the first free space is); < 0 when unpushable
|
||||
static int FirstPassthroughBlock(int a_PistonX, int a_PistonY, int a_PistonZ, NIBBLETYPE a_PistonMeta, cWorld * a_World);
|
||||
} ;
|
||||
|
||||
|
||||
@ -40,7 +172,7 @@ public:
|
||||
virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override
|
||||
{
|
||||
// No pickups
|
||||
// Also with 1.7, the item forms of these tecnical blocks have been removed, so giving someone this will crash their client...
|
||||
// Also with 1.7, the item forms of these technical blocks have been removed, so giving someone this will crash their client...
|
||||
}
|
||||
} ;
|
||||
|
||||
|
215
src/Chunk.cpp
@ -238,26 +238,12 @@ void cChunk::MarkLoadFailed(void)
|
||||
|
||||
void cChunk::GetAllData(cChunkDataCallback & a_Callback)
|
||||
{
|
||||
a_Callback.HeightMap (&m_HeightMap);
|
||||
a_Callback.BiomeData (&m_BiomeMap);
|
||||
a_Callback.HeightMap(&m_HeightMap);
|
||||
a_Callback.BiomeData(&m_BiomeMap);
|
||||
|
||||
COMPRESSED_BLOCKTYPE Blocks = m_BlockTypes;
|
||||
Blocks.resize(NumBlocks);
|
||||
a_Callback.BlockTypes (&Blocks[0]);
|
||||
a_Callback.LightIsValid(m_IsLightValid);
|
||||
|
||||
COMPRESSED_NIBBLETYPE Metas = m_BlockMeta;
|
||||
Metas.resize(NumBlocks / 2);
|
||||
a_Callback.BlockMeta (&Metas[0]);
|
||||
|
||||
a_Callback.LightIsValid (m_IsLightValid);
|
||||
|
||||
COMPRESSED_NIBBLETYPE BlockLights = m_BlockLight;
|
||||
BlockLights.resize(NumBlocks / 2);
|
||||
a_Callback.BlockLight (&BlockLights[0]);
|
||||
|
||||
COMPRESSED_NIBBLETYPE BlockSkyLights = m_BlockSkyLight;
|
||||
BlockSkyLights.resize(NumBlocks / 2, 0xff);
|
||||
a_Callback.BlockSkyLight(&BlockSkyLights[0]);
|
||||
a_Callback.ChunkData(m_ChunkData);
|
||||
|
||||
for (cEntityList::iterator itr = m_Entities.begin(); itr != m_Entities.end(); ++itr)
|
||||
{
|
||||
@ -296,48 +282,10 @@ void cChunk::SetAllData(
|
||||
CalculateHeightmap(a_BlockTypes);
|
||||
}
|
||||
|
||||
int IdxWhereNonEmptyStarts = 0;
|
||||
{ // Blocktype compression
|
||||
unsigned char Highest = 0;
|
||||
int X = 0, Z = 0;
|
||||
m_BlockTypes.clear();
|
||||
|
||||
for (int x = 0; x < Width; x++)
|
||||
{
|
||||
for (int z = 0; z < Width; z++)
|
||||
{
|
||||
unsigned char Height = m_HeightMap[x + z * Width];
|
||||
if (Height > Highest)
|
||||
{
|
||||
Highest = Height;
|
||||
X = x; Z = z;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
IdxWhereNonEmptyStarts = MakeIndexNoCheck(X, Highest + 1, Z);
|
||||
|
||||
m_BlockTypes.insert(m_BlockTypes.end(), &a_BlockTypes[0], &a_BlockTypes[IdxWhereNonEmptyStarts]);
|
||||
}
|
||||
|
||||
{ // Blockmeta compression
|
||||
m_BlockMeta.clear();
|
||||
m_BlockMeta.insert(m_BlockMeta.end(), &a_BlockMeta[0], &a_BlockMeta[IdxWhereNonEmptyStarts / 2]);
|
||||
}
|
||||
|
||||
if (a_BlockLight != NULL)
|
||||
{
|
||||
// Compress blocklight
|
||||
m_BlockLight.clear();
|
||||
m_BlockLight.insert(m_BlockLight.end(), &a_BlockLight[0], &a_BlockLight[IdxWhereNonEmptyStarts / 2]);
|
||||
}
|
||||
|
||||
if (a_BlockSkyLight != NULL)
|
||||
{
|
||||
// Compress skylight
|
||||
m_BlockSkyLight.clear();
|
||||
m_BlockSkyLight.insert(m_BlockSkyLight.end(), &a_BlockSkyLight[0], &a_BlockSkyLight[IdxWhereNonEmptyStarts / 2]);
|
||||
}
|
||||
m_ChunkData.SetBlockTypes(a_BlockTypes);
|
||||
m_ChunkData.SetMetas(a_BlockMeta);
|
||||
m_ChunkData.SetBlockLight(a_BlockLight);
|
||||
m_ChunkData.SetSkyLight(a_BlockSkyLight);
|
||||
|
||||
m_IsLightValid = (a_BlockLight != NULL) && (a_BlockSkyLight != NULL);
|
||||
|
||||
@ -378,15 +326,9 @@ void cChunk::SetLight(
|
||||
// TODO: We might get cases of wrong lighting when a chunk changes in the middle of a lighting calculation.
|
||||
// Postponing until we see how bad it is :)
|
||||
|
||||
{ // Compress blocklight
|
||||
m_BlockLight.clear();
|
||||
m_BlockLight.insert(m_BlockLight.end(), &a_BlockLight[0], &a_BlockLight[m_BlockTypes.size() / 2]);
|
||||
}
|
||||
m_ChunkData.SetBlockLight(a_BlockLight);
|
||||
|
||||
{ // Compress skylight
|
||||
m_BlockSkyLight.clear();
|
||||
m_BlockSkyLight.insert(m_BlockSkyLight.end(), &a_SkyLight[0], &a_SkyLight[m_BlockTypes.size() / 2]);
|
||||
}
|
||||
m_ChunkData.SetSkyLight(a_SkyLight);
|
||||
|
||||
m_IsLightValid = true;
|
||||
}
|
||||
@ -397,8 +339,7 @@ void cChunk::SetLight(
|
||||
|
||||
void cChunk::GetBlockTypes(BLOCKTYPE * a_BlockTypes)
|
||||
{
|
||||
std::copy(m_BlockTypes.begin(), m_BlockTypes.end(), a_BlockTypes);
|
||||
std::fill_n(&a_BlockTypes[m_BlockTypes.size()], NumBlocks - m_BlockTypes.size(), E_BLOCK_AIR);
|
||||
m_ChunkData.CopyBlockTypes(a_BlockTypes);
|
||||
}
|
||||
|
||||
|
||||
@ -686,8 +627,7 @@ void cChunk::Tick(float a_Dt)
|
||||
|
||||
void cChunk::TickBlock(int a_RelX, int a_RelY, int a_RelZ)
|
||||
{
|
||||
unsigned Index = MakeIndex(a_RelX, a_RelY, a_RelZ);
|
||||
cBlockHandler * Handler = BlockHandler(GetBlock(Index));
|
||||
cBlockHandler * Handler = BlockHandler(GetBlock(a_RelX, a_RelY, a_RelZ));
|
||||
ASSERT(Handler != NULL); // Happenned on server restart, FS #243
|
||||
cChunkInterface ChunkInterface(this->GetWorld()->GetChunkMap());
|
||||
cBlockInServerPluginInterface PluginInterface(*this->GetWorld());
|
||||
@ -812,19 +752,18 @@ void cChunk::CheckBlocks()
|
||||
{
|
||||
return;
|
||||
}
|
||||
std::vector<unsigned int> ToTickBlocks;
|
||||
std::vector<Vector3i> ToTickBlocks;
|
||||
std::swap(m_ToTickBlocks, ToTickBlocks);
|
||||
|
||||
cChunkInterface ChunkInterface(m_World->GetChunkMap());
|
||||
cBlockInServerPluginInterface PluginInterface(*m_World);
|
||||
|
||||
for (std::vector<unsigned int>::const_iterator itr = ToTickBlocks.begin(), end = ToTickBlocks.end(); itr != end; ++itr)
|
||||
for (std::vector<Vector3i>::const_iterator itr = ToTickBlocks.begin(), end = ToTickBlocks.end(); itr != end; ++itr)
|
||||
{
|
||||
unsigned int index = (*itr);
|
||||
Vector3i BlockPos = IndexToCoordinate(index);
|
||||
Vector3i Pos = (*itr);
|
||||
|
||||
cBlockHandler * Handler = BlockHandler(GetBlock(index));
|
||||
Handler->Check(ChunkInterface, PluginInterface, BlockPos.x, BlockPos.y, BlockPos.z, *this);
|
||||
cBlockHandler * Handler = BlockHandler(GetBlock(Pos));
|
||||
Handler->Check(ChunkInterface, PluginInterface, Pos.x, Pos.y, Pos.z, *this);
|
||||
} // for itr - ToTickBlocks[]
|
||||
}
|
||||
|
||||
@ -867,8 +806,7 @@ void cChunk::TickBlocks(void)
|
||||
continue; // It's all air up here
|
||||
}
|
||||
|
||||
unsigned int Index = MakeIndexNoCheck(m_BlockTickX, m_BlockTickY, m_BlockTickZ);
|
||||
cBlockHandler * Handler = BlockHandler(GetBlock(Index));
|
||||
cBlockHandler * Handler = BlockHandler(GetBlock(m_BlockTickX, m_BlockTickY, m_BlockTickZ));
|
||||
ASSERT(Handler != NULL); // Happenned on server restart, FS #243
|
||||
Handler->OnUpdate(ChunkInterface, *this->GetWorld(), PluginInterface, *this, m_BlockTickX, m_BlockTickY, m_BlockTickZ);
|
||||
} // for i - tickblocks
|
||||
@ -1259,9 +1197,8 @@ bool cChunk::UnboundedRelGetBlockLights(int a_RelX, int a_RelY, int a_RelZ, NIBB
|
||||
// The chunk is not available, bail out
|
||||
return false;
|
||||
}
|
||||
int idx = Chunk->MakeIndex(a_RelX, a_RelY, a_RelZ);
|
||||
a_BlockLight = Chunk->GetBlockLight(idx);
|
||||
a_SkyLight = Chunk->GetSkyLight(idx);
|
||||
a_BlockLight = Chunk->GetBlockLight(a_RelX, a_RelY, a_RelZ);
|
||||
a_SkyLight = Chunk->GetSkyLight(a_RelX, a_RelY, a_RelZ);
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -1451,7 +1388,7 @@ void cChunk::CalculateHeightmap(const BLOCKTYPE * a_BlockTypes)
|
||||
int index = MakeIndex( x, y, z );
|
||||
if (a_BlockTypes[index] != E_BLOCK_AIR)
|
||||
{
|
||||
m_HeightMap[x + z * Width] = (unsigned char)y;
|
||||
m_HeightMap[x + z * Width] = (HEIGHTTYPE)y;
|
||||
break;
|
||||
}
|
||||
} // for y
|
||||
@ -1463,14 +1400,12 @@ void cChunk::CalculateHeightmap(const BLOCKTYPE * a_BlockTypes)
|
||||
|
||||
|
||||
|
||||
void cChunk::SetBlock(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta)
|
||||
void cChunk::SetBlock(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta, bool a_SendToClients)
|
||||
{
|
||||
FastSetBlock(a_RelX, a_RelY, a_RelZ, a_BlockType, a_BlockMeta);
|
||||
|
||||
const int index = MakeIndexNoCheck(a_RelX, a_RelY, a_RelZ);
|
||||
FastSetBlock(a_RelX, a_RelY, a_RelZ, a_BlockType, a_BlockMeta, a_SendToClients);
|
||||
|
||||
// Tick this block and its neighbors:
|
||||
m_ToTickBlocks.push_back(index);
|
||||
m_ToTickBlocks.push_back(Vector3i(a_RelX, a_RelY, a_RelZ));
|
||||
QueueTickBlockNeighbors(a_RelX, a_RelY, a_RelZ);
|
||||
|
||||
// If there was a block entity, remove it:
|
||||
@ -1534,7 +1469,7 @@ void cChunk::QueueTickBlock(int a_RelX, int a_RelY, int a_RelZ)
|
||||
return;
|
||||
}
|
||||
|
||||
m_ToTickBlocks.push_back(MakeIndexNoCheck(a_RelX, a_RelY, a_RelZ));
|
||||
m_ToTickBlocks.push_back(Vector3i(a_RelX, a_RelY, a_RelZ));
|
||||
}
|
||||
|
||||
|
||||
@ -1566,15 +1501,14 @@ void cChunk::QueueTickBlockNeighbors(int a_RelX, int a_RelY, int a_RelZ)
|
||||
|
||||
|
||||
|
||||
void cChunk::FastSetBlock(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockType, BLOCKTYPE a_BlockMeta)
|
||||
void cChunk::FastSetBlock(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockType, BLOCKTYPE a_BlockMeta, bool a_SendToClients)
|
||||
{
|
||||
ASSERT(!((a_RelX < 0) || (a_RelX >= Width) || (a_RelY < 0) || (a_RelY >= Height) || (a_RelZ < 0) || (a_RelZ >= Width)));
|
||||
|
||||
ASSERT(IsValid());
|
||||
|
||||
const int index = MakeIndexNoCheck(a_RelX, a_RelY, a_RelZ);
|
||||
const BLOCKTYPE OldBlockType = GetBlock(index);
|
||||
const BLOCKTYPE OldBlockMeta = GetNibble(m_BlockMeta, index);
|
||||
const BLOCKTYPE OldBlockType = GetBlock(a_RelX, a_RelY, a_RelZ);
|
||||
const BLOCKTYPE OldBlockMeta = m_ChunkData.GetMeta(a_RelX, a_RelY, a_RelZ);
|
||||
if ((OldBlockType == a_BlockType) && (OldBlockMeta == a_BlockMeta))
|
||||
{
|
||||
return;
|
||||
@ -1582,27 +1516,25 @@ void cChunk::FastSetBlock(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockT
|
||||
|
||||
MarkDirty();
|
||||
|
||||
if ((size_t)index >= m_BlockTypes.size())
|
||||
{
|
||||
m_BlockTypes.resize(index + 1);
|
||||
}
|
||||
m_BlockTypes[index] = a_BlockType;
|
||||
m_ChunkData.SetBlock(a_RelX, a_RelY, a_RelZ, a_BlockType);
|
||||
|
||||
// The client doesn't need to distinguish between stationary and nonstationary fluids:
|
||||
if (
|
||||
(OldBlockMeta != a_BlockMeta) || // Different meta always gets sent to the client
|
||||
!(
|
||||
((OldBlockType == E_BLOCK_STATIONARY_WATER) && (a_BlockType == E_BLOCK_WATER)) || // Replacing stationary water with water
|
||||
((OldBlockType == E_BLOCK_WATER) && (a_BlockType == E_BLOCK_STATIONARY_WATER)) || // Replacing water with stationary water
|
||||
((OldBlockType == E_BLOCK_STATIONARY_LAVA) && (a_BlockType == E_BLOCK_LAVA)) || // Replacing stationary water with water
|
||||
((OldBlockType == E_BLOCK_LAVA) && (a_BlockType == E_BLOCK_STATIONARY_LAVA)) // Replacing water with stationary water
|
||||
if ( // Queue block to be sent only if ...
|
||||
a_SendToClients && // ... we are told to do so AND ...
|
||||
(
|
||||
(OldBlockMeta != a_BlockMeta) || // ... the meta value is different OR ...
|
||||
!( // ... the old and new blocktypes AREN'T liquids (because client doesn't need to distinguish betwixt them); see below for specifics:
|
||||
((OldBlockType == E_BLOCK_STATIONARY_WATER) && (a_BlockType == E_BLOCK_WATER)) || // Replacing stationary water with water
|
||||
((OldBlockType == E_BLOCK_WATER) && (a_BlockType == E_BLOCK_STATIONARY_WATER)) || // Replacing water with stationary water
|
||||
((OldBlockType == E_BLOCK_STATIONARY_LAVA) && (a_BlockType == E_BLOCK_LAVA)) || // Replacing stationary water with water
|
||||
((OldBlockType == E_BLOCK_LAVA) && (a_BlockType == E_BLOCK_STATIONARY_LAVA)) // Replacing water with stationary water
|
||||
)
|
||||
)
|
||||
)
|
||||
{
|
||||
m_PendingSendBlocks.push_back(sSetBlock(m_PosX, m_PosZ, a_RelX, a_RelY, a_RelZ, a_BlockType, a_BlockMeta));
|
||||
}
|
||||
|
||||
SetNibble(m_BlockMeta, index, a_BlockMeta);
|
||||
m_ChunkData.SetMeta(a_RelX, a_RelY, a_RelZ, a_BlockMeta);
|
||||
|
||||
// ONLY recalculate lighting if it's necessary!
|
||||
if (
|
||||
@ -1619,15 +1551,15 @@ void cChunk::FastSetBlock(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockT
|
||||
{
|
||||
if (a_BlockType != E_BLOCK_AIR)
|
||||
{
|
||||
m_HeightMap[a_RelX + a_RelZ * Width] = (unsigned char)a_RelY;
|
||||
m_HeightMap[a_RelX + a_RelZ * Width] = (HEIGHTTYPE)a_RelY;
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int y = a_RelY - 1; y > 0; --y)
|
||||
{
|
||||
if (GetBlock(MakeIndexNoCheck(a_RelX, y, a_RelZ)) != E_BLOCK_AIR)
|
||||
if (GetBlock(a_RelX, y, a_RelZ) != E_BLOCK_AIR)
|
||||
{
|
||||
m_HeightMap[a_RelX + a_RelZ * Width] = (unsigned char)y;
|
||||
m_HeightMap[a_RelX + a_RelZ * Width] = (HEIGHTTYPE)y;
|
||||
break;
|
||||
}
|
||||
} // for y - column in m_BlockData
|
||||
@ -1639,39 +1571,18 @@ void cChunk::FastSetBlock(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockT
|
||||
|
||||
|
||||
|
||||
void cChunk::SetMeta(int a_BlockIdx, NIBBLETYPE a_Meta)
|
||||
{
|
||||
if (GetNibble(m_BlockMeta, a_BlockIdx) == a_Meta)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
MarkDirty();
|
||||
SetNibble(m_BlockMeta, a_BlockIdx, a_Meta);
|
||||
Vector3i Coords(IndexToCoordinate(a_BlockIdx));
|
||||
|
||||
m_PendingSendBlocks.push_back(sSetBlock(m_PosX, m_PosZ, Coords.x, Coords.y, Coords.z, GetBlock(a_BlockIdx), a_Meta));
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cChunk::SendBlockTo(int a_RelX, int a_RelY, int a_RelZ, cClientHandle * a_Client)
|
||||
{
|
||||
// The coords must be valid, because the upper level already does chunk lookup. No need to check them again.
|
||||
// There's an debug-time assert in MakeIndexNoCheck anyway
|
||||
unsigned int index = MakeIndexNoCheck(a_RelX, a_RelY, a_RelZ);
|
||||
|
||||
if (a_Client == NULL)
|
||||
{
|
||||
// Queue the block for all clients in the chunk (will be sent in Tick())
|
||||
m_PendingSendBlocks.push_back(sSetBlock(m_PosX, m_PosZ, a_RelX, a_RelY, a_RelZ, GetBlock(index), GetMeta(index)));
|
||||
m_PendingSendBlocks.push_back(sSetBlock(m_PosX, m_PosZ, a_RelX, a_RelY, a_RelZ, GetBlock(a_RelX, a_RelY, a_RelZ), GetMeta(a_RelX, a_RelY, a_RelZ)));
|
||||
return;
|
||||
}
|
||||
|
||||
Vector3i wp = PositionToWorldPosition(a_RelX, a_RelY, a_RelZ);
|
||||
a_Client->SendBlockChange(wp.x, wp.y, wp.z, GetBlock(index), GetMeta(index));
|
||||
a_Client->SendBlockChange(wp.x, wp.y, wp.z, GetBlock(a_RelX, a_RelY, a_RelZ), GetMeta(a_RelX, a_RelY, a_RelZ));
|
||||
|
||||
// FS #268 - if a BlockEntity digging is cancelled by a plugin, the entire block entity must be re-sent to the client:
|
||||
for (cBlockEntityList::iterator itr = m_BlockEntities.begin(), end = m_BlockEntities.end(); itr != end; ++itr)
|
||||
@ -2530,27 +2441,7 @@ BLOCKTYPE cChunk::GetBlock(int a_RelX, int a_RelY, int a_RelZ) const
|
||||
return 0; // Clip
|
||||
}
|
||||
|
||||
return GetBlock(MakeIndexNoCheck(a_RelX, a_RelY, a_RelZ));
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
BLOCKTYPE cChunk::GetBlock(int a_BlockIdx) const
|
||||
{
|
||||
if ((a_BlockIdx < 0) || (a_BlockIdx >= NumBlocks))
|
||||
{
|
||||
ASSERT(!"GetBlock(idx) out of bounds!");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if ((size_t)a_BlockIdx >= m_BlockTypes.size())
|
||||
{
|
||||
return E_BLOCK_AIR;
|
||||
}
|
||||
|
||||
return m_BlockTypes[a_BlockIdx];
|
||||
return m_ChunkData.GetBlock(a_RelX, a_RelY, a_RelZ);
|
||||
}
|
||||
|
||||
|
||||
@ -2559,9 +2450,8 @@ BLOCKTYPE cChunk::GetBlock(int a_BlockIdx) const
|
||||
|
||||
void cChunk::GetBlockTypeMeta(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta)
|
||||
{
|
||||
int Idx = cChunkDef::MakeIndexNoCheck(a_RelX, a_RelY, a_RelZ);
|
||||
a_BlockType = GetBlock(Idx);
|
||||
a_BlockMeta = cChunkDef::GetNibble(m_BlockMeta, Idx);
|
||||
a_BlockType = GetBlock(a_RelX, a_RelY, a_RelZ);
|
||||
a_BlockMeta = m_ChunkData.GetMeta(a_RelX, a_RelY, a_RelZ);
|
||||
}
|
||||
|
||||
|
||||
@ -2570,11 +2460,10 @@ void cChunk::GetBlockTypeMeta(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE & a_
|
||||
|
||||
void cChunk::GetBlockInfo(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE & a_BlockType, NIBBLETYPE & a_Meta, NIBBLETYPE & a_SkyLight, NIBBLETYPE & a_BlockLight)
|
||||
{
|
||||
int Idx = cChunkDef::MakeIndexNoCheck(a_RelX, a_RelY, a_RelZ);
|
||||
a_BlockType = GetBlock(Idx);
|
||||
a_Meta = cChunkDef::GetNibble(m_BlockMeta, Idx);
|
||||
a_SkyLight = cChunkDef::GetNibble(m_BlockSkyLight, Idx);
|
||||
a_BlockLight = cChunkDef::GetNibble(m_BlockLight, Idx);
|
||||
a_BlockType = GetBlock(a_RelX, a_RelY, a_RelZ);
|
||||
a_Meta = m_ChunkData.GetMeta(a_RelX, a_RelY, a_RelZ);
|
||||
a_SkyLight = m_ChunkData.GetSkyLight(a_RelX, a_RelY, a_RelZ);
|
||||
a_BlockLight = m_ChunkData.GetBlockLight(a_RelX, a_RelY, a_RelZ);
|
||||
}
|
||||
|
||||
|
||||
|
41
src/Chunk.h
@ -3,6 +3,7 @@
|
||||
|
||||
#include "Entities/Entity.h"
|
||||
#include "ChunkDef.h"
|
||||
#include "ChunkData.h"
|
||||
|
||||
#include "Simulator/FireSimulator.h"
|
||||
#include "Simulator/SandSimulator.h"
|
||||
@ -66,6 +67,7 @@ public:
|
||||
cChunkMap * a_ChunkMap, cWorld * a_World, // Parent objects
|
||||
cChunk * a_NeighborXM, cChunk * a_NeighborXP, cChunk * a_NeighborZM, cChunk * a_NeighborZP // Neighbor chunks
|
||||
);
|
||||
cChunk(cChunk & other);
|
||||
~cChunk();
|
||||
|
||||
bool IsValid(void) const {return m_IsValid; } // Returns true if the chunk block data is valid (loaded / generated)
|
||||
@ -139,7 +141,7 @@ public:
|
||||
|
||||
cWorld * GetWorld(void) const { return m_World; }
|
||||
|
||||
void SetBlock(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta );
|
||||
void SetBlock(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta, bool a_SendToClients = true);
|
||||
// SetBlock() does a lot of work (heightmap, tickblocks, blockentities) so a BlockIdx version doesn't make sense
|
||||
void SetBlock( const Vector3i & a_RelBlockPos, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta ) { SetBlock( a_RelBlockPos.x, a_RelBlockPos.y, a_RelBlockPos.z, a_BlockType, a_BlockMeta ); }
|
||||
|
||||
@ -152,9 +154,9 @@ public:
|
||||
/** Queues all 6 neighbors of the specified block for ticking (m_ToTickQueue). If any are outside the chunk, relays the checking to the proper neighboring chunk */
|
||||
void QueueTickBlockNeighbors(int a_RelX, int a_RelY, int a_RelZ);
|
||||
|
||||
void FastSetBlock(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockType, BLOCKTYPE a_BlockMeta ); // Doesn't force block updates on neighbors, use for simple changes such as grass growing etc.
|
||||
void FastSetBlock(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockType, BLOCKTYPE a_BlockMeta, bool a_SendToClients = true); // Doesn't force block updates on neighbors, use for simple changes such as grass growing etc.
|
||||
BLOCKTYPE GetBlock(int a_RelX, int a_RelY, int a_RelZ) const;
|
||||
BLOCKTYPE GetBlock(int a_BlockIdx) const;
|
||||
BLOCKTYPE GetBlock(Vector3i a_cords) const { return GetBlock(a_cords.x, a_cords.y, a_cords.z);}
|
||||
void GetBlockTypeMeta(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta);
|
||||
void GetBlockInfo (int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE & a_BlockType, NIBBLETYPE & a_Meta, NIBBLETYPE & a_SkyLight, NIBBLETYPE & a_BlockLight);
|
||||
|
||||
@ -320,15 +322,23 @@ public:
|
||||
m_BlockTickZ = a_RelZ;
|
||||
}
|
||||
|
||||
inline NIBBLETYPE GetMeta(int a_RelX, int a_RelY, int a_RelZ) const { return cChunkDef::GetNibble(m_BlockMeta, a_RelX, a_RelY, a_RelZ); }
|
||||
inline NIBBLETYPE GetMeta(int a_BlockIdx) const { return cChunkDef::GetNibble(m_BlockMeta, a_BlockIdx); }
|
||||
inline void SetMeta(int a_RelX, int a_RelY, int a_RelZ, NIBBLETYPE a_Meta) { SetMeta(MakeIndex(a_RelX, a_RelY, a_RelZ), a_Meta); }
|
||||
void SetMeta(int a_BlockIdx, NIBBLETYPE a_Meta);
|
||||
inline NIBBLETYPE GetMeta(int a_RelX, int a_RelY, int a_RelZ) const
|
||||
{
|
||||
return m_ChunkData.GetMeta(a_RelX, a_RelY, a_RelZ);
|
||||
}
|
||||
inline void SetMeta(int a_RelX, int a_RelY, int a_RelZ, NIBBLETYPE a_Meta)
|
||||
{
|
||||
bool hasChanged = m_ChunkData.SetMeta(a_RelX, a_RelY, a_RelZ, a_Meta);
|
||||
if (hasChanged)
|
||||
{
|
||||
MarkDirty();
|
||||
|
||||
m_PendingSendBlocks.push_back(sSetBlock(m_PosX, m_PosZ, a_RelX, a_RelY, a_RelZ, GetBlock(a_RelX, a_RelY, a_RelZ), a_Meta));
|
||||
}
|
||||
}
|
||||
|
||||
inline NIBBLETYPE GetBlockLight(int a_RelX, int a_RelY, int a_RelZ) const {return cChunkDef::GetNibble(m_BlockLight, a_RelX, a_RelY, a_RelZ); }
|
||||
inline NIBBLETYPE GetSkyLight (int a_RelX, int a_RelY, int a_RelZ) const {return cChunkDef::GetNibble(m_BlockSkyLight, a_RelX, a_RelY, a_RelZ, true); }
|
||||
inline NIBBLETYPE GetBlockLight(int a_Idx) const {return cChunkDef::GetNibble(m_BlockLight, a_Idx); }
|
||||
inline NIBBLETYPE GetSkyLight (int a_Idx) const {return cChunkDef::GetNibble(m_BlockSkyLight, a_Idx, true); }
|
||||
inline NIBBLETYPE GetBlockLight(int a_RelX, int a_RelY, int a_RelZ) const {return m_ChunkData.GetBlockLight(a_RelX, a_RelY, a_RelZ); }
|
||||
inline NIBBLETYPE GetSkyLight (int a_RelX, int a_RelY, int a_RelZ) const {return m_ChunkData.GetSkyLight(a_RelX, a_RelY, a_RelZ); }
|
||||
|
||||
/** Same as GetBlock(), but relative coords needn't be in this chunk (uses m_Neighbor-s or m_ChunkMap in such a case); returns true on success */
|
||||
bool UnboundedRelGetBlock(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta) const;
|
||||
@ -403,8 +413,8 @@ private:
|
||||
bool m_IsSaving; // True if the chunk is being saved
|
||||
bool m_HasLoadFailed; // True if chunk failed to load and hasn't been generated yet since then
|
||||
|
||||
std::vector<unsigned int> m_ToTickBlocks;
|
||||
sSetBlockVector m_PendingSendBlocks; ///< Blocks that have changed and need to be sent to all clients
|
||||
std::vector<Vector3i> m_ToTickBlocks;
|
||||
sSetBlockVector m_PendingSendBlocks; ///< Blocks that have changed and need to be sent to all clients
|
||||
|
||||
sSetBlockQueueVector m_SetBlockQueue; ///< Block changes that are queued to a specific tick
|
||||
|
||||
@ -421,10 +431,7 @@ private:
|
||||
cWorld * m_World;
|
||||
cChunkMap * m_ChunkMap;
|
||||
|
||||
COMPRESSED_BLOCKTYPE m_BlockTypes;
|
||||
COMPRESSED_NIBBLETYPE m_BlockMeta;
|
||||
COMPRESSED_NIBBLETYPE m_BlockLight;
|
||||
COMPRESSED_NIBBLETYPE m_BlockSkyLight;
|
||||
cChunkData m_ChunkData;
|
||||
|
||||
cChunkDef::HeightMap m_HeightMap;
|
||||
cChunkDef::BiomeMap m_BiomeMap;
|
||||
|
592
src/ChunkData.cpp
Normal file
@ -0,0 +1,592 @@
|
||||
|
||||
// ChunkData.cpp
|
||||
|
||||
// Implements the cChunkData class that represents the block's type, meta, blocklight and skylight storage for a chunk
|
||||
|
||||
#include "Globals.h"
|
||||
#include "ChunkData.h"
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/** Returns true if all a_Array's elements between [0] and [a_NumElements - 1] are equal to a_Value. */
|
||||
template <typename T> inline bool IsAllValue(const T * a_Array, size_t a_NumElements, T a_Value)
|
||||
{
|
||||
for (size_t i = 0; i < a_NumElements; i++)
|
||||
{
|
||||
if (a_Array[i] != a_Value)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
cChunkData::cChunkData(void)
|
||||
#if __cplusplus < 201103L
|
||||
// auto_ptr style interface for memory management
|
||||
: m_IsOwner(true)
|
||||
#endif
|
||||
{
|
||||
for (size_t i = 0; i < NumSections; i++)
|
||||
{
|
||||
m_Sections[i] = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
cChunkData::~cChunkData()
|
||||
{
|
||||
#if __cplusplus < 201103L
|
||||
// auto_ptr style interface for memory management
|
||||
if (!m_IsOwner)
|
||||
{
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
for (size_t i = 0; i < NumSections; i++)
|
||||
{
|
||||
Free(m_Sections[i]);
|
||||
m_Sections[i] = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#if __cplusplus < 201103L
|
||||
// auto_ptr style interface for memory management
|
||||
cChunkData::cChunkData(const cChunkData & a_Other) :
|
||||
m_IsOwner(true)
|
||||
{
|
||||
// Move contents and ownership from a_Other to this, pointer-wise:
|
||||
for (size_t i = 0; i < NumSections; i++)
|
||||
{
|
||||
m_Sections[i] = a_Other.m_Sections[i];
|
||||
}
|
||||
a_Other.m_IsOwner = false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
cChunkData & cChunkData::operator =(const cChunkData & a_Other)
|
||||
{
|
||||
// If assigning to self, no-op
|
||||
if (&a_Other == this)
|
||||
{
|
||||
return *this;
|
||||
}
|
||||
|
||||
// Free any currently held contents:
|
||||
if (m_IsOwner)
|
||||
{
|
||||
for (size_t i = 0; i < NumSections; i++)
|
||||
{
|
||||
Free(m_Sections[i]);
|
||||
m_Sections[i] = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
// Move contents and ownership from a_Other to this, pointer-wise:
|
||||
m_IsOwner = true;
|
||||
for (size_t i = 0; i < NumSections; i++)
|
||||
{
|
||||
m_Sections[i] = a_Other.m_Sections[i];
|
||||
}
|
||||
a_Other.m_IsOwner = false;
|
||||
return *this;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
// unique_ptr style interface for memory management
|
||||
cChunkData::cChunkData(cChunkData && other)
|
||||
{
|
||||
for (size_t i = 0; i < NumSections; i++)
|
||||
{
|
||||
m_Sections[i] = other.m_Sections[i];
|
||||
other.m_Sections[i] = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
cChunkData & cChunkData::operator =(cChunkData && other)
|
||||
{
|
||||
if (&other != this)
|
||||
{
|
||||
for (size_t i = 0; i < NumSections; i++)
|
||||
{
|
||||
Free(m_Sections[i]);
|
||||
m_Sections[i] = other.m_Sections[i];
|
||||
other.m_Sections[i] = NULL;
|
||||
}
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
BLOCKTYPE cChunkData::GetBlock(int a_X, int a_Y, int a_Z) const
|
||||
{
|
||||
ASSERT((a_X >= 0) && (a_X < cChunkDef::Width));
|
||||
ASSERT((a_Y >= 0) && (a_Y < cChunkDef::Height));
|
||||
ASSERT((a_Z >= 0) && (a_Z < cChunkDef::Width));
|
||||
int Section = a_Y / SectionHeight;
|
||||
if (m_Sections[Section] != NULL)
|
||||
{
|
||||
int Index = cChunkDef::MakeIndexNoCheck(a_X, a_Y - (Section * SectionHeight), a_Z);
|
||||
return m_Sections[Section]->m_BlockTypes[Index];
|
||||
}
|
||||
else
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cChunkData::SetBlock(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_Block)
|
||||
{
|
||||
if (
|
||||
(a_RelX >= cChunkDef::Width) || (a_RelX < 0) ||
|
||||
(a_RelY >= cChunkDef::Height) || (a_RelY < 0) ||
|
||||
(a_RelZ >= cChunkDef::Width) || (a_RelZ < 0)
|
||||
)
|
||||
{
|
||||
ASSERT(!"cChunkData::SetMeta(): index out of range!");
|
||||
return;
|
||||
}
|
||||
|
||||
int Section = a_RelY / SectionHeight;
|
||||
if (m_Sections[Section] == NULL)
|
||||
{
|
||||
if (a_Block == 0x00)
|
||||
{
|
||||
return;
|
||||
}
|
||||
m_Sections[Section] = Allocate();
|
||||
if (m_Sections[Section] == NULL)
|
||||
{
|
||||
ASSERT(!"Failed to allocate a new section in Chunkbuffer");
|
||||
return;
|
||||
}
|
||||
ZeroSection(m_Sections[Section]);
|
||||
}
|
||||
int Index = cChunkDef::MakeIndexNoCheck(a_RelX, a_RelY - (Section * SectionHeight), a_RelZ);
|
||||
m_Sections[Section]->m_BlockTypes[Index] = a_Block;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
NIBBLETYPE cChunkData::GetMeta(int a_RelX, int a_RelY, int a_RelZ) const
|
||||
{
|
||||
if (
|
||||
(a_RelX < cChunkDef::Width) && (a_RelX > -1) &&
|
||||
(a_RelY < cChunkDef::Height) && (a_RelY > -1) &&
|
||||
(a_RelZ < cChunkDef::Width) && (a_RelZ > -1))
|
||||
{
|
||||
int Section = a_RelY / SectionHeight;
|
||||
if (m_Sections[Section] != NULL)
|
||||
{
|
||||
int Index = cChunkDef::MakeIndexNoCheck(a_RelX, a_RelY - (Section * SectionHeight), a_RelZ);
|
||||
return (m_Sections[Section]->m_BlockMetas[Index / 2] >> ((Index & 1) * 4)) & 0x0f;
|
||||
}
|
||||
else
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
ASSERT(!"cChunkData::GetMeta(): coords out of chunk range!");
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
bool cChunkData::SetMeta(int a_RelX, int a_RelY, int a_RelZ, NIBBLETYPE a_Nibble)
|
||||
{
|
||||
if (
|
||||
(a_RelX >= cChunkDef::Width) || (a_RelX < 0) ||
|
||||
(a_RelY >= cChunkDef::Height) || (a_RelY < 0) ||
|
||||
(a_RelZ >= cChunkDef::Width) || (a_RelZ < 0)
|
||||
)
|
||||
{
|
||||
ASSERT(!"cChunkData::SetMeta(): index out of range!");
|
||||
return false;
|
||||
}
|
||||
|
||||
int Section = a_RelY / SectionHeight;
|
||||
if (m_Sections[Section] == NULL)
|
||||
{
|
||||
if ((a_Nibble & 0xf) == 0x00)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
m_Sections[Section] = Allocate();
|
||||
if (m_Sections[Section] == NULL)
|
||||
{
|
||||
ASSERT(!"Failed to allocate a new section in Chunkbuffer");
|
||||
return false;
|
||||
}
|
||||
ZeroSection(m_Sections[Section]);
|
||||
}
|
||||
int Index = cChunkDef::MakeIndexNoCheck(a_RelX, a_RelY - (Section * SectionHeight), a_RelZ);
|
||||
NIBBLETYPE oldval = m_Sections[Section]->m_BlockMetas[Index / 2] >> ((Index & 1) * 4) & 0xf;
|
||||
m_Sections[Section]->m_BlockMetas[Index / 2] = static_cast<NIBBLETYPE>(
|
||||
(m_Sections[Section]->m_BlockMetas[Index / 2] & (0xf0 >> ((Index & 1) * 4))) | // The untouched nibble
|
||||
((a_Nibble & 0x0f) << ((Index & 1) * 4)) // The nibble being set
|
||||
);
|
||||
return oldval == a_Nibble;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
NIBBLETYPE cChunkData::GetBlockLight(int a_RelX, int a_RelY, int a_RelZ) const
|
||||
{
|
||||
if (
|
||||
(a_RelX < cChunkDef::Width) && (a_RelX > -1) &&
|
||||
(a_RelY < cChunkDef::Height) && (a_RelY > -1) &&
|
||||
(a_RelZ < cChunkDef::Width) && (a_RelZ > -1)
|
||||
)
|
||||
{
|
||||
int Section = a_RelY / SectionHeight;
|
||||
if (m_Sections[Section] != NULL)
|
||||
{
|
||||
int Index = cChunkDef::MakeIndexNoCheck(a_RelX, a_RelY - (Section * SectionHeight), a_RelZ);
|
||||
return (m_Sections[Section]->m_BlockLight[Index / 2] >> ((Index & 1) * 4)) & 0x0f;
|
||||
}
|
||||
else
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
ASSERT(!"cChunkData::GetMeta(): coords out of chunk range!");
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
NIBBLETYPE cChunkData::GetSkyLight(int a_RelX, int a_RelY, int a_RelZ) const
|
||||
{
|
||||
if ((a_RelX < cChunkDef::Width) && (a_RelX > -1) && (a_RelY < cChunkDef::Height) && (a_RelY > -1) && (a_RelZ < cChunkDef::Width) && (a_RelZ > -1))
|
||||
{
|
||||
int Section = a_RelY / SectionHeight;
|
||||
if (m_Sections[Section] != NULL)
|
||||
{
|
||||
int Index = cChunkDef::MakeIndexNoCheck(a_RelX, a_RelY - (Section * SectionHeight), a_RelZ);
|
||||
return (m_Sections[Section]->m_BlockSkyLight[Index / 2] >> ((Index & 1) * 4)) & 0x0f;
|
||||
}
|
||||
else
|
||||
{
|
||||
return 0xF;
|
||||
}
|
||||
}
|
||||
ASSERT(!"cChunkData::GetMeta(): coords out of chunk range!");
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
cChunkData cChunkData::Copy(void) const
|
||||
{
|
||||
cChunkData copy;
|
||||
for (size_t i = 0; i < NumSections; i++)
|
||||
{
|
||||
if (m_Sections[i] != NULL)
|
||||
{
|
||||
copy.m_Sections[i] = Allocate();
|
||||
*copy.m_Sections[i] = *m_Sections[i];
|
||||
}
|
||||
}
|
||||
return copy;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cChunkData::CopyBlockTypes(BLOCKTYPE * a_Dest, size_t a_Idx, size_t a_Length) const
|
||||
{
|
||||
size_t ToSkip = a_Idx;
|
||||
|
||||
for (size_t i = 0; i < NumSections; i++)
|
||||
{
|
||||
size_t StartPos = 0;
|
||||
if (ToSkip > 0)
|
||||
{
|
||||
StartPos = std::min(ToSkip, +SectionBlockCount);
|
||||
ToSkip -= StartPos;
|
||||
}
|
||||
if (StartPos < SectionBlockCount)
|
||||
{
|
||||
size_t ToCopy = std::min(+SectionBlockCount - StartPos, a_Length);
|
||||
a_Length -= ToCopy;
|
||||
if (m_Sections[i] != NULL)
|
||||
{
|
||||
BLOCKTYPE * blockbuffer = m_Sections[i]->m_BlockTypes;
|
||||
memcpy(&a_Dest[(i * SectionBlockCount) + StartPos - a_Idx], blockbuffer + StartPos, sizeof(BLOCKTYPE) * ToCopy);
|
||||
}
|
||||
else
|
||||
{
|
||||
memset(&a_Dest[(i * SectionBlockCount) - a_Idx], 0, sizeof(BLOCKTYPE) * ToCopy);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cChunkData::CopyMetas(NIBBLETYPE * a_Dest) const
|
||||
{
|
||||
for (size_t i = 0; i < NumSections; i++)
|
||||
{
|
||||
if (m_Sections[i] != NULL)
|
||||
{
|
||||
memcpy(&a_Dest[i * SectionBlockCount / 2], &m_Sections[i]->m_BlockMetas, sizeof(m_Sections[i]->m_BlockMetas));
|
||||
}
|
||||
else
|
||||
{
|
||||
memset(&a_Dest[i * SectionBlockCount / 2], 0, sizeof(m_Sections[i]->m_BlockMetas));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cChunkData::CopyBlockLight(NIBBLETYPE * a_Dest) const
|
||||
{
|
||||
for (size_t i = 0; i < NumSections; i++)
|
||||
{
|
||||
if (m_Sections[i] != NULL)
|
||||
{
|
||||
memcpy(&a_Dest[i * SectionBlockCount / 2], &m_Sections[i]->m_BlockLight, sizeof(m_Sections[i]->m_BlockLight));
|
||||
}
|
||||
else
|
||||
{
|
||||
memset(&a_Dest[i * SectionBlockCount / 2], 0, sizeof(m_Sections[i]->m_BlockLight));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cChunkData::CopySkyLight(NIBBLETYPE * a_Dest) const
|
||||
{
|
||||
for (size_t i = 0; i < NumSections; i++)
|
||||
{
|
||||
if (m_Sections[i] != NULL)
|
||||
{
|
||||
memcpy(&a_Dest[i * SectionBlockCount / 2], &m_Sections[i]->m_BlockSkyLight, sizeof(m_Sections[i]->m_BlockSkyLight));
|
||||
}
|
||||
else
|
||||
{
|
||||
memset(&a_Dest[i * SectionBlockCount / 2], 0xff, sizeof(m_Sections[i]->m_BlockSkyLight));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cChunkData::SetBlockTypes(const BLOCKTYPE * a_Src)
|
||||
{
|
||||
ASSERT(a_Src != NULL);
|
||||
|
||||
for (size_t i = 0; i < NumSections; i++)
|
||||
{
|
||||
// If the section is already allocated, copy the data into it:
|
||||
if (m_Sections[i] != NULL)
|
||||
{
|
||||
memcpy(m_Sections[i]->m_BlockTypes, &a_Src[i * SectionBlockCount], sizeof(m_Sections[i]->m_BlockTypes));
|
||||
continue;
|
||||
}
|
||||
|
||||
// The section doesn't exist, find out if it is needed:
|
||||
if (IsAllValue(a_Src + i * SectionBlockCount, SectionBlockCount, (const BLOCKTYPE)0))
|
||||
{
|
||||
// No need for the section, the data is all-air
|
||||
continue;
|
||||
}
|
||||
|
||||
// Allocate the section and copy the data into it:
|
||||
m_Sections[i] = Allocate();
|
||||
memcpy(m_Sections[i]->m_BlockTypes, &a_Src[i * SectionBlockCount], sizeof(m_Sections[i]->m_BlockTypes));
|
||||
memset(m_Sections[i]->m_BlockMetas, 0x00, sizeof(m_Sections[i]->m_BlockMetas));
|
||||
memset(m_Sections[i]->m_BlockLight, 0x00, sizeof(m_Sections[i]->m_BlockLight));
|
||||
memset(m_Sections[i]->m_BlockSkyLight, 0xff, sizeof(m_Sections[i]->m_BlockSkyLight));
|
||||
} // for i - m_Sections[]
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void cChunkData::SetMetas(const NIBBLETYPE * a_Src)
|
||||
{
|
||||
ASSERT(a_Src != NULL);
|
||||
|
||||
for (size_t i = 0; i < NumSections; i++)
|
||||
{
|
||||
// If the section is already allocated, copy the data into it:
|
||||
if (m_Sections[i] != NULL)
|
||||
{
|
||||
memcpy(m_Sections[i]->m_BlockMetas, &a_Src[i * SectionBlockCount / 2], sizeof(m_Sections[i]->m_BlockMetas));
|
||||
continue;
|
||||
}
|
||||
|
||||
// The section doesn't exist, find out if it is needed:
|
||||
if (IsAllValue(a_Src + i * SectionBlockCount / 2, SectionBlockCount / 2, (NIBBLETYPE)0))
|
||||
{
|
||||
// No need for the section, the data is all zeroes
|
||||
continue;
|
||||
}
|
||||
|
||||
// Allocate the section and copy the data into it:
|
||||
m_Sections[i] = Allocate();
|
||||
memcpy(m_Sections[i]->m_BlockMetas, &a_Src[i * SectionBlockCount / 2], sizeof(m_Sections[i]->m_BlockMetas));
|
||||
memset(m_Sections[i]->m_BlockTypes, 0x00, sizeof(m_Sections[i]->m_BlockTypes));
|
||||
memset(m_Sections[i]->m_BlockLight, 0x00, sizeof(m_Sections[i]->m_BlockLight));
|
||||
memset(m_Sections[i]->m_BlockSkyLight, 0xff, sizeof(m_Sections[i]->m_BlockSkyLight));
|
||||
} // for i - m_Sections[]
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cChunkData::SetBlockLight(const NIBBLETYPE * a_Src)
|
||||
{
|
||||
if (a_Src == NULL)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < NumSections; i++)
|
||||
{
|
||||
// If the section is already allocated, copy the data into it:
|
||||
if (m_Sections[i] != NULL)
|
||||
{
|
||||
memcpy(m_Sections[i]->m_BlockLight, &a_Src[i * SectionBlockCount / 2], sizeof(m_Sections[i]->m_BlockLight));
|
||||
continue;
|
||||
}
|
||||
|
||||
// The section doesn't exist, find out if it is needed:
|
||||
if (IsAllValue(a_Src + i * SectionBlockCount / 2, SectionBlockCount / 2, (NIBBLETYPE)0))
|
||||
{
|
||||
// No need for the section, the data is all zeroes
|
||||
continue;
|
||||
}
|
||||
|
||||
// Allocate the section and copy the data into it:
|
||||
m_Sections[i] = Allocate();
|
||||
memcpy(m_Sections[i]->m_BlockLight, &a_Src[i * SectionBlockCount / 2], sizeof(m_Sections[i]->m_BlockLight));
|
||||
memset(m_Sections[i]->m_BlockTypes, 0x00, sizeof(m_Sections[i]->m_BlockTypes));
|
||||
memset(m_Sections[i]->m_BlockMetas, 0x00, sizeof(m_Sections[i]->m_BlockMetas));
|
||||
memset(m_Sections[i]->m_BlockSkyLight, 0xff, sizeof(m_Sections[i]->m_BlockSkyLight));
|
||||
} // for i - m_Sections[]
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void cChunkData::SetSkyLight(const NIBBLETYPE * a_Src)
|
||||
{
|
||||
if (a_Src == NULL)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < NumSections; i++)
|
||||
{
|
||||
// If the section is already allocated, copy the data into it:
|
||||
if (m_Sections[i] != NULL)
|
||||
{
|
||||
memcpy(m_Sections[i]->m_BlockSkyLight, &a_Src[i * SectionBlockCount / 2], sizeof(m_Sections[i]->m_BlockSkyLight));
|
||||
continue;
|
||||
}
|
||||
|
||||
// The section doesn't exist, find out if it is needed:
|
||||
if (IsAllValue(a_Src + i * SectionBlockCount / 2, SectionBlockCount / 2, (NIBBLETYPE)0xff))
|
||||
{
|
||||
// No need for the section, the data is all zeroes
|
||||
continue;
|
||||
}
|
||||
|
||||
// Allocate the section and copy the data into it:
|
||||
m_Sections[i] = Allocate();
|
||||
memcpy(m_Sections[i]->m_BlockSkyLight, &a_Src[i * SectionBlockCount / 2], sizeof(m_Sections[i]->m_BlockSkyLight));
|
||||
memset(m_Sections[i]->m_BlockTypes, 0x00, sizeof(m_Sections[i]->m_BlockTypes));
|
||||
memset(m_Sections[i]->m_BlockMetas, 0x00, sizeof(m_Sections[i]->m_BlockMetas));
|
||||
memset(m_Sections[i]->m_BlockLight, 0x00, sizeof(m_Sections[i]->m_BlockLight));
|
||||
} // for i - m_Sections[]
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
cChunkData::sChunkSection * cChunkData::Allocate(void)
|
||||
{
|
||||
// TODO: Use an allocation pool
|
||||
return new cChunkData::sChunkSection;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cChunkData::Free(cChunkData::sChunkSection * a_Section)
|
||||
{
|
||||
// TODO: Use an allocation pool
|
||||
delete a_Section;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cChunkData::ZeroSection(cChunkData::sChunkSection * a_Section) const
|
||||
{
|
||||
memset(a_Section->m_BlockTypes, 0x00, sizeof(a_Section->m_BlockTypes));
|
||||
memset(a_Section->m_BlockMetas, 0x00, sizeof(a_Section->m_BlockMetas));
|
||||
memset(a_Section->m_BlockLight, 0x00, sizeof(a_Section->m_BlockLight));
|
||||
memset(a_Section->m_BlockSkyLight, 0xff, sizeof(a_Section->m_BlockSkyLight));
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
125
src/ChunkData.h
Normal file
@ -0,0 +1,125 @@
|
||||
|
||||
// ChunkData.h
|
||||
|
||||
// Declares the cChunkData class that represents the block's type, meta, blocklight and skylight storage for a chunk
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
|
||||
#include <cstring>
|
||||
|
||||
|
||||
#include "ChunkDef.h"
|
||||
|
||||
|
||||
|
||||
|
||||
#if __cplusplus < 201103L
|
||||
// auto_ptr style interface for memory management
|
||||
#else
|
||||
// unique_ptr style interface for memory management
|
||||
#endif
|
||||
|
||||
class cChunkData
|
||||
{
|
||||
public:
|
||||
|
||||
cChunkData(void);
|
||||
~cChunkData();
|
||||
|
||||
#if __cplusplus < 201103L
|
||||
// auto_ptr style interface for memory management
|
||||
cChunkData(const cChunkData & a_Other);
|
||||
cChunkData & operator =(const cChunkData & a_Other);
|
||||
#else
|
||||
// unique_ptr style interface for memory management
|
||||
cChunkData(cChunkData && a_Other);
|
||||
cChunkData & operator =(cChunkData && a_ther);
|
||||
#endif
|
||||
|
||||
BLOCKTYPE GetBlock(int a_X, int a_Y, int a_Z) const;
|
||||
void SetBlock(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_Block);
|
||||
|
||||
NIBBLETYPE GetMeta(int a_RelX, int a_RelY, int a_RelZ) const;
|
||||
bool SetMeta(int a_RelX, int a_RelY, int a_RelZ, NIBBLETYPE a_Nibble);
|
||||
|
||||
NIBBLETYPE GetBlockLight(int a_RelX, int a_RelY, int a_RelZ) const;
|
||||
|
||||
NIBBLETYPE GetSkyLight(int a_RelX, int a_RelY, int a_RelZ) const;
|
||||
|
||||
/** Creates a (deep) copy of self. */
|
||||
cChunkData Copy(void) const;
|
||||
|
||||
/** Copies the blocktype data into the specified flat array.
|
||||
Optionally, only a part of the data is copied, as specified by the a_Idx and a_Length parameters. */
|
||||
void CopyBlockTypes(BLOCKTYPE * a_Dest, size_t a_Idx = 0, size_t a_Length = cChunkDef::NumBlocks) const;
|
||||
|
||||
/** Copies the metadata into the specified flat array. */
|
||||
void CopyMetas(NIBBLETYPE * a_Dest) const;
|
||||
|
||||
/** Copies the block light data into the specified flat array. */
|
||||
void CopyBlockLight(NIBBLETYPE * a_Dest) const;
|
||||
|
||||
/** Copies the skylight data into the specified flat array. */
|
||||
void CopySkyLight (NIBBLETYPE * a_Dest) const;
|
||||
|
||||
/** Copies the blocktype data from the specified flat array into the internal representation.
|
||||
Allocates sections that are needed for the operation.
|
||||
Requires that a_Src is a valid pointer. */
|
||||
void SetBlockTypes(const BLOCKTYPE * a_Src);
|
||||
|
||||
/** Copies the metadata from the specified flat array into the internal representation.
|
||||
Allocates sectios that are needed for the operation.
|
||||
Requires that a_Src is a valid pointer. */
|
||||
void SetMetas(const NIBBLETYPE * a_Src);
|
||||
|
||||
/** Copies the blocklight data from the specified flat array into the internal representation.
|
||||
Allocates sectios that are needed for the operation.
|
||||
Allows a_Src to be NULL, in which case it doesn't do anything. */
|
||||
void SetBlockLight(const NIBBLETYPE * a_Src);
|
||||
|
||||
/** Copies the skylight data from the specified flat array into the internal representation.
|
||||
Allocates sectios that are needed for the operation.
|
||||
Allows a_Src to be NULL, in which case it doesn't do anything. */
|
||||
void SetSkyLight(const NIBBLETYPE * a_Src);
|
||||
|
||||
private:
|
||||
|
||||
static const size_t SectionHeight = 16;
|
||||
static const size_t NumSections = (cChunkDef::Height / SectionHeight);
|
||||
static const size_t SectionBlockCount = SectionHeight * cChunkDef::Width * cChunkDef::Width;
|
||||
|
||||
#if __cplusplus < 201103L
|
||||
// auto_ptr style interface for memory management
|
||||
mutable bool m_IsOwner;
|
||||
#endif
|
||||
|
||||
struct sChunkSection {
|
||||
BLOCKTYPE m_BlockTypes [SectionBlockCount];
|
||||
NIBBLETYPE m_BlockMetas [SectionBlockCount / 2];
|
||||
NIBBLETYPE m_BlockLight [SectionBlockCount / 2];
|
||||
NIBBLETYPE m_BlockSkyLight[SectionBlockCount / 2];
|
||||
};
|
||||
|
||||
sChunkSection * m_Sections[NumSections];
|
||||
|
||||
/** Allocates a new section. Entry-point to custom allocators. */
|
||||
static sChunkSection * Allocate(void);
|
||||
|
||||
/** Frees the specified section, previously allocated using Allocate().
|
||||
Note that a_Section may be NULL. */
|
||||
static void Free(sChunkSection * a_Section);
|
||||
|
||||
/** Sets the data in the specified section to their default values. */
|
||||
void ZeroSection(sChunkSection * a_Section) const;
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
127
src/ChunkDataCallback.h
Normal file
@ -0,0 +1,127 @@
|
||||
|
||||
// ChunkDataCallback.h
|
||||
|
||||
// Declares the cChunkDataCallback interface and several trivial descendants, for reading chunk data
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "ChunkData.h"
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/** Interface class used for getting data out of a chunk using the GetAllData() function.
|
||||
Implementation must use the pointers immediately and NOT store any of them for later use
|
||||
The virtual methods are called in the same order as they're declared here.
|
||||
*/
|
||||
class cChunkDataCallback abstract
|
||||
{
|
||||
public:
|
||||
|
||||
virtual ~cChunkDataCallback() {}
|
||||
|
||||
/** Called before any other callbacks to inform of the current coords
|
||||
(only in processes where multiple chunks can be processed, such as cWorld::ForEachChunkInRect()).
|
||||
If false is returned, the chunk is skipped.
|
||||
*/
|
||||
virtual bool Coords(int a_ChunkX, int a_ChunkZ) { UNUSED(a_ChunkX); UNUSED(a_ChunkZ); return true; };
|
||||
|
||||
/// Called once to provide heightmap data
|
||||
virtual void HeightMap(const cChunkDef::HeightMap * a_HeightMap) {UNUSED(a_HeightMap); };
|
||||
|
||||
/// Called once to provide biome data
|
||||
virtual void BiomeData(const cChunkDef::BiomeMap * a_BiomeMap) {UNUSED(a_BiomeMap); };
|
||||
|
||||
/// Called once to let know if the chunk lighting is valid. Return value is ignored
|
||||
virtual void LightIsValid(bool a_IsLightValid) {UNUSED(a_IsLightValid); };
|
||||
|
||||
/// Called once to export block info
|
||||
virtual void ChunkData(const cChunkData & a_Buffer) {UNUSED(a_Buffer); };
|
||||
|
||||
/// Called for each entity in the chunk
|
||||
virtual void Entity(cEntity * a_Entity) {UNUSED(a_Entity); };
|
||||
|
||||
/// Called for each blockentity in the chunk
|
||||
virtual void BlockEntity(cBlockEntity * a_Entity) {UNUSED(a_Entity); };
|
||||
} ;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/** A simple implementation of the cChunkDataCallback interface that collects all block data into a buffer
|
||||
*/
|
||||
class cChunkDataCollector :
|
||||
public cChunkDataCallback
|
||||
{
|
||||
public:
|
||||
|
||||
cChunkData m_BlockData;
|
||||
|
||||
protected:
|
||||
|
||||
virtual void ChunkData(const cChunkData & a_BlockData) override
|
||||
{
|
||||
m_BlockData = a_BlockData.Copy();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/** A simple implementation of the cChunkDataCallback interface that collects all block data into a single buffer
|
||||
*/
|
||||
class cChunkDataArrayCollector :
|
||||
public cChunkDataCallback
|
||||
{
|
||||
public:
|
||||
|
||||
// Must be unsigned char instead of BLOCKTYPE or NIBBLETYPE, because it houses both.
|
||||
unsigned char m_BlockData[cChunkDef::BlockDataSize];
|
||||
|
||||
protected:
|
||||
|
||||
virtual void ChunkData(const cChunkData & a_ChunkBuffer) override
|
||||
{
|
||||
a_ChunkBuffer.CopyBlockTypes(m_BlockData);
|
||||
a_ChunkBuffer.CopyMetas(m_BlockData + cChunkDef::NumBlocks);
|
||||
a_ChunkBuffer.CopyBlockLight(m_BlockData + 3 * cChunkDef::NumBlocks / 2);
|
||||
a_ChunkBuffer.CopySkyLight(m_BlockData + 2 * cChunkDef::NumBlocks);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/** A simple implementation of the cChunkDataCallback interface that collects all block data into separate buffers */
|
||||
class cChunkDataSeparateCollector :
|
||||
public cChunkDataCallback
|
||||
{
|
||||
public:
|
||||
|
||||
cChunkDef::BlockTypes m_BlockTypes;
|
||||
cChunkDef::BlockNibbles m_BlockMetas;
|
||||
cChunkDef::BlockNibbles m_BlockLight;
|
||||
cChunkDef::BlockNibbles m_BlockSkyLight;
|
||||
|
||||
protected:
|
||||
|
||||
virtual void ChunkData(const cChunkData & a_ChunkBuffer) override
|
||||
{
|
||||
a_ChunkBuffer.CopyBlockTypes(m_BlockTypes);
|
||||
a_ChunkBuffer.CopyMetas(m_BlockMetas);
|
||||
a_ChunkBuffer.CopyBlockLight(m_BlockLight);
|
||||
a_ChunkBuffer.CopySkyLight(m_BlockSkyLight);
|
||||
}
|
||||
} ;
|
||||
|
||||
|
||||
|
||||
|
130
src/ChunkDef.h
@ -330,136 +330,6 @@ private:
|
||||
|
||||
|
||||
|
||||
/** Interface class used for getting data out of a chunk using the GetAllData() function.
|
||||
Implementation must use the pointers immediately and NOT store any of them for later use
|
||||
The virtual methods are called in the same order as they're declared here.
|
||||
*/
|
||||
class cChunkDataCallback abstract
|
||||
{
|
||||
public:
|
||||
|
||||
virtual ~cChunkDataCallback() {}
|
||||
|
||||
/** Called before any other callbacks to inform of the current coords
|
||||
(only in processes where multiple chunks can be processed, such as cWorld::ForEachChunkInRect()).
|
||||
If false is returned, the chunk is skipped.
|
||||
*/
|
||||
virtual bool Coords(int a_ChunkX, int a_ChunkZ) { UNUSED(a_ChunkX); UNUSED(a_ChunkZ); return true; };
|
||||
|
||||
/// Called once to provide heightmap data
|
||||
virtual void HeightMap(const cChunkDef::HeightMap * a_HeightMap) {UNUSED(a_HeightMap); };
|
||||
|
||||
/// Called once to provide biome data
|
||||
virtual void BiomeData (const cChunkDef::BiomeMap * a_BiomeMap) {UNUSED(a_BiomeMap); };
|
||||
|
||||
/// Called once to export block types
|
||||
virtual void BlockTypes (const BLOCKTYPE * a_Type) {UNUSED(a_Type); };
|
||||
|
||||
/// Called once to export block meta
|
||||
virtual void BlockMeta (const NIBBLETYPE * a_Meta) {UNUSED(a_Meta); };
|
||||
|
||||
/// Called once to let know if the chunk lighting is valid. Return value is used to control if BlockLight() and BlockSkyLight() are called next (if true)
|
||||
virtual bool LightIsValid(bool a_IsLightValid) {UNUSED(a_IsLightValid); return true; };
|
||||
|
||||
/// Called once to export block light
|
||||
virtual void BlockLight (const NIBBLETYPE * a_BlockLight) {UNUSED(a_BlockLight); };
|
||||
|
||||
/// Called once to export sky light
|
||||
virtual void BlockSkyLight(const NIBBLETYPE * a_SkyLight) {UNUSED(a_SkyLight); };
|
||||
|
||||
/// Called for each entity in the chunk
|
||||
virtual void Entity(cEntity * a_Entity) {UNUSED(a_Entity); };
|
||||
|
||||
/// Called for each blockentity in the chunk
|
||||
virtual void BlockEntity(cBlockEntity * a_Entity) {UNUSED(a_Entity); };
|
||||
} ;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/** A simple implementation of the cChunkDataCallback interface that collects all block data into a single buffer
|
||||
*/
|
||||
class cChunkDataCollector :
|
||||
public cChunkDataCallback
|
||||
{
|
||||
public:
|
||||
|
||||
// Must be unsigned char instead of BLOCKTYPE or NIBBLETYPE, because it houses both.
|
||||
unsigned char m_BlockData[cChunkDef::BlockDataSize];
|
||||
|
||||
protected:
|
||||
|
||||
virtual void BlockTypes(const BLOCKTYPE * a_BlockTypes) override
|
||||
{
|
||||
memcpy(m_BlockData, a_BlockTypes, sizeof(cChunkDef::BlockTypes));
|
||||
}
|
||||
|
||||
|
||||
virtual void BlockMeta(const NIBBLETYPE * a_BlockMeta) override
|
||||
{
|
||||
memcpy(m_BlockData + cChunkDef::NumBlocks, a_BlockMeta, cChunkDef::NumBlocks / 2);
|
||||
}
|
||||
|
||||
|
||||
virtual void BlockLight(const NIBBLETYPE * a_BlockLight) override
|
||||
{
|
||||
memcpy(m_BlockData + 3 * cChunkDef::NumBlocks / 2, a_BlockLight, cChunkDef::NumBlocks / 2);
|
||||
}
|
||||
|
||||
|
||||
virtual void BlockSkyLight(const NIBBLETYPE * a_BlockSkyLight) override
|
||||
{
|
||||
memcpy(m_BlockData + 2 * cChunkDef::NumBlocks, a_BlockSkyLight, cChunkDef::NumBlocks / 2);
|
||||
}
|
||||
} ;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/** A simple implementation of the cChunkDataCallback interface that collects all block data into a separate buffers
|
||||
*/
|
||||
class cChunkDataSeparateCollector :
|
||||
public cChunkDataCallback
|
||||
{
|
||||
public:
|
||||
|
||||
cChunkDef::BlockTypes m_BlockTypes;
|
||||
cChunkDef::BlockNibbles m_BlockMetas;
|
||||
cChunkDef::BlockNibbles m_BlockLight;
|
||||
cChunkDef::BlockNibbles m_BlockSkyLight;
|
||||
|
||||
protected:
|
||||
|
||||
virtual void BlockTypes(const BLOCKTYPE * a_BlockTypes) override
|
||||
{
|
||||
memcpy(m_BlockTypes, a_BlockTypes, sizeof(m_BlockTypes));
|
||||
}
|
||||
|
||||
|
||||
virtual void BlockMeta(const NIBBLETYPE * a_BlockMeta) override
|
||||
{
|
||||
memcpy(m_BlockMetas, a_BlockMeta, sizeof(m_BlockMetas));
|
||||
}
|
||||
|
||||
|
||||
virtual void BlockLight(const NIBBLETYPE * a_BlockLight) override
|
||||
{
|
||||
memcpy(m_BlockLight, a_BlockLight, sizeof(m_BlockLight));
|
||||
}
|
||||
|
||||
|
||||
virtual void BlockSkyLight(const NIBBLETYPE * a_BlockSkyLight) override
|
||||
{
|
||||
memcpy(m_BlockSkyLight, a_BlockSkyLight, sizeof(m_BlockSkyLight));
|
||||
}
|
||||
} ;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/** Interface class used for comparing clients of two chunks.
|
||||
Used primarily for entity moving while both chunks are locked.
|
||||
*/
|
||||
|
@ -219,9 +219,8 @@ bool cChunkMap::LockedGetBlock(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTY
|
||||
return false;
|
||||
}
|
||||
|
||||
int Index = cChunkDef::MakeIndexNoCheck(a_BlockX, a_BlockY, a_BlockZ);
|
||||
a_BlockType = Chunk->GetBlock(Index);
|
||||
a_BlockMeta = Chunk->GetMeta(Index);
|
||||
a_BlockType = Chunk->GetBlock(a_BlockX, a_BlockY, a_BlockZ);
|
||||
a_BlockMeta = Chunk->GetMeta(a_BlockX, a_BlockY, a_BlockZ);
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -242,8 +241,7 @@ bool cChunkMap::LockedGetBlockType(int a_BlockX, int a_BlockY, int a_BlockZ, BLO
|
||||
return false;
|
||||
}
|
||||
|
||||
int Index = cChunkDef::MakeIndexNoCheck(a_BlockX, a_BlockY, a_BlockZ);
|
||||
a_BlockType = Chunk->GetBlock(Index);
|
||||
a_BlockType = Chunk->GetBlock(a_BlockX, a_BlockY, a_BlockZ);
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -264,8 +262,7 @@ bool cChunkMap::LockedGetBlockMeta(int a_BlockX, int a_BlockY, int a_BlockZ, NIB
|
||||
return false;
|
||||
}
|
||||
|
||||
int Index = cChunkDef::MakeIndexNoCheck(a_BlockX, a_BlockY, a_BlockZ);
|
||||
a_BlockMeta = Chunk->GetMeta(Index);
|
||||
a_BlockMeta = Chunk->GetMeta(a_BlockX, a_BlockY, a_BlockZ);
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -1255,7 +1252,7 @@ void cChunkMap::SetBlockMeta(int a_BlockX, int a_BlockY, int a_BlockZ, NIBBLETYP
|
||||
|
||||
|
||||
|
||||
void cChunkMap::SetBlock(cWorldInterface & a_WorldInterface, int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, BLOCKTYPE a_BlockMeta)
|
||||
void cChunkMap::SetBlock(cWorldInterface & a_WorldInterface, int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, BLOCKTYPE a_BlockMeta, bool a_SendToClients)
|
||||
{
|
||||
cChunkInterface ChunkInterface(this);
|
||||
if (a_BlockType == E_BLOCK_AIR)
|
||||
@ -1270,7 +1267,7 @@ void cChunkMap::SetBlock(cWorldInterface & a_WorldInterface, int a_BlockX, int a
|
||||
cChunkPtr Chunk = GetChunk( ChunkX, ZERO_CHUNK_Y, ChunkZ );
|
||||
if ((Chunk != NULL) && Chunk->IsValid())
|
||||
{
|
||||
Chunk->SetBlock(X, Y, Z, a_BlockType, a_BlockMeta );
|
||||
Chunk->SetBlock(X, Y, Z, a_BlockType, a_BlockMeta, a_SendToClients);
|
||||
m_World->GetSimulatorManager()->WakeUp(a_BlockX, a_BlockY, a_BlockZ, Chunk);
|
||||
}
|
||||
BlockHandler(a_BlockType)->OnPlaced(ChunkInterface, a_WorldInterface, a_BlockX, a_BlockY, a_BlockZ, a_BlockType, a_BlockMeta);
|
||||
@ -1484,9 +1481,8 @@ bool cChunkMap::GetBlocks(sSetBlockVector & a_Blocks, bool a_ContinueOnFailure)
|
||||
res = false;
|
||||
continue;
|
||||
}
|
||||
int idx = cChunkDef::MakeIndexNoCheck(itr->x, itr->y, itr->z);
|
||||
itr->BlockType = Chunk->GetBlock(idx);
|
||||
itr->BlockMeta = Chunk->GetMeta(idx);
|
||||
itr->BlockType = Chunk->GetBlock(itr->x, itr->y, itr->z);
|
||||
itr->BlockMeta = Chunk->GetMeta(itr->x, itr->y, itr->z);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
@ -5,7 +5,8 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "ChunkDef.h"
|
||||
|
||||
#include "ChunkDataCallback.h"
|
||||
|
||||
|
||||
|
||||
@ -152,7 +153,7 @@ public:
|
||||
NIBBLETYPE GetBlockSkyLight (int a_BlockX, int a_BlockY, int a_BlockZ);
|
||||
NIBBLETYPE GetBlockBlockLight(int a_BlockX, int a_BlockY, int a_BlockZ);
|
||||
void SetBlockMeta (int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockMeta);
|
||||
void SetBlock (cWorldInterface & a_WorldInterface, int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, BLOCKTYPE a_BlockMeta);
|
||||
void SetBlock (cWorldInterface & a_WorldInterface, int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, BLOCKTYPE a_BlockMeta, bool a_SendToClients = true);
|
||||
void QueueSetBlock(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, BLOCKTYPE a_BlockMeta, Int64 a_Tick, BLOCKTYPE a_PreviousBlockType = E_BLOCK_AIR);
|
||||
bool GetBlockTypeMeta (int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta);
|
||||
bool GetBlockInfo (int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE & a_BlockType, NIBBLETYPE & a_Meta, NIBBLETYPE & a_SkyLight, NIBBLETYPE & a_BlockLight);
|
||||
|
@ -27,6 +27,7 @@ Note that it may be called by world's BroadcastToChunk() if the client is still
|
||||
|
||||
#include "OSSupport/IsThread.h"
|
||||
#include "ChunkDef.h"
|
||||
#include "ChunkDataCallback.h"
|
||||
|
||||
|
||||
|
||||
|
@ -12,7 +12,6 @@
|
||||
#include "BlockEntities/SignEntity.h"
|
||||
#include "UI/Window.h"
|
||||
#include "Item.h"
|
||||
#include "Piston.h"
|
||||
#include "Mobs/Monster.h"
|
||||
#include "ChatColor.h"
|
||||
#include "OSSupport/Socket.h"
|
||||
@ -1306,7 +1305,7 @@ void cClientHandle::HandlePlaceBlock(int a_BlockX, int a_BlockY, int a_BlockZ, e
|
||||
if (!a_ItemHandler.GetPlacementBlockTypeMeta(World, m_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ, BlockType, BlockMeta))
|
||||
{
|
||||
// Handler refused the placement, send that information back to the client:
|
||||
World->SendBlockTo(a_BlockX, a_BlockY, a_BlockY, m_Player);
|
||||
World->SendBlockTo(a_BlockX, a_BlockY, a_BlockZ, m_Player);
|
||||
m_Player->GetInventory().SendEquippedSlot();
|
||||
return;
|
||||
}
|
||||
|
@ -55,9 +55,8 @@ void cFallingBlock::Tick(float a_Dt, cChunk & a_Chunk)
|
||||
return;
|
||||
}
|
||||
|
||||
int idx = a_Chunk.MakeIndexNoCheck(BlockX - a_Chunk.GetPosX() * cChunkDef::Width, BlockY, BlockZ - a_Chunk.GetPosZ() * cChunkDef::Width);
|
||||
BLOCKTYPE BlockBelow = a_Chunk.GetBlock(idx);
|
||||
NIBBLETYPE BelowMeta = a_Chunk.GetMeta(idx);
|
||||
BLOCKTYPE BlockBelow = a_Chunk.GetBlock(BlockX - a_Chunk.GetPosX() * cChunkDef::Width, BlockY, BlockZ - a_Chunk.GetPosZ() * cChunkDef::Width);
|
||||
NIBBLETYPE BelowMeta = a_Chunk.GetMeta(BlockX - a_Chunk.GetPosX() * cChunkDef::Width, BlockY, BlockZ - a_Chunk.GetPosZ() * cChunkDef::Width);
|
||||
if (cSandSimulator::DoesBreakFallingThrough(BlockBelow, BelowMeta))
|
||||
{
|
||||
// Fallen onto a block that breaks this into pickups (e. g. half-slab)
|
||||
|
@ -1669,13 +1669,6 @@ bool cPlayer::LoadFromDisk(cWorld * a_World)
|
||||
|
||||
LoadPermissionsFromDisk();
|
||||
|
||||
// Log player permissions, cause it's what the cool kids do
|
||||
LOGINFO("Player %s has permissions:", GetName().c_str() );
|
||||
for( PermissionMap::iterator itr = m_ResolvedPermissions.begin(); itr != m_ResolvedPermissions.end(); ++itr )
|
||||
{
|
||||
if( itr->second ) LOG(" - %s", itr->first.c_str() );
|
||||
}
|
||||
|
||||
AString SourceFile;
|
||||
Printf(SourceFile, "players/%s.json", GetName().c_str() );
|
||||
|
||||
|
@ -76,12 +76,12 @@ protected:
|
||||
eBlockFace Face;
|
||||
if (bb.CalcLineIntersection(Line1, Line2, LineCoeff, Face))
|
||||
{
|
||||
if (cPluginManager::Get()->CallHookProjectileHitBlock(*m_Projectile))
|
||||
Vector3d Intersection = Line1 + m_Projectile->GetSpeed() * LineCoeff;
|
||||
if (cPluginManager::Get()->CallHookProjectileHitBlock(*m_Projectile, a_BlockX, a_BlockY, a_BlockZ, Face, &Intersection))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
Vector3d Intersection = Line1 + m_Projectile->GetSpeed() * LineCoeff;
|
||||
m_Projectile->OnHitSolidBlock(Intersection, Face);
|
||||
return true;
|
||||
}
|
||||
|
@ -212,7 +212,7 @@ void cBioGenCache::InitializeBiomeGen(cIniFile & a_IniFile)
|
||||
|
||||
void cBiomeGenList::InitializeBiomes(const AString & a_Biomes)
|
||||
{
|
||||
AStringVector Split = StringSplit(a_Biomes, ",");
|
||||
AStringVector Split = StringSplitAndTrim(a_Biomes, ",");
|
||||
|
||||
// Convert each string in the list into biome:
|
||||
for (AStringVector::const_iterator itr = Split.begin(); itr != Split.end(); ++itr)
|
||||
|
@ -25,6 +25,8 @@
|
||||
#include "Noise3DGenerator.h"
|
||||
#include "POCPieceGenerator.h"
|
||||
#include "Ravines.h"
|
||||
#include "UnderwaterBaseGen.h"
|
||||
#include "VillageGen.h"
|
||||
|
||||
|
||||
|
||||
@ -32,6 +34,7 @@
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// cTerrainCompositionGen:
|
||||
|
||||
cTerrainCompositionGen * cTerrainCompositionGen::CreateCompositionGen(cIniFile & a_IniFile, cBiomeGen & a_BiomeGen, cTerrainHeightGen & a_HeightGen, int a_Seed)
|
||||
{
|
||||
AString CompoGenName = a_IniFile.GetValueSet("Generator", "CompositionGen", "");
|
||||
@ -390,6 +393,22 @@ void cComposableGenerator::InitFinishGens(cIniFile & a_IniFile)
|
||||
{
|
||||
m_FinishGens.push_back(new cStructGenTrees(Seed, m_BiomeGen, m_HeightGen, m_CompositionGen));
|
||||
}
|
||||
else if (NoCaseCompare(*itr, "UnderwaterBases") == 0)
|
||||
{
|
||||
int GridSize = a_IniFile.GetValueSetI("Generator", "UnderwaterBaseGridSize", 1024);
|
||||
int MaxDepth = a_IniFile.GetValueSetI("Generator", "UnderwaterBaseMaxDepth", 7);
|
||||
int MaxSize = a_IniFile.GetValueSetI("Generator", "UnderwaterBaseMaxSize", 128);
|
||||
m_FinishGens.push_back(new cUnderwaterBaseGen(Seed, GridSize, MaxDepth, MaxSize, *m_BiomeGen));
|
||||
}
|
||||
else if (NoCaseCompare(*itr, "Villages") == 0)
|
||||
{
|
||||
int GridSize = a_IniFile.GetValueSetI("Generator", "VillageGridSize", 384);
|
||||
int MaxDepth = a_IniFile.GetValueSetI("Generator", "VillageMaxDepth", 2);
|
||||
int MaxSize = a_IniFile.GetValueSetI("Generator", "VillageMaxSize", 128);
|
||||
int MinDensity = a_IniFile.GetValueSetI("Generator", "VillageMinDensity", 50);
|
||||
int MaxDensity = a_IniFile.GetValueSetI("Generator", "VillageMaxDensity", 80);
|
||||
m_FinishGens.push_back(new cVillageGen(Seed, GridSize, MaxDepth, MaxSize, MinDensity, MaxDensity, *m_BiomeGen, *m_HeightGen));
|
||||
}
|
||||
else if (NoCaseCompare(*itr, "WaterLakes") == 0)
|
||||
{
|
||||
int Probability = a_IniFile.GetValueSetI("Generator", "WaterLakesProbability", 25);
|
||||
|
@ -9,6 +9,34 @@
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// cEmptyStructure:
|
||||
|
||||
/** A cStructure descendant representing an empty structure.
|
||||
Used when the generator descended from cGridStructGen doesn't return any structure, to keep at least the
|
||||
Origin coords so that the structure isn't queried over and over again. */
|
||||
class cEmptyStructure :
|
||||
public cGridStructGen::cStructure
|
||||
{
|
||||
typedef cGridStructGen::cStructure super;
|
||||
|
||||
public:
|
||||
cEmptyStructure(int a_OriginX, int a_OriginZ) :
|
||||
super(a_OriginX, a_OriginZ)
|
||||
{
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual void DrawIntoChunk(cChunkDesc & a_ChunkDesc) override
|
||||
{
|
||||
// Do nothing
|
||||
}
|
||||
} ;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
cGridStructGen::cGridStructGen(
|
||||
int a_Seed,
|
||||
int a_GridSizeX, int a_GridSizeZ,
|
||||
@ -90,7 +118,12 @@ void cGridStructGen::GetStructuresForChunk(int a_ChunkX, int a_ChunkZ, cStructur
|
||||
} // for itr - a_Structures[]
|
||||
if (!Found)
|
||||
{
|
||||
a_Structures.push_back(CreateStructure(OriginX, OriginZ));
|
||||
cStructurePtr Structure = CreateStructure(OriginX, OriginZ);
|
||||
if (Structure.get() == NULL)
|
||||
{
|
||||
Structure.reset(new cEmptyStructure(OriginX, OriginZ));
|
||||
}
|
||||
a_Structures.push_back(Structure);
|
||||
}
|
||||
} // for z
|
||||
} // for x
|
||||
|
@ -39,14 +39,6 @@ class cGridStructGen :
|
||||
public cFinishGen
|
||||
{
|
||||
public:
|
||||
cGridStructGen(
|
||||
int a_Seed,
|
||||
int a_GridSizeX, int a_GridSizeZ,
|
||||
int a_MaxStructureSizeX, int a_MaxStructureSizeZ,
|
||||
size_t a_MaxCacheSize
|
||||
);
|
||||
|
||||
protected:
|
||||
/** Represents a single structure that occupies the grid point. Knows how to draw itself into a chunk. */
|
||||
class cStructure
|
||||
{
|
||||
@ -75,6 +67,14 @@ protected:
|
||||
typedef std::list<cStructurePtr> cStructurePtrs;
|
||||
|
||||
|
||||
cGridStructGen(
|
||||
int a_Seed,
|
||||
int a_GridSizeX, int a_GridSizeZ,
|
||||
int a_MaxStructureSizeX, int a_MaxStructureSizeZ,
|
||||
size_t a_MaxCacheSize
|
||||
);
|
||||
|
||||
protected:
|
||||
/** Seed for generating the semi-random grid. */
|
||||
int m_Seed;
|
||||
|
||||
|
@ -186,6 +186,11 @@ cPOCPieceGenerator::cPOCPieceGenerator(int a_Seed) :
|
||||
cPOCPieceGenerator::~cPOCPieceGenerator()
|
||||
{
|
||||
cPieceGenerator::FreePieces(m_Pieces);
|
||||
for (cPieces::iterator itr = m_AvailPieces.begin(), end = m_AvailPieces.end(); itr != end; ++itr)
|
||||
{
|
||||
delete *itr;
|
||||
}
|
||||
m_AvailPieces.clear();
|
||||
}
|
||||
|
||||
|
||||
|
@ -286,7 +286,8 @@ cPlacedPiece::cPlacedPiece(const cPlacedPiece * a_Parent, const cPiece & a_Piece
|
||||
m_Parent(a_Parent),
|
||||
m_Piece(&a_Piece),
|
||||
m_Coords(a_Coords),
|
||||
m_NumCCWRotations(a_NumCCWRotations)
|
||||
m_NumCCWRotations(a_NumCCWRotations),
|
||||
m_HasBeenMovedToGround(false)
|
||||
{
|
||||
m_Depth = (m_Parent == NULL) ? 0 : (m_Parent->GetDepth() + 1);
|
||||
m_HitBox = a_Piece.RotateMoveHitBox(a_NumCCWRotations, a_Coords.x, a_Coords.y, a_Coords.z);
|
||||
@ -297,6 +298,36 @@ cPlacedPiece::cPlacedPiece(const cPlacedPiece * a_Parent, const cPiece & a_Piece
|
||||
|
||||
|
||||
|
||||
cPiece::cConnector cPlacedPiece::GetRotatedConnector(size_t a_Index) const
|
||||
{
|
||||
cPiece::cConnectors Connectors = m_Piece->GetConnectors();
|
||||
ASSERT(Connectors.size() >= a_Index);
|
||||
return m_Piece->RotateMoveConnector(Connectors[a_Index], m_NumCCWRotations, m_Coords.x, m_Coords.y, m_Coords.z);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
cPiece::cConnector cPlacedPiece::GetRotatedConnector(const cPiece::cConnector & a_Connector) const
|
||||
{
|
||||
return m_Piece->RotateMoveConnector(a_Connector, m_NumCCWRotations, m_Coords.x, m_Coords.y, m_Coords.z);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cPlacedPiece::MoveToGroundBy(int a_OffsetY)
|
||||
{
|
||||
m_Coords.y += a_OffsetY;
|
||||
m_HasBeenMovedToGround = true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// cPieceGenerator:
|
||||
|
||||
@ -331,7 +362,31 @@ cPlacedPiece * cPieceGenerator::PlaceStartingPiece(int a_BlockX, int a_BlockY, i
|
||||
|
||||
// Choose a random one of the starting pieces:
|
||||
cPieces StartingPieces = m_PiecePool.GetStartingPieces();
|
||||
cPiece * StartingPiece = StartingPieces[rnd % StartingPieces.size()];
|
||||
int Total = 0;
|
||||
for (cPieces::const_iterator itr = StartingPieces.begin(), end = StartingPieces.end(); itr != end; ++itr)
|
||||
{
|
||||
Total += m_PiecePool.GetStartingPieceWeight(**itr);
|
||||
}
|
||||
cPiece * StartingPiece;
|
||||
if (Total > 0)
|
||||
{
|
||||
int Chosen = rnd % Total;
|
||||
StartingPiece = StartingPieces.front();
|
||||
for (cPieces::const_iterator itr = StartingPieces.begin(), end = StartingPieces.end(); itr != end; ++itr)
|
||||
{
|
||||
Chosen -= m_PiecePool.GetStartingPieceWeight(**itr);
|
||||
if (Chosen <= 0)
|
||||
{
|
||||
StartingPiece = *itr;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// All pieces returned zero weight, but we need one to start. Choose with equal chance:
|
||||
StartingPiece = StartingPieces[rnd % StartingPieces.size()];
|
||||
}
|
||||
rnd = rnd >> 16;
|
||||
|
||||
// Choose a random supported rotation:
|
||||
|
@ -110,6 +110,7 @@ public:
|
||||
virtual cPieces GetStartingPieces(void) = 0;
|
||||
|
||||
/** Returns the relative weight with which the a_NewPiece is to be selected for placing under a_PlacedPiece through a_ExistingConnector.
|
||||
a_ExistingConnector is the original connector, before any movement or rotation is applied to it.
|
||||
This allows the pool to tweak the piece's chances, based on the previous pieces in the tree and the connector used.
|
||||
The higher the number returned, the higher the chance the piece will be chosen. 0 means the piece will never be chosen.
|
||||
*/
|
||||
@ -119,6 +120,15 @@ public:
|
||||
const cPiece & a_NewPiece
|
||||
) { return 1; }
|
||||
|
||||
/** Returns the relative weight with which the a_NewPiece is to be selected for placing as the first piece.
|
||||
This allows the pool to tweak the piece's chances.
|
||||
The higher the number returned, the higher the chance the piece will be chosen. 0 means the piece will not be chosen.
|
||||
If all pieces return 0, a random piece is chosen, with all equal chances.
|
||||
*/
|
||||
virtual int GetStartingPieceWeight(
|
||||
const cPiece & a_NewPiece
|
||||
) { return 1; }
|
||||
|
||||
/** Called after a piece is placed, to notify the pool that it has been used.
|
||||
The pool may adjust the pieces it will return the next time. */
|
||||
virtual void PiecePlaced(const cPiece & a_Piece) = 0;
|
||||
@ -138,19 +148,41 @@ class cPlacedPiece
|
||||
public:
|
||||
cPlacedPiece(const cPlacedPiece * a_Parent, const cPiece & a_Piece, const Vector3i & a_Coords, int a_NumCCWRotations);
|
||||
|
||||
const cPiece & GetPiece (void) const { return *m_Piece; }
|
||||
const Vector3i & GetCoords (void) const { return m_Coords; }
|
||||
int GetNumCCWRotations(void) const { return m_NumCCWRotations; }
|
||||
const cCuboid & GetHitBox (void) const { return m_HitBox; }
|
||||
int GetDepth (void) const { return m_Depth; }
|
||||
const cPlacedPiece * GetParent (void) const { return m_Parent; }
|
||||
const cPiece & GetPiece (void) const { return *m_Piece; }
|
||||
const Vector3i & GetCoords (void) const { return m_Coords; }
|
||||
int GetNumCCWRotations (void) const { return m_NumCCWRotations; }
|
||||
const cCuboid & GetHitBox (void) const { return m_HitBox; }
|
||||
int GetDepth (void) const { return m_Depth; }
|
||||
bool HasBeenMovedToGround(void) const { return m_HasBeenMovedToGround; }
|
||||
|
||||
/** Returns the coords as a modifiable object. */
|
||||
Vector3i & GetCoords(void) { return m_Coords; }
|
||||
|
||||
/** Returns the connector at the specified index, rotated in the actual placement.
|
||||
Undefined behavior if a_Index is out of range. */
|
||||
cPiece::cConnector GetRotatedConnector(size_t a_Index) const;
|
||||
|
||||
/** Returns a copy of the specified connector, modified to account for the translation and rotation for
|
||||
this placement. */
|
||||
cPiece::cConnector GetRotatedConnector(const cPiece::cConnector & a_Connector) const;
|
||||
|
||||
/** Moves the placed piece Y-wise by the specified offset.
|
||||
Sets m_HasBeenMovedToGround to true, too.
|
||||
Used eg. by village houses. */
|
||||
void MoveToGroundBy(int a_OffsetY);
|
||||
|
||||
protected:
|
||||
const cPlacedPiece * m_Parent;
|
||||
const cPiece * m_Piece;
|
||||
Vector3i m_Coords;
|
||||
int m_NumCCWRotations;
|
||||
cCuboid m_HitBox;
|
||||
int m_Depth;
|
||||
cCuboid m_HitBox; // Hitbox of the placed piece, in world coords
|
||||
int m_Depth; // Depth in the generated piece tree
|
||||
|
||||
/** Set to true once the piece has been moved Y-wise.
|
||||
Used eg. by village houses. */
|
||||
bool m_HasBeenMovedToGround;
|
||||
};
|
||||
|
||||
typedef std::vector<cPlacedPiece *> cPlacedPieces;
|
||||
|
@ -108,6 +108,9 @@ static const cPrefab::sDef g_TestPrefabDef =
|
||||
|
||||
// AddWeightIfSame:
|
||||
1000,
|
||||
|
||||
// MoveToGround:
|
||||
false,
|
||||
};
|
||||
|
||||
static cPrefab g_TestPrefab(g_TestPrefabDef);
|
||||
@ -127,7 +130,8 @@ cPrefab::cPrefab(const cPrefab::sDef & a_Def) :
|
||||
m_MergeStrategy(a_Def.m_MergeStrategy),
|
||||
m_ShouldExtendFloor(a_Def.m_ShouldExtendFloor),
|
||||
m_DefaultWeight(a_Def.m_DefaultWeight),
|
||||
m_AddWeightIfSame(a_Def.m_AddWeightIfSame)
|
||||
m_AddWeightIfSame(a_Def.m_AddWeightIfSame),
|
||||
m_MoveToGround(a_Def.m_MoveToGround)
|
||||
{
|
||||
m_BlockArea[0].Create(m_Size);
|
||||
CharMap cm;
|
||||
@ -136,6 +140,34 @@ cPrefab::cPrefab(const cPrefab::sDef & a_Def) :
|
||||
ParseConnectors(a_Def.m_Connectors);
|
||||
ParseDepthWeight(a_Def.m_DepthWeight);
|
||||
|
||||
AddRotatedBlockAreas();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
cPrefab::cPrefab(const cBlockArea & a_Image, int a_AllowedRotations) :
|
||||
m_Size(a_Image.GetSize()),
|
||||
m_AllowedRotations(a_AllowedRotations),
|
||||
m_MergeStrategy(cBlockArea::msOverwrite),
|
||||
m_ShouldExtendFloor(false),
|
||||
m_DefaultWeight(1),
|
||||
m_AddWeightIfSame(0),
|
||||
m_MoveToGround(false)
|
||||
{
|
||||
m_HitBox.p1.Set(0, 0, 0);
|
||||
m_HitBox.p2.Set(m_Size.x - 1, m_Size.y - 1, m_Size.z - 1);
|
||||
m_BlockArea[0].CopyFrom(a_Image);
|
||||
AddRotatedBlockAreas();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cPrefab::AddRotatedBlockAreas(void)
|
||||
{
|
||||
// 1 CCW rotation:
|
||||
if ((m_AllowedRotations & 0x01) != 0)
|
||||
{
|
||||
@ -164,13 +196,21 @@ cPrefab::cPrefab(const cPrefab::sDef & a_Def) :
|
||||
|
||||
|
||||
void cPrefab::Draw(cChunkDesc & a_Dest, const cPlacedPiece * a_Placement) const
|
||||
{
|
||||
Draw(a_Dest, a_Placement->GetCoords(), a_Placement->GetNumCCWRotations());
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void cPrefab::Draw(cChunkDesc & a_Dest, const Vector3i & a_Placement, int a_NumRotations) const
|
||||
{
|
||||
// Draw the basic image:
|
||||
Vector3i Placement = a_Placement->GetCoords();
|
||||
Vector3i Placement(a_Placement);
|
||||
int ChunkStartX = a_Dest.GetChunkX() * cChunkDef::Width;
|
||||
int ChunkStartZ = a_Dest.GetChunkZ() * cChunkDef::Width;
|
||||
Placement.Move(-ChunkStartX, 0, -ChunkStartZ);
|
||||
const cBlockArea & Image = m_BlockArea[a_Placement->GetNumCCWRotations()];
|
||||
const cBlockArea & Image = m_BlockArea[a_NumRotations];
|
||||
a_Dest.WriteBlockArea(Image, Placement.x, Placement.y, Placement.z, m_MergeStrategy);
|
||||
|
||||
// If requested, draw the floor (from the bottom of the prefab down to the nearest non-air)
|
||||
@ -257,6 +297,24 @@ int cPrefab::GetPieceWeight(const cPlacedPiece & a_PlacedPiece, const cPiece::cC
|
||||
|
||||
|
||||
|
||||
void cPrefab::SetDefaultWeight(int a_DefaultWeight)
|
||||
{
|
||||
m_DefaultWeight = a_DefaultWeight;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cPrefab::AddConnector(int a_RelX, int a_RelY, int a_RelZ, eBlockFace a_Direction, int a_Type)
|
||||
{
|
||||
m_Connectors.push_back(cConnector(a_RelX, a_RelY, a_RelZ, a_Type, a_Direction));
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cPrefab::ParseCharMap(CharMap & a_CharMapOut, const char * a_CharMapDef)
|
||||
{
|
||||
ASSERT(a_CharMapDef != NULL);
|
||||
|
@ -82,19 +82,47 @@ public:
|
||||
Can be positive or negative.
|
||||
This is used e. g. to make nether bridges prefer spanning multiple segments or to penalize turrets next to each other. */
|
||||
int m_AddWeightIfSame;
|
||||
|
||||
/** If true, the piece will be moved Y-wise so that its first connector is sitting on the terrain.
|
||||
This is used e. g. for village houses. */
|
||||
bool m_MoveToGround;
|
||||
};
|
||||
|
||||
|
||||
/** Creates a prefab from the provided definition. */
|
||||
cPrefab(const sDef & a_Def);
|
||||
|
||||
/** Creates a prefab based on the given BlockArea and allowed rotations. */
|
||||
cPrefab(const cBlockArea & a_Image, int a_AllowedRotations);
|
||||
|
||||
/** Draws the prefab into the specified chunk, according to the placement stored in the PlacedPiece. */
|
||||
void Draw(cChunkDesc & a_Dest, const cPlacedPiece * a_Placement) const;
|
||||
|
||||
/** Draws the prefab into the specified chunks, according to the specified placement and rotations. */
|
||||
void Draw(cChunkDesc & a_Dest, const Vector3i & a_Placement, int a_NumRotations) const;
|
||||
|
||||
/** Returns true if the prefab has any connector of the specified type. */
|
||||
bool HasConnectorType(int a_ConnectorType) const;
|
||||
|
||||
/** Returns the weight (chance) of this prefab generating as the next piece after the specified placed piece.
|
||||
PiecePool implementations can use this for their GetPieceWeight() implementations. */
|
||||
int GetPieceWeight(const cPlacedPiece & a_PlacedPiece, const cPiece::cConnector & a_ExistingConnector) const;
|
||||
|
||||
/** Sets the (unmodified) DefaultWeight property for this piece. */
|
||||
void SetDefaultWeight(int a_DefaultWeight);
|
||||
|
||||
/** Returns the unmodified DefaultWeight property for the piece. */
|
||||
int GetDefaultWeight(void) const { return m_DefaultWeight; }
|
||||
|
||||
/** Sets the AddWeightIfSame member, that is used to modify the weight when the previous piece is the same prefab */
|
||||
void SetAddWeightIfSame(int a_AddWeightIfSame) { m_AddWeightIfSame = a_AddWeightIfSame; }
|
||||
|
||||
/** Adds the specified connector to the list of connectors this piece supports. */
|
||||
void AddConnector(int a_RelX, int a_RelY, int a_RelZ, eBlockFace a_Direction, int a_Type);
|
||||
|
||||
/** Returns whether the prefab should be moved Y-wise to ground before drawing, rather than staying
|
||||
at the coords governed by the connectors. */
|
||||
bool ShouldMoveToGround(void) const { return m_MoveToGround; }
|
||||
|
||||
protected:
|
||||
/** Packs complete definition of a single block, for per-letter assignment. */
|
||||
@ -149,6 +177,10 @@ protected:
|
||||
Can be positive or negative.
|
||||
This is used e. g. to make nether bridges prefer spanning multiple segments or to penalize turrets next to each other. */
|
||||
int m_AddWeightIfSame;
|
||||
|
||||
/** If true, the piece will be moved Y-wise so that its first connector is sitting on the terrain.
|
||||
This is used e. g. for village houses. */
|
||||
bool m_MoveToGround;
|
||||
|
||||
|
||||
// cPiece overrides:
|
||||
@ -157,6 +189,10 @@ protected:
|
||||
virtual cCuboid GetHitBox(void) const override;
|
||||
virtual bool CanRotateCCW(int a_NumRotations) const override;
|
||||
|
||||
/** Based on the m_AllowedRotations, adds rotated cBlockAreas to the m_BlockArea array.
|
||||
To be called only from this class's constructor! */
|
||||
void AddRotatedBlockAreas(void);
|
||||
|
||||
/** Parses the CharMap in the definition into a CharMap binary data used for translating the definition into BlockArea. */
|
||||
void ParseCharMap(CharMap & a_CharMapOut, const char * a_CharMapDef);
|
||||
|
||||
|
@ -129,6 +129,15 @@ int cPrefabPiecePool::GetPieceWeight(const cPlacedPiece & a_PlacedPiece, const c
|
||||
|
||||
|
||||
|
||||
int cPrefabPiecePool::GetStartingPieceWeight(const cPiece & a_NewPiece)
|
||||
{
|
||||
return ((const cPrefab &)a_NewPiece).GetDefaultWeight();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cPrefabPiecePool::PiecePlaced(const cPiece & a_Piece)
|
||||
{
|
||||
// Do nothing
|
||||
|
@ -75,6 +75,7 @@ protected:
|
||||
virtual cPieces GetPiecesWithConnector(int a_ConnectorType) override;
|
||||
virtual cPieces GetStartingPieces(void) override;
|
||||
virtual int GetPieceWeight(const cPlacedPiece & a_PlacedPiece, const cPiece::cConnector & a_ExistingConnector, const cPiece & a_NewPiece) override;
|
||||
virtual int GetStartingPieceWeight(const cPiece & a_NewPiece) override;
|
||||
virtual void PiecePlaced(const cPiece & a_Piece) override;
|
||||
virtual void Reset(void) override;
|
||||
} ;
|
||||
|
3184
src/Generating/Prefabs/AlchemistVillagePrefabs.cpp
Normal file
15
src/Generating/Prefabs/AlchemistVillagePrefabs.h
Normal file
@ -0,0 +1,15 @@
|
||||
|
||||
// AlchemistVillagePrefabs.h
|
||||
|
||||
// Declares the prefabs in the group AlchemistVillage
|
||||
|
||||
#include "../Prefab.h"
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
extern const cPrefab::sDef g_AlchemistVillagePrefabs[];
|
||||
extern const cPrefab::sDef g_AlchemistVillageStartingPrefabs[];
|
||||
extern const size_t g_AlchemistVillagePrefabsCount;
|
||||
extern const size_t g_AlchemistVillageStartingPrefabsCount;
|
3200
src/Generating/Prefabs/JapaneseVillagePrefabs.cpp
Normal file
15
src/Generating/Prefabs/JapaneseVillagePrefabs.h
Normal file
@ -0,0 +1,15 @@
|
||||
|
||||
// JapaneseVillagePrefabs.h
|
||||
|
||||
// Declares the prefabs in the group JapaneseVillage
|
||||
|
||||
#include "../Prefab.h"
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
extern const cPrefab::sDef g_JapaneseVillagePrefabs[];
|
||||
extern const cPrefab::sDef g_JapaneseVillageStartingPrefabs[];
|
||||
extern const size_t g_JapaneseVillagePrefabsCount;
|
||||
extern const size_t g_JapaneseVillageStartingPrefabsCount;
|
@ -155,6 +155,9 @@ const cPrefab::sDef g_NetherFortPrefabs[] =
|
||||
|
||||
// AddWeightIfSame:
|
||||
0,
|
||||
|
||||
// MoveToGround:
|
||||
false,
|
||||
}, // BalconyCorridor
|
||||
|
||||
|
||||
@ -315,6 +318,9 @@ const cPrefab::sDef g_NetherFortPrefabs[] =
|
||||
|
||||
// AddWeightIfSame:
|
||||
0,
|
||||
|
||||
// MoveToGround:
|
||||
false,
|
||||
}, // BalconyTee2
|
||||
|
||||
|
||||
@ -435,6 +441,9 @@ const cPrefab::sDef g_NetherFortPrefabs[] =
|
||||
|
||||
// AddWeightIfSame:
|
||||
0,
|
||||
|
||||
// MoveToGround:
|
||||
false,
|
||||
}, // BlazePlatform
|
||||
|
||||
|
||||
@ -605,6 +614,9 @@ const cPrefab::sDef g_NetherFortPrefabs[] =
|
||||
|
||||
// AddWeightIfSame:
|
||||
0,
|
||||
|
||||
// MoveToGround:
|
||||
false,
|
||||
}, // BlazePlatformOverhang
|
||||
|
||||
|
||||
@ -805,6 +817,9 @@ const cPrefab::sDef g_NetherFortPrefabs[] =
|
||||
|
||||
// AddWeightIfSame:
|
||||
-1000,
|
||||
|
||||
// MoveToGround:
|
||||
false,
|
||||
}, // BridgeCircleCrossing
|
||||
|
||||
|
||||
@ -1006,6 +1021,9 @@ const cPrefab::sDef g_NetherFortPrefabs[] =
|
||||
|
||||
// AddWeightIfSame:
|
||||
0,
|
||||
|
||||
// MoveToGround:
|
||||
false,
|
||||
}, // BridgeCrossing
|
||||
|
||||
|
||||
@ -1100,6 +1118,9 @@ const cPrefab::sDef g_NetherFortPrefabs[] =
|
||||
|
||||
// AddWeightIfSame:
|
||||
0,
|
||||
|
||||
// MoveToGround:
|
||||
false,
|
||||
}, // BridgeCrumble1
|
||||
|
||||
|
||||
@ -1200,6 +1221,9 @@ const cPrefab::sDef g_NetherFortPrefabs[] =
|
||||
|
||||
// AddWeightIfSame:
|
||||
0,
|
||||
|
||||
// MoveToGround:
|
||||
false,
|
||||
}, // BridgeCrumble2
|
||||
|
||||
|
||||
@ -1379,6 +1403,9 @@ const cPrefab::sDef g_NetherFortPrefabs[] =
|
||||
|
||||
// AddWeightIfSame:
|
||||
1000,
|
||||
|
||||
// MoveToGround:
|
||||
false,
|
||||
}, // BridgeDoubleCrumble
|
||||
|
||||
|
||||
@ -1619,6 +1646,9 @@ const cPrefab::sDef g_NetherFortPrefabs[] =
|
||||
|
||||
// AddWeightIfSame:
|
||||
0,
|
||||
|
||||
// MoveToGround:
|
||||
false,
|
||||
}, // BridgeFunnelDown
|
||||
|
||||
|
||||
@ -1948,6 +1978,9 @@ const cPrefab::sDef g_NetherFortPrefabs[] =
|
||||
|
||||
// AddWeightIfSame:
|
||||
0,
|
||||
|
||||
// MoveToGround:
|
||||
false,
|
||||
}, // BridgeLevelCrossing
|
||||
|
||||
|
||||
@ -2067,6 +2100,9 @@ const cPrefab::sDef g_NetherFortPrefabs[] =
|
||||
|
||||
// AddWeightIfSame:
|
||||
1000,
|
||||
|
||||
// MoveToGround:
|
||||
false,
|
||||
}, // BridgeSegment
|
||||
|
||||
|
||||
@ -2227,6 +2263,9 @@ const cPrefab::sDef g_NetherFortPrefabs[] =
|
||||
|
||||
// AddWeightIfSame:
|
||||
0,
|
||||
|
||||
// MoveToGround:
|
||||
false,
|
||||
}, // BridgeTee
|
||||
|
||||
|
||||
@ -2328,6 +2367,9 @@ const cPrefab::sDef g_NetherFortPrefabs[] =
|
||||
|
||||
// AddWeightIfSame:
|
||||
0,
|
||||
|
||||
// MoveToGround:
|
||||
false,
|
||||
}, // Corridor11
|
||||
|
||||
|
||||
@ -2429,6 +2471,9 @@ const cPrefab::sDef g_NetherFortPrefabs[] =
|
||||
|
||||
// AddWeightIfSame:
|
||||
0,
|
||||
|
||||
// MoveToGround:
|
||||
false,
|
||||
}, // Corridor13
|
||||
|
||||
|
||||
@ -2524,6 +2569,9 @@ const cPrefab::sDef g_NetherFortPrefabs[] =
|
||||
|
||||
// AddWeightIfSame:
|
||||
500,
|
||||
|
||||
// MoveToGround:
|
||||
false,
|
||||
}, // Corridor5
|
||||
|
||||
|
||||
@ -2663,6 +2711,9 @@ const cPrefab::sDef g_NetherFortPrefabs[] =
|
||||
|
||||
// AddWeightIfSame:
|
||||
0,
|
||||
|
||||
// MoveToGround:
|
||||
false,
|
||||
}, // CorridorCorner5
|
||||
|
||||
|
||||
@ -2803,6 +2854,9 @@ const cPrefab::sDef g_NetherFortPrefabs[] =
|
||||
|
||||
// AddWeightIfSame:
|
||||
0,
|
||||
|
||||
// MoveToGround:
|
||||
false,
|
||||
}, // CorridorCornerChest5
|
||||
|
||||
|
||||
@ -2928,6 +2982,9 @@ const cPrefab::sDef g_NetherFortPrefabs[] =
|
||||
|
||||
// AddWeightIfSame:
|
||||
-50,
|
||||
|
||||
// MoveToGround:
|
||||
false,
|
||||
}, // CorridorCrossing
|
||||
|
||||
|
||||
@ -3080,6 +3137,9 @@ const cPrefab::sDef g_NetherFortPrefabs[] =
|
||||
|
||||
// AddWeightIfSame:
|
||||
0,
|
||||
|
||||
// MoveToGround:
|
||||
false,
|
||||
}, // CorridorStairs
|
||||
|
||||
|
||||
@ -3181,6 +3241,9 @@ const cPrefab::sDef g_NetherFortPrefabs[] =
|
||||
|
||||
// AddWeightIfSame:
|
||||
0,
|
||||
|
||||
// MoveToGround:
|
||||
false,
|
||||
}, // DarkCorridor
|
||||
|
||||
|
||||
@ -3438,6 +3501,9 @@ const cPrefab::sDef g_NetherFortPrefabs[] =
|
||||
|
||||
// AddWeightIfSame:
|
||||
0,
|
||||
|
||||
// MoveToGround:
|
||||
false,
|
||||
}, // LavaStaircase
|
||||
|
||||
|
||||
@ -3769,6 +3835,9 @@ const cPrefab::sDef g_NetherFortPrefabs[] =
|
||||
|
||||
// AddWeightIfSame:
|
||||
-1000,
|
||||
|
||||
// MoveToGround:
|
||||
false,
|
||||
}, // LavaStaircaseBig
|
||||
|
||||
|
||||
@ -4047,6 +4116,9 @@ const cPrefab::sDef g_NetherFortPrefabs[] =
|
||||
|
||||
// AddWeightIfSame:
|
||||
0,
|
||||
|
||||
// MoveToGround:
|
||||
false,
|
||||
}, // LavaStairsBridge
|
||||
|
||||
|
||||
@ -4235,6 +4307,9 @@ const cPrefab::sDef g_NetherFortPrefabs[] =
|
||||
|
||||
// AddWeightIfSame:
|
||||
-1000,
|
||||
|
||||
// MoveToGround:
|
||||
false,
|
||||
}, // MidStaircase
|
||||
|
||||
|
||||
@ -4378,6 +4453,9 @@ const cPrefab::sDef g_NetherFortPrefabs[] =
|
||||
|
||||
// AddWeightIfSame:
|
||||
0,
|
||||
|
||||
// MoveToGround:
|
||||
false,
|
||||
}, // StairsToOpen1
|
||||
|
||||
|
||||
@ -4521,6 +4599,9 @@ const cPrefab::sDef g_NetherFortPrefabs[] =
|
||||
|
||||
// AddWeightIfSame:
|
||||
0,
|
||||
|
||||
// MoveToGround:
|
||||
false,
|
||||
}, // StairsToOpen2
|
||||
|
||||
|
||||
@ -4638,6 +4719,9 @@ const cPrefab::sDef g_NetherFortPrefabs[] =
|
||||
|
||||
// AddWeightIfSame:
|
||||
0,
|
||||
|
||||
// MoveToGround:
|
||||
false,
|
||||
}, // Tee2x4
|
||||
|
||||
|
||||
@ -4767,6 +4851,9 @@ const cPrefab::sDef g_NetherFortPrefabs[] =
|
||||
|
||||
// AddWeightIfSame:
|
||||
0,
|
||||
|
||||
// MoveToGround:
|
||||
false,
|
||||
}, // Tee4x4
|
||||
|
||||
|
||||
@ -4863,6 +4950,9 @@ const cPrefab::sDef g_NetherFortPrefabs[] =
|
||||
|
||||
// AddWeightIfSame:
|
||||
-50,
|
||||
|
||||
// MoveToGround:
|
||||
false,
|
||||
}, // TinyCorridorCorner
|
||||
|
||||
|
||||
@ -4960,6 +5050,9 @@ const cPrefab::sDef g_NetherFortPrefabs[] =
|
||||
|
||||
// AddWeightIfSame:
|
||||
0,
|
||||
|
||||
// MoveToGround:
|
||||
false,
|
||||
}, // TinyCorridorCornerChest
|
||||
|
||||
|
||||
@ -5059,6 +5152,9 @@ const cPrefab::sDef g_NetherFortPrefabs[] =
|
||||
|
||||
// AddWeightIfSame:
|
||||
-50,
|
||||
|
||||
// MoveToGround:
|
||||
false,
|
||||
}, // TinyCorridorCrossing
|
||||
|
||||
|
||||
@ -5174,6 +5270,9 @@ const cPrefab::sDef g_NetherFortPrefabs[] =
|
||||
|
||||
// AddWeightIfSame:
|
||||
-99,
|
||||
|
||||
// MoveToGround:
|
||||
false,
|
||||
}, // Turret
|
||||
}; // g_NetherFortPrefabs
|
||||
|
||||
@ -5378,6 +5477,9 @@ const cPrefab::sDef g_NetherFortStartingPrefabs[] =
|
||||
|
||||
// AddWeightIfSame:
|
||||
0,
|
||||
|
||||
// MoveToGround:
|
||||
false,
|
||||
}, // CentralRoom
|
||||
};
|
||||
|
||||
|
6114
src/Generating/Prefabs/PlainsVillagePrefabs.cpp
Normal file
15
src/Generating/Prefabs/PlainsVillagePrefabs.h
Normal file
@ -0,0 +1,15 @@
|
||||
|
||||
// PlainsVillagePrefabs.h
|
||||
|
||||
// Declares the prefabs in the group PlainsVillage
|
||||
|
||||
#include "../Prefab.h"
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
extern const cPrefab::sDef g_PlainsVillagePrefabs[];
|
||||
extern const cPrefab::sDef g_PlainsVillageStartingPrefabs[];
|
||||
extern const size_t g_PlainsVillagePrefabsCount;
|
||||
extern const size_t g_PlainsVillageStartingPrefabsCount;
|
1558
src/Generating/Prefabs/SandFlatRoofVillagePrefabs.cpp
Normal file
15
src/Generating/Prefabs/SandFlatRoofVillagePrefabs.h
Normal file
@ -0,0 +1,15 @@
|
||||
|
||||
// SandFlatRoofVillagePrefabs.h
|
||||
|
||||
// Declares the prefabs in the group SandFlatRoofVillage
|
||||
|
||||
#include "../Prefab.h"
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
extern const cPrefab::sDef g_SandFlatRoofVillagePrefabs[];
|
||||
extern const cPrefab::sDef g_SandFlatRoofVillageStartingPrefabs[];
|
||||
extern const size_t g_SandFlatRoofVillagePrefabsCount;
|
||||
extern const size_t g_SandFlatRoofVillageStartingPrefabsCount;
|
2133
src/Generating/Prefabs/SandVillagePrefabs.cpp
Normal file
15
src/Generating/Prefabs/SandVillagePrefabs.h
Normal file
@ -0,0 +1,15 @@
|
||||
|
||||
// SandVillagePrefabs.h
|
||||
|
||||
// Declares the prefabs in the group SandVillage
|
||||
|
||||
#include "../Prefab.h"
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
extern const cPrefab::sDef g_SandVillagePrefabs[];
|
||||
extern const cPrefab::sDef g_SandVillageStartingPrefabs[];
|
||||
extern const size_t g_SandVillagePrefabsCount;
|
||||
extern const size_t g_SandVillageStartingPrefabsCount;
|
2274
src/Generating/Prefabs/UnderwaterBasePrefabs.cpp
Normal file
15
src/Generating/Prefabs/UnderwaterBasePrefabs.h
Normal file
@ -0,0 +1,15 @@
|
||||
|
||||
// UnderwaterBasePrefabs.h
|
||||
|
||||
// Declares the prefabs in the group UnderwaterBase
|
||||
|
||||
#include "../Prefab.h"
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
extern const cPrefab::sDef g_UnderwaterBasePrefabs[];
|
||||
extern const cPrefab::sDef g_UnderwaterBaseStartingPrefabs[];
|
||||
extern const size_t g_UnderwaterBasePrefabsCount;
|
||||
extern const size_t g_UnderwaterBaseStartingPrefabsCount;
|
142
src/Generating/UnderwaterBaseGen.cpp
Normal file
@ -0,0 +1,142 @@
|
||||
|
||||
// UnderwaterBaseGen.cpp
|
||||
|
||||
// Implements the cUnderwaterBaseGen class representing the underwater base generator
|
||||
|
||||
#include "Globals.h"
|
||||
#include "UnderwaterBaseGen.h"
|
||||
#include "Prefabs/UnderwaterBasePrefabs.h"
|
||||
#include "PieceGenerator.h"
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
static cPrefabPiecePool g_UnderwaterBase(g_UnderwaterBasePrefabs, g_UnderwaterBasePrefabsCount, g_UnderwaterBaseStartingPrefabs, g_UnderwaterBaseStartingPrefabsCount);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// cUnderwaterBaseGen::cUnderwaterBase:
|
||||
|
||||
class cUnderwaterBaseGen::cUnderwaterBase :
|
||||
public cGridStructGen::cStructure
|
||||
{
|
||||
typedef cGridStructGen::cStructure super;
|
||||
|
||||
public:
|
||||
cUnderwaterBase(
|
||||
int a_Seed,
|
||||
int a_OriginX, int a_OriginZ,
|
||||
int a_MaxDepth,
|
||||
int a_MaxSize
|
||||
) :
|
||||
super(a_OriginX, a_OriginZ),
|
||||
m_Seed(a_Seed),
|
||||
m_Noise(a_Seed),
|
||||
m_MaxSize(a_MaxSize),
|
||||
m_Borders(a_OriginX - a_MaxSize, 0, a_OriginZ - a_MaxSize, a_OriginX + a_MaxSize, 255, a_OriginZ + a_MaxSize)
|
||||
{
|
||||
// Generate the pieces for this base:
|
||||
cBFSPieceGenerator pg(g_UnderwaterBase, a_Seed);
|
||||
pg.PlacePieces(a_OriginX, 50, a_OriginZ, a_MaxDepth, m_Pieces);
|
||||
if (m_Pieces.empty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
~cUnderwaterBase()
|
||||
{
|
||||
cPieceGenerator::FreePieces(m_Pieces);
|
||||
}
|
||||
|
||||
protected:
|
||||
/** Seed for the random functions */
|
||||
int m_Seed;
|
||||
|
||||
/** The noise used as a pseudo-random generator */
|
||||
cNoise m_Noise;
|
||||
|
||||
/** Maximum size, in X/Z blocks, of the village (radius from the origin) */
|
||||
int m_MaxSize;
|
||||
|
||||
/** Borders of the vilalge - no item may reach out of this cuboid. */
|
||||
cCuboid m_Borders;
|
||||
|
||||
/** The village pieces, placed by the generator. */
|
||||
cPlacedPieces m_Pieces;
|
||||
|
||||
|
||||
// cGridStructGen::cStructure overrides:
|
||||
virtual void DrawIntoChunk(cChunkDesc & a_Chunk) override
|
||||
{
|
||||
for (cPlacedPieces::iterator itr = m_Pieces.begin(), end = m_Pieces.end(); itr != end; ++itr)
|
||||
{
|
||||
cPrefab & Prefab = (cPrefab &)((*itr)->GetPiece());
|
||||
Prefab.Draw(a_Chunk, *itr);
|
||||
} // for itr - m_PlacedPieces[]
|
||||
}
|
||||
} ;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// cUnderwaterBaseGen:
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
cUnderwaterBaseGen::cUnderwaterBaseGen(int a_Seed, int a_GridSize, int a_MaxDepth, int a_MaxSize, cBiomeGen & a_BiomeGen) :
|
||||
super(a_Seed, a_GridSize, a_GridSize, a_MaxSize, a_MaxSize, 100),
|
||||
m_Noise(a_Seed + 1000),
|
||||
m_MaxDepth(a_MaxDepth),
|
||||
m_MaxSize(a_MaxSize),
|
||||
m_BiomeGen(a_BiomeGen)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
cGridStructGen::cStructurePtr cUnderwaterBaseGen::CreateStructure(int a_OriginX, int a_OriginZ)
|
||||
{
|
||||
// Generate the biomes for the chunk surrounding the origin:
|
||||
int ChunkX, ChunkZ;
|
||||
cChunkDef::BlockToChunk(a_OriginX, a_OriginZ, ChunkX, ChunkZ);
|
||||
cChunkDef::BiomeMap Biomes;
|
||||
m_BiomeGen.GenBiomes(ChunkX, ChunkZ, Biomes);
|
||||
|
||||
// Check if all the biomes are ocean:
|
||||
// If just one is not, no base is created, because it's likely that an unfriendly biome is too close
|
||||
for (size_t i = 0; i < ARRAYCOUNT(Biomes); i++)
|
||||
{
|
||||
switch (Biomes[i])
|
||||
{
|
||||
case biOcean:
|
||||
case biDeepOcean:
|
||||
{
|
||||
// These biomes allow underwater bases
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
// base-unfriendly biome, bail out with zero structure:
|
||||
return cStructurePtr();
|
||||
}
|
||||
} // switch (Biomes[i])
|
||||
} // for i - Biomes[]
|
||||
|
||||
// Create a base based on the chosen prefabs:
|
||||
return cStructurePtr(new cUnderwaterBase(m_Seed, a_OriginX, a_OriginZ, m_MaxDepth, m_MaxSize));
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
50
src/Generating/UnderwaterBaseGen.h
Normal file
@ -0,0 +1,50 @@
|
||||
|
||||
// UnderwaterBaseGen.h
|
||||
|
||||
// Declares the cUnderwaterBaseGen class representing the underwater base generator
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "GridStructGen.h"
|
||||
#include "PrefabPiecePool.h"
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
class cUnderwaterBaseGen :
|
||||
public cGridStructGen
|
||||
{
|
||||
typedef cGridStructGen super;
|
||||
|
||||
public:
|
||||
cUnderwaterBaseGen(int a_Seed, int a_GridSize, int a_MaxDepth, int a_MaxSize, cBiomeGen & a_BiomeGen);
|
||||
|
||||
protected:
|
||||
class cUnderwaterBase; // fwd: UnderwaterBaseGen.cpp
|
||||
|
||||
|
||||
/** The noise used for generating random numbers */
|
||||
cNoise m_Noise;
|
||||
|
||||
/** Maximum depth of the generator tree*/
|
||||
int m_MaxDepth;
|
||||
|
||||
/** Maximum size, in X/Z blocks, of the base (radius from the origin) */
|
||||
int m_MaxSize;
|
||||
|
||||
/** The underlying biome generator that defines whether the base is created or not */
|
||||
cBiomeGen & m_BiomeGen;
|
||||
|
||||
|
||||
// cGridStructGen overrides:
|
||||
virtual cStructurePtr CreateStructure(int a_OriginX, int a_OriginZ) override;
|
||||
} ;
|
||||
|
||||
|
||||
|
||||
|
430
src/Generating/VillageGen.cpp
Normal file
@ -0,0 +1,430 @@
|
||||
|
||||
// VillageGen.cpp
|
||||
|
||||
// Implements the cVillageGen class representing the village generator
|
||||
|
||||
#include "Globals.h"
|
||||
#include "VillageGen.h"
|
||||
#include "Prefabs/AlchemistVillagePrefabs.h"
|
||||
#include "Prefabs/JapaneseVillagePrefabs.h"
|
||||
#include "Prefabs/PlainsVillagePrefabs.h"
|
||||
#include "Prefabs/SandVillagePrefabs.h"
|
||||
#include "Prefabs/SandFlatRoofVillagePrefabs.h"
|
||||
#include "PieceGenerator.h"
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
How village generating works:
|
||||
By descending from a cGridStructGen, a semi-random grid is generated. A village may be generated for each of
|
||||
the grid's cells. Each cell checks the biomes in an entire chunk around it, only generating a village if all
|
||||
biomes are village-friendly. If yes, the entire village structure is built for that cell. If not, the cell
|
||||
is left village-less.
|
||||
|
||||
A village is generated using the regular BFS piece generator. The well piece is used as the starting piece,
|
||||
the roads and houses are then used as the following pieces. Only the houses are read from the prefabs,
|
||||
though, the roads are generated by code and their content is ignored. A special subclass of the cPiecePool
|
||||
class is used, so that the roads connect to each other and to the well only in predefined manners.
|
||||
|
||||
The well has connectors of type "2". The houses have connectors of type "-1". The roads have connectors of
|
||||
both types' opposites, type "-2" at the far ends and type "1" on the long edges. Additionally, there are
|
||||
type "2" connectors along the long edges of the roads as well, so that the roads create T junctions.
|
||||
|
||||
When the village is about to be drawn into a chunk, it queries the heights for each piece intersecting the
|
||||
chunk. The pieces are shifted so that their pivot points lie on the surface, and the roads are drawn
|
||||
directly by turning the surface blocks into gravel / sandstone.
|
||||
|
||||
The village prefabs are stored in global piecepools (one pool per village type). In order to support
|
||||
per-village density setting, the cVillage class itself implements the cPiecePool interface, relaying the
|
||||
calls to the underlying cVillagePiecePool, after processing the density check.
|
||||
*/
|
||||
|
||||
class cVillagePiecePool :
|
||||
public cPrefabPiecePool
|
||||
{
|
||||
typedef cPrefabPiecePool super;
|
||||
public:
|
||||
cVillagePiecePool(
|
||||
const cPrefab::sDef * a_PieceDefs, size_t a_NumPieceDefs,
|
||||
const cPrefab::sDef * a_StartingPieceDefs, size_t a_NumStartingPieceDefs
|
||||
) :
|
||||
super(a_PieceDefs, a_NumPieceDefs, a_StartingPieceDefs, a_NumStartingPieceDefs)
|
||||
{
|
||||
// Add the road pieces:
|
||||
for (int len = 27; len < 60; len += 12)
|
||||
{
|
||||
cBlockArea BA;
|
||||
BA.Create(len, 1, 3, cBlockArea::baTypes | cBlockArea::baMetas);
|
||||
BA.Fill(cBlockArea::baTypes | cBlockArea::baMetas, E_BLOCK_GRAVEL, 0);
|
||||
cPrefab * RoadPiece = new cPrefab(BA, 1);
|
||||
RoadPiece->AddConnector(0, 0, 1, BLOCK_FACE_XM, -2);
|
||||
RoadPiece->AddConnector(len - 1, 0, 1, BLOCK_FACE_XP, -2);
|
||||
RoadPiece->SetDefaultWeight(100);
|
||||
|
||||
// Add the road connectors:
|
||||
for (int x = 1; x < len; x += 12)
|
||||
{
|
||||
RoadPiece->AddConnector(x, 0, 0, BLOCK_FACE_ZM, 2);
|
||||
RoadPiece->AddConnector(x, 0, 2, BLOCK_FACE_ZP, 2);
|
||||
}
|
||||
|
||||
// Add the buildings connectors:
|
||||
for (int x = 7; x < len; x += 12)
|
||||
{
|
||||
RoadPiece->AddConnector(x, 0, 0, BLOCK_FACE_ZM, 1);
|
||||
RoadPiece->AddConnector(x, 0, 2, BLOCK_FACE_ZP, 1);
|
||||
}
|
||||
m_AllPieces.push_back(RoadPiece);
|
||||
m_PiecesByConnector[-2].push_back(RoadPiece);
|
||||
m_PiecesByConnector[1].push_back(RoadPiece);
|
||||
m_PiecesByConnector[2].push_back(RoadPiece);
|
||||
} // for len - roads of varying length
|
||||
}
|
||||
|
||||
|
||||
// cPrefabPiecePool overrides:
|
||||
virtual int GetPieceWeight(const cPlacedPiece & a_PlacedPiece, const cPiece::cConnector & a_ExistingConnector, const cPiece & a_NewPiece) override
|
||||
{
|
||||
// Roads cannot branch T-wise (appending -2 connector to a +2 connector on a 1-high piece):
|
||||
if ((a_ExistingConnector.m_Type == 2) && (a_PlacedPiece.GetDepth() > 0) && (a_PlacedPiece.GetPiece().GetSize().y == 1))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
return ((const cPrefab &)a_NewPiece).GetPieceWeight(a_PlacedPiece, a_ExistingConnector);
|
||||
}
|
||||
} ;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
class cVillageGen::cVillage :
|
||||
public cGridStructGen::cStructure,
|
||||
protected cPiecePool
|
||||
{
|
||||
typedef cGridStructGen::cStructure super;
|
||||
|
||||
public:
|
||||
cVillage(
|
||||
int a_Seed,
|
||||
int a_OriginX, int a_OriginZ,
|
||||
int a_MaxRoadDepth,
|
||||
int a_MaxSize,
|
||||
int a_Density,
|
||||
cPiecePool & a_Prefabs,
|
||||
cTerrainHeightGen & a_HeightGen,
|
||||
BLOCKTYPE a_RoadBlock
|
||||
) :
|
||||
super(a_OriginX, a_OriginZ),
|
||||
m_Seed(a_Seed),
|
||||
m_Noise(a_Seed),
|
||||
m_MaxSize(a_MaxSize),
|
||||
m_Density(a_Density),
|
||||
m_Borders(a_OriginX - a_MaxSize, 0, a_OriginZ - a_MaxSize, a_OriginX + a_MaxSize, 255, a_OriginZ + a_MaxSize),
|
||||
m_Prefabs(a_Prefabs),
|
||||
m_HeightGen(a_HeightGen),
|
||||
m_RoadBlock(a_RoadBlock)
|
||||
{
|
||||
// Generate the pieces for this village; don't care about the Y coord:
|
||||
cBFSPieceGenerator pg(*this, a_Seed);
|
||||
pg.PlacePieces(a_OriginX, 0, a_OriginZ, a_MaxRoadDepth + 1, m_Pieces);
|
||||
if (m_Pieces.empty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// If the central piece should be moved to ground, move it, and
|
||||
// check all of its dependents and move those that are strictly connector-driven based on its new Y coord:
|
||||
if (((cPrefab &)m_Pieces[0]->GetPiece()).ShouldMoveToGround())
|
||||
{
|
||||
int OrigPosY = m_Pieces[0]->GetCoords().y;
|
||||
PlacePieceOnGround(*m_Pieces[0]);
|
||||
int NewPosY = m_Pieces[0]->GetCoords().y;
|
||||
MoveAllDescendants(m_Pieces, 0, NewPosY - OrigPosY);
|
||||
}
|
||||
}
|
||||
|
||||
~cVillage()
|
||||
{
|
||||
cPieceGenerator::FreePieces(m_Pieces);
|
||||
}
|
||||
|
||||
protected:
|
||||
/** Seed for the random functions */
|
||||
int m_Seed;
|
||||
|
||||
/** The noise used as a pseudo-random generator */
|
||||
cNoise m_Noise;
|
||||
|
||||
/** Maximum size, in X/Z blocks, of the village (radius from the origin) */
|
||||
int m_MaxSize;
|
||||
|
||||
/** The density for this village. Used to refrain from populating all house connectors. Range [0, 100] */
|
||||
int m_Density;
|
||||
|
||||
/** Borders of the vilalge - no item may reach out of this cuboid. */
|
||||
cCuboid m_Borders;
|
||||
|
||||
/** Prefabs to use for buildings */
|
||||
cPiecePool & m_Prefabs;
|
||||
|
||||
/** The underlying height generator, used for placing the structures on top of the terrain. */
|
||||
cTerrainHeightGen & m_HeightGen;
|
||||
|
||||
/** The village pieces, placed by the generator. */
|
||||
cPlacedPieces m_Pieces;
|
||||
|
||||
/** The block to use for the roads. */
|
||||
BLOCKTYPE m_RoadBlock;
|
||||
|
||||
|
||||
// cGridStructGen::cStructure overrides:
|
||||
virtual void DrawIntoChunk(cChunkDesc & a_Chunk) override
|
||||
{
|
||||
// Iterate over all items
|
||||
// Each intersecting prefab is placed on ground, then drawn
|
||||
// Each intersecting road is drawn by replacing top soil blocks with gravel / sandstone blocks
|
||||
cChunkDef::HeightMap HeightMap; // Heightmap for this chunk, used by roads
|
||||
m_HeightGen.GenHeightMap(a_Chunk.GetChunkX(), a_Chunk.GetChunkZ(), HeightMap);
|
||||
for (cPlacedPieces::iterator itr = m_Pieces.begin(), end = m_Pieces.end(); itr != end; ++itr)
|
||||
{
|
||||
cPrefab & Prefab = (cPrefab &)((*itr)->GetPiece());
|
||||
if ((*itr)->GetPiece().GetSize().y == 1)
|
||||
{
|
||||
// It's a road, special handling (change top terrain blocks to m_RoadBlock)
|
||||
DrawRoad(a_Chunk, **itr, HeightMap);
|
||||
continue;
|
||||
}
|
||||
if (Prefab.ShouldMoveToGround() && !(*itr)->HasBeenMovedToGround())
|
||||
{
|
||||
PlacePieceOnGround(**itr);
|
||||
}
|
||||
Prefab.Draw(a_Chunk, *itr);
|
||||
} // for itr - m_PlacedPieces[]
|
||||
}
|
||||
|
||||
|
||||
/** Adjusts the Y coord of the given piece so that the piece is on the ground.
|
||||
Ground level is assumed to be represented by the first connector in the piece. */
|
||||
void PlacePieceOnGround(cPlacedPiece & a_Piece)
|
||||
{
|
||||
cPiece::cConnector FirstConnector = a_Piece.GetRotatedConnector(0);
|
||||
int ChunkX, ChunkZ;
|
||||
int BlockX = FirstConnector.m_Pos.x;
|
||||
int BlockZ = FirstConnector.m_Pos.z;
|
||||
int BlockY;
|
||||
cChunkDef::AbsoluteToRelative(BlockX, BlockY, BlockZ, ChunkX, ChunkZ);
|
||||
cChunkDef::HeightMap HeightMap;
|
||||
m_HeightGen.GenHeightMap(ChunkX, ChunkZ, HeightMap);
|
||||
int TerrainHeight = cChunkDef::GetHeight(HeightMap, BlockX, BlockZ);
|
||||
a_Piece.MoveToGroundBy(TerrainHeight - FirstConnector.m_Pos.y + 1);
|
||||
}
|
||||
|
||||
|
||||
/** Draws the road into the chunk.
|
||||
The heightmap is not queried from the heightgen, but is given via parameter, so that it may be queried just
|
||||
once for all roads in a chunk. */
|
||||
void DrawRoad(cChunkDesc & a_Chunk, cPlacedPiece & a_Road, cChunkDef::HeightMap & a_HeightMap)
|
||||
{
|
||||
cCuboid RoadCoords = a_Road.GetHitBox();
|
||||
RoadCoords.Sort();
|
||||
int MinX = std::max(RoadCoords.p1.x - a_Chunk.GetChunkX() * cChunkDef::Width, 0);
|
||||
int MaxX = std::min(RoadCoords.p2.x - a_Chunk.GetChunkX() * cChunkDef::Width, cChunkDef::Width - 1);
|
||||
int MinZ = std::max(RoadCoords.p1.z - a_Chunk.GetChunkZ() * cChunkDef::Width, 0);
|
||||
int MaxZ = std::min(RoadCoords.p2.z - a_Chunk.GetChunkZ() * cChunkDef::Width, cChunkDef::Width - 1);
|
||||
for (int z = MinZ; z <= MaxZ; z++)
|
||||
{
|
||||
for (int x = MinX; x <= MaxX; x++)
|
||||
{
|
||||
a_Chunk.SetBlockType(x, cChunkDef::GetHeight(a_HeightMap, x, z), z, m_RoadBlock);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// cPiecePool overrides:
|
||||
virtual cPieces GetPiecesWithConnector(int a_ConnectorType)
|
||||
{
|
||||
return m_Prefabs.GetPiecesWithConnector(a_ConnectorType);
|
||||
}
|
||||
|
||||
|
||||
virtual cPieces GetStartingPieces(void)
|
||||
{
|
||||
return m_Prefabs.GetStartingPieces();
|
||||
}
|
||||
|
||||
|
||||
virtual int GetPieceWeight(
|
||||
const cPlacedPiece & a_PlacedPiece,
|
||||
const cPiece::cConnector & a_ExistingConnector,
|
||||
const cPiece & a_NewPiece
|
||||
) override
|
||||
{
|
||||
// Check against the density:
|
||||
if (a_ExistingConnector.m_Type == 1)
|
||||
{
|
||||
const Vector3i & Coords = a_PlacedPiece.GetRotatedConnector(a_ExistingConnector).m_Pos;
|
||||
int rnd = (m_Noise.IntNoise3DInt(Coords.x, Coords.y, Coords.z) / 7) % 100;
|
||||
if (rnd > m_Density)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Density check passed, relay to m_Prefabs:
|
||||
return m_Prefabs.GetPieceWeight(a_PlacedPiece, a_ExistingConnector, a_NewPiece);
|
||||
}
|
||||
|
||||
|
||||
virtual int GetStartingPieceWeight(const cPiece & a_NewPiece) override
|
||||
{
|
||||
return m_Prefabs.GetStartingPieceWeight(a_NewPiece);
|
||||
}
|
||||
|
||||
|
||||
virtual void PiecePlaced(const cPiece & a_Piece) override
|
||||
{
|
||||
m_Prefabs.PiecePlaced(a_Piece);
|
||||
}
|
||||
|
||||
|
||||
virtual void Reset(void) override
|
||||
{
|
||||
m_Prefabs.Reset();
|
||||
}
|
||||
|
||||
|
||||
void MoveAllDescendants(cPlacedPieces & a_PlacedPieces, size_t a_Pivot, int a_HeightDifference)
|
||||
{
|
||||
size_t num = a_PlacedPieces.size();
|
||||
cPlacedPiece * Pivot = a_PlacedPieces[a_Pivot];
|
||||
for (size_t i = a_Pivot + 1; i < num; i++)
|
||||
{
|
||||
if (
|
||||
(a_PlacedPieces[i]->GetParent() == Pivot) && // It is a direct dependant of the pivot
|
||||
!((const cPrefab &)a_PlacedPieces[i]->GetPiece()).ShouldMoveToGround() // It attaches strictly by connectors
|
||||
)
|
||||
{
|
||||
a_PlacedPieces[i]->MoveToGroundBy(a_HeightDifference);
|
||||
MoveAllDescendants(a_PlacedPieces, i, a_HeightDifference);
|
||||
}
|
||||
} // for i - a_PlacedPieces[]
|
||||
}
|
||||
} ;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// cVillageGen:
|
||||
|
||||
static cVillagePiecePool g_SandVillage(g_SandVillagePrefabs, g_SandVillagePrefabsCount, g_SandVillageStartingPrefabs, g_SandVillageStartingPrefabsCount);
|
||||
static cVillagePiecePool g_SandFlatRoofVillage(g_SandFlatRoofVillagePrefabs, g_SandFlatRoofVillagePrefabsCount, g_SandFlatRoofVillageStartingPrefabs, g_SandFlatRoofVillageStartingPrefabsCount);
|
||||
static cVillagePiecePool g_AlchemistVillage(g_AlchemistVillagePrefabs, g_AlchemistVillagePrefabsCount, g_AlchemistVillageStartingPrefabs, g_AlchemistVillageStartingPrefabsCount);
|
||||
static cVillagePiecePool g_PlainsVillage(g_PlainsVillagePrefabs, g_PlainsVillagePrefabsCount, g_PlainsVillageStartingPrefabs, g_PlainsVillageStartingPrefabsCount);
|
||||
static cVillagePiecePool g_JapaneseVillage(g_JapaneseVillagePrefabs, g_JapaneseVillagePrefabsCount, g_JapaneseVillageStartingPrefabs, g_JapaneseVillageStartingPrefabsCount);
|
||||
|
||||
static cVillagePiecePool * g_DesertVillagePools[] =
|
||||
{
|
||||
&g_SandVillage,
|
||||
&g_SandFlatRoofVillage,
|
||||
&g_AlchemistVillage,
|
||||
} ;
|
||||
|
||||
static cVillagePiecePool * g_PlainsVillagePools[] =
|
||||
{
|
||||
&g_PlainsVillage,
|
||||
&g_JapaneseVillage,
|
||||
} ;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
cVillageGen::cVillageGen(int a_Seed, int a_GridSize, int a_MaxDepth, int a_MaxSize, int a_MinDensity, int a_MaxDensity, cBiomeGen & a_BiomeGen, cTerrainHeightGen & a_HeightGen) :
|
||||
super(a_Seed, a_GridSize, a_GridSize, a_MaxSize, a_MaxSize, 100),
|
||||
m_Noise(a_Seed + 1000),
|
||||
m_MaxDepth(a_MaxDepth),
|
||||
m_MaxSize(a_MaxSize),
|
||||
m_MinDensity(a_MinDensity),
|
||||
m_MaxDensity(a_MaxDensity),
|
||||
m_BiomeGen(a_BiomeGen),
|
||||
m_HeightGen(a_HeightGen)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
cGridStructGen::cStructurePtr cVillageGen::CreateStructure(int a_OriginX, int a_OriginZ)
|
||||
{
|
||||
// Generate the biomes for the chunk surrounding the origin:
|
||||
int ChunkX, ChunkZ;
|
||||
cChunkDef::BlockToChunk(a_OriginX, a_OriginZ, ChunkX, ChunkZ);
|
||||
cChunkDef::BiomeMap Biomes;
|
||||
m_BiomeGen.GenBiomes(ChunkX, ChunkZ, Biomes);
|
||||
|
||||
// Check if all the biomes are village-friendly:
|
||||
// If just one is not, no village is created, because it's likely that an unfriendly biome is too close
|
||||
cVillagePiecePool * VillagePrefabs = NULL;
|
||||
BLOCKTYPE RoadBlock = E_BLOCK_GRAVEL;
|
||||
int rnd = m_Noise.IntNoise2DInt(a_OriginX, a_OriginZ) / 11;
|
||||
cVillagePiecePool * PlainsVillage = g_PlainsVillagePools[rnd % ARRAYCOUNT(g_PlainsVillagePools)];
|
||||
cVillagePiecePool * DesertVillage = g_DesertVillagePools[rnd % ARRAYCOUNT(g_DesertVillagePools)];
|
||||
for (size_t i = 0; i < ARRAYCOUNT(Biomes); i++)
|
||||
{
|
||||
switch (Biomes[i])
|
||||
{
|
||||
case biDesert:
|
||||
case biDesertM:
|
||||
{
|
||||
// These biomes allow sand villages
|
||||
VillagePrefabs = DesertVillage;
|
||||
// RoadBlock = E_BLOCK_SANDSTONE;
|
||||
break;
|
||||
}
|
||||
case biPlains:
|
||||
case biSavanna:
|
||||
case biSavannaM:
|
||||
case biSunflowerPlains:
|
||||
{
|
||||
// These biomes allow plains-style villages
|
||||
VillagePrefabs = PlainsVillage;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
// Village-unfriendly biome, bail out with zero structure:
|
||||
return cStructurePtr();
|
||||
}
|
||||
} // switch (Biomes[i])
|
||||
} // for i - Biomes[]
|
||||
|
||||
// Choose density for the village, random between m_MinDensity and m_MaxDensity:
|
||||
int Density;
|
||||
if (m_MaxDensity > m_MinDensity)
|
||||
{
|
||||
Density = m_MinDensity + rnd % (m_MaxDensity - m_MinDensity);
|
||||
}
|
||||
else
|
||||
{
|
||||
Density = m_MinDensity;
|
||||
}
|
||||
|
||||
// Create a village based on the chosen prefabs:
|
||||
if (VillagePrefabs == NULL)
|
||||
{
|
||||
return cStructurePtr();
|
||||
}
|
||||
return cStructurePtr(new cVillage(m_Seed, a_OriginX, a_OriginZ, m_MaxDepth, m_MaxSize, Density, *VillagePrefabs, m_HeightGen, RoadBlock));
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
57
src/Generating/VillageGen.h
Normal file
@ -0,0 +1,57 @@
|
||||
|
||||
// VillageGen.h
|
||||
|
||||
// Declares the cVillageGen class representing the village generator
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "GridStructGen.h"
|
||||
#include "PrefabPiecePool.h"
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
class cVillageGen :
|
||||
public cGridStructGen
|
||||
{
|
||||
typedef cGridStructGen super;
|
||||
public:
|
||||
cVillageGen(int a_Seed, int a_GridSize, int a_MaxDepth, int a_MaxSize, int a_MinDensity, int a_MaxDensity, cBiomeGen & a_BiomeGen, cTerrainHeightGen & a_HeightGen);
|
||||
|
||||
protected:
|
||||
class cVillage; // fwd: VillageGen.cpp
|
||||
|
||||
/** The noise used for generating random numbers */
|
||||
cNoise m_Noise;
|
||||
|
||||
/** Maximum depth of the generator tree*/
|
||||
int m_MaxDepth;
|
||||
|
||||
/** Maximum size, in X/Z blocks, of the village (radius from the origin) */
|
||||
int m_MaxSize;
|
||||
|
||||
/** Minimum density - percentage of allowed house connections. Range [0, 100] */
|
||||
int m_MinDensity;
|
||||
|
||||
/** Maximum density - percentage of allowed house connections. Range [0, 100] */
|
||||
int m_MaxDensity;
|
||||
|
||||
/** The underlying biome generator that defines whether the village is created or not */
|
||||
cBiomeGen & m_BiomeGen;
|
||||
|
||||
/** The underlying height generator, used to position the prefabs crossing chunk borders */
|
||||
cTerrainHeightGen & m_HeightGen;
|
||||
|
||||
|
||||
// cGridStructGen overrides:
|
||||
virtual cStructurePtr CreateStructure(int a_OriginX, int a_OriginZ) override;
|
||||
} ;
|
||||
|
||||
|
||||
|
||||
|
@ -225,16 +225,28 @@ template class SizeChecker<UInt16, 2>;
|
||||
|
||||
|
||||
|
||||
#ifndef TEST_GLOBALS
|
||||
// Common headers (part 1, without macros):
|
||||
#include "StringUtils.h"
|
||||
#include "OSSupport/Sleep.h"
|
||||
#include "OSSupport/CriticalSection.h"
|
||||
#include "OSSupport/Semaphore.h"
|
||||
#include "OSSupport/Event.h"
|
||||
#include "OSSupport/Thread.h"
|
||||
#include "OSSupport/File.h"
|
||||
#include "MCLogger.h"
|
||||
#else
|
||||
// Logging functions
|
||||
void inline LOGERROR(const char* a_Format, ...) FORMATSTRING(1,2);
|
||||
|
||||
// Common headers (part 1, without macros):
|
||||
#include "StringUtils.h"
|
||||
#include "OSSupport/Sleep.h"
|
||||
#include "OSSupport/CriticalSection.h"
|
||||
#include "OSSupport/Semaphore.h"
|
||||
#include "OSSupport/Event.h"
|
||||
#include "OSSupport/Thread.h"
|
||||
#include "OSSupport/File.h"
|
||||
#include "MCLogger.h"
|
||||
void inline LOGERROR(const char* a_Format, ...)
|
||||
{
|
||||
va_list argList;
|
||||
va_start(argList, a_Format);
|
||||
vprintf(a_Format, argList);
|
||||
va_end(argList);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
@ -253,10 +265,44 @@ template class SizeChecker<UInt16, 2>;
|
||||
#define FAST_FLOOR_DIV( x, div ) (((x) - (((x) < 0) ? ((div) - 1) : 0)) / (div))
|
||||
|
||||
// Own version of assert() that writes failed assertions to the log for review
|
||||
#ifdef _DEBUG
|
||||
#define ASSERT( x ) ( !!(x) || ( LOGERROR("Assertion failed: %s, file %s, line %i", #x, __FILE__, __LINE__ ), assert(0), 0 ) )
|
||||
#ifdef TEST_GLOBALS
|
||||
|
||||
class cAssertFailure
|
||||
{
|
||||
};
|
||||
|
||||
#ifdef _WIN32
|
||||
#if (defined(_MSC_VER) && defined(_DEBUG))
|
||||
#define DBG_BREAK _CrtDbgBreak()
|
||||
#else
|
||||
#define DBG_BREAK
|
||||
#endif
|
||||
#define REPORT_ERROR(FMT, ...) \
|
||||
{ \
|
||||
AString msg = Printf(FMT, __VA_ARGS__); \
|
||||
puts(msg.c_str()); \
|
||||
fflush(stdout); \
|
||||
OutputDebugStringA(msg.c_str()); \
|
||||
DBG_BREAK; \
|
||||
}
|
||||
#else
|
||||
#define REPORT_ERROR(FMT, ...) \
|
||||
{ \
|
||||
AString msg = Printf(FMT, __VA_ARGS__); \
|
||||
puts(msg.c_str()); \
|
||||
fflush(stdout); \
|
||||
}
|
||||
#endif
|
||||
#define ASSERT(x) do { if (!(x)) { throw cAssertFailure();} } while (0)
|
||||
#define testassert(x) do { if(!(x)) { REPORT_ERROR("Test failure: %s, file %s, line %d\n", #x, __FILE__, __LINE__); exit(1); } } while (0)
|
||||
#define CheckAsserts(x) do { try {x} catch (cAssertFailure) { break; } REPORT_ERROR("Test failure: assert didn't fire for %s, file %s, line %d\n", #x, __FILE__, __LINE__); exit(1); } while (0)
|
||||
|
||||
#else
|
||||
#define ASSERT(x) ((void)(x))
|
||||
#ifdef _DEBUG
|
||||
#define ASSERT( x ) ( !!(x) || ( LOGERROR("Assertion failed: %s, file %s, line %i", #x, __FILE__, __LINE__ ), assert(0), 0 ) )
|
||||
#else
|
||||
#define ASSERT(x) ((void)(x))
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// Pretty much the same as ASSERT() but stays in Release builds
|
||||
|
@ -18,20 +18,17 @@
|
||||
class cReader :
|
||||
public cChunkDataCallback
|
||||
{
|
||||
virtual void BlockTypes(const BLOCKTYPE * a_Type) override
|
||||
virtual void ChunkData(const cChunkData & a_ChunkBuffer) override
|
||||
{
|
||||
// ROW is a block of 16 Blocks, one whole row is copied at a time (hopefully the compiler will optimize that)
|
||||
// C++ doesn't permit copying arrays, but arrays as a part of a struct is ok :)
|
||||
typedef struct {BLOCKTYPE m_Row[16]; } ROW;
|
||||
ROW * InputRows = (ROW *)a_Type;
|
||||
ROW * OutputRows = (ROW *)m_BlockTypes;
|
||||
BLOCKTYPE * OutputRows = m_BlockTypes;
|
||||
int InputIdx = 0;
|
||||
int OutputIdx = m_ReadingChunkX + m_ReadingChunkZ * cChunkDef::Width * 3;
|
||||
for (int y = 0; y < cChunkDef::Height; y++)
|
||||
{
|
||||
for (int z = 0; z < cChunkDef::Width; z++)
|
||||
{
|
||||
OutputRows[OutputIdx] = InputRows[InputIdx++];
|
||||
a_ChunkBuffer.CopyBlockTypes(OutputRows + OutputIdx * 16, InputIdx * 16, 16);
|
||||
InputIdx++;
|
||||
OutputIdx += 3;
|
||||
} // for z
|
||||
// Skip into the next y-level in the 3x3 chunk blob; each level has cChunkDef::Width * 9 rows
|
||||
|
@ -24,7 +24,7 @@ void cMobProximityCounter::CollectMob(cEntity& a_Monster, cChunk& a_Chunk, doubl
|
||||
if (a_Distance < it->second.m_Distance)
|
||||
{
|
||||
it->second.m_Distance = a_Distance;
|
||||
it->second.m_Chunk = a_Chunk;
|
||||
it->second.m_Chunk = &a_Chunk;
|
||||
}
|
||||
}
|
||||
|
||||
@ -36,7 +36,7 @@ void cMobProximityCounter::convertMaps()
|
||||
{
|
||||
for(tMonsterToDistance::const_iterator itr = m_MonsterToDistance.begin(); itr != m_MonsterToDistance.end(); ++itr)
|
||||
{
|
||||
m_DistanceToMonster.insert(tDistanceToMonster::value_type(itr->second.m_Distance,sMonsterAndChunk(*itr->first,itr->second.m_Chunk)));
|
||||
m_DistanceToMonster.insert(tDistanceToMonster::value_type(itr->second.m_Distance,sMonsterAndChunk(*itr->first,*itr->second.m_Chunk)));
|
||||
}
|
||||
}
|
||||
|
||||
|