mirror of
https://github.com/cuberite/cuberite.git
synced 2025-01-09 04:19:26 +08:00
Merged branch 'master' into NameToUUID.
This commit is contained in:
commit
6d02fce9a2
2
.gitignore
vendored
2
.gitignore
vendored
@ -64,7 +64,7 @@ install_mainfest.txt
|
||||
src/MCServer
|
||||
lib/tolua++/tolua
|
||||
src/Bindings/Bindings.*
|
||||
src/Bindings/BindingDependecies.txt
|
||||
src/Bindings/BindingDependencies.txt
|
||||
MCServer.dir/
|
||||
src/AllFiles.lst
|
||||
|
||||
|
@ -88,3 +88,4 @@
|
||||
! 269:1 = 200 # 1 Wooden Shovel -> 10 sec
|
||||
! 290:1 = 200 # 1 Wooden Hoe -> 10 sec
|
||||
! 268:1 = 200 # 1 Wooden Sword -> 10 sec
|
||||
|
||||
|
@ -447,6 +447,15 @@ bool cIniFile::SetValueI(const AString & a_KeyName, const AString & a_ValueName,
|
||||
|
||||
|
||||
|
||||
bool cIniFile::SetValueI(const AString & a_Keyname, const AString & a_ValueName, const Int64 a_Value, const bool a_CreateIfNotExists)
|
||||
{
|
||||
return SetValue(a_Keyname, a_ValueName, Printf("%lld", a_Value), a_CreateIfNotExists);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
bool cIniFile::SetValueF(const AString & a_KeyName, const AString & a_ValueName, double const a_Value, const bool a_CreateIfNotExists)
|
||||
{
|
||||
return SetValue(a_KeyName, a_ValueName, Printf("%f", a_Value), a_CreateIfNotExists);
|
||||
@ -571,6 +580,24 @@ int cIniFile::GetValueSetI(const AString & keyname, const AString & valuename, c
|
||||
|
||||
|
||||
|
||||
Int64 cIniFile::GetValueSetI(const AString & keyname, const AString & valuename, const Int64 defValue)
|
||||
{
|
||||
AString Data;
|
||||
Printf(Data, "%lld", defValue);
|
||||
AString resultstring = GetValueSet(keyname, valuename, Data);
|
||||
Int64 result = defValue;
|
||||
#ifdef _WIN32
|
||||
sscanf_s(resultstring.c_str(), "%lld", &result);
|
||||
#else
|
||||
sscanf(resultstring.c_str(), "%lld", &result);
|
||||
#endif
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
bool cIniFile::DeleteValueByID(const int keyID, const int valueID)
|
||||
{
|
||||
if ((keyID < (int)keys.size()) && (valueID < (int)keys[keyID].names.size()))
|
||||
|
@ -119,6 +119,7 @@ public:
|
||||
AString GetValueSet (const AString & keyname, const AString & valuename, const AString & defValue = "");
|
||||
double GetValueSetF(const AString & keyname, const AString & valuename, const double defValue = 0.0);
|
||||
int GetValueSetI(const AString & keyname, const AString & valuename, const int defValue = 0);
|
||||
Int64 GetValueSetI(const AString & keyname, const AString & valuename, const Int64 defValue = 0);
|
||||
bool GetValueSetB(const AString & keyname, const AString & valuename, const bool defValue = false)
|
||||
{
|
||||
return (GetValueSetI(keyname, valuename, defValue ? 1 : 0) != 0);
|
||||
@ -141,6 +142,7 @@ public:
|
||||
bool SetValue (const int keyID, const int valueID, const AString & value);
|
||||
bool SetValue (const AString & a_KeyName, const AString & a_ValueName, const AString & a_Value, const bool a_CreateIfNotExists = true);
|
||||
bool SetValueI(const AString & a_KeyName, const AString & a_ValueName, const int a_Value, const bool a_CreateIfNotExists = true);
|
||||
bool SetValueI(const AString & a_Keyname, const AString & a_ValueName, const Int64 a_Value, const bool a_CreateIfNotExists = true);
|
||||
bool SetValueB(const AString & a_KeyName, const AString & a_ValueName, const bool a_Value, const bool a_CreateIfNotExists = true)
|
||||
{
|
||||
return SetValueI(a_KeyName, a_ValueName, int(a_Value), a_CreateIfNotExists);
|
||||
|
@ -125,8 +125,8 @@ if (NOT MSVC)
|
||||
DEPENDS ${BINDING_DEPENDENCIES}
|
||||
)
|
||||
endif ()
|
||||
set_source_files_properties(Bindings/Bindings.cpp PROPERTIES GENERATED TRUE)
|
||||
set_source_files_properties(Bindings/Bindings.h PROPERTIES GENERATED TRUE)
|
||||
set_source_files_properties(${CMAKE_SOURCE_DIR}/src/Bindings/Bindings.cpp PROPERTIES GENERATED TRUE)
|
||||
set_source_files_properties(${CMAKE_SOURCE_DIR}/src/Bindings/Bindings.h PROPERTIES GENERATED TRUE)
|
||||
|
||||
if(NOT MSVC)
|
||||
add_library(Bindings ${SRCS} ${HDRS})
|
||||
|
@ -124,44 +124,58 @@ void cPluginManager::ReloadPluginsNow(cIniFile & a_SettingsIni)
|
||||
// Check if the Plugins section exists.
|
||||
int KeyNum = a_SettingsIni.FindKey("Plugins");
|
||||
|
||||
// If it does, how many plugins are there?
|
||||
int NumPlugins = ((KeyNum != -1) ? (a_SettingsIni.GetNumValues(KeyNum)) : 0);
|
||||
|
||||
if (KeyNum == -1)
|
||||
{
|
||||
InsertDefaultPlugins(a_SettingsIni);
|
||||
KeyNum = a_SettingsIni.FindKey("Plugins");
|
||||
}
|
||||
else if (NumPlugins > 0)
|
||||
|
||||
// How many plugins are there?
|
||||
int NumPlugins = a_SettingsIni.GetNumValues(KeyNum);
|
||||
|
||||
for (int i = 0; i < NumPlugins; i++)
|
||||
{
|
||||
for (int i = 0; i < NumPlugins; i++)
|
||||
AString ValueName = a_SettingsIni.GetValueName(KeyNum, i);
|
||||
if (ValueName.compare("Plugin") == 0)
|
||||
{
|
||||
AString ValueName = a_SettingsIni.GetValueName(KeyNum, i);
|
||||
if (ValueName.compare("Plugin") == 0)
|
||||
AString PluginFile = a_SettingsIni.GetValue(KeyNum, i);
|
||||
if (!PluginFile.empty())
|
||||
{
|
||||
AString PluginFile = a_SettingsIni.GetValue(KeyNum, i);
|
||||
if (!PluginFile.empty())
|
||||
if (m_Plugins.find(PluginFile) != m_Plugins.end())
|
||||
{
|
||||
if (m_Plugins.find(PluginFile) != m_Plugins.end())
|
||||
{
|
||||
LoadPlugin(PluginFile);
|
||||
}
|
||||
LoadPlugin(PluginFile);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Remove invalid plugins from the PluginMap.
|
||||
for (PluginMap::iterator itr = m_Plugins.begin(); itr != m_Plugins.end();)
|
||||
{
|
||||
if (itr->second == NULL)
|
||||
{
|
||||
PluginMap::iterator thiz = itr;
|
||||
++thiz;
|
||||
m_Plugins.erase(itr);
|
||||
itr = thiz;
|
||||
continue;
|
||||
}
|
||||
++itr;
|
||||
}
|
||||
|
||||
size_t NumLoadedPlugins = GetNumPlugins();
|
||||
if (NumLoadedPlugins == 0)
|
||||
{
|
||||
LOG("-- No Plugins Loaded --");
|
||||
}
|
||||
else if (NumLoadedPlugins > 1)
|
||||
else if (NumLoadedPlugins == 1)
|
||||
{
|
||||
LOG("-- Loaded %i Plugins --", (int)NumLoadedPlugins);
|
||||
LOG("-- Loaded 1 Plugin --");
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG("-- Loaded 1 Plugin --");
|
||||
LOG("-- Loaded %i Plugins --", (int)NumLoadedPlugins);
|
||||
}
|
||||
CallHookPluginsLoaded();
|
||||
}
|
||||
@ -476,11 +490,9 @@ bool cPluginManager::CallHookDisconnect(cClientHandle & a_Client, const AString
|
||||
|
||||
bool cPluginManager::CallHookEntityAddEffect(cEntity & a_Entity, int a_EffectType, int a_EffectDurationTicks, int a_EffectIntensity, double a_DistanceModifier)
|
||||
{
|
||||
HookMap::iterator Plugins = m_Hooks.find(HOOK_ENTITY_ADD_EFFECT);
|
||||
if (Plugins == m_Hooks.end())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
FIND_HOOK(HOOK_ENTITY_ADD_EFFECT);
|
||||
VERIFY_HOOK;
|
||||
|
||||
for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr)
|
||||
{
|
||||
if ((*itr)->OnEntityAddEffect(a_Entity, a_EffectType, a_EffectDurationTicks, a_EffectIntensity, a_DistanceModifier))
|
||||
|
@ -346,6 +346,37 @@ eDimension StringToDimension(const AString & a_DimensionString)
|
||||
|
||||
|
||||
|
||||
AString DimensionToString(eDimension a_Dimension)
|
||||
{
|
||||
// Decode using a built-in map:
|
||||
static struct
|
||||
{
|
||||
eDimension m_Dimension;
|
||||
const char * m_String;
|
||||
} DimensionMap[] =
|
||||
{
|
||||
{ dimOverworld, "Overworld" },
|
||||
{ dimNether, "Nether" },
|
||||
{ dimEnd, "End" },
|
||||
};
|
||||
|
||||
for (size_t i = 0; i < ARRAYCOUNT(DimensionMap); i++)
|
||||
{
|
||||
if (DimensionMap[i].m_Dimension == a_Dimension)
|
||||
{
|
||||
return DimensionMap[i].m_String;
|
||||
}
|
||||
} // for i - DimensionMap[]
|
||||
|
||||
// Not found
|
||||
LOGWARNING("Unknown dimension: \"%i\". Setting to Overworld", (int)a_Dimension);
|
||||
return "Overworld";
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/// Translates damage type constant to a string representation (built-in).
|
||||
AString DamageTypeToString(eDamageType a_DamageType)
|
||||
{
|
||||
|
@ -920,9 +920,14 @@ extern AString ItemToFullString(const cItem & a_Item);
|
||||
/// Translates a mob string ("ocelot") to mobtype (E_ENTITY_TYPE_OCELOT)
|
||||
extern int StringToMobType(const AString & a_MobString);
|
||||
|
||||
/// Translates a dimension string to dimension enum. Takes either a number or a dimension alias (built-in). Returns -1000 on failure
|
||||
/// Translates a dimension string to dimension enum. Takes either a number or a dimension alias (built-in). Returns dimOverworld on failure
|
||||
extern eDimension StringToDimension(const AString & a_DimensionString);
|
||||
|
||||
/** Translates a dimension enum to dimension string.
|
||||
Takes an eDimension enum value and returns "Overworld" on failure
|
||||
*/
|
||||
extern AString DimensionToString(eDimension a_Dimension);
|
||||
|
||||
/// Translates damage type constant to a string representation (built-in).
|
||||
extern AString DamageTypeToString(eDamageType a_DamageType);
|
||||
|
||||
|
@ -17,6 +17,7 @@ cBlockInfo::cBlockInfo()
|
||||
, m_IsSnowable(false)
|
||||
, m_IsSolid(true)
|
||||
, m_FullyOccupiesVoxel(false)
|
||||
, m_CanBeTerraformed(false)
|
||||
, m_Handler(NULL)
|
||||
{}
|
||||
|
||||
@ -450,6 +451,7 @@ void cBlockInfo::Initialize(cBlockInfoArray & a_Info)
|
||||
a_Info[E_BLOCK_CROPS ].m_IsSolid = false;
|
||||
a_Info[E_BLOCK_DANDELION ].m_IsSolid = false;
|
||||
a_Info[E_BLOCK_DETECTOR_RAIL ].m_IsSolid = false;
|
||||
a_Info[E_BLOCK_END_PORTAL ].m_IsSolid = false;
|
||||
a_Info[E_BLOCK_FIRE ].m_IsSolid = false;
|
||||
a_Info[E_BLOCK_FLOWER ].m_IsSolid = false;
|
||||
a_Info[E_BLOCK_HEAVY_WEIGHTED_PRESSURE_PLATE].m_IsSolid = false;
|
||||
@ -547,6 +549,27 @@ void cBlockInfo::Initialize(cBlockInfoArray & a_Info)
|
||||
a_Info[E_BLOCK_STONE ].m_FullyOccupiesVoxel = true;
|
||||
a_Info[E_BLOCK_STONE_BRICKS ].m_FullyOccupiesVoxel = true;
|
||||
a_Info[E_BLOCK_WOOL ].m_FullyOccupiesVoxel = true;
|
||||
|
||||
|
||||
// Blocks that can be terraformed
|
||||
a_Info[E_BLOCK_COAL_ORE ].m_CanBeTerraformed = true;
|
||||
a_Info[E_BLOCK_COBBLESTONE ].m_CanBeTerraformed = true;
|
||||
a_Info[E_BLOCK_DIAMOND_ORE ].m_CanBeTerraformed = true;
|
||||
a_Info[E_BLOCK_DIRT ].m_CanBeTerraformed = true;
|
||||
a_Info[E_BLOCK_GOLD_ORE ].m_CanBeTerraformed = true;
|
||||
a_Info[E_BLOCK_GRASS ].m_CanBeTerraformed = true;
|
||||
a_Info[E_BLOCK_GRAVEL ].m_CanBeTerraformed = true;
|
||||
a_Info[E_BLOCK_HARDENED_CLAY ].m_CanBeTerraformed = true;
|
||||
a_Info[E_BLOCK_IRON_ORE ].m_CanBeTerraformed = true;
|
||||
a_Info[E_BLOCK_MYCELIUM ].m_CanBeTerraformed = true;
|
||||
a_Info[E_BLOCK_NETHERRACK ].m_CanBeTerraformed = true;
|
||||
a_Info[E_BLOCK_REDSTONE_ORE ].m_CanBeTerraformed = true;
|
||||
a_Info[E_BLOCK_REDSTONE_ORE_GLOWING].m_CanBeTerraformed = true;
|
||||
a_Info[E_BLOCK_SAND ].m_CanBeTerraformed = true;
|
||||
a_Info[E_BLOCK_SANDSTONE ].m_CanBeTerraformed = true;
|
||||
a_Info[E_BLOCK_SOULSAND ].m_CanBeTerraformed = true;
|
||||
a_Info[E_BLOCK_STAINED_CLAY ].m_CanBeTerraformed = true;
|
||||
a_Info[E_BLOCK_STONE ].m_CanBeTerraformed = true;
|
||||
}
|
||||
|
||||
|
||||
|
@ -45,6 +45,9 @@ public:
|
||||
/** Does this block fully occupy its voxel - is it a 'full' block? */
|
||||
bool m_FullyOccupiesVoxel;
|
||||
|
||||
/** Can a finisher change it? */
|
||||
bool m_CanBeTerraformed;
|
||||
|
||||
// tolua_end
|
||||
|
||||
/** Associated block handler. */
|
||||
@ -60,6 +63,7 @@ public:
|
||||
inline static bool IsSnowable (BLOCKTYPE a_Type) { return Get(a_Type).m_IsSnowable; }
|
||||
inline static bool IsSolid (BLOCKTYPE a_Type) { return Get(a_Type).m_IsSolid; }
|
||||
inline static bool FullyOccupiesVoxel (BLOCKTYPE a_Type) { return Get(a_Type).m_FullyOccupiesVoxel; }
|
||||
inline static bool CanBeTerraformed (BLOCKTYPE a_Type) { return Get(a_Type).m_CanBeTerraformed; }
|
||||
|
||||
// tolua_end
|
||||
|
||||
|
@ -108,7 +108,7 @@ void cBlockBedHandler::OnUse(cChunkInterface & a_ChunkInterface, cWorldInterface
|
||||
NIBBLETYPE Meta = a_ChunkInterface.GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ);
|
||||
if (Meta & 0x4)
|
||||
{
|
||||
a_Player->SendMessageFailure("This bed is occupied.");
|
||||
a_Player->SendMessageFailure("This bed is occupied");
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -133,6 +133,8 @@ void cBlockBedHandler::OnUse(cChunkInterface & a_ChunkInterface, cWorldInterface
|
||||
|
||||
a_ChunkInterface.SetBlockMeta(a_BlockX, a_BlockY, a_BlockZ, Meta | 0x4); // Where 0x4 = occupied bit
|
||||
a_Player->SetIsInBed(true);
|
||||
a_Player->SetBedPos(Vector3i(a_BlockX, a_BlockY, a_BlockZ));
|
||||
a_Player->SendMessageSuccess("Home position set successfully");
|
||||
|
||||
cTimeFastForwardTester Tester;
|
||||
if (a_WorldInterface.ForEachPlayer(Tester))
|
||||
|
@ -58,6 +58,24 @@ public:
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual void OnUpdate(cChunkInterface & a_ChunkInterface, cWorldInterface & a_WorldInterface, cBlockPluginInterface & a_PluginInterface, cChunk & a_Chunk, int a_RelX, int a_RelY, int a_RelZ) override
|
||||
{
|
||||
int BlockX = a_RelX + a_Chunk.GetPosX() * cChunkDef::Width;
|
||||
int BlockZ = a_RelZ + a_Chunk.GetPosZ() * cChunkDef::Width;
|
||||
if (!a_WorldInterface.IsWeatherWetAt(BlockX, BlockZ) || (a_RelY != a_WorldInterface.GetHeight(BlockX, BlockZ)))
|
||||
{
|
||||
// It's not raining at our current location or we do not have a direct view of the sky
|
||||
// We cannot eat the rain :(
|
||||
return;
|
||||
}
|
||||
|
||||
NIBBLETYPE Meta = a_Chunk.GetMeta(a_RelX, a_RelY, a_RelZ);
|
||||
if (Meta < 3)
|
||||
{
|
||||
a_Chunk.SetMeta(a_RelX, a_RelY, a_RelZ, Meta + 1);
|
||||
}
|
||||
}
|
||||
} ;
|
||||
|
||||
|
||||
|
@ -61,22 +61,6 @@ public:
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void OnDestroyed(cChunkInterface & a_ChunkInterface, cWorldInterface & a_WorldInterface, int a_BlockX, int a_BlockY, int a_BlockZ) override
|
||||
{
|
||||
cBlockHandler::OnDestroyed(a_ChunkInterface, a_WorldInterface, a_BlockX, a_BlockY, a_BlockZ);
|
||||
|
||||
// 0.5% chance of dropping an apple, if the leaves' type is Apple Leaves:
|
||||
NIBBLETYPE Meta = a_ChunkInterface.GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ);
|
||||
cFastRandom rand;
|
||||
if (((Meta & 3) == E_META_LEAVES_APPLE) && (rand.NextInt(201) == 100))
|
||||
{
|
||||
cItems Drops;
|
||||
Drops.push_back(cItem(E_ITEM_RED_APPLE, 1, 0));
|
||||
a_WorldInterface.SpawnItemPickups(Drops, a_BlockX, a_BlockY, a_BlockZ);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
virtual void OnNeighborChanged(cChunkInterface & a_ChunkInterface, int a_BlockX, int a_BlockY, int a_BlockZ) override
|
||||
|
@ -43,8 +43,9 @@ public:
|
||||
cItems Pickups;
|
||||
Pickups.Add(E_BLOCK_TALL_GRASS, 1, Meta);
|
||||
a_WorldInterface.SpawnItemPickups(Pickups, a_BlockX, a_BlockY, a_BlockZ);
|
||||
|
||||
a_Player->UseEquippedItem();
|
||||
}
|
||||
a_Player->UseEquippedItem();
|
||||
}
|
||||
|
||||
|
||||
|
@ -44,6 +44,13 @@ public:
|
||||
}
|
||||
|
||||
|
||||
virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override
|
||||
{
|
||||
// Reset meta to 0
|
||||
a_Pickups.push_back(cItem(E_BLOCK_VINES, 1, 0));
|
||||
}
|
||||
|
||||
|
||||
static NIBBLETYPE DirectionToMetaData(char a_BlockFace)
|
||||
{
|
||||
switch (a_BlockFace)
|
||||
|
@ -46,6 +46,12 @@ public:
|
||||
|
||||
virtual void SetTimeOfDay(Int64 a_TimeOfDay) = 0;
|
||||
|
||||
/** Returns true if it is raining, stormy or snowing at the specified location. This takes into account biomes. */
|
||||
virtual bool IsWeatherWetAt(int a_BlockX, int a_BlockZ) = 0;
|
||||
|
||||
/** Returns the world height at the specified coords; waits for the chunk to get loaded / generated */
|
||||
virtual int GetHeight(int a_BlockX, int a_BlockZ) = 0;
|
||||
|
||||
/** Wakes up the simulators for the specified block */
|
||||
virtual void WakeUpSimulators(int a_BlockX, int a_BlockY, int a_BlockZ) = 0;
|
||||
|
||||
|
@ -153,10 +153,10 @@ if (NOT MSVC)
|
||||
get_directory_property(BINDING_DEPENDENCIES DIRECTORY "Bindings" DEFINITION BINDING_DEPENDENCIES)
|
||||
|
||||
#clear file
|
||||
file(WRITE ${CMAKE_CURRENT_SOURCE_DIR}/Bindings/BindingDependecies.txt)
|
||||
foreach(dependecy ${BINDING_DEPENDECIES})
|
||||
#write each dependecy on a seperate line
|
||||
file(APPEND ${CMAKE_CURRENT_SOURCE_DIR}/Bindings/BindingDependecies.txt "${dependecy}\n")
|
||||
file(WRITE ${CMAKE_CURRENT_SOURCE_DIR}/Bindings/BindingDependencies.txt)
|
||||
foreach(dependency ${BINDING_DEPENDENCIES})
|
||||
#write each dependency on a seperate line
|
||||
file(APPEND ${CMAKE_CURRENT_SOURCE_DIR}/Bindings/BindingDependencies.txt "${dependency}\n")
|
||||
endforeach()
|
||||
|
||||
set_directory_properties(PROPERTIES ADDITIONAL_MAKE_CLEAN_FILES "Bindings.cpp Bindings.h")
|
||||
|
@ -577,39 +577,34 @@ void cChunk::Tick(float a_Dt)
|
||||
m_IsDirty = (*itr)->Tick(a_Dt, *this) | m_IsDirty;
|
||||
}
|
||||
|
||||
// Tick all entities in this chunk (except mobs):
|
||||
for (cEntityList::iterator itr = m_Entities.begin(); itr != m_Entities.end(); ++itr)
|
||||
{
|
||||
// Mobs are tickes inside MobTick (as we don't have to tick them if they are far away from players)
|
||||
if (!((*itr)->IsMob()))
|
||||
for (cEntityList::iterator itr = m_Entities.begin(); itr != m_Entities.end();)
|
||||
{
|
||||
if (!((*itr)->IsMob())) // Mobs are ticked inside cWorld::TickMobs() (as we don't have to tick them if they are far away from players)
|
||||
{
|
||||
// Tick all entities in this chunk (except mobs):
|
||||
(*itr)->Tick(a_Dt, *this);
|
||||
}
|
||||
} // for itr - m_Entitites[]
|
||||
|
||||
// Remove all entities that were scheduled for removal:
|
||||
for (cEntityList::iterator itr = m_Entities.begin(); itr != m_Entities.end();)
|
||||
{
|
||||
if ((*itr)->IsDestroyed())
|
||||
{
|
||||
LOGD("Destroying entity #%i (%s)", (*itr)->GetUniqueID(), (*itr)->GetClass());
|
||||
cEntity * ToDelete = *itr;
|
||||
itr = m_Entities.erase(itr);
|
||||
delete ToDelete;
|
||||
ToDelete = NULL;
|
||||
continue;
|
||||
}
|
||||
++itr;
|
||||
} // for itr - m_Entitites[]
|
||||
|
||||
// If any entity moved out of the chunk, move it to the neighbor:
|
||||
for (cEntityList::iterator itr = m_Entities.begin(); itr != m_Entities.end();)
|
||||
{
|
||||
if (
|
||||
|
||||
if ((*itr)->IsDestroyed()) // Remove all entities that were scheduled for removal:
|
||||
{
|
||||
LOGD("Destroying entity #%i (%s)", (*itr)->GetUniqueID(), (*itr)->GetClass());
|
||||
MarkDirty();
|
||||
cEntity * ToDelete = *itr;
|
||||
itr = m_Entities.erase(itr);
|
||||
delete ToDelete;
|
||||
}
|
||||
else if ((*itr)->IsWorldTravellingFrom(m_World)) // Remove all entities that are travelling to another world:
|
||||
{
|
||||
MarkDirty();
|
||||
(*itr)->SetWorldTravellingFrom(NULL);
|
||||
itr = m_Entities.erase(itr);
|
||||
}
|
||||
else if ( // If any entity moved out of the chunk, move it to the neighbor:
|
||||
((*itr)->GetChunkX() != m_PosX) ||
|
||||
((*itr)->GetChunkZ() != m_PosZ)
|
||||
)
|
||||
{
|
||||
MarkDirty();
|
||||
MoveEntityToNewChunk(*itr);
|
||||
itr = m_Entities.erase(itr);
|
||||
}
|
||||
@ -617,7 +612,7 @@ void cChunk::Tick(float a_Dt)
|
||||
{
|
||||
++itr;
|
||||
}
|
||||
}
|
||||
} // for itr - m_Entitites[]
|
||||
|
||||
ApplyWeatherToTop();
|
||||
}
|
||||
@ -902,7 +897,6 @@ void cChunk::ApplyWeatherToTop()
|
||||
}
|
||||
break;
|
||||
} // case (snowy biomes)
|
||||
// TODO: Rainy biomes should check for farmland and cauldrons
|
||||
default:
|
||||
{
|
||||
break;
|
||||
@ -1798,7 +1792,7 @@ void cChunk::RemoveBlockEntity( cBlockEntity* a_BlockEntity)
|
||||
|
||||
|
||||
|
||||
bool cChunk::AddClient(cClientHandle* a_Client)
|
||||
bool cChunk::AddClient(cClientHandle * a_Client)
|
||||
{
|
||||
for (cClientHandleList::iterator itr = m_LoadedByClient.begin(); itr != m_LoadedByClient.end(); ++itr)
|
||||
{
|
||||
@ -1829,7 +1823,7 @@ bool cChunk::AddClient(cClientHandle* a_Client)
|
||||
|
||||
|
||||
|
||||
void cChunk::RemoveClient( cClientHandle* a_Client)
|
||||
void cChunk::RemoveClient(cClientHandle * a_Client)
|
||||
{
|
||||
for (cClientHandleList::iterator itr = m_LoadedByClient.begin(); itr != m_LoadedByClient.end(); ++itr)
|
||||
{
|
||||
@ -1837,7 +1831,7 @@ void cChunk::RemoveClient( cClientHandle* a_Client)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
m_LoadedByClient.erase(itr);
|
||||
|
||||
if (!a_Client->IsDestroyed())
|
||||
@ -1862,7 +1856,7 @@ void cChunk::RemoveClient( cClientHandle* a_Client)
|
||||
|
||||
|
||||
|
||||
bool cChunk::HasClient( cClientHandle* a_Client)
|
||||
bool cChunk::HasClient(cClientHandle * a_Client)
|
||||
{
|
||||
for (cClientHandleList::const_iterator itr = m_LoadedByClient.begin(); itr != m_LoadedByClient.end(); ++itr)
|
||||
{
|
||||
@ -1893,9 +1887,9 @@ void cChunk::AddEntity(cEntity * a_Entity)
|
||||
{
|
||||
MarkDirty();
|
||||
}
|
||||
|
||||
|
||||
ASSERT(std::find(m_Entities.begin(), m_Entities.end(), a_Entity) == m_Entities.end()); // Not there already
|
||||
|
||||
|
||||
m_Entities.push_back(a_Entity);
|
||||
}
|
||||
|
||||
@ -1905,17 +1899,12 @@ void cChunk::AddEntity(cEntity * a_Entity)
|
||||
|
||||
void cChunk::RemoveEntity(cEntity * a_Entity)
|
||||
{
|
||||
size_t SizeBefore = m_Entities.size();
|
||||
m_Entities.remove(a_Entity);
|
||||
size_t SizeAfter = m_Entities.size();
|
||||
|
||||
if (SizeBefore != SizeAfter)
|
||||
|
||||
// Mark as dirty if it was a server-generated entity:
|
||||
if (!a_Entity->IsPlayer())
|
||||
{
|
||||
// Mark as dirty if it was a server-generated entity:
|
||||
if (!a_Entity->IsPlayer())
|
||||
{
|
||||
MarkDirty();
|
||||
}
|
||||
MarkDirty();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -120,7 +120,7 @@ cClientHandle::~cClientHandle()
|
||||
}
|
||||
if (World != NULL)
|
||||
{
|
||||
World->RemovePlayer(m_Player);
|
||||
World->RemovePlayer(m_Player, true); // Must be called before cPlayer::Destroy() as otherwise cChunk tries to delete the player, and then we do it again
|
||||
m_Player->Destroy();
|
||||
}
|
||||
delete m_Player;
|
||||
@ -1796,8 +1796,7 @@ void cClientHandle::RemoveFromWorld(void)
|
||||
m_Protocol->SendUnloadChunk(itr->m_ChunkX, itr->m_ChunkZ);
|
||||
} // for itr - Chunks[]
|
||||
|
||||
// StreamChunks() called in cPlayer::MoveToWorld() after new world has been set
|
||||
// Meanwhile here, we set last streamed values to bogus ones so everything is resent
|
||||
// Here, we set last streamed values to bogus ones so everything is resent
|
||||
m_LastStreamedChunkX = 0x7fffffff;
|
||||
m_LastStreamedChunkZ = 0x7fffffff;
|
||||
m_HasSentPlayerChunk = false;
|
||||
@ -1974,28 +1973,17 @@ void cClientHandle::SendBlockChanges(int a_ChunkX, int a_ChunkZ, const sSetBlock
|
||||
|
||||
void cClientHandle::SendChat(const AString & a_Message, eMessageType a_ChatPrefix, const AString & a_AdditionalData)
|
||||
{
|
||||
bool ShouldAppendChatPrefixes = true;
|
||||
|
||||
if (GetPlayer()->GetWorld() == NULL)
|
||||
cWorld * World = GetPlayer()->GetWorld();
|
||||
if (World == NULL)
|
||||
{
|
||||
cWorld * World = cRoot::Get()->GetWorld(GetPlayer()->GetLoadedWorldName());
|
||||
World = cRoot::Get()->GetWorld(GetPlayer()->GetLoadedWorldName());
|
||||
if (World == NULL)
|
||||
{
|
||||
World = cRoot::Get()->GetDefaultWorld();
|
||||
}
|
||||
|
||||
if (!World->ShouldUseChatPrefixes())
|
||||
{
|
||||
ShouldAppendChatPrefixes = false;
|
||||
}
|
||||
}
|
||||
else if (!GetPlayer()->GetWorld()->ShouldUseChatPrefixes())
|
||||
{
|
||||
ShouldAppendChatPrefixes = false;
|
||||
}
|
||||
|
||||
AString Message = FormatMessageType(ShouldAppendChatPrefixes, a_ChatPrefix, a_AdditionalData);
|
||||
|
||||
AString Message = FormatMessageType(World->ShouldUseChatPrefixes(), a_ChatPrefix, a_AdditionalData);
|
||||
m_Protocol->SendChat(Message.append(a_Message));
|
||||
}
|
||||
|
||||
@ -2378,9 +2366,9 @@ void cClientHandle::SendRemoveEntityEffect(const cEntity & a_Entity, int a_Effec
|
||||
|
||||
|
||||
|
||||
void cClientHandle::SendRespawn(const cWorld & a_World, bool a_ShouldIgnoreDimensionChecks)
|
||||
void cClientHandle::SendRespawn(eDimension a_Dimension, bool a_ShouldIgnoreDimensionChecks)
|
||||
{
|
||||
m_Protocol->SendRespawn(a_World, a_ShouldIgnoreDimensionChecks);
|
||||
m_Protocol->SendRespawn(a_Dimension, a_ShouldIgnoreDimensionChecks);
|
||||
}
|
||||
|
||||
|
||||
|
@ -163,7 +163,7 @@ public:
|
||||
void SendPlayerSpawn (const cPlayer & a_Player);
|
||||
void SendPluginMessage (const AString & a_Channel, const AString & a_Message); // Exported in ManualBindings.cpp
|
||||
void SendRemoveEntityEffect (const cEntity & a_Entity, int a_EffectID);
|
||||
void SendRespawn (const cWorld & a_World, bool a_ShouldIgnoreDimensionChecks = false);
|
||||
void SendRespawn (eDimension a_Dimension, bool a_ShouldIgnoreDimensionChecks = false);
|
||||
void SendExperience (void);
|
||||
void SendExperienceOrb (const cExpOrb & a_ExpOrb);
|
||||
void SendScoreboardObjective (const AString & a_Name, const AString & a_DisplayName, Byte a_Mode);
|
||||
|
@ -12,6 +12,7 @@
|
||||
#include "../Bindings/PluginManager.h"
|
||||
#include "../Tracer.h"
|
||||
#include "Player.h"
|
||||
#include "Items/ItemHandler.h"
|
||||
|
||||
|
||||
|
||||
@ -37,6 +38,7 @@ cEntity::cEntity(eEntityType a_EntityType, double a_X, double a_Y, double a_Z, d
|
||||
, m_Gravity(-9.81f)
|
||||
, m_LastPos(a_X, a_Y, a_Z)
|
||||
, m_IsInitialized(false)
|
||||
, m_WorldTravellingFrom(NULL)
|
||||
, m_EntityType(a_EntityType)
|
||||
, m_World(NULL)
|
||||
, m_IsFireproof(false)
|
||||
@ -289,11 +291,6 @@ void cEntity::SetPitchFromSpeed(void)
|
||||
|
||||
bool cEntity::DoTakeDamage(TakeDamageInfo & a_TDI)
|
||||
{
|
||||
if (cRoot::Get()->GetPluginManager()->CallHookTakeDamage(*this, a_TDI))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (m_Health <= 0)
|
||||
{
|
||||
// Can't take damage if already dead
|
||||
@ -306,10 +303,17 @@ bool cEntity::DoTakeDamage(TakeDamageInfo & a_TDI)
|
||||
return false;
|
||||
}
|
||||
|
||||
if (cRoot::Get()->GetPluginManager()->CallHookTakeDamage(*this, a_TDI))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((a_TDI.Attacker != NULL) && (a_TDI.Attacker->IsPlayer()))
|
||||
{
|
||||
cPlayer * Player = (cPlayer *)a_TDI.Attacker;
|
||||
|
||||
Player->GetEquippedItem().GetHandler()->OnEntityAttack(Player, this);
|
||||
|
||||
// IsOnGround() only is false if the player is moving downwards
|
||||
// TODO: Better damage increase, and check for enchantments (and use magic critical instead of plain)
|
||||
if (!Player->IsOnGround())
|
||||
@ -614,9 +618,12 @@ void cEntity::Tick(float a_Dt, cChunk & a_Chunk)
|
||||
// Handle drowning
|
||||
HandleAir();
|
||||
}
|
||||
|
||||
// None of the above functions change position, we remain in the chunk of NextChunk
|
||||
HandlePhysics(a_Dt, *NextChunk);
|
||||
|
||||
if (!DetectPortal()) // Our chunk is invalid if we have moved to another world
|
||||
{
|
||||
// None of the above functions changed position, we remain in the chunk of NextChunk
|
||||
HandlePhysics(a_Dt, *NextChunk);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -853,7 +860,7 @@ void cEntity::TickBurning(cChunk & a_Chunk)
|
||||
// Remember the current burning state:
|
||||
bool HasBeenBurning = (m_TicksLeftBurning > 0);
|
||||
|
||||
if (m_World->IsWeatherWet())
|
||||
if (GetWorld()->IsWeatherWetAt(POSX_TOINT, POSZ_TOINT))
|
||||
{
|
||||
if (POSY_TOINT > m_World->GetHeight(POSX_TOINT, POSZ_TOINT))
|
||||
{
|
||||
@ -1024,6 +1031,184 @@ void cEntity::DetectCacti(void)
|
||||
|
||||
|
||||
|
||||
bool cEntity::DetectPortal()
|
||||
{
|
||||
if (GetWorld()->GetDimension() == dimOverworld)
|
||||
{
|
||||
if (GetWorld()->GetNetherWorldName().empty() && GetWorld()->GetEndWorldName().empty())
|
||||
{
|
||||
// Teleportation to either dimension not enabled, don't bother proceeding
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if (GetWorld()->GetLinkedOverworldName().empty())
|
||||
{
|
||||
// Overworld teleportation disabled, abort
|
||||
return false;
|
||||
}
|
||||
|
||||
int X = POSX_TOINT, Y = POSY_TOINT, Z = POSZ_TOINT;
|
||||
if ((Y > 0) && (Y < cChunkDef::Height))
|
||||
{
|
||||
switch (GetWorld()->GetBlock(X, Y, Z))
|
||||
{
|
||||
case E_BLOCK_NETHER_PORTAL:
|
||||
{
|
||||
if (m_PortalCooldownData.m_ShouldPreventTeleportation)
|
||||
{
|
||||
// Just exited a portal, don't teleport again
|
||||
return false;
|
||||
}
|
||||
|
||||
if (IsPlayer() && !((cPlayer *)this)->IsGameModeCreative() && m_PortalCooldownData.m_TicksDelayed != 80)
|
||||
{
|
||||
// Delay teleportation for four seconds if the entity is a non-creative player
|
||||
m_PortalCooldownData.m_TicksDelayed++;
|
||||
return false;
|
||||
}
|
||||
m_PortalCooldownData.m_TicksDelayed = 0;
|
||||
|
||||
switch (GetWorld()->GetDimension())
|
||||
{
|
||||
case dimNether:
|
||||
{
|
||||
if (GetWorld()->GetLinkedOverworldName().empty())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
m_PortalCooldownData.m_ShouldPreventTeleportation = true; // Stop portals from working on respawn
|
||||
|
||||
if (IsPlayer())
|
||||
{
|
||||
((cPlayer *)this)->GetClientHandle()->SendRespawn(dimOverworld); // Send a respawn packet before world is loaded/generated so the client isn't left in limbo
|
||||
}
|
||||
|
||||
return MoveToWorld(cRoot::Get()->CreateAndInitializeWorld(GetWorld()->GetLinkedOverworldName()), false);
|
||||
}
|
||||
case dimOverworld:
|
||||
{
|
||||
if (GetWorld()->GetNetherWorldName().empty())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
m_PortalCooldownData.m_ShouldPreventTeleportation = true;
|
||||
|
||||
if (IsPlayer())
|
||||
{
|
||||
((cPlayer *)this)->AwardAchievement(achEnterPortal);
|
||||
((cPlayer *)this)->GetClientHandle()->SendRespawn(dimNether);
|
||||
}
|
||||
|
||||
return MoveToWorld(cRoot::Get()->CreateAndInitializeWorld(GetWorld()->GetNetherWorldName(), dimNether, GetWorld()->GetName()), false);
|
||||
}
|
||||
default: return false;
|
||||
}
|
||||
}
|
||||
case E_BLOCK_END_PORTAL:
|
||||
{
|
||||
if (m_PortalCooldownData.m_ShouldPreventTeleportation)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (GetWorld()->GetDimension())
|
||||
{
|
||||
case dimEnd:
|
||||
{
|
||||
if (GetWorld()->GetLinkedOverworldName().empty())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
m_PortalCooldownData.m_ShouldPreventTeleportation = true;
|
||||
|
||||
if (IsPlayer())
|
||||
{
|
||||
cPlayer * Player = (cPlayer *)this;
|
||||
Player->TeleportToCoords(Player->GetLastBedPos().x, Player->GetLastBedPos().y, Player->GetLastBedPos().z);
|
||||
Player->GetClientHandle()->SendRespawn(dimOverworld);
|
||||
}
|
||||
|
||||
return MoveToWorld(cRoot::Get()->CreateAndInitializeWorld(GetWorld()->GetLinkedOverworldName()), false);
|
||||
}
|
||||
case dimOverworld:
|
||||
{
|
||||
if (GetWorld()->GetEndWorldName().empty())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
m_PortalCooldownData.m_ShouldPreventTeleportation = true;
|
||||
|
||||
if (IsPlayer())
|
||||
{
|
||||
((cPlayer *)this)->AwardAchievement(achEnterTheEnd);
|
||||
((cPlayer *)this)->GetClientHandle()->SendRespawn(dimEnd);
|
||||
}
|
||||
|
||||
return MoveToWorld(cRoot::Get()->CreateAndInitializeWorld(GetWorld()->GetEndWorldName(), dimEnd, GetWorld()->GetName()), false);
|
||||
}
|
||||
default: return false;
|
||||
}
|
||||
}
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
|
||||
// Allow portals to work again
|
||||
m_PortalCooldownData.m_ShouldPreventTeleportation = false;
|
||||
m_PortalCooldownData.m_TicksDelayed = 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
bool cEntity::DoMoveToWorld(cWorld * a_World, bool a_ShouldSendRespawn)
|
||||
{
|
||||
UNUSED(a_ShouldSendRespawn);
|
||||
ASSERT(a_World != NULL);
|
||||
|
||||
if (GetWorld() == a_World)
|
||||
{
|
||||
// Don't move to same world
|
||||
return false;
|
||||
}
|
||||
|
||||
// Remove all links to the old world
|
||||
SetWorldTravellingFrom(GetWorld()); // cChunk::Tick() handles entity removal
|
||||
GetWorld()->BroadcastDestroyEntity(*this);
|
||||
|
||||
// Queue add to new world
|
||||
a_World->AddEntity(this);
|
||||
SetWorld(a_World);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
bool cEntity::MoveToWorld(const AString & a_WorldName, bool a_ShouldSendRespawn)
|
||||
{
|
||||
cWorld * World = cRoot::Get()->GetWorld(a_WorldName);
|
||||
if (World == NULL)
|
||||
{
|
||||
LOG("%s: Couldn't find world \"%s\".", __FUNCTION__, a_WorldName.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
return DoMoveToWorld(World, a_ShouldSendRespawn);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cEntity::SetSwimState(cChunk & a_Chunk)
|
||||
{
|
||||
int RelY = (int)floor(GetPosY() + 0.1);
|
||||
|
@ -128,20 +128,20 @@ public:
|
||||
esFireworkExploding = 17,
|
||||
} ;
|
||||
|
||||
enum
|
||||
{
|
||||
FIRE_TICKS_PER_DAMAGE = 10, ///< How many ticks to wait between damaging an entity when it stands in fire
|
||||
FIRE_DAMAGE = 1, ///< How much damage to deal when standing in fire
|
||||
LAVA_TICKS_PER_DAMAGE = 10, ///< How many ticks to wait between damaging an entity when it stands in lava
|
||||
LAVA_DAMAGE = 5, ///< How much damage to deal when standing in lava
|
||||
BURN_TICKS_PER_DAMAGE = 20, ///< How many ticks to wait between damaging an entity when it is burning
|
||||
BURN_DAMAGE = 1, ///< How much damage to deal when the entity is burning
|
||||
BURN_TICKS = 200, ///< How long to keep an entity burning after it has stood in lava / fire
|
||||
MAX_AIR_LEVEL = 300, ///< Maximum air an entity can have
|
||||
DROWNING_TICKS = 20, ///< Number of ticks per heart of damage
|
||||
VOID_BOUNDARY = -46, ///< At what position Y to begin applying void damage
|
||||
FALL_DAMAGE_HEIGHT = 4 ///< At what position Y fall damage is applied
|
||||
} ;
|
||||
static const int FIRE_TICKS_PER_DAMAGE = 10; ///< Ticks to wait between damaging an entity when it stands in fire
|
||||
static const int FIRE_DAMAGE = 1; ///< Damage to deal when standing in fire
|
||||
static const int LAVA_TICKS_PER_DAMAGE = 10; ///< Ticks to wait between damaging an entity when it stands in lava
|
||||
static const int LAVA_DAMAGE = 5; ///< Damage to deal when standing in lava
|
||||
static const int BURN_TICKS_PER_DAMAGE = 20; ///< Ticks to wait between damaging an entity when it is burning
|
||||
static const int BURN_DAMAGE = 1; ///< Damage to deal when the entity is burning
|
||||
|
||||
static const int BURN_TICKS = 200; ///< Ticks to keep an entity burning after it has stood in lava / fire
|
||||
|
||||
static const int MAX_AIR_LEVEL = 300; ///< Maximum air an entity can have
|
||||
static const int DROWNING_TICKS = 20; ///< Number of ticks per heart of damage
|
||||
|
||||
static const int VOID_BOUNDARY = -46; ///< Y position to begin applying void damage
|
||||
static const int FALL_DAMAGE_HEIGHT = 4; ///< Y difference after which fall damage is applied
|
||||
|
||||
cEntity(eEntityType a_EntityType, double a_X, double a_Y, double a_Z, double a_Width, double a_Height);
|
||||
virtual ~cEntity();
|
||||
@ -336,6 +336,11 @@ public:
|
||||
|
||||
/** Detects the time for application of cacti damage */
|
||||
virtual void DetectCacti(void);
|
||||
|
||||
/** Detects whether we are in a portal block and begins teleportation procedures if so
|
||||
Returns true if MoveToWorld() was called, false if not
|
||||
*/
|
||||
virtual bool DetectPortal(void);
|
||||
|
||||
/// Handles when the entity is in the void
|
||||
virtual void TickInVoid(cChunk & a_Chunk);
|
||||
@ -378,8 +383,22 @@ public:
|
||||
|
||||
/// Teleports to the coordinates specified
|
||||
virtual void TeleportToCoords(double a_PosX, double a_PosY, double a_PosZ);
|
||||
|
||||
/** Moves entity to specified world, taking a world pointer */
|
||||
bool MoveToWorld(cWorld * a_World, bool a_ShouldSendRespawn = true) { return DoMoveToWorld(a_World, a_ShouldSendRespawn); }
|
||||
|
||||
/** Moves entity to specified world, taking a world name */
|
||||
bool MoveToWorld(const AString & a_WorldName, bool a_ShouldSendRespawn = true);
|
||||
|
||||
// tolua_end
|
||||
|
||||
virtual bool DoMoveToWorld(cWorld * a_World, bool a_ShouldSendRespawn);
|
||||
|
||||
/** Returns if the entity is travelling away from a specified world */
|
||||
bool IsWorldTravellingFrom(cWorld * a_World) const { return (m_WorldTravellingFrom == a_World); }
|
||||
|
||||
/** Sets the world the entity will be leaving */
|
||||
void SetWorldTravellingFrom(cWorld * a_World) { m_WorldTravellingFrom = a_World; }
|
||||
|
||||
/// Updates clients of changes in the entity.
|
||||
virtual void BroadcastMovementUpdate(const cClientHandle * a_Exclude = NULL);
|
||||
@ -482,6 +501,12 @@ protected:
|
||||
/** True when entity is initialised (Initialize()) and false when destroyed pending deletion (Destroy()) */
|
||||
bool m_IsInitialized;
|
||||
|
||||
/** World entity is travelling from
|
||||
Set to a valid world pointer by MoveToWorld; reset to NULL when the entity is removed from the old world
|
||||
Can't be a simple boolean as context switches between worlds may leave the new chunk processing (and therefore immediately removing) the entity before the old chunk could remove it
|
||||
*/
|
||||
cWorld * m_WorldTravellingFrom;
|
||||
|
||||
eEntityType m_EntityType;
|
||||
|
||||
cWorld * m_World;
|
||||
@ -503,7 +528,6 @@ protected:
|
||||
|
||||
/// Time, in ticks, since the last damage dealt by the void. Reset to zero when moving out of the void.
|
||||
int m_TicksSinceLastVoidDamage;
|
||||
|
||||
|
||||
/** Does the actual speed-setting. The default implementation just sets the member variable value;
|
||||
overrides can provide further processing, such as forcing players to move at the given speed. */
|
||||
@ -523,6 +547,21 @@ protected:
|
||||
/** Air level of a mobile */
|
||||
int m_AirLevel;
|
||||
int m_AirTickTimer;
|
||||
|
||||
/** Structure storing the portal delay timer and cooldown boolean */
|
||||
struct sPortalCooldownData
|
||||
{
|
||||
/** Ticks since entry of portal, used to delay teleportation */
|
||||
unsigned short m_TicksDelayed;
|
||||
|
||||
/** Whether the entity has just exited the portal, and should therefore not be teleported again
|
||||
This prevents teleportation loops, and is reset when the entity has moved out of the portal
|
||||
*/
|
||||
bool m_ShouldPreventTeleportation;
|
||||
};
|
||||
|
||||
/** Portal delay timer and cooldown boolean data */
|
||||
sPortalCooldownData m_PortalCooldownData;
|
||||
|
||||
/** The number of ticks this entity has been alive for */
|
||||
long int m_TicksAlive;
|
||||
|
@ -88,13 +88,14 @@ cPlayer::cPlayer(cClientHandle* a_Client, const AString & a_PlayerName) :
|
||||
|
||||
m_PlayerName = a_PlayerName;
|
||||
|
||||
if (!LoadFromDisk())
|
||||
cWorld * World = NULL;
|
||||
if (!LoadFromDisk(World))
|
||||
{
|
||||
m_Inventory.Clear();
|
||||
cWorld * DefaultWorld = cRoot::Get()->GetDefaultWorld();
|
||||
SetPosX(DefaultWorld->GetSpawnX());
|
||||
SetPosY(DefaultWorld->GetSpawnY());
|
||||
SetPosZ(DefaultWorld->GetSpawnZ());
|
||||
SetPosX(World->GetSpawnX());
|
||||
SetPosY(World->GetSpawnY());
|
||||
SetPosZ(World->GetSpawnZ());
|
||||
SetBedPos(Vector3i((int)World->GetSpawnX(), (int)World->GetSpawnY(), (int)World->GetSpawnZ()));
|
||||
|
||||
LOGD("Player \"%s\" is connecting for the first time, spawning at default world spawn {%.2f, %.2f, %.2f}",
|
||||
a_PlayerName.c_str(), GetPosX(), GetPosY(), GetPosZ()
|
||||
@ -107,11 +108,6 @@ cPlayer::cPlayer(cClientHandle* a_Client, const AString & a_PlayerName) :
|
||||
|
||||
if (m_GameMode == gmNotSet)
|
||||
{
|
||||
cWorld * World = cRoot::Get()->GetWorld(GetLoadedWorldName());
|
||||
if (World == NULL)
|
||||
{
|
||||
World = cRoot::Get()->GetDefaultWorld();
|
||||
}
|
||||
if (World->IsGameModeCreative())
|
||||
{
|
||||
m_CanFly = true;
|
||||
@ -140,8 +136,6 @@ cPlayer::~cPlayer(void)
|
||||
|
||||
SaveToDisk();
|
||||
|
||||
m_World->RemovePlayer( this);
|
||||
|
||||
m_ClientHandle = NULL;
|
||||
|
||||
delete m_InventoryWindow;
|
||||
@ -157,8 +151,6 @@ cPlayer::~cPlayer(void)
|
||||
void cPlayer::Destroyed()
|
||||
{
|
||||
CloseWindow(false);
|
||||
|
||||
m_ClientHandle = NULL;
|
||||
}
|
||||
|
||||
|
||||
@ -890,7 +882,7 @@ void cPlayer::KilledBy(TakeDamageInfo & a_TDI)
|
||||
m_World->SpawnItemPickups(Pickups, GetPosX(), GetPosY(), GetPosZ(), 10);
|
||||
SaveToDisk(); // Save it, yeah the world is a tough place !
|
||||
|
||||
if (a_TDI.Attacker == NULL)
|
||||
if ((a_TDI.Attacker == NULL) && m_World->ShouldBroadcastDeathMessages())
|
||||
{
|
||||
AString DamageText;
|
||||
switch (a_TDI.DamageType)
|
||||
@ -983,12 +975,12 @@ void cPlayer::Respawn(void)
|
||||
m_LifetimeTotalXp = 0;
|
||||
// ToDo: send score to client? How?
|
||||
|
||||
m_ClientHandle->SendRespawn(*m_World, true);
|
||||
m_ClientHandle->SendRespawn(GetWorld()->GetDimension(), true);
|
||||
|
||||
// Extinguish the fire:
|
||||
StopBurning();
|
||||
|
||||
TeleportToCoords(GetWorld()->GetSpawnX(), GetWorld()->GetSpawnY(), GetWorld()->GetSpawnZ());
|
||||
TeleportToCoords(GetLastBedPos().x, GetLastBedPos().y, GetLastBedPos().z);
|
||||
|
||||
SetVisible(true);
|
||||
}
|
||||
@ -1208,11 +1200,13 @@ unsigned int cPlayer::AwardAchievement(const eStatistic a_Ach)
|
||||
}
|
||||
else
|
||||
{
|
||||
// First time, announce it
|
||||
cCompositeChat Msg;
|
||||
Msg.SetMessageType(mtSuccess);
|
||||
Msg.AddShowAchievementPart(GetName(), cStatInfo::GetName(a_Ach));
|
||||
m_World->BroadcastChat(Msg);
|
||||
if (m_World->ShouldBroadcastAchievementMessages())
|
||||
{
|
||||
cCompositeChat Msg;
|
||||
Msg.SetMessageType(mtSuccess);
|
||||
Msg.AddShowAchievementPart(GetName(), cStatInfo::GetName(a_Ach));
|
||||
m_World->BroadcastChat(Msg);
|
||||
}
|
||||
|
||||
// Increment the statistic
|
||||
StatValue New = m_Stats.AddValue(a_Ach);
|
||||
@ -1617,29 +1611,29 @@ void cPlayer::TossItems(const cItems & a_Items)
|
||||
|
||||
|
||||
|
||||
bool cPlayer::MoveToWorld(const char * a_WorldName)
|
||||
bool cPlayer::DoMoveToWorld(cWorld * a_World, bool a_ShouldSendRespawn)
|
||||
{
|
||||
cWorld * World = cRoot::Get()->GetWorld(a_WorldName);
|
||||
if (World == NULL)
|
||||
ASSERT(a_World != NULL);
|
||||
|
||||
if (GetWorld() == a_World)
|
||||
{
|
||||
LOG("%s: Couldn't find world \"%s\".", __FUNCTION__, a_WorldName);
|
||||
// Don't move to same world
|
||||
return false;
|
||||
}
|
||||
|
||||
// Send the respawn packet:
|
||||
if (m_ClientHandle != NULL)
|
||||
if (a_ShouldSendRespawn && (m_ClientHandle != NULL))
|
||||
{
|
||||
m_ClientHandle->SendRespawn(*World);
|
||||
m_ClientHandle->SendRespawn(a_World->GetDimension());
|
||||
}
|
||||
|
||||
// Remove all links to the old world
|
||||
m_World->RemovePlayer(this);
|
||||
|
||||
// If the dimension is different, we can send the respawn packet
|
||||
// http://wiki.vg/Protocol#0x09 says "don't send if dimension is the same" as of 2013_07_02
|
||||
// Remove player from the old world
|
||||
SetWorldTravellingFrom(GetWorld()); // cChunk handles entity removal
|
||||
GetWorld()->RemovePlayer(this, false);
|
||||
|
||||
// Queue adding player to the new world, including all the necessary adjustments to the object
|
||||
World->AddPlayer(this);
|
||||
a_World->AddPlayer(this);
|
||||
SetWorld(a_World); // Chunks may be streamed before cWorld::AddPlayer() sets the world to the new value
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -1687,13 +1681,12 @@ void cPlayer::LoadPermissionsFromDisk()
|
||||
|
||||
|
||||
|
||||
|
||||
bool cPlayer::LoadFromDisk(void)
|
||||
bool cPlayer::LoadFromDisk(cWorldPtr & a_World)
|
||||
{
|
||||
LoadPermissionsFromDisk();
|
||||
|
||||
// Load from the UUID file:
|
||||
if (LoadFromFile(GetUUIDFileName(m_UUID)))
|
||||
if (LoadFromFile(GetUUIDFileName(m_UUID), a_World))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
@ -1704,7 +1697,7 @@ bool cPlayer::LoadFromDisk(void)
|
||||
if (cRoot::Get()->GetServer()->ShouldLoadOfflinePlayerData())
|
||||
{
|
||||
OfflineUsage = "";
|
||||
if (LoadFromFile(GetUUIDFileName(OfflineUUID)))
|
||||
if (LoadFromFile(GetUUIDFileName(OfflineUUID), a_World))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
@ -1714,7 +1707,7 @@ bool cPlayer::LoadFromDisk(void)
|
||||
if (cRoot::Get()->GetServer()->ShouldLoadNamedPlayerData())
|
||||
{
|
||||
AString OldStyleFileName = Printf("players/%s.json", GetName().c_str());
|
||||
if (LoadFromFile(OldStyleFileName))
|
||||
if (LoadFromFile(OldStyleFileName, a_World))
|
||||
{
|
||||
// Save in new format and remove the old file
|
||||
if (SaveToDisk())
|
||||
@ -1729,6 +1722,11 @@ bool cPlayer::LoadFromDisk(void)
|
||||
LOG("Player data file not found for %s (%s, offline %s%s), will be reset to defaults.",
|
||||
GetName().c_str(), m_UUID.c_str(), OfflineUUID.c_str(), OfflineUsage
|
||||
);
|
||||
|
||||
if (a_World == NULL)
|
||||
{
|
||||
a_World = cRoot::Get()->GetDefaultWorld();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -1736,7 +1734,7 @@ bool cPlayer::LoadFromDisk(void)
|
||||
|
||||
|
||||
|
||||
bool cPlayer::LoadFromFile(const AString & a_FileName)
|
||||
bool cPlayer::LoadFromFile(const AString & a_FileName, cWorldPtr & a_World)
|
||||
{
|
||||
// Load the data from the file:
|
||||
cFile f;
|
||||
@ -1801,6 +1799,11 @@ bool cPlayer::LoadFromFile(const AString & a_FileName)
|
||||
cEnderChestEntity::LoadFromJson(root["enderchestinventory"], m_EnderChestContents);
|
||||
|
||||
m_LoadedWorldName = root.get("world", "world").asString();
|
||||
a_World = cRoot::Get()->GetWorld(GetLoadedWorldName(), true);
|
||||
|
||||
m_LastBedPos.x = root.get("SpawnX", a_World->GetSpawnX()).asInt();
|
||||
m_LastBedPos.y = root.get("SpawnY", a_World->GetSpawnY()).asInt();
|
||||
m_LastBedPos.z = root.get("SpawnZ", a_World->GetSpawnZ()).asInt();
|
||||
|
||||
// Load the player stats.
|
||||
// We use the default world name (like bukkit) because stats are shared between dimensions/worlds.
|
||||
@ -1808,7 +1811,7 @@ bool cPlayer::LoadFromFile(const AString & a_FileName)
|
||||
StatSerializer.Load();
|
||||
|
||||
LOGD("Player %s was read from file \"%s\", spawning at {%.2f, %.2f, %.2f} in world \"%s\"",
|
||||
GetName().c_str(), a_FileName.c_str(), GetPosX(), GetPosY(), GetPosZ(), m_LoadedWorldName.c_str()
|
||||
GetName().c_str(), a_FileName.c_str(), GetPosX(), GetPosY(), GetPosZ(), a_World->GetName().c_str()
|
||||
);
|
||||
|
||||
return true;
|
||||
@ -1820,7 +1823,6 @@ bool cPlayer::LoadFromFile(const AString & a_FileName)
|
||||
|
||||
bool cPlayer::SaveToDisk()
|
||||
{
|
||||
cFile::CreateFolder(FILE_IO_PREFIX + AString("players"));
|
||||
cFile::CreateFolder(FILE_IO_PREFIX + AString("players/") + m_UUID.substr(0, 2));
|
||||
|
||||
// create the JSON data
|
||||
@ -1855,6 +1857,10 @@ bool cPlayer::SaveToDisk()
|
||||
root["foodExhaustion"] = m_FoodExhaustionLevel;
|
||||
root["isflying"] = IsFlying();
|
||||
root["lastknownname"] = GetName();
|
||||
root["SpawnX"] = GetLastBedPos().x;
|
||||
root["SpawnY"] = GetLastBedPos().y;
|
||||
root["SpawnZ"] = GetLastBedPos().z;
|
||||
|
||||
if (m_World != NULL)
|
||||
{
|
||||
root["world"] = m_World->GetName();
|
||||
@ -1931,14 +1937,14 @@ cPlayer::StringList cPlayer::GetResolvedPermissions()
|
||||
|
||||
|
||||
|
||||
void cPlayer::UseEquippedItem(void)
|
||||
void cPlayer::UseEquippedItem(int a_Amount)
|
||||
{
|
||||
if (IsGameModeCreative()) // No damage in creative
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (GetInventory().DamageEquippedItem())
|
||||
if (GetInventory().DamageEquippedItem(a_Amount))
|
||||
{
|
||||
m_World->BroadcastSoundEffect("random.break", GetPosX(), GetPosY(), GetPosZ(), 0.5f, (float)(0.75 + ((float)((GetUniqueID() * 23) % 32)) / 64));
|
||||
}
|
||||
|
@ -131,7 +131,7 @@ public:
|
||||
|
||||
inline const cItem & GetEquippedItem(void) const { return GetInventory().GetEquippedItem(); } // tolua_export
|
||||
|
||||
/** Returns whether the player is climbing (ladders, vines e.t.c). */
|
||||
/** Returns whether the player is climbing (ladders, vines etc.) */
|
||||
bool IsClimbing(void) const;
|
||||
|
||||
virtual void TeleportToCoords(double a_PosX, double a_PosY, double a_PosZ) override;
|
||||
@ -325,7 +325,7 @@ public:
|
||||
virtual void KilledBy(TakeDamageInfo & a_TDI) override;
|
||||
|
||||
virtual void Killed(cEntity * a_Victim) override;
|
||||
|
||||
|
||||
void Respawn(void); // tolua_export
|
||||
|
||||
void SetVisible( bool a_bVisible); // tolua_export
|
||||
@ -333,30 +333,36 @@ public:
|
||||
|
||||
/** Moves the player to the specified world.
|
||||
Returns true if successful, false on failure (world not found). */
|
||||
bool MoveToWorld(const char * a_WorldName); // tolua_export
|
||||
virtual bool DoMoveToWorld(cWorld * a_World, bool a_ShouldSendRespawn) override;
|
||||
|
||||
/** Saves all player data, such as inventory, to JSON */
|
||||
bool SaveToDisk(void);
|
||||
|
||||
typedef cWorld * cWorldPtr;
|
||||
|
||||
/** Loads the player data from the disk file.
|
||||
Returns true on success, false on failure. */
|
||||
bool LoadFromDisk(void);
|
||||
/** Loads the player data from the disk file
|
||||
Sets a_World to the world where the player will spawn, based on the stored world name or the default world by calling LoadFromFile()
|
||||
Returns true on success, false on failure
|
||||
*/
|
||||
bool LoadFromDisk(cWorldPtr & a_World);
|
||||
|
||||
/** Loads the player data from the specified file.
|
||||
Returns true on success, false on failure. */
|
||||
bool LoadFromFile(const AString & a_FileName);
|
||||
/** Loads the player data from the specified file
|
||||
Sets a_World to the world where the player will spawn, based on the stored world name or the default world
|
||||
Returns true on success, false on failure
|
||||
*/
|
||||
bool LoadFromFile(const AString & a_FileName, cWorldPtr & a_World);
|
||||
|
||||
void LoadPermissionsFromDisk(void); // tolua_export
|
||||
|
||||
const AString & GetLoadedWorldName() { return m_LoadedWorldName; }
|
||||
|
||||
void UseEquippedItem(void);
|
||||
void UseEquippedItem(int a_Amount = 1);
|
||||
|
||||
void SendHealth(void);
|
||||
|
||||
void SendExperience(void);
|
||||
|
||||
// In UI windows, the item that the player is dragging:
|
||||
bool IsDraggingItem(void) const { return !m_DraggingItem.IsEmpty(); }
|
||||
/** In UI windows, get the item that the player is dragging */
|
||||
cItem & GetDraggingItem(void) {return m_DraggingItem; }
|
||||
|
||||
// In UI windows, when inventory-painting:
|
||||
@ -404,11 +410,20 @@ public:
|
||||
/** If true the player can fly even when he's not in creative. */
|
||||
void SetCanFly(bool a_CanFly);
|
||||
|
||||
/** Gets the last position that the player slept in
|
||||
This is initialised to the world spawn point if the player has not slept in a bed as of yet
|
||||
*/
|
||||
Vector3i GetLastBedPos(void) const { return m_LastBedPos; }
|
||||
|
||||
/** Sets the player's bed (home) position */
|
||||
void SetBedPos(const Vector3i & a_Pos) { m_LastBedPos = a_Pos; }
|
||||
|
||||
/** Update movement-related statistics. */
|
||||
void UpdateMovementStats(const Vector3d & a_DeltaPos);
|
||||
|
||||
/** Returns wheter the player can fly or not. */
|
||||
virtual bool CanFly(void) const { return m_CanFly; }
|
||||
|
||||
// tolua_end
|
||||
|
||||
// cEntity overrides:
|
||||
@ -466,6 +481,9 @@ protected:
|
||||
cWindow * m_CurrentWindow;
|
||||
cWindow * m_InventoryWindow;
|
||||
|
||||
/** The player's last saved bed position */
|
||||
Vector3i m_LastBedPos;
|
||||
|
||||
char m_Color;
|
||||
|
||||
eGameMode m_GameMode;
|
||||
@ -540,7 +558,6 @@ protected:
|
||||
If no ClientHandle is given, the UUID is initialized to empty. */
|
||||
AString m_UUID;
|
||||
|
||||
|
||||
/** Sets the speed and sends it to the client, so that they are forced to move so. */
|
||||
virtual void DoSetSpeed(double a_SpeedX, double a_SpeedY, double a_SpeedZ) override;
|
||||
|
||||
|
@ -44,6 +44,9 @@ public:
|
||||
/// Returns a random float in the range [0 .. a_Range]; a_Range must be less than 1M; a_Salt is additional source of randomness
|
||||
float NextFloat(float a_Range, int a_Salt);
|
||||
|
||||
/** Returns a random float between 0 and 1. */
|
||||
float NextFloat(void) { return NextFloat(1); };
|
||||
|
||||
/** Returns a random int in the range [a_Begin .. a_End] */
|
||||
int GenerateRandomInteger(int a_Begin, int a_End);
|
||||
|
||||
|
@ -95,7 +95,7 @@ void cBioGenConstant::GenBiomes(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap
|
||||
|
||||
void cBioGenConstant::InitializeBiomeGen(cIniFile & a_IniFile)
|
||||
{
|
||||
AString Biome = a_IniFile.GetValueSet("Generator", "ConstantBiome", "Plains");
|
||||
AString Biome = a_IniFile.GetValueSet("Generator", "ConstantBiome", "");
|
||||
m_Biome = StringToBiome(Biome);
|
||||
if (m_Biome == biInvalidBiome)
|
||||
{
|
||||
|
@ -25,6 +25,7 @@ SET (SRCS
|
||||
PrefabPiecePool.cpp
|
||||
RainbowRoadsGen.cpp
|
||||
Ravines.cpp
|
||||
RoughRavines.cpp
|
||||
StructGen.cpp
|
||||
TestRailsGen.cpp
|
||||
Trees.cpp
|
||||
@ -52,6 +53,7 @@ SET (HDRS
|
||||
PrefabPiecePool.h
|
||||
RainbowRoadsGen.h
|
||||
Ravines.h
|
||||
RoughRavines.h
|
||||
StructGen.h
|
||||
TestRailsGen.h
|
||||
Trees.h
|
||||
|
@ -497,29 +497,9 @@ void cCaveTunnel::ProcessChunk(
|
||||
int SqDist = (DifX - x) * (DifX - x) + (DifY - y) * (DifY - y) + (DifZ - z) * (DifZ - z);
|
||||
if (4 * SqDist <= SqRad)
|
||||
{
|
||||
switch (cChunkDef::GetBlock(a_BlockTypes, x, y, z))
|
||||
if (cBlockInfo::CanBeTerraformed(cChunkDef::GetBlock(a_BlockTypes, x, y, z)))
|
||||
{
|
||||
// Only carve out these specific block types
|
||||
case E_BLOCK_DIRT:
|
||||
case E_BLOCK_GRASS:
|
||||
case E_BLOCK_STONE:
|
||||
case E_BLOCK_COBBLESTONE:
|
||||
case E_BLOCK_GRAVEL:
|
||||
case E_BLOCK_SAND:
|
||||
case E_BLOCK_SANDSTONE:
|
||||
case E_BLOCK_SOULSAND:
|
||||
case E_BLOCK_NETHERRACK:
|
||||
case E_BLOCK_COAL_ORE:
|
||||
case E_BLOCK_IRON_ORE:
|
||||
case E_BLOCK_GOLD_ORE:
|
||||
case E_BLOCK_DIAMOND_ORE:
|
||||
case E_BLOCK_REDSTONE_ORE:
|
||||
case E_BLOCK_REDSTONE_ORE_GLOWING:
|
||||
{
|
||||
cChunkDef::SetBlock(a_BlockTypes, x, y, z, E_BLOCK_AIR);
|
||||
break;
|
||||
}
|
||||
default: break;
|
||||
cChunkDef::SetBlock(a_BlockTypes, x, y, z, E_BLOCK_AIR);
|
||||
}
|
||||
}
|
||||
} // for y
|
||||
|
@ -20,7 +20,6 @@ cChunkDesc::cChunkDesc(int a_ChunkX, int a_ChunkZ) :
|
||||
m_bUseDefaultBiomes(true),
|
||||
m_bUseDefaultHeight(true),
|
||||
m_bUseDefaultComposition(true),
|
||||
m_bUseDefaultStructures(true),
|
||||
m_bUseDefaultFinish(true)
|
||||
{
|
||||
m_BlockArea.Create(cChunkDef::Width, cChunkDef::Height, cChunkDef::Width);
|
||||
@ -207,26 +206,6 @@ bool cChunkDesc::IsUsingDefaultComposition(void) const
|
||||
|
||||
|
||||
|
||||
void cChunkDesc::SetUseDefaultStructures(bool a_bUseDefaultStructures)
|
||||
{
|
||||
LOGWARNING("%s: Structures are no longer accounted for, use Finishers instead", __FUNCTION__);
|
||||
m_bUseDefaultStructures = a_bUseDefaultStructures;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
bool cChunkDesc::IsUsingDefaultStructures(void) const
|
||||
{
|
||||
LOGWARNING("%s: Structures are no longer accounted for, use Finishers instead", __FUNCTION__);
|
||||
return m_bUseDefaultStructures;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cChunkDesc::SetUseDefaultFinish(bool a_bUseDefaultFinish)
|
||||
{
|
||||
m_bUseDefaultFinish = a_bUseDefaultFinish;
|
||||
|
@ -68,8 +68,6 @@ public:
|
||||
bool IsUsingDefaultHeight(void) const;
|
||||
void SetUseDefaultComposition(bool a_bUseDefaultComposition);
|
||||
bool IsUsingDefaultComposition(void) const;
|
||||
void SetUseDefaultStructures(bool a_bUseDefaultStructures);
|
||||
bool IsUsingDefaultStructures(void) const;
|
||||
void SetUseDefaultFinish(bool a_bUseDefaultFinish);
|
||||
bool IsUsingDefaultFinish(void) const;
|
||||
|
||||
@ -214,7 +212,6 @@ private:
|
||||
bool m_bUseDefaultBiomes;
|
||||
bool m_bUseDefaultHeight;
|
||||
bool m_bUseDefaultComposition;
|
||||
bool m_bUseDefaultStructures;
|
||||
bool m_bUseDefaultFinish;
|
||||
} ; // tolua_export
|
||||
|
||||
|
@ -52,7 +52,7 @@ bool cChunkGenerator::Start(cPluginInterface & a_PluginInterface, cChunkSink & a
|
||||
m_ChunkSink = &a_ChunkSink;
|
||||
|
||||
MTRand rnd;
|
||||
m_Seed = a_IniFile.GetValueSetI("Seed", "Seed", rnd.randInt());
|
||||
m_Seed = a_IniFile.GetValueSetI("Seed", "Seed", (int)rnd.randInt());
|
||||
AString GeneratorName = a_IniFile.GetValueSet("Generator", "Generator", "Composable");
|
||||
|
||||
if (NoCaseCompare(GeneratorName, "Noise3D") == 0)
|
||||
|
@ -638,7 +638,7 @@ void cCompoGenNether::ComposeTerrain(cChunkDesc & a_ChunkDesc)
|
||||
CeilingDisguise = -CeilingDisguise;
|
||||
}
|
||||
|
||||
int CeilingDisguiseHeight = Height - 2 - CeilingDisguise * 3;
|
||||
int CeilingDisguiseHeight = Height - 2 - (int)CeilingDisguise * 3;
|
||||
|
||||
for (int y = Height - 1; y > CeilingDisguiseHeight; y--)
|
||||
{
|
||||
|
@ -26,6 +26,7 @@
|
||||
#include "POCPieceGenerator.h"
|
||||
#include "RainbowRoadsGen.h"
|
||||
#include "Ravines.h"
|
||||
#include "RoughRavines.h"
|
||||
#include "TestRailsGen.h"
|
||||
#include "UnderwaterBaseGen.h"
|
||||
#include "VillageGen.h"
|
||||
@ -44,7 +45,6 @@ cTerrainCompositionGen * cTerrainCompositionGen::CreateCompositionGen(cIniFile &
|
||||
{
|
||||
LOGWARN("[Generator] CompositionGen value not set in world.ini, using \"Biomal\".");
|
||||
CompoGenName = "Biomal";
|
||||
a_IniFile.SetValue("Generator", "CompositionGen", CompoGenName);
|
||||
}
|
||||
|
||||
cTerrainCompositionGen * res = NULL;
|
||||
@ -98,7 +98,6 @@ cTerrainCompositionGen * cTerrainCompositionGen::CreateCompositionGen(cIniFile &
|
||||
else
|
||||
{
|
||||
LOGWARN("Unknown CompositionGen \"%s\", using \"Biomal\" instead.", CompoGenName.c_str());
|
||||
a_IniFile.DeleteValue("Generator", "CompositionGen");
|
||||
a_IniFile.SetValue("Generator", "CompositionGen", "Biomal");
|
||||
return CreateCompositionGen(a_IniFile, a_BiomeGen, a_HeightGen, a_Seed);
|
||||
}
|
||||
@ -296,19 +295,7 @@ void cComposableGenerator::InitFinishGens(cIniFile & a_IniFile)
|
||||
int Seed = m_ChunkGenerator.GetSeed();
|
||||
eDimension Dimension = StringToDimension(a_IniFile.GetValue("General", "Dimension", "Overworld"));
|
||||
|
||||
// Older configuration used "Structures" in addition to "Finishers"; we don't distinguish between the two anymore (#398)
|
||||
// Therefore, we load Structures from the ini file for compatibility, but move its contents over to Finishers:
|
||||
AString Structures = a_IniFile.GetValue("Generator", "Structures", "");
|
||||
AString Finishers = a_IniFile.GetValueSet("Generator", "Finishers", "Ravines, WormNestCaves, WaterLakes, LavaLakes, OreNests, Trees, SprinkleFoliage, Ice, Snow, Lilypads, BottomLava, DeadBushes, PreSimulator");
|
||||
if (!Structures.empty())
|
||||
{
|
||||
LOGINFO("[Generator].Structures is deprecated, moving the contents to [Generator].Finishers.");
|
||||
// Structures used to generate before Finishers, so place them first:
|
||||
Structures.append(", ");
|
||||
Finishers = Structures + Finishers;
|
||||
a_IniFile.SetValue("Generator", "Finishers", Finishers);
|
||||
}
|
||||
a_IniFile.DeleteValue("Generator", "Structures");
|
||||
AString Finishers = a_IniFile.GetValueSet("Generator", "Finishers", "");
|
||||
|
||||
// Create all requested finishers:
|
||||
AStringVector Str = StringSplitAndTrim(Finishers, ",");
|
||||
@ -323,7 +310,25 @@ void cComposableGenerator::InitFinishGens(cIniFile & a_IniFile)
|
||||
}
|
||||
else if (NoCaseCompare(*itr, "DeadBushes") == 0)
|
||||
{
|
||||
m_FinishGens.push_back(new cFinishGenSingleBiomeSingleTopBlock(Seed, E_BLOCK_DEAD_BUSH, biDesert, 2, E_BLOCK_SAND, E_BLOCK_SAND));
|
||||
// A list with all the allowed biomes.
|
||||
cFinishGenSingleTopBlock::BiomeList AllowedBiomes;
|
||||
AllowedBiomes.push_back(biDesert);
|
||||
AllowedBiomes.push_back(biDesertHills);
|
||||
AllowedBiomes.push_back(biDesertM);
|
||||
AllowedBiomes.push_back(biMesa);
|
||||
AllowedBiomes.push_back(biMesaBryce);
|
||||
AllowedBiomes.push_back(biMesaPlateau);
|
||||
AllowedBiomes.push_back(biMesaPlateauF);
|
||||
AllowedBiomes.push_back(biMesaPlateauFM);
|
||||
AllowedBiomes.push_back(biMesaPlateauM);
|
||||
|
||||
// A list with all the allowed blocks that can be below the dead bush.
|
||||
cFinishGenSingleTopBlock::BlockList AllowedBlocks;
|
||||
AllowedBlocks.push_back(E_BLOCK_SAND);
|
||||
AllowedBlocks.push_back(E_BLOCK_HARDENED_CLAY);
|
||||
AllowedBlocks.push_back(E_BLOCK_STAINED_CLAY);
|
||||
|
||||
m_FinishGens.push_back(new cFinishGenSingleTopBlock(Seed, E_BLOCK_DEAD_BUSH, AllowedBiomes, 2, AllowedBlocks));
|
||||
}
|
||||
else if (NoCaseCompare(*itr, "DirectOverhangs") == 0)
|
||||
{
|
||||
@ -370,7 +375,17 @@ void cComposableGenerator::InitFinishGens(cIniFile & a_IniFile)
|
||||
}
|
||||
else if (NoCaseCompare(*itr, "Lilypads") == 0)
|
||||
{
|
||||
m_FinishGens.push_back(new cFinishGenSingleBiomeSingleTopBlock(Seed, E_BLOCK_LILY_PAD, biSwampland, 4, E_BLOCK_WATER, E_BLOCK_STATIONARY_WATER));
|
||||
// A list with all the allowed biomes.
|
||||
cFinishGenSingleTopBlock::BiomeList AllowedBiomes;
|
||||
AllowedBiomes.push_back(biSwampland);
|
||||
AllowedBiomes.push_back(biSwamplandM);
|
||||
|
||||
// A list with all the allowed blocks that can be below the lilypad.
|
||||
cFinishGenSingleTopBlock::BlockList AllowedBlocks;
|
||||
AllowedBlocks.push_back(E_BLOCK_WATER);
|
||||
AllowedBlocks.push_back(E_BLOCK_STATIONARY_WATER);
|
||||
|
||||
m_FinishGens.push_back(new cFinishGenSingleTopBlock(Seed, E_BLOCK_LILY_PAD, AllowedBiomes, 4, AllowedBlocks));
|
||||
}
|
||||
else if (NoCaseCompare(*itr, "NetherClumpFoliage") == 0)
|
||||
{
|
||||
@ -393,20 +408,54 @@ void cComposableGenerator::InitFinishGens(cIniFile & a_IniFile)
|
||||
}
|
||||
else if (NoCaseCompare(*itr, "PreSimulator") == 0)
|
||||
{
|
||||
m_FinishGens.push_back(new cFinishGenPreSimulator);
|
||||
// Load the settings
|
||||
bool PreSimulateFallingBlocks = a_IniFile.GetValueSetB("Generator", "PreSimulatorFallingBlocks", true);
|
||||
bool PreSimulateWater = a_IniFile.GetValueSetB("Generator", "PreSimulatorWater", true);
|
||||
bool PreSimulateLava = a_IniFile.GetValueSetB("Generator", "PreSimulatorLava", true);
|
||||
|
||||
m_FinishGens.push_back(new cFinishGenPreSimulator(PreSimulateFallingBlocks, PreSimulateWater, PreSimulateLava));
|
||||
}
|
||||
else if (NoCaseCompare(*itr, "RainbowRoads") == 0)
|
||||
{
|
||||
int GridSize = a_IniFile.GetValueSetI("Generator", "RainbowRoadsGridSize", 512);
|
||||
int GridSize = a_IniFile.GetValueSetI("Generator", "RainbowRoadsGridSize", 512);
|
||||
int MaxOffset = a_IniFile.GetValueSetI("Generator", "RainbowRoadsMaxOffset", 128);
|
||||
int MaxDepth = a_IniFile.GetValueSetI("Generator", "RainbowRoadsMaxDepth", 30);
|
||||
int MaxSize = a_IniFile.GetValueSetI("Generator", "RainbowRoadsMaxSize", 260);
|
||||
int MaxDepth = a_IniFile.GetValueSetI("Generator", "RainbowRoadsMaxDepth", 30);
|
||||
int MaxSize = a_IniFile.GetValueSetI("Generator", "RainbowRoadsMaxSize", 260);
|
||||
m_FinishGens.push_back(new cRainbowRoadsGen(Seed, GridSize, MaxOffset, MaxDepth, MaxSize));
|
||||
}
|
||||
else if (NoCaseCompare(*itr, "Ravines") == 0)
|
||||
{
|
||||
m_FinishGens.push_back(new cStructGenRavines(Seed, 128));
|
||||
}
|
||||
else if (NoCaseCompare(*itr, "RoughRavines") == 0)
|
||||
{
|
||||
int GridSize = a_IniFile.GetValueSetI("Generator", "RoughRavinesGridSize", 256);
|
||||
int MaxOffset = a_IniFile.GetValueSetI("Generator", "RoughRavinesMaxOffset", 128);
|
||||
int MaxSize = a_IniFile.GetValueSetI("Generator", "RoughRavinesMaxSize", 128);
|
||||
int MinSize = a_IniFile.GetValueSetI("Generator", "RoughRavinesMinSize", 64);
|
||||
double MaxCenterWidth = a_IniFile.GetValueSetF("Generator", "RoughRavinesMaxCenterWidth", 8);
|
||||
double MinCenterWidth = a_IniFile.GetValueSetF("Generator", "RoughRavinesMinCenterWidth", 2);
|
||||
double MaxRoughness = a_IniFile.GetValueSetF("Generator", "RoughRavinesMaxRoughness", 0.2);
|
||||
double MinRoughness = a_IniFile.GetValueSetF("Generator", "RoughRavinesMinRoughness", 0.05);
|
||||
double MaxFloorHeightEdge = a_IniFile.GetValueSetF("Generator", "RoughRavinesMaxFloorHeightEdge", 8);
|
||||
double MinFloorHeightEdge = a_IniFile.GetValueSetF("Generator", "RoughRavinesMinFloorHeightEdge", 30);
|
||||
double MaxFloorHeightCenter = a_IniFile.GetValueSetF("Generator", "RoughRavinesMaxFloorHeightCenter", 20);
|
||||
double MinFloorHeightCenter = a_IniFile.GetValueSetF("Generator", "RoughRavinesMinFloorHeightCenter", 6);
|
||||
double MaxCeilingHeightEdge = a_IniFile.GetValueSetF("Generator", "RoughRavinesMaxCeilingHeightEdge", 56);
|
||||
double MinCeilingHeightEdge = a_IniFile.GetValueSetF("Generator", "RoughRavinesMinCeilingHeightEdge", 38);
|
||||
double MaxCeilingHeightCenter = a_IniFile.GetValueSetF("Generator", "RoughRavinesMaxCeilingHeightCenter", 58);
|
||||
double MinCeilingHeightCenter = a_IniFile.GetValueSetF("Generator", "RoughRavinesMinCeilingHeightCenter", 36);
|
||||
m_FinishGens.push_back(new cRoughRavines(
|
||||
Seed, MaxSize, MinSize,
|
||||
(float)MaxCenterWidth, (float)MinCenterWidth,
|
||||
(float)MaxRoughness, (float)MinRoughness,
|
||||
(float)MaxFloorHeightEdge, (float)MinFloorHeightEdge,
|
||||
(float)MaxFloorHeightCenter, (float)MinFloorHeightCenter,
|
||||
(float)MaxCeilingHeightEdge, (float)MinCeilingHeightEdge,
|
||||
(float)MaxCeilingHeightCenter, (float)MinCeilingHeightCenter,
|
||||
GridSize, MaxOffset
|
||||
));
|
||||
}
|
||||
else if (NoCaseCompare(*itr, "Snow") == 0)
|
||||
{
|
||||
m_FinishGens.push_back(new cFinishGenSnow);
|
||||
|
@ -809,7 +809,7 @@ void cDistortedHeightmap::FillColumnPattern(cChunkDesc & a_ChunkDesc, int a_RelX
|
||||
}
|
||||
|
||||
// Select the ocean-floor pattern to use:
|
||||
a_Pattern = ChooseOceanFloorPattern(a_RelX, a_RelZ);
|
||||
a_Pattern = a_ChunkDesc.GetBiome(a_RelX, a_RelZ) == biDeepOcean ? patGravel.Get() : ChooseOceanFloorPattern(a_RelX, a_RelZ);
|
||||
HasHadWater = true;
|
||||
} // for y
|
||||
a_ChunkDesc.SetBlockType(a_RelX, 0, a_RelZ, E_BLOCK_BEDROCK);
|
||||
|
@ -50,7 +50,7 @@ cEndGen::cEndGen(int a_Seed) :
|
||||
|
||||
|
||||
|
||||
void cEndGen::Initialize(cIniFile & a_IniFile)
|
||||
void cEndGen::InitializeCompoGen(cIniFile & a_IniFile)
|
||||
{
|
||||
m_IslandSizeX = a_IniFile.GetValueSetI("Generator", "EndGenIslandSizeX", m_IslandSizeX);
|
||||
m_IslandSizeY = a_IniFile.GetValueSetI("Generator", "EndGenIslandSizeY", m_IslandSizeY);
|
||||
|
@ -23,8 +23,6 @@ class cEndGen :
|
||||
public:
|
||||
cEndGen(int a_Seed);
|
||||
|
||||
void Initialize(cIniFile & a_IniFile);
|
||||
|
||||
protected:
|
||||
|
||||
/// Seed for the noise
|
||||
@ -66,4 +64,5 @@ protected:
|
||||
|
||||
// cTerrainCompositionGen overrides:
|
||||
virtual void ComposeTerrain(cChunkDesc & a_ChunkDesc) override;
|
||||
virtual void InitializeCompoGen(cIniFile & a_IniFile) override;
|
||||
} ;
|
||||
|
@ -135,13 +135,22 @@ void cFinishGenNetherClumpFoliage::TryPlaceClump(cChunkDesc & a_ChunkDesc, int a
|
||||
int zz = a_ChunkDesc.GetChunkZ() * cChunkDef::Width + z;
|
||||
for (int y = MinY; y < MaxY; y++)
|
||||
{
|
||||
if (
|
||||
((x < 0) || (x >= cChunkDef::Width)) ||
|
||||
((y < 0) || (y >= cChunkDef::Height)) ||
|
||||
((z < 0) || (z >= cChunkDef::Width))
|
||||
)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (a_ChunkDesc.GetBlockType(x, y, z) != E_BLOCK_AIR) // Don't replace non air blocks.
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
BLOCKTYPE BlockBelow = a_ChunkDesc.GetBlockType(x, y - 1, z);
|
||||
if (!cBlockInfo::IsSolid(BlockBelow)) // Only place on solid blocks
|
||||
if (!cBlockInfo::FullyOccupiesVoxel(BlockBelow)) // Only place on solid blocks
|
||||
{
|
||||
continue;
|
||||
}
|
||||
@ -454,14 +463,14 @@ void cFinishGenIce::GenFinish(cChunkDesc & a_ChunkDesc)
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// cFinishGenLilypads:
|
||||
// cFinishGenSingleTopBlock:
|
||||
|
||||
int cFinishGenSingleBiomeSingleTopBlock::GetNumToGen(const cChunkDef::BiomeMap & a_BiomeMap)
|
||||
int cFinishGenSingleTopBlock::GetNumToGen(const cChunkDef::BiomeMap & a_BiomeMap)
|
||||
{
|
||||
int res = 0;
|
||||
for (size_t i = 0; i < ARRAYCOUNT(a_BiomeMap); i++)
|
||||
{
|
||||
if (a_BiomeMap[i] == m_Biome)
|
||||
if (IsAllowedBiome(a_BiomeMap[i]))
|
||||
{
|
||||
res++;
|
||||
}
|
||||
@ -473,7 +482,7 @@ int cFinishGenSingleBiomeSingleTopBlock::GetNumToGen(const cChunkDef::BiomeMap &
|
||||
|
||||
|
||||
|
||||
void cFinishGenSingleBiomeSingleTopBlock::GenFinish(cChunkDesc & a_ChunkDesc)
|
||||
void cFinishGenSingleTopBlock::GenFinish(cChunkDesc & a_ChunkDesc)
|
||||
{
|
||||
// Add Lilypads on top of water surface in Swampland
|
||||
|
||||
@ -486,11 +495,13 @@ void cFinishGenSingleBiomeSingleTopBlock::GenFinish(cChunkDesc & a_ChunkDesc)
|
||||
int z = (m_Noise.IntNoise3DInt(ChunkX - ChunkZ, i, ChunkZ) / 11) % cChunkDef::Width;
|
||||
|
||||
// Place the block at {x, z} if possible:
|
||||
if (a_ChunkDesc.GetBiome(x, z) != m_Biome)
|
||||
EMCSBiome Biome = a_ChunkDesc.GetBiome(x, z);
|
||||
if (!IsAllowedBiome(Biome))
|
||||
{
|
||||
// Incorrect biome
|
||||
continue;
|
||||
}
|
||||
|
||||
int Height = a_ChunkDesc.GetHeight(x, z);
|
||||
if (Height >= cChunkDef::Height)
|
||||
{
|
||||
@ -502,13 +513,16 @@ void cFinishGenSingleBiomeSingleTopBlock::GenFinish(cChunkDesc & a_ChunkDesc)
|
||||
// Not an empty block
|
||||
continue;
|
||||
}
|
||||
|
||||
BLOCKTYPE BlockBelow = a_ChunkDesc.GetBlockType(x, Height, z);
|
||||
if ((BlockBelow == m_AllowedBelow1) || (BlockBelow == m_AllowedBelow2))
|
||||
if (!IsAllowedBlockBelow(BlockBelow))
|
||||
{
|
||||
a_ChunkDesc.SetBlockType(x, Height + 1, z, m_BlockType);
|
||||
a_ChunkDesc.SetHeight(x, z, Height + 1);
|
||||
continue;
|
||||
}
|
||||
} // for i
|
||||
|
||||
a_ChunkDesc.SetBlockType(x, Height + 1, z, m_BlockType);
|
||||
a_ChunkDesc.SetHeight(x, z, Height + 1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -541,7 +555,10 @@ void cFinishGenBottomLava::GenFinish(cChunkDesc & a_ChunkDesc)
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// cFinishGenPreSimulator:
|
||||
|
||||
cFinishGenPreSimulator::cFinishGenPreSimulator(void)
|
||||
cFinishGenPreSimulator::cFinishGenPreSimulator(bool a_PreSimulateFallingBlocks, bool a_PreSimulateWater, bool a_PreSimulateLava) :
|
||||
m_PreSimulateFallingBlocks(a_PreSimulateFallingBlocks),
|
||||
m_PreSimulateWater(a_PreSimulateWater),
|
||||
m_PreSimulateLava(a_PreSimulateLava)
|
||||
{
|
||||
// Nothing needed yet
|
||||
}
|
||||
@ -552,9 +569,20 @@ cFinishGenPreSimulator::cFinishGenPreSimulator(void)
|
||||
|
||||
void cFinishGenPreSimulator::GenFinish(cChunkDesc & a_ChunkDesc)
|
||||
{
|
||||
CollapseSandGravel(a_ChunkDesc.GetBlockTypes(), a_ChunkDesc.GetHeightMap());
|
||||
StationarizeFluid(a_ChunkDesc.GetBlockTypes(), a_ChunkDesc.GetHeightMap(), E_BLOCK_WATER, E_BLOCK_STATIONARY_WATER);
|
||||
StationarizeFluid(a_ChunkDesc.GetBlockTypes(), a_ChunkDesc.GetHeightMap(), E_BLOCK_LAVA, E_BLOCK_STATIONARY_LAVA);
|
||||
if (m_PreSimulateFallingBlocks)
|
||||
{
|
||||
CollapseSandGravel(a_ChunkDesc.GetBlockTypes(), a_ChunkDesc.GetHeightMap());
|
||||
}
|
||||
|
||||
if (m_PreSimulateWater)
|
||||
{
|
||||
StationarizeFluid(a_ChunkDesc.GetBlockTypes(), a_ChunkDesc.GetHeightMap(), E_BLOCK_WATER, E_BLOCK_STATIONARY_WATER);
|
||||
}
|
||||
|
||||
if (m_PreSimulateLava)
|
||||
{
|
||||
StationarizeFluid(a_ChunkDesc.GetBlockTypes(), a_ChunkDesc.GetHeightMap(), E_BLOCK_LAVA, E_BLOCK_STATIONARY_LAVA);
|
||||
}
|
||||
// TODO: other operations
|
||||
}
|
||||
|
||||
|
@ -143,32 +143,69 @@ Used for:
|
||||
- Lilypads finisher
|
||||
- DeadBushes finisher
|
||||
*/
|
||||
class cFinishGenSingleBiomeSingleTopBlock :
|
||||
class cFinishGenSingleTopBlock :
|
||||
public cFinishGen
|
||||
{
|
||||
public:
|
||||
cFinishGenSingleBiomeSingleTopBlock(
|
||||
int a_Seed, BLOCKTYPE a_BlockType, EMCSBiome a_Biome, int a_Amount,
|
||||
BLOCKTYPE a_AllowedBelow1, BLOCKTYPE a_AllowedBelow2
|
||||
typedef std::vector<BLOCKTYPE> BlockList;
|
||||
bool m_IsAllowedBelow[256];
|
||||
|
||||
typedef std::vector<EMCSBiome> BiomeList;
|
||||
bool m_IsBiomeAllowed[256];
|
||||
|
||||
|
||||
cFinishGenSingleTopBlock(
|
||||
int a_Seed, BLOCKTYPE a_BlockType, BiomeList a_Biomes, int a_Amount,
|
||||
BlockList a_AllowedBelow
|
||||
) :
|
||||
m_Noise(a_Seed),
|
||||
m_BlockType(a_BlockType),
|
||||
m_Biome(a_Biome),
|
||||
m_Amount(a_Amount),
|
||||
m_AllowedBelow1(a_AllowedBelow1),
|
||||
m_AllowedBelow2(a_AllowedBelow2)
|
||||
m_Amount(a_Amount)
|
||||
{
|
||||
// Initialize all the block types.
|
||||
for (size_t idx = 0; idx < ARRAYCOUNT(m_IsAllowedBelow); ++idx)
|
||||
{
|
||||
m_IsAllowedBelow[idx] = false;
|
||||
}
|
||||
|
||||
// Load the allowed blocks into m_IsAllowedBelow
|
||||
for (BlockList::iterator itr = a_AllowedBelow.begin(); itr != a_AllowedBelow.end(); ++itr)
|
||||
{
|
||||
m_IsAllowedBelow[*itr] = true;
|
||||
}
|
||||
|
||||
// Initialize all the biome types.
|
||||
for (size_t idx = 0; idx < ARRAYCOUNT(m_IsBiomeAllowed); ++idx)
|
||||
{
|
||||
m_IsBiomeAllowed[idx] = false;
|
||||
}
|
||||
|
||||
// Load the allowed biomes into m_IsBiomeAllowed
|
||||
for (BiomeList::iterator itr = a_Biomes.begin(); itr != a_Biomes.end(); ++itr)
|
||||
{
|
||||
m_IsBiomeAllowed[*itr] = true;
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
cNoise m_Noise;
|
||||
BLOCKTYPE m_BlockType;
|
||||
EMCSBiome m_Biome;
|
||||
int m_Amount; ///< Relative amount of blocks to try adding. 1 = one block per 256 biome columns.
|
||||
BLOCKTYPE m_AllowedBelow1; ///< First of the two blocktypes that are allowed below m_BlockType
|
||||
BLOCKTYPE m_AllowedBelow2; ///< Second of the two blocktypes that are allowed below m_BlockType
|
||||
|
||||
int GetNumToGen(const cChunkDef::BiomeMap & a_BiomeMap);
|
||||
|
||||
// Returns true if the given biome is a biome that is allowed.
|
||||
inline bool IsAllowedBiome(EMCSBiome a_Biome)
|
||||
{
|
||||
return m_IsBiomeAllowed[a_Biome];
|
||||
}
|
||||
|
||||
// Returns true if the given blocktype may be below m_BlockType
|
||||
inline bool IsAllowedBlockBelow(BLOCKTYPE a_BlockBelow)
|
||||
{
|
||||
return m_IsAllowedBelow[a_BlockBelow];
|
||||
}
|
||||
|
||||
|
||||
// cFinishGen override:
|
||||
virtual void GenFinish(cChunkDesc & a_ChunkDesc) override;
|
||||
@ -203,9 +240,14 @@ class cFinishGenPreSimulator :
|
||||
public cFinishGen
|
||||
{
|
||||
public:
|
||||
cFinishGenPreSimulator(void);
|
||||
cFinishGenPreSimulator(bool a_PreSimulateFallingBlocks, bool a_PreSimulateWater, bool a_PreSimulateLava);
|
||||
|
||||
protected:
|
||||
|
||||
bool m_PreSimulateFallingBlocks;
|
||||
bool m_PreSimulateWater;
|
||||
bool m_PreSimulateLava;
|
||||
|
||||
// Drops hanging sand and gravel down to the ground, recalculates heightmap
|
||||
void CollapseSandGravel(
|
||||
cChunkDef::BlockTypes & a_BlockTypes, // Block types to read and change
|
||||
|
300
src/Generating/RoughRavines.cpp
Normal file
300
src/Generating/RoughRavines.cpp
Normal file
@ -0,0 +1,300 @@
|
||||
|
||||
// RoughRavines.cpp
|
||||
|
||||
// Implements the cRoughRavines class representing the rough ravine generator
|
||||
|
||||
#include "Globals.h"
|
||||
|
||||
#include "RoughRavines.h"
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// cRoughRavine:
|
||||
|
||||
class cRoughRavine :
|
||||
public cGridStructGen::cStructure
|
||||
{
|
||||
typedef cGridStructGen::cStructure super;
|
||||
|
||||
public:
|
||||
cRoughRavine(
|
||||
int a_Seed, int a_Size,
|
||||
float a_CenterWidth, float a_Roughness,
|
||||
float a_FloorHeightEdge1, float a_FloorHeightEdge2, float a_FloorHeightCenter,
|
||||
float a_CeilingHeightEdge1, float a_CeilingHeightEdge2, float a_CeilingHeightCenter,
|
||||
int a_GridX, int a_GridZ, int a_OriginX, int a_OriginZ
|
||||
) :
|
||||
super(a_GridX, a_GridZ, a_OriginX, a_OriginZ),
|
||||
m_Seed(a_Seed + 100),
|
||||
m_Noise(a_Seed + 100),
|
||||
m_Roughness(a_Roughness)
|
||||
{
|
||||
// Create the basic structure - 2 lines meeting at the centerpoint:
|
||||
int Max = 2 * a_Size;
|
||||
int Half = a_Size; // m_DefPoints[Half] will be the centerpoint
|
||||
m_DefPoints.resize(Max + 1);
|
||||
int rnd = m_Noise.IntNoise2DInt(a_OriginX, a_OriginZ) / 7;
|
||||
float Len = (float)a_Size;
|
||||
float Angle = (float)rnd; // Angle is in radians, will be wrapped in the "sin" and "cos" operations
|
||||
float OfsX = sin(Angle) * Len;
|
||||
float OfsZ = cos(Angle) * Len;
|
||||
m_DefPoints[0].Set (a_OriginX - OfsX, a_OriginZ - OfsZ, 1, a_CeilingHeightEdge1, a_FloorHeightEdge1);
|
||||
m_DefPoints[Half].Set((float)a_OriginX, (float)a_OriginZ, a_CenterWidth, a_CeilingHeightCenter, a_FloorHeightCenter);
|
||||
m_DefPoints[Max].Set (a_OriginX + OfsX, a_OriginZ + OfsZ, 1, a_CeilingHeightEdge2, a_FloorHeightEdge2);
|
||||
|
||||
// Calculate the points in between, recursively:
|
||||
SubdivideLine(0, Half);
|
||||
SubdivideLine(Half, Max);
|
||||
|
||||
// Initialize the per-height radius modifiers:
|
||||
InitPerHeightRadius(a_GridX, a_GridZ);
|
||||
}
|
||||
|
||||
protected:
|
||||
struct sRavineDefPoint
|
||||
{
|
||||
float m_X;
|
||||
float m_Z;
|
||||
float m_Radius;
|
||||
float m_Top;
|
||||
float m_Bottom;
|
||||
|
||||
void Set(float a_X, float a_Z, float a_Radius, float a_Top, float a_Bottom)
|
||||
{
|
||||
m_X = a_X;
|
||||
m_Z = a_Z;
|
||||
m_Radius = a_Radius;
|
||||
m_Top = a_Top;
|
||||
m_Bottom = a_Bottom;
|
||||
}
|
||||
};
|
||||
typedef std::vector<sRavineDefPoint> sRavineDefPoints;
|
||||
|
||||
int m_Seed;
|
||||
|
||||
cNoise m_Noise;
|
||||
|
||||
int m_MaxSize;
|
||||
|
||||
sRavineDefPoints m_DefPoints;
|
||||
|
||||
float m_Roughness;
|
||||
|
||||
/** Number to add to the radius based on the height. This creates the "ledges" in the ravine walls. */
|
||||
float m_PerHeightRadius[cChunkDef::Height];
|
||||
|
||||
|
||||
/** Recursively subdivides the line between the points of the specified index.
|
||||
Sets the midpoint to the center of the line plus or minus a random offset, then calls itself for each half
|
||||
of the new line. */
|
||||
void SubdivideLine(int a_Idx1, int a_Idx2)
|
||||
{
|
||||
// Calculate the midpoint:
|
||||
const sRavineDefPoint & p1 = m_DefPoints[a_Idx1];
|
||||
const sRavineDefPoint & p2 = m_DefPoints[a_Idx2];
|
||||
float MidX = (p1.m_X + p2.m_X) / 2;
|
||||
float MidZ = (p1.m_Z + p2.m_Z) / 2;
|
||||
float MidR = (p1.m_Radius + p2.m_Radius) / 2 + 0.1f;
|
||||
float MidT = (p1.m_Top + p2.m_Top) / 2;
|
||||
float MidB = (p1.m_Bottom + p2.m_Bottom) / 2;
|
||||
|
||||
// Adjust the midpoint by a small amount of perpendicular vector in a random one of its two directions:
|
||||
float dx = p2.m_X - p1.m_X;
|
||||
float dz = p2.m_Z - p1.m_Z;
|
||||
if ((m_Noise.IntNoise2DInt((int)MidX, (int)MidZ) / 11) % 2 == 0)
|
||||
{
|
||||
MidX += dz * m_Roughness;
|
||||
MidZ -= dx * m_Roughness;
|
||||
}
|
||||
else
|
||||
{
|
||||
MidX -= dz * m_Roughness;
|
||||
MidZ += dx * m_Roughness;
|
||||
}
|
||||
int MidIdx = (a_Idx1 + a_Idx2) / 2;
|
||||
m_DefPoints[MidIdx].Set(MidX, MidZ, MidR, MidT, MidB);
|
||||
|
||||
// Recurse the two halves, if they are worth recursing:
|
||||
if (MidIdx - a_Idx1 > 1)
|
||||
{
|
||||
SubdivideLine(a_Idx1, MidIdx);
|
||||
}
|
||||
if (a_Idx2 - MidIdx > 1)
|
||||
{
|
||||
SubdivideLine(MidIdx, a_Idx2);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void InitPerHeightRadius(int a_GridX, int a_GridZ)
|
||||
{
|
||||
int h = 0;
|
||||
while (h < cChunkDef::Height)
|
||||
{
|
||||
m_Noise.SetSeed(m_Seed + h);
|
||||
int rnd = m_Noise.IntNoise2DInt(a_GridX, a_GridZ) / 13;
|
||||
int NumBlocks = (rnd % 3) + 2;
|
||||
rnd = rnd / 4;
|
||||
float Val = (float)(rnd % 256) / 128 - 1; // Random float in range [-1, +1]
|
||||
if (h + NumBlocks > cChunkDef::Height)
|
||||
{
|
||||
NumBlocks = cChunkDef::Height - h;
|
||||
}
|
||||
for (int i = 0; i < NumBlocks; i++)
|
||||
{
|
||||
m_PerHeightRadius[h + i] = Val;
|
||||
}
|
||||
h += NumBlocks;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
virtual void DrawIntoChunk(cChunkDesc & a_ChunkDesc) override
|
||||
{
|
||||
int BlockStartX = a_ChunkDesc.GetChunkX() * cChunkDef::Width;
|
||||
int BlockStartZ = a_ChunkDesc.GetChunkZ() * cChunkDef::Width;
|
||||
int BlockEndX = BlockStartX + cChunkDef::Width;
|
||||
int BlockEndZ = BlockStartZ + cChunkDef::Width;
|
||||
for (sRavineDefPoints::const_iterator itr = m_DefPoints.begin(), end = m_DefPoints.end(); itr != end; ++itr)
|
||||
{
|
||||
if (
|
||||
(ceilf (itr->m_X + itr->m_Radius + 2) < BlockStartX) ||
|
||||
(floorf(itr->m_X - itr->m_Radius - 2) > BlockEndX) ||
|
||||
(ceilf (itr->m_Z + itr->m_Radius + 2) < BlockStartZ) ||
|
||||
(floorf(itr->m_Z - itr->m_Radius - 2) > BlockEndZ)
|
||||
)
|
||||
{
|
||||
// Cannot intersect, bail out early
|
||||
continue;
|
||||
}
|
||||
|
||||
// Carve out a cylinder around the xz point, up to (m_Radius + 2) in diameter, from Bottom to Top:
|
||||
// On each height level, use m_PerHeightRadius[] to modify the actual radius used
|
||||
// EnlargedRadiusSq is the square of the radius enlarged by the maximum m_PerHeightRadius offset - anything outside it will never be touched.
|
||||
float RadiusSq = (itr->m_Radius + 2) * (itr->m_Radius + 2);
|
||||
float DifX = BlockStartX - itr->m_X; // substitution for faster calc
|
||||
float DifZ = BlockStartZ - itr->m_Z; // substitution for faster calc
|
||||
for (int x = 0; x < cChunkDef::Width; x++) for (int z = 0; z < cChunkDef::Width; z++)
|
||||
{
|
||||
#ifdef _DEBUG
|
||||
// DEBUG: Make the roughravine shapepoints visible on a single layer (so that we can see with Minutor what's going on)
|
||||
if ((DifX + x == 0) && (DifZ + z == 0))
|
||||
{
|
||||
a_ChunkDesc.SetBlockType(x, 4, z, E_BLOCK_LAPIS_ORE);
|
||||
}
|
||||
#endif // _DEBUG
|
||||
|
||||
// If the column is outside the enlarged radius, bail out completely
|
||||
float DistSq = (DifX + x) * (DifX + x) + (DifZ + z) * (DifZ + z);
|
||||
if (DistSq > RadiusSq)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
int Top = std::min((int)ceilf(itr->m_Top), +cChunkDef::Height);
|
||||
for (int y = std::max((int)floorf(itr->m_Bottom), 1); y <= Top; y++)
|
||||
{
|
||||
if ((itr->m_Radius + m_PerHeightRadius[y]) * (itr->m_Radius + m_PerHeightRadius[y]) < DistSq)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (cBlockInfo::CanBeTerraformed(a_ChunkDesc.GetBlockType(x, y, z)))
|
||||
{
|
||||
a_ChunkDesc.SetBlockType(x, y, z, E_BLOCK_AIR);
|
||||
}
|
||||
} // for y
|
||||
} // for x, z - a_BlockTypes
|
||||
} // for itr - m_Points[]
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// cRoughRavines:
|
||||
|
||||
cRoughRavines::cRoughRavines(
|
||||
int a_Seed,
|
||||
int a_MaxSize, int a_MinSize,
|
||||
float a_MaxCenterWidth, float a_MinCenterWidth,
|
||||
float a_MaxRoughness, float a_MinRoughness,
|
||||
float a_MaxFloorHeightEdge, float a_MinFloorHeightEdge,
|
||||
float a_MaxFloorHeightCenter, float a_MinFloorHeightCenter,
|
||||
float a_MaxCeilingHeightEdge, float a_MinCeilingHeightEdge,
|
||||
float a_MaxCeilingHeightCenter, float a_MinCeilingHeightCenter,
|
||||
int a_GridSize, int a_MaxOffset
|
||||
) :
|
||||
super(a_Seed, a_GridSize, a_GridSize, a_MaxOffset, a_MaxOffset, a_MaxSize, a_MaxSize, 64),
|
||||
m_Seed(a_Seed),
|
||||
m_MaxSize(a_MaxSize),
|
||||
m_MinSize(a_MinSize),
|
||||
m_MaxCenterWidth(a_MaxCenterWidth),
|
||||
m_MinCenterWidth(a_MinCenterWidth),
|
||||
m_MaxRoughness(a_MaxRoughness),
|
||||
m_MinRoughness(a_MinRoughness),
|
||||
m_MaxFloorHeightEdge(a_MaxFloorHeightEdge),
|
||||
m_MinFloorHeightEdge(a_MinFloorHeightEdge),
|
||||
m_MaxFloorHeightCenter(a_MaxFloorHeightCenter),
|
||||
m_MinFloorHeightCenter(a_MinFloorHeightCenter),
|
||||
m_MaxCeilingHeightEdge(a_MaxCeilingHeightEdge),
|
||||
m_MinCeilingHeightEdge(a_MinCeilingHeightEdge),
|
||||
m_MaxCeilingHeightCenter(a_MaxCeilingHeightCenter),
|
||||
m_MinCeilingHeightCenter(a_MinCeilingHeightCenter)
|
||||
{
|
||||
if (m_MinSize > m_MaxSize)
|
||||
{
|
||||
std::swap(m_MinSize, m_MaxSize);
|
||||
std::swap(a_MinSize, a_MaxSize);
|
||||
}
|
||||
if (m_MaxSize < 16)
|
||||
{
|
||||
m_MaxSize = 16;
|
||||
LOGWARNING("RoughRavines: MaxSize too small, adjusting request from %d to %d", a_MaxSize, m_MaxSize);
|
||||
}
|
||||
if (m_MinSize < 16)
|
||||
{
|
||||
m_MinSize = 16;
|
||||
LOGWARNING("RoughRavines: MinSize too small, adjusting request from %d to %d", a_MinSize, m_MinSize);
|
||||
}
|
||||
if (m_MinSize == m_MaxSize)
|
||||
{
|
||||
m_MaxSize = m_MinSize + 1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
cGridStructGen::cStructurePtr cRoughRavines::CreateStructure(int a_GridX, int a_GridZ, int a_OriginX, int a_OriginZ)
|
||||
{
|
||||
// Pick a random value for each of the ravine's parameters:
|
||||
int Size = m_MinSize + (m_Noise.IntNoise2DInt(a_GridX, a_GridZ) / 7) % (m_MaxSize - m_MinSize); // Random int from m_MinSize to m_MaxSize
|
||||
float CenterWidth = m_Noise.IntNoise2DInRange(a_GridX + 10, a_GridZ, m_MinCenterWidth, m_MaxCenterWidth);
|
||||
float Roughness = m_Noise.IntNoise2DInRange(a_GridX + 20, a_GridZ, m_MinRoughness, m_MaxRoughness);
|
||||
float FloorHeightEdge1 = m_Noise.IntNoise2DInRange(a_GridX + 30, a_GridZ, m_MinFloorHeightEdge, m_MaxFloorHeightEdge);
|
||||
float FloorHeightEdge2 = m_Noise.IntNoise2DInRange(a_GridX + 40, a_GridZ, m_MinFloorHeightEdge, m_MaxFloorHeightEdge);
|
||||
float FloorHeightCenter = m_Noise.IntNoise2DInRange(a_GridX + 50, a_GridZ, m_MinFloorHeightCenter, m_MaxFloorHeightCenter);
|
||||
float CeilingHeightEdge1 = m_Noise.IntNoise2DInRange(a_GridX + 60, a_GridZ, m_MinCeilingHeightEdge, m_MaxCeilingHeightEdge);
|
||||
float CeilingHeightEdge2 = m_Noise.IntNoise2DInRange(a_GridX + 70, a_GridZ, m_MinCeilingHeightEdge, m_MaxCeilingHeightEdge);
|
||||
float CeilingHeightCenter = m_Noise.IntNoise2DInRange(a_GridX + 80, a_GridZ, m_MinCeilingHeightCenter, m_MaxCeilingHeightCenter);
|
||||
|
||||
// Create a ravine:
|
||||
return cStructurePtr(new cRoughRavine(
|
||||
m_Seed,
|
||||
Size, CenterWidth, Roughness,
|
||||
FloorHeightEdge1, FloorHeightEdge2, FloorHeightCenter,
|
||||
CeilingHeightEdge1, CeilingHeightEdge2, CeilingHeightCenter,
|
||||
a_GridX, a_GridZ, a_OriginX, a_OriginZ
|
||||
));
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
86
src/Generating/RoughRavines.h
Normal file
86
src/Generating/RoughRavines.h
Normal file
@ -0,0 +1,86 @@
|
||||
|
||||
// RoughRavines.h
|
||||
|
||||
// Declares the cRoughRavines class representing the rough ravine generator
|
||||
|
||||
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "GridStructGen.h"
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
class cRoughRavines :
|
||||
public cGridStructGen
|
||||
{
|
||||
typedef cGridStructGen super;
|
||||
|
||||
public:
|
||||
cRoughRavines(
|
||||
int a_Seed,
|
||||
int a_MaxSize, int a_MinSize,
|
||||
float a_MaxCenterWidth, float a_MinCenterWidth,
|
||||
float a_MaxRoughness, float a_MinRoughness,
|
||||
float a_MaxFloorHeightEdge, float a_MinFloorHeightEdge,
|
||||
float a_MaxFloorHeightCenter, float a_MinFloorHeightCenter,
|
||||
float a_MaxCeilingHeightEdge, float a_MinCeilingHeightEdge,
|
||||
float a_MaxCeilingHeightCenter, float a_MinCeilingHeightCenter,
|
||||
int a_GridSize, int a_MaxOffset
|
||||
);
|
||||
|
||||
protected:
|
||||
int m_Seed;
|
||||
|
||||
/** Maximum size of the ravine, in each of the X / Z axis */
|
||||
int m_MaxSize;
|
||||
|
||||
/** Minimum size of the ravine */
|
||||
int m_MinSize;
|
||||
|
||||
/** Maximum width of the ravine's center, in blocks */
|
||||
float m_MaxCenterWidth;
|
||||
|
||||
/** Minimum width of the ravine's center, in blocks */
|
||||
float m_MinCenterWidth;
|
||||
|
||||
/** Maximum roughness of the ravine */
|
||||
float m_MaxRoughness;
|
||||
|
||||
/** Minimum roughness of the ravine */
|
||||
float m_MinRoughness;
|
||||
|
||||
/** Maximum floor height at the ravine's edge */
|
||||
float m_MaxFloorHeightEdge;
|
||||
|
||||
/** Minimum floor height at the ravine's edge */
|
||||
float m_MinFloorHeightEdge;
|
||||
|
||||
/** Maximum floor height at the ravine's center */
|
||||
float m_MaxFloorHeightCenter;
|
||||
|
||||
/** Minimum floor height at the ravine's center */
|
||||
float m_MinFloorHeightCenter;
|
||||
|
||||
/** Maximum ceiling height at the ravine's edge */
|
||||
float m_MaxCeilingHeightEdge;
|
||||
|
||||
/** Minimum ceiling height at the ravine's edge */
|
||||
float m_MinCeilingHeightEdge;
|
||||
|
||||
/** Maximum ceiling height at the ravine's center */
|
||||
float m_MaxCeilingHeightCenter;
|
||||
|
||||
/** Minimum ceiling height at the ravine's center */
|
||||
float m_MinCeilingHeightCenter;
|
||||
|
||||
// cGridStructGen overrides:
|
||||
virtual cStructurePtr CreateStructure(int a_GridX, int a_GridZ, int a_OriginX, int a_OriginZ) override;
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
@ -218,7 +218,6 @@ void GetTreeImageByBiome(int a_BlockX, int a_BlockY, int a_BlockZ, cNoise & a_No
|
||||
return;
|
||||
}
|
||||
|
||||
case biRoofedForest:
|
||||
case biColdTaiga:
|
||||
case biColdTaigaHills:
|
||||
case biMegaTaiga:
|
||||
@ -238,7 +237,6 @@ void GetTreeImageByBiome(int a_BlockX, int a_BlockY, int a_BlockZ, cNoise & a_No
|
||||
case biIcePlainsSpikes:
|
||||
case biJungleM:
|
||||
case biJungleEdgeM:
|
||||
case biRoofedForestM:
|
||||
case biColdTaigaM:
|
||||
case biMegaSpruceTaiga:
|
||||
case biMegaSpruceTaigaHills:
|
||||
@ -253,6 +251,13 @@ void GetTreeImageByBiome(int a_BlockX, int a_BlockY, int a_BlockZ, cNoise & a_No
|
||||
GetBirchTreeImage(a_BlockX, a_BlockY, a_BlockZ, a_Noise, a_Seq, a_LogBlocks, a_OtherBlocks);
|
||||
return;
|
||||
}
|
||||
|
||||
case biRoofedForest:
|
||||
case biRoofedForestM:
|
||||
{
|
||||
GetDarkoakTreeImage(a_BlockX, a_BlockY, a_BlockZ, a_Noise, a_Seq, a_LogBlocks, a_OtherBlocks);
|
||||
return;
|
||||
}
|
||||
|
||||
case biDesert:
|
||||
case biDesertHills:
|
||||
@ -407,7 +412,60 @@ void GetAcaciaTreeImage(int a_BlockX, int a_BlockY, int a_BlockZ, cNoise & a_Noi
|
||||
|
||||
void GetDarkoakTreeImage(int a_BlockX, int a_BlockY, int a_BlockZ, cNoise & a_Noise, int a_Seq, sSetBlockVector & a_LogBlocks, sSetBlockVector & a_OtherBlocks)
|
||||
{
|
||||
// TODO
|
||||
// Pick a height
|
||||
int Height = 5 + (a_Noise.IntNoise3DInt(a_BlockX + 32 * a_Seq, a_BlockY, a_BlockZ + 32 * a_Seq) / 11) % 4;
|
||||
|
||||
// Create the trunk
|
||||
for (int i = 0; i < Height; i++)
|
||||
{
|
||||
a_LogBlocks.push_back(sSetBlock(a_BlockX, a_BlockY + i, a_BlockZ, E_BLOCK_NEW_LOG, E_META_NEW_LOG_DARK_OAK_WOOD));
|
||||
a_LogBlocks.push_back(sSetBlock(a_BlockX + 1, a_BlockY + i, a_BlockZ, E_BLOCK_NEW_LOG, E_META_NEW_LOG_DARK_OAK_WOOD));
|
||||
a_LogBlocks.push_back(sSetBlock(a_BlockX, a_BlockY + i, a_BlockZ + 1, E_BLOCK_NEW_LOG, E_META_NEW_LOG_DARK_OAK_WOOD));
|
||||
a_LogBlocks.push_back(sSetBlock(a_BlockX + 1, a_BlockY + i, a_BlockZ + 1, E_BLOCK_NEW_LOG, E_META_NEW_LOG_DARK_OAK_WOOD));
|
||||
}
|
||||
|
||||
// Create branches
|
||||
for (int i = 0; i < 3; i++)
|
||||
{
|
||||
int x = (a_Noise.IntNoise3DInt(a_BlockX + 32 * a_Seq, a_BlockY * i, a_BlockZ + 32 * a_Seq) % 3) - 1;
|
||||
int z = (a_Noise.IntNoise3DInt(a_BlockX - 32 * a_Seq, a_BlockY * i, a_BlockZ - 32 * a_Seq) % 3) - 1;
|
||||
|
||||
// The branches would end up in the trunk.
|
||||
if ((x >= a_BlockX) && (x <= a_BlockX + 1) && (z >= a_BlockZ) && (z <= a_BlockZ + 1))
|
||||
{
|
||||
NOISE_DATATYPE Val1 = a_Noise.IntNoise2D(x, z);
|
||||
if (Val1 < 0)
|
||||
{
|
||||
x = a_BlockX + ((Val1 < -0.5) ? -1 : 3);
|
||||
}
|
||||
else
|
||||
{
|
||||
z = a_BlockZ + ((Val1 < 0.5) ? -1 : 3);
|
||||
}
|
||||
}
|
||||
|
||||
int y = Height - (a_Noise.IntNoise3DInt(a_BlockX + x, a_BlockY * i, a_BlockZ - z) % (Height - (Height / 4)));
|
||||
|
||||
for (int Y = y; Y < Height; Y++)
|
||||
{
|
||||
a_LogBlocks.push_back(sSetBlock(a_BlockX + x, a_BlockY + Y, a_BlockZ + z, E_BLOCK_NEW_LOG, E_META_NEW_LOG_DARK_OAK_WOOD));
|
||||
}
|
||||
}
|
||||
|
||||
int hei = a_BlockY + Height - 2;
|
||||
|
||||
// The lower two leaves layers are BigO4 with log in the middle and possibly corners:
|
||||
for (int i = 0; i < 2; i++)
|
||||
{
|
||||
PushCoordBlocks(a_BlockX, hei, a_BlockZ, a_OtherBlocks, BigO4, ARRAYCOUNT(BigO4), E_BLOCK_NEW_LEAVES, E_META_NEW_LEAVES_DARK_OAK_WOOD);
|
||||
PushCornerBlocks(a_BlockX, hei, a_BlockZ, a_Seq, a_Noise, 0x5fffffff, a_OtherBlocks, 3, E_BLOCK_NEW_LEAVES, E_META_NEW_LEAVES_DARK_OAK_WOOD);
|
||||
hei++;
|
||||
} // for i < 2
|
||||
|
||||
// The top leaves layer is a BigO3 with leaves in the middle and possibly corners:
|
||||
PushCoordBlocks(a_BlockX, hei, a_BlockZ, a_OtherBlocks, BigO3, ARRAYCOUNT(BigO3), E_BLOCK_NEW_LEAVES, E_META_NEW_LEAVES_DARK_OAK_WOOD);
|
||||
PushCornerBlocks(a_BlockX, hei, a_BlockZ, a_Seq, a_Noise, 0x5fffffff, a_OtherBlocks, 3, E_BLOCK_NEW_LEAVES, E_META_NEW_LEAVES_DARK_OAK_WOOD);
|
||||
a_OtherBlocks.push_back(sSetBlock(a_BlockX, hei, a_BlockZ, E_BLOCK_NEW_LEAVES, E_META_NEW_LEAVES_DARK_OAK_WOOD));
|
||||
}
|
||||
|
||||
|
||||
|
@ -153,7 +153,7 @@ bool cGroupManager::LoadGroups()
|
||||
AString Color = IniFile.GetValue(KeyName, "Color", "-");
|
||||
if ((Color != "-") && (Color.length() >= 1))
|
||||
{
|
||||
Group->SetColor(cChatColor::Delimiter + Color[0]);
|
||||
Group->SetColor(AString(cChatColor::Delimiter) + Color[0]);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -226,12 +226,6 @@ void cInventory::SetSlot(int a_SlotNum, const cItem & a_Item)
|
||||
return;
|
||||
}
|
||||
Grid->SetSlot(GridSlotNum, a_Item);
|
||||
|
||||
// Broadcast the Equipped Item, if the Slot is changed.
|
||||
if ((Grid == &m_HotbarSlots) && (m_EquippedSlotNum == (a_SlotNum - invHotbarOffset)))
|
||||
{
|
||||
m_Owner.GetWorld()->BroadcastEntityEquipment(m_Owner, 0, a_Item, m_Owner.GetClientHandle());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -397,6 +391,10 @@ bool cInventory::DamageItem(int a_SlotNum, short a_Amount)
|
||||
LOGWARNING("%s: requesting an invalid slot index: %d out of %d", __FUNCTION__, a_SlotNum, invNumSlots - 1);
|
||||
return false;
|
||||
}
|
||||
if (a_Amount <= 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
int GridSlotNum = 0;
|
||||
cItemGrid * Grid = GetGridForSlotNum(a_SlotNum, GridSlotNum);
|
||||
@ -717,6 +715,12 @@ void cInventory::OnSlotChanged(cItemGrid * a_ItemGrid, int a_SlotNum)
|
||||
m_ArmorSlots.GetSlot(a_SlotNum), m_Owner.GetClientHandle()
|
||||
);
|
||||
}
|
||||
|
||||
// Broadcast the Equipped Item, if the Slot is changed.
|
||||
if ((a_ItemGrid == &m_HotbarSlots) && (m_EquippedSlotNum == a_SlotNum))
|
||||
{
|
||||
m_Owner.GetWorld()->BroadcastEntityEquipment(m_Owner, 0, GetEquippedItem(), m_Owner.GetClientHandle());
|
||||
}
|
||||
|
||||
// Convert the grid-local a_SlotNum to our global SlotNum:
|
||||
int Base = 0;
|
||||
|
46
src/Item.cpp
46
src/Item.cpp
@ -41,33 +41,33 @@ short cItem::GetMaxDamage(void) const
|
||||
switch (m_ItemType)
|
||||
{
|
||||
case E_ITEM_BOW: return 384;
|
||||
case E_ITEM_DIAMOND_AXE: return 1563;
|
||||
case E_ITEM_DIAMOND_HOE: return 1563;
|
||||
case E_ITEM_DIAMOND_PICKAXE: return 1563;
|
||||
case E_ITEM_DIAMOND_SHOVEL: return 1563;
|
||||
case E_ITEM_DIAMOND_SWORD: return 1563;
|
||||
case E_ITEM_FLINT_AND_STEEL: return 65;
|
||||
case E_ITEM_DIAMOND_AXE: return 1561;
|
||||
case E_ITEM_DIAMOND_HOE: return 1561;
|
||||
case E_ITEM_DIAMOND_PICKAXE: return 1561;
|
||||
case E_ITEM_DIAMOND_SHOVEL: return 1561;
|
||||
case E_ITEM_DIAMOND_SWORD: return 1561;
|
||||
case E_ITEM_FLINT_AND_STEEL: return 64;
|
||||
case E_ITEM_GOLD_AXE: return 32;
|
||||
case E_ITEM_GOLD_HOE: return 32;
|
||||
case E_ITEM_GOLD_PICKAXE: return 32;
|
||||
case E_ITEM_GOLD_SHOVEL: return 32;
|
||||
case E_ITEM_GOLD_SWORD: return 32;
|
||||
case E_ITEM_IRON_AXE: return 251;
|
||||
case E_ITEM_IRON_HOE: return 251;
|
||||
case E_ITEM_IRON_PICKAXE: return 251;
|
||||
case E_ITEM_IRON_SHOVEL: return 251;
|
||||
case E_ITEM_IRON_SWORD: return 251;
|
||||
case E_ITEM_SHEARS: return 251;
|
||||
case E_ITEM_STONE_AXE: return 132;
|
||||
case E_ITEM_STONE_HOE: return 132;
|
||||
case E_ITEM_STONE_PICKAXE: return 132;
|
||||
case E_ITEM_STONE_SHOVEL: return 132;
|
||||
case E_ITEM_STONE_SWORD: return 132;
|
||||
case E_ITEM_WOODEN_AXE: return 60;
|
||||
case E_ITEM_WOODEN_HOE: return 60;
|
||||
case E_ITEM_WOODEN_PICKAXE: return 60;
|
||||
case E_ITEM_WOODEN_SHOVEL: return 60;
|
||||
case E_ITEM_WOODEN_SWORD: return 60;
|
||||
case E_ITEM_IRON_AXE: return 250;
|
||||
case E_ITEM_IRON_HOE: return 250;
|
||||
case E_ITEM_IRON_PICKAXE: return 250;
|
||||
case E_ITEM_IRON_SHOVEL: return 250;
|
||||
case E_ITEM_IRON_SWORD: return 250;
|
||||
case E_ITEM_SHEARS: return 250;
|
||||
case E_ITEM_STONE_AXE: return 131;
|
||||
case E_ITEM_STONE_HOE: return 131;
|
||||
case E_ITEM_STONE_PICKAXE: return 131;
|
||||
case E_ITEM_STONE_SHOVEL: return 131;
|
||||
case E_ITEM_STONE_SWORD: return 131;
|
||||
case E_ITEM_WOODEN_AXE: return 59;
|
||||
case E_ITEM_WOODEN_HOE: return 59;
|
||||
case E_ITEM_WOODEN_PICKAXE: return 59;
|
||||
case E_ITEM_WOODEN_SHOVEL: return 59;
|
||||
case E_ITEM_WOODEN_SWORD: return 59;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@ -86,7 +86,7 @@ bool cItem::DamageItem(short a_Amount)
|
||||
}
|
||||
|
||||
m_ItemDamage += a_Amount;
|
||||
return (m_ItemDamage >= MaxDamage);
|
||||
return (m_ItemDamage > MaxDamage);
|
||||
}
|
||||
|
||||
|
||||
|
@ -332,8 +332,21 @@ void cItemHandler::OnBlockDestroyed(cWorld * a_World, cPlayer * a_Player, const
|
||||
cBlockInServerPluginInterface PluginInterface(*a_World);
|
||||
Handler->DropBlock(ChunkInterface, *a_World, PluginInterface, a_Player, a_BlockX, a_BlockY, a_BlockZ, CanHarvestBlock(Block), a_Player->GetEquippedItem().m_Enchantments.GetLevel(cEnchantments::enchSilkTouch) > 0);
|
||||
}
|
||||
|
||||
a_Player->UseEquippedItem();
|
||||
|
||||
if (!cBlockInfo::IsOneHitDig(Block))
|
||||
{
|
||||
a_Player->UseEquippedItem(GetDurabilityLossByAction(dlaBreakBlock));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cItemHandler::OnEntityAttack(cPlayer * a_Attacker, cEntity * a_AttackedEntity)
|
||||
{
|
||||
UNUSED(a_AttackedEntity);
|
||||
a_Attacker->UseEquippedItem(GetDurabilityLossByAction(dlaAttackEntity));
|
||||
}
|
||||
|
||||
|
||||
@ -351,6 +364,20 @@ void cItemHandler::OnFoodEaten(cWorld * a_World, cPlayer * a_Player, cItem * a_I
|
||||
|
||||
|
||||
|
||||
short cItemHandler::GetDurabilityLossByAction(eDurabilityLostAction a_Action)
|
||||
{
|
||||
switch ((int)a_Action)
|
||||
{
|
||||
case dlaAttackEntity: return 2;
|
||||
case dlaBreakBlock: return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
char cItemHandler::GetMaxStackSize(void)
|
||||
{
|
||||
if (m_ItemType < 256)
|
||||
@ -502,6 +529,7 @@ bool cItemHandler::IsPlaceable(void)
|
||||
|
||||
bool cItemHandler::CanRepairWithRawMaterial(short a_ItemType)
|
||||
{
|
||||
UNUSED(a_ItemType);
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -548,6 +576,8 @@ bool cItemHandler::CanHarvestBlock(BLOCKTYPE a_BlockType)
|
||||
case E_BLOCK_IRON_ORE:
|
||||
case E_BLOCK_LAPIS_ORE:
|
||||
case E_BLOCK_LAPIS_BLOCK:
|
||||
case E_BLOCK_SNOW:
|
||||
case E_BLOCK_VINES:
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
@ -19,6 +19,13 @@ class cPlayer;
|
||||
class cItemHandler
|
||||
{
|
||||
public:
|
||||
|
||||
enum eDurabilityLostAction
|
||||
{
|
||||
dlaBreakBlock,
|
||||
dlaAttackEntity,
|
||||
};
|
||||
|
||||
cItemHandler(int a_ItemType);
|
||||
|
||||
/** Force virtual destructor */
|
||||
@ -48,11 +55,17 @@ public:
|
||||
virtual bool OnDiggingBlock(cWorld * a_World, cPlayer * a_Player, const cItem & a_HeldItem, int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace);
|
||||
|
||||
/** Called when the player destroys a block using this item. This also calls the drop function for the destroyed block */
|
||||
virtual void OnBlockDestroyed(cWorld * a_World, cPlayer * a_Player, const cItem & a_Item, int a_X, int a_Y, int a_Z);
|
||||
virtual void OnBlockDestroyed(cWorld * a_World, cPlayer * a_Player, const cItem & a_Item, int a_BlockX, int a_BlockY, int a_BlockZ);
|
||||
|
||||
/** Called when a player attacks a other entity. */
|
||||
virtual void OnEntityAttack(cPlayer * a_Attacker, cEntity * a_AttackedEntity);
|
||||
|
||||
/** Called after the player has eaten this item. */
|
||||
virtual void OnFoodEaten(cWorld *a_World, cPlayer *a_Player, cItem *a_Item);
|
||||
|
||||
|
||||
/** Get the durability lost which the item will get, when a specified action was performed. */
|
||||
virtual short GetDurabilityLossByAction(eDurabilityLostAction a_Action);
|
||||
|
||||
/** Returns the maximum stack size for a given item */
|
||||
virtual char GetMaxStackSize(void);
|
||||
|
||||
|
@ -16,7 +16,6 @@ public:
|
||||
cItemHoeHandler(int a_ItemType)
|
||||
: cItemHandler(a_ItemType)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
virtual bool OnItemUse(cWorld *a_World, cPlayer *a_Player, const cItem & a_Item, int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_Dir) override
|
||||
@ -26,13 +25,18 @@ public:
|
||||
if ((Block == E_BLOCK_DIRT) || (Block == E_BLOCK_GRASS))
|
||||
{
|
||||
a_World->FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_FARMLAND, 0);
|
||||
|
||||
a_Player->UseEquippedItem();
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
virtual short GetDurabilityLossByAction(eDurabilityLostAction a_Action) override
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
} ;
|
||||
|
||||
|
||||
|
@ -8,6 +8,7 @@
|
||||
class cItemPickaxeHandler :
|
||||
public cItemHandler
|
||||
{
|
||||
typedef cItemHandler super;
|
||||
public:
|
||||
cItemPickaxeHandler(int a_ItemType)
|
||||
: cItemHandler(a_ItemType)
|
||||
@ -84,7 +85,7 @@ public:
|
||||
return PickaxeLevel() >= 1;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
return super::CanHarvestBlock(a_BlockType);
|
||||
}
|
||||
|
||||
virtual bool CanRepairWithRawMaterial(short a_ItemType) override
|
||||
|
@ -12,6 +12,7 @@
|
||||
class cItemShearsHandler :
|
||||
public cItemHandler
|
||||
{
|
||||
typedef cItemHandler super;
|
||||
public:
|
||||
cItemShearsHandler(int a_ItemType) :
|
||||
cItemHandler(a_ItemType)
|
||||
@ -30,8 +31,12 @@ public:
|
||||
BLOCKTYPE Block = a_World->GetBlock(a_BlockX, a_BlockY, a_BlockZ);
|
||||
if ((Block == E_BLOCK_LEAVES) || (Block == E_BLOCK_NEW_LEAVES))
|
||||
{
|
||||
NIBBLETYPE Meta = a_World->GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ);
|
||||
cBlockHandler * Handler = cBlockInfo::GetHandler(Block);
|
||||
|
||||
cItems Drops;
|
||||
Drops.push_back(cItem(Block, 1, a_World->GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ) & 0x03));
|
||||
Handler->ConvertToPickups(Drops, Meta);
|
||||
Drops.push_back(cItem(Block, 1, Meta & 3));
|
||||
a_World->SpawnItemPickups(Drops, a_BlockX, a_BlockY, a_BlockZ);
|
||||
|
||||
a_World->SetBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_AIR, 0);
|
||||
@ -54,7 +59,25 @@ public:
|
||||
return true;
|
||||
}
|
||||
} // switch (a_BlockType)
|
||||
return false;
|
||||
return super::CanHarvestBlock(a_BlockType);
|
||||
}
|
||||
|
||||
|
||||
virtual short GetDurabilityLossByAction(eDurabilityLostAction a_Action) override
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
virtual void OnBlockDestroyed(cWorld * a_World, cPlayer * a_Player, const cItem & a_Item, int a_BlockX, int a_BlockY, int a_BlockZ) override
|
||||
{
|
||||
super::OnBlockDestroyed(a_World, a_Player, a_Item, a_BlockX, a_BlockY, a_BlockZ);
|
||||
|
||||
BLOCKTYPE Block = a_World->GetBlock(a_BlockX, a_BlockY, a_BlockZ);
|
||||
if ((Block == E_BLOCK_TRIPWIRE) || (Block == E_BLOCK_VINES))
|
||||
{
|
||||
a_Player->UseEquippedItem();
|
||||
}
|
||||
}
|
||||
} ;
|
||||
|
||||
|
@ -14,6 +14,7 @@
|
||||
|
||||
class cItemShovelHandler : public cItemHandler
|
||||
{
|
||||
typedef cItemHandler super;
|
||||
public:
|
||||
cItemShovelHandler(int a_ItemType)
|
||||
: cItemHandler(a_ItemType)
|
||||
@ -39,7 +40,11 @@ public:
|
||||
|
||||
virtual bool CanHarvestBlock(BLOCKTYPE a_BlockType) override
|
||||
{
|
||||
return (a_BlockType == E_BLOCK_SNOW);
|
||||
if (a_BlockType == E_BLOCK_SNOW)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return super::CanHarvestBlock(a_BlockType);
|
||||
}
|
||||
|
||||
virtual bool CanRepairWithRawMaterial(short a_ItemType) override
|
||||
|
@ -12,17 +12,24 @@
|
||||
class cItemSwordHandler :
|
||||
public cItemHandler
|
||||
{
|
||||
typedef cItemHandler super;
|
||||
public:
|
||||
cItemSwordHandler(int a_ItemType)
|
||||
: cItemHandler(a_ItemType)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
|
||||
virtual bool CanHarvestBlock(BLOCKTYPE a_BlockType) override
|
||||
{
|
||||
return (a_BlockType == E_BLOCK_COBWEB);
|
||||
if (a_BlockType == E_BLOCK_COBWEB)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return super::CanHarvestBlock(a_BlockType);
|
||||
}
|
||||
|
||||
|
||||
virtual bool CanRepairWithRawMaterial(short a_ItemType) override
|
||||
{
|
||||
switch (m_ItemType)
|
||||
@ -35,6 +42,17 @@ public:
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
virtual short GetDurabilityLossByAction(eDurabilityLostAction a_Action) override
|
||||
{
|
||||
switch ((int)a_Action)
|
||||
{
|
||||
case dlaAttackEntity: return 1;
|
||||
case dlaBreakBlock: return 2;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
} ;
|
||||
|
||||
|
||||
|
@ -241,7 +241,8 @@ bool cMobSpawner::CanSpawnHere(cChunk * a_Chunk, int a_RelX, int a_RelY, int a_R
|
||||
(m_Random.NextInt(2, a_Biome) == 0)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
case cMonster::mtMagmaCube:
|
||||
case cMonster::mtSlime:
|
||||
{
|
||||
return (
|
||||
|
@ -1015,7 +1015,7 @@ void cMonster::HandleDaylightBurning(cChunk & a_Chunk)
|
||||
(a_Chunk.GetBlock(RelX, RelY, RelZ) != E_BLOCK_SOULSAND) && // Not on soulsand
|
||||
(GetWorld()->GetTimeOfDay() < (12000 + 1000)) && // It is nighttime
|
||||
!IsOnFire() && // Not already burning
|
||||
(GetWorld()->GetWeather() != eWeather_Rain) // Not raining
|
||||
GetWorld()->IsWeatherWetAt(POSX_TOINT, POSZ_TOINT) // Not raining
|
||||
)
|
||||
{
|
||||
// Burn for 100 ticks, then decide again
|
||||
|
@ -52,11 +52,7 @@ void cSheep::OnRightClicked(cPlayer & a_Player)
|
||||
{
|
||||
m_IsSheared = true;
|
||||
m_World->BroadcastEntityMetadata(*this);
|
||||
|
||||
if (!a_Player.IsGameModeCreative())
|
||||
{
|
||||
a_Player.UseEquippedItem();
|
||||
}
|
||||
a_Player.UseEquippedItem();
|
||||
|
||||
cItems Drops;
|
||||
int NumDrops = m_World->GetTickRandomNumber(2) + 1;
|
||||
|
12
src/Noise.h
12
src/Noise.h
@ -5,6 +5,12 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cmath>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// Some settings
|
||||
#define NOISE_DATATYPE float
|
||||
|
||||
@ -33,6 +39,12 @@ public:
|
||||
INLINE NOISE_DATATYPE IntNoise2D(int a_X, int a_Y) const;
|
||||
INLINE NOISE_DATATYPE IntNoise3D(int a_X, int a_Y, int a_Z) const;
|
||||
|
||||
// Return a float number in the specified range:
|
||||
INLINE NOISE_DATATYPE IntNoise2DInRange(int a_X, int a_Y, float a_Min, float a_Max) const
|
||||
{
|
||||
return a_Min + std::abs(IntNoise2D(a_X, a_Y)) * (a_Max - a_Min);
|
||||
}
|
||||
|
||||
// Note: These functions have a mod8-irregular chance - each of the mod8 remainders has different chance of occurrence. Divide by 8 to rectify.
|
||||
INLINE int IntNoise1DInt(int a_X) const;
|
||||
INLINE int IntNoise2DInt(int a_X, int a_Y) const;
|
||||
|
@ -100,7 +100,7 @@ public:
|
||||
virtual void SendPlayerSpawn (const cPlayer & a_Player) = 0;
|
||||
virtual void SendPluginMessage (const AString & a_Channel, const AString & a_Message) = 0;
|
||||
virtual void SendRemoveEntityEffect (const cEntity & a_Entity, int a_EffectID) = 0;
|
||||
virtual void SendRespawn (const cWorld & a_World, bool a_ShouldIgnoreDimensionChecks = false) = 0;
|
||||
virtual void SendRespawn (eDimension a_Dimension, bool a_ShouldIgnoreDimensionChecks) = 0;
|
||||
virtual void SendExperience (void) = 0;
|
||||
virtual void SendExperienceOrb (const cExpOrb & a_ExpOrb) = 0;
|
||||
virtual void SendScoreboardObjective (const AString & a_Name, const AString & a_DisplayName, Byte a_Mode) = 0;
|
||||
|
@ -833,23 +833,23 @@ void cProtocol125::SendRemoveEntityEffect(const cEntity & a_Entity, int a_Effect
|
||||
|
||||
|
||||
|
||||
void cProtocol125::SendRespawn(const cWorld & a_World, bool a_ShouldIgnoreDimensionChecks)
|
||||
void cProtocol125::SendRespawn(eDimension a_Dimension, bool a_ShouldIgnoreDimensionChecks)
|
||||
{
|
||||
cCSLock Lock(m_CSPacket);
|
||||
if ((m_LastSentDimension == a_World.GetDimension()) && !a_ShouldIgnoreDimensionChecks)
|
||||
if ((m_LastSentDimension == a_Dimension) && !a_ShouldIgnoreDimensionChecks)
|
||||
{
|
||||
// Must not send a respawn for the world with the same dimension, the client goes cuckoo if we do (unless we are respawning from death)
|
||||
return;
|
||||
}
|
||||
cPlayer * Player = m_Client->GetPlayer();
|
||||
WriteByte (PACKET_RESPAWN);
|
||||
WriteInt (a_World.GetDimension());
|
||||
WriteInt ((int)(a_Dimension));
|
||||
WriteByte (2); // TODO: Difficulty; 2 = Normal
|
||||
WriteChar ((char)Player->GetGameMode());
|
||||
WriteShort (256); // Current world height
|
||||
WriteString("default");
|
||||
Flush();
|
||||
m_LastSentDimension = a_World.GetDimension();
|
||||
m_LastSentDimension = a_Dimension;
|
||||
}
|
||||
|
||||
|
||||
|
@ -72,7 +72,7 @@ public:
|
||||
virtual void SendPlayerSpawn (const cPlayer & a_Player) override;
|
||||
virtual void SendPluginMessage (const AString & a_Channel, const AString & a_Message) override;
|
||||
virtual void SendRemoveEntityEffect (const cEntity & a_Entity, int a_EffectID) override;
|
||||
virtual void SendRespawn (const cWorld & a_World, bool a_ShouldIgnoreDimensionChecks = false) override;
|
||||
virtual void SendRespawn (eDimension a_Dimension, bool a_ShouldIgnoreDimensionChecks) override;
|
||||
virtual void SendExperience (void) override;
|
||||
virtual void SendExperienceOrb (const cExpOrb & a_ExpOrb) override;
|
||||
virtual void SendScoreboardObjective (const AString & a_Name, const AString & a_DisplayName, Byte a_Mode) override;
|
||||
|
@ -158,10 +158,10 @@ void cProtocol161::SendPlayerMaxSpeed(void)
|
||||
|
||||
|
||||
|
||||
void cProtocol161::SendRespawn(const cWorld & a_World, bool a_ShouldIgnoreDimensionChecks)
|
||||
void cProtocol161::SendRespawn(eDimension a_Dimension, bool a_ShouldIgnoreDimensionChecks)
|
||||
{
|
||||
// Besides sending the respawn, we need to also send the player max speed, otherwise the client reverts to super-fast
|
||||
super::SendRespawn(a_World, a_ShouldIgnoreDimensionChecks);
|
||||
super::SendRespawn(a_Dimension, a_ShouldIgnoreDimensionChecks);
|
||||
SendPlayerMaxSpeed();
|
||||
}
|
||||
|
||||
|
@ -42,7 +42,7 @@ protected:
|
||||
virtual void SendGameMode (eGameMode a_GameMode) override;
|
||||
virtual void SendHealth (void) override;
|
||||
virtual void SendPlayerMaxSpeed(void) override;
|
||||
virtual void SendRespawn (const cWorld & a_World, bool a_ShouldIgnoreDimensionChecks = false) override;
|
||||
virtual void SendRespawn (eDimension a_Dimension, bool a_ShouldIgnoreDimensionChecks) override;
|
||||
virtual void SendWindowOpen (const cWindow & a_Window) override;
|
||||
|
||||
virtual int ParseEntityAction (void) override;
|
||||
|
@ -986,9 +986,9 @@ void cProtocol172::SendRemoveEntityEffect(const cEntity & a_Entity, int a_Effect
|
||||
|
||||
|
||||
|
||||
void cProtocol172::SendRespawn(const cWorld & a_World, bool a_ShouldIgnoreDimensionChecks)
|
||||
void cProtocol172::SendRespawn(eDimension a_Dimension, bool a_ShouldIgnoreDimensionChecks)
|
||||
{
|
||||
if ((m_LastSentDimension == a_World.GetDimension()) && !a_ShouldIgnoreDimensionChecks)
|
||||
if ((m_LastSentDimension == a_Dimension) && !a_ShouldIgnoreDimensionChecks)
|
||||
{
|
||||
// Must not send a respawn for the world with the same dimension, the client goes cuckoo if we do (unless we are respawning from death)
|
||||
return;
|
||||
@ -996,11 +996,11 @@ void cProtocol172::SendRespawn(const cWorld & a_World, bool a_ShouldIgnoreDimens
|
||||
|
||||
cPacketizer Pkt(*this, 0x07); // Respawn packet
|
||||
cPlayer * Player = m_Client->GetPlayer();
|
||||
Pkt.WriteInt(a_World.GetDimension());
|
||||
Pkt.WriteInt((int)a_Dimension);
|
||||
Pkt.WriteByte(2); // TODO: Difficulty (set to Normal)
|
||||
Pkt.WriteByte((Byte)Player->GetEffectiveGameMode());
|
||||
Pkt.WriteString("default");
|
||||
m_LastSentDimension = a_World.GetDimension();
|
||||
m_LastSentDimension = a_Dimension;
|
||||
}
|
||||
|
||||
|
||||
|
@ -104,7 +104,7 @@ public:
|
||||
virtual void SendPlayerSpawn (const cPlayer & a_Player) override;
|
||||
virtual void SendPluginMessage (const AString & a_Channel, const AString & a_Message) override;
|
||||
virtual void SendRemoveEntityEffect (const cEntity & a_Entity, int a_EffectID) override;
|
||||
virtual void SendRespawn (const cWorld & a_World, bool a_ShouldIgnoreDimensionChecks = false) override;
|
||||
virtual void SendRespawn (eDimension a_Dimension, bool a_ShouldIgnoreDimensionChecks) override;
|
||||
virtual void SendSoundEffect (const AString & a_SoundName, double a_X, double a_Y, double a_Z, float a_Volume, float a_Pitch) override;
|
||||
virtual void SendExperience (void) override;
|
||||
virtual void SendExperienceOrb (const cExpOrb & a_ExpOrb) override;
|
||||
|
@ -556,10 +556,10 @@ void cProtocolRecognizer::SendRemoveEntityEffect(const cEntity & a_Entity, int a
|
||||
|
||||
|
||||
|
||||
void cProtocolRecognizer::SendRespawn(const cWorld & a_World, bool a_ShouldIgnoreDimensionChecks)
|
||||
void cProtocolRecognizer::SendRespawn(eDimension a_Dimension, bool a_ShouldIgnoreDimensionChecks)
|
||||
{
|
||||
ASSERT(m_Protocol != NULL);
|
||||
m_Protocol->SendRespawn(a_World, a_ShouldIgnoreDimensionChecks);
|
||||
m_Protocol->SendRespawn(a_Dimension, a_ShouldIgnoreDimensionChecks);
|
||||
}
|
||||
|
||||
|
||||
|
@ -107,7 +107,7 @@ public:
|
||||
virtual void SendPlayerSpawn (const cPlayer & a_Player) override;
|
||||
virtual void SendPluginMessage (const AString & a_Channel, const AString & a_Message) override;
|
||||
virtual void SendRemoveEntityEffect (const cEntity & a_Entity, int a_EffectID) override;
|
||||
virtual void SendRespawn (const cWorld & a_World, bool a_ShouldIgnoreDimensionChecks = false) override;
|
||||
virtual void SendRespawn (eDimension a_Dimension, bool a_ShouldIgnoreDimensionChecks) override;
|
||||
virtual void SendExperience (void) override;
|
||||
virtual void SendExperienceOrb (const cExpOrb & a_ExpOrb) override;
|
||||
virtual void SendScoreboardObjective (const AString & a_Name, const AString & a_DisplayName, Byte a_Mode) override;
|
||||
|
36
src/Root.cpp
36
src/Root.cpp
@ -270,12 +270,12 @@ void cRoot::LoadWorlds(cIniFile & IniFile)
|
||||
{
|
||||
// First get the default world
|
||||
AString DefaultWorldName = IniFile.GetValueSet("Worlds", "DefaultWorld", "world");
|
||||
m_pDefaultWorld = new cWorld( DefaultWorldName.c_str());
|
||||
m_pDefaultWorld = new cWorld(DefaultWorldName.c_str());
|
||||
m_WorldsByName[ DefaultWorldName ] = m_pDefaultWorld;
|
||||
|
||||
// Then load the other worlds
|
||||
unsigned int KeyNum = IniFile.FindKey("Worlds");
|
||||
unsigned int NumWorlds = IniFile.GetNumValues( KeyNum);
|
||||
unsigned int NumWorlds = IniFile.GetNumValues(KeyNum);
|
||||
if (NumWorlds <= 0)
|
||||
{
|
||||
return;
|
||||
@ -313,13 +313,15 @@ void cRoot::LoadWorlds(cIniFile & IniFile)
|
||||
|
||||
|
||||
|
||||
cWorld * cRoot::CreateAndInitializeWorld(const AString & a_WorldName)
|
||||
cWorld * cRoot::CreateAndInitializeWorld(const AString & a_WorldName, eDimension a_Dimension, const AString & a_OverworldName)
|
||||
{
|
||||
if (m_WorldsByName[a_WorldName] != NULL)
|
||||
cWorld * World = m_WorldsByName[a_WorldName];
|
||||
if (World != NULL)
|
||||
{
|
||||
return NULL;
|
||||
return World;
|
||||
}
|
||||
cWorld* NewWorld = new cWorld(a_WorldName.c_str());
|
||||
|
||||
cWorld * NewWorld = new cWorld(a_WorldName.c_str(), a_Dimension, a_OverworldName);
|
||||
m_WorldsByName[a_WorldName] = NewWorld;
|
||||
NewWorld->Start();
|
||||
NewWorld->InitializeSpawn();
|
||||
@ -371,7 +373,7 @@ void cRoot::UnloadWorlds(void)
|
||||
|
||||
|
||||
|
||||
cWorld* cRoot::GetDefaultWorld()
|
||||
cWorld * cRoot::GetDefaultWorld()
|
||||
{
|
||||
return m_pDefaultWorld;
|
||||
}
|
||||
@ -380,12 +382,19 @@ cWorld* cRoot::GetDefaultWorld()
|
||||
|
||||
|
||||
|
||||
cWorld* cRoot::GetWorld( const AString & a_WorldName)
|
||||
cWorld * cRoot::GetWorld(const AString & a_WorldName, bool a_SearchForFolder)
|
||||
{
|
||||
WorldMap::iterator itr = m_WorldsByName.find( a_WorldName);
|
||||
WorldMap::iterator itr = m_WorldsByName.find(a_WorldName);
|
||||
if (itr != m_WorldsByName.end())
|
||||
{
|
||||
return itr->second;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (a_SearchForFolder && cFile::IsFolder(FILE_IO_PREFIX + a_WorldName))
|
||||
{
|
||||
return CreateAndInitializeWorld(a_WorldName);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
@ -397,9 +406,12 @@ bool cRoot::ForEachWorld(cWorldListCallback & a_Callback)
|
||||
for (WorldMap::iterator itr = m_WorldsByName.begin(), itr2 = itr; itr != m_WorldsByName.end(); itr = itr2)
|
||||
{
|
||||
++itr2;
|
||||
if (a_Callback.Item(itr->second))
|
||||
if (itr->second != NULL)
|
||||
{
|
||||
return false;
|
||||
if (a_Callback.Item(itr->second))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
|
13
src/Root.h
13
src/Root.h
@ -54,8 +54,17 @@ public:
|
||||
// tolua_begin
|
||||
cServer * GetServer(void) { return m_Server; }
|
||||
cWorld * GetDefaultWorld(void);
|
||||
cWorld * GetWorld(const AString & a_WorldName);
|
||||
cWorld * CreateAndInitializeWorld(const AString & a_WorldName);
|
||||
|
||||
/** Returns a pointer to the world specified
|
||||
If no world of that name was currently loaded and a_SearchForFolder was true, it will consult cFile::IsFolder() to see if a world folder of that name exists and if so, initialise a world based on that name
|
||||
*/
|
||||
cWorld * GetWorld(const AString & a_WorldName, bool a_SearchForFolder = false);
|
||||
|
||||
/** Returns a pointer to a world of specified name - will search loaded worlds first, then create anew if not found
|
||||
The dimension parameter is used to create a world with a specific dimension
|
||||
a_OverworldName should be set for non-overworld dimensions if one wishes that world to link back to an overworld via portals
|
||||
*/
|
||||
cWorld * CreateAndInitializeWorld(const AString & a_WorldName, eDimension a_Dimension = dimOverworld, const AString & a_OverworldName = "");
|
||||
// tolua_end
|
||||
|
||||
/// Calls the callback for each world; returns true if the callback didn't abort (return true)
|
||||
|
@ -63,12 +63,12 @@ public: // tolua_export
|
||||
const AString & GetDescription(void) const {return m_Description; }
|
||||
|
||||
// Player counts:
|
||||
int GetMaxPlayers(void) const {return m_MaxPlayers; }
|
||||
int GetMaxPlayers(void) const { return m_MaxPlayers; }
|
||||
int GetNumPlayers(void) const;
|
||||
void SetMaxPlayers(int a_MaxPlayers) { m_MaxPlayers = a_MaxPlayers; }
|
||||
|
||||
// Hardcore mode or not:
|
||||
bool IsHardcore(void) const {return m_bIsHardcore; }
|
||||
bool IsHardcore(void) const { return m_bIsHardcore; }
|
||||
|
||||
// tolua_end
|
||||
|
||||
|
@ -274,7 +274,7 @@ void cWindow::OpenedByPlayer(cPlayer & a_Player)
|
||||
bool cWindow::ClosedByPlayer(cPlayer & a_Player, bool a_CanRefuse)
|
||||
{
|
||||
// Checks whether the player is still holding an item
|
||||
if (a_Player.IsDraggingItem())
|
||||
if (!a_Player.GetDraggingItem().IsEmpty())
|
||||
{
|
||||
LOGD("Player holds item! Dropping it...");
|
||||
a_Player.TossHeldItem(a_Player.GetDraggingItem().m_ItemCount);
|
||||
|
187
src/World.cpp
187
src/World.cpp
@ -230,8 +230,9 @@ void cWorld::cTickThread::Execute(void)
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// cWorld:
|
||||
|
||||
cWorld::cWorld(const AString & a_WorldName) :
|
||||
cWorld::cWorld(const AString & a_WorldName, eDimension a_Dimension, const AString & a_OverworldName) :
|
||||
m_WorldName(a_WorldName),
|
||||
m_OverworldName(a_OverworldName),
|
||||
m_IniFileName(m_WorldName + "/world.ini"),
|
||||
m_StorageSchema("Default"),
|
||||
#ifdef __arm__
|
||||
@ -239,6 +240,7 @@ cWorld::cWorld(const AString & a_WorldName) :
|
||||
#else
|
||||
m_StorageCompressionFactor(6),
|
||||
#endif
|
||||
m_Dimension(a_Dimension),
|
||||
m_IsSpawnExplicitlySet(false),
|
||||
m_WorldAgeSecs(0),
|
||||
m_TimeOfDaySecs(0),
|
||||
@ -518,9 +520,18 @@ void cWorld::Start(void)
|
||||
if (!IniFile.ReadFile(m_IniFileName))
|
||||
{
|
||||
LOGWARNING("Cannot read world settings from \"%s\", defaults will be used.", m_IniFileName.c_str());
|
||||
|
||||
// TODO: More descriptions for each key
|
||||
IniFile.AddHeaderComment(" This is the per-world configuration file, managing settings such as generators, simulators, and spawn points");
|
||||
IniFile.AddKeyComment(" LinkedWorlds", "This section governs portal world linkage; leave a value blank to disabled that associated method of teleportation");
|
||||
}
|
||||
AString Dimension = IniFile.GetValueSet("General", "Dimension", "Overworld");
|
||||
m_Dimension = StringToDimension(Dimension);
|
||||
|
||||
// The presence of a configuration value overrides everything
|
||||
// If no configuration value is found, GetDimension() is written to file and the variable is written to again to ensure that cosmic rays haven't sneakily changed its value
|
||||
m_Dimension = StringToDimension(IniFile.GetValueSet("General", "Dimension", DimensionToString(GetDimension())));
|
||||
|
||||
m_BroadcastDeathMessages = IniFile.GetValueSetB("Broadcasting", "BroadcastDeathMessages", true);
|
||||
m_BroadcastAchievementMessages = IniFile.GetValueSetB("Broadcasting", "BroadcastAchievementMessages", true);
|
||||
|
||||
// Try to find the "SpawnPosition" key and coord values in the world configuration, set the flag if found
|
||||
int KeyNum = IniFile.FindKey("SpawnPosition");
|
||||
@ -528,8 +539,8 @@ void cWorld::Start(void)
|
||||
(
|
||||
(KeyNum >= 0) &&
|
||||
(
|
||||
(IniFile.FindValue(KeyNum, "X") >= 0) ||
|
||||
(IniFile.FindValue(KeyNum, "Y") >= 0) ||
|
||||
(IniFile.FindValue(KeyNum, "X") >= 0) &&
|
||||
(IniFile.FindValue(KeyNum, "Y") >= 0) &&
|
||||
(IniFile.FindValue(KeyNum, "Z") >= 0)
|
||||
)
|
||||
);
|
||||
@ -565,36 +576,26 @@ void cWorld::Start(void)
|
||||
m_bUseChatPrefixes = IniFile.GetValueSetB("Mechanics", "UseChatPrefixes", true);
|
||||
m_VillagersShouldHarvestCrops = IniFile.GetValueSetB("Monsters", "VillagersShouldHarvestCrops", true);
|
||||
int GameMode = IniFile.GetValueSetI("General", "Gamemode", (int)m_GameMode);
|
||||
int Weather = IniFile.GetValueSetI("General", "Weather", (int)m_Weather);
|
||||
m_TimeOfDay = IniFile.GetValueSetI("General", "TimeInTicks", m_TimeOfDay);
|
||||
|
||||
if (GetDimension() == dimOverworld)
|
||||
{
|
||||
m_NetherWorldName = IniFile.GetValueSet("LinkedWorlds", "NetherWorldName", GetName() + "_nether");
|
||||
m_EndWorldName = IniFile.GetValueSet("LinkedWorlds", "EndWorldName", GetName() + "_end");
|
||||
}
|
||||
else
|
||||
{
|
||||
m_OverworldName = IniFile.GetValueSet("LinkedWorlds", "OverworldName", GetLinkedOverworldName());
|
||||
}
|
||||
|
||||
// Adjust the enum-backed variables into their respective bounds:
|
||||
m_GameMode = (eGameMode) Clamp(GameMode, (int)gmSurvival, (int)gmAdventure);
|
||||
m_TNTShrapnelLevel = (eShrapnelLevel)Clamp(TNTShrapnelLevel, (int)slNone, (int)slAll);
|
||||
m_Weather = (eWeather) Clamp(Weather, (int)wSunny, (int)wStorm);
|
||||
|
||||
// Load allowed mobs:
|
||||
AString DefaultMonsters;
|
||||
switch (m_Dimension)
|
||||
{
|
||||
case dimOverworld: DefaultMonsters = "bat, cavespider, chicken, cow, creeper, enderman, horse, mooshroom, ocelot, pig, sheep, silverfish, skeleton, slime, spider, squid, wolf, zombie"; break;
|
||||
case dimNether: DefaultMonsters = "blaze, ghast, magmacube, skeleton, zombie, zombiepigman"; break;
|
||||
case dimEnd: DefaultMonsters = "enderman"; break;
|
||||
case dimNotSet: break;
|
||||
}
|
||||
m_bAnimals = IniFile.GetValueSetB("Monsters", "AnimalsOn", true);
|
||||
AString AllMonsters = IniFile.GetValueSet("Monsters", "Types", DefaultMonsters);
|
||||
AStringVector SplitList = StringSplitAndTrim(AllMonsters, ",");
|
||||
for (AStringVector::const_iterator itr = SplitList.begin(), end = SplitList.end(); itr != end; ++itr)
|
||||
{
|
||||
cMonster::eType ToAdd = cMonster::StringToMobType(*itr);
|
||||
if (ToAdd != cMonster::mtInvalidType)
|
||||
{
|
||||
m_AllowedMobs.insert(ToAdd);
|
||||
LOGD("Allowed mob: %s", itr->c_str());
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG("World \"%s\": Unknown mob type: %s", m_WorldName.c_str(), itr->c_str());
|
||||
}
|
||||
}
|
||||
InitialiseGeneratorDefaults(IniFile);
|
||||
InitialiseAndLoadMobSpawningValues(IniFile);
|
||||
|
||||
m_ChunkMap = new cChunkMap(this);
|
||||
|
||||
@ -691,6 +692,82 @@ eWeather cWorld::ChooseNewWeather()
|
||||
|
||||
|
||||
|
||||
void cWorld::InitialiseGeneratorDefaults(cIniFile & a_IniFile)
|
||||
{
|
||||
switch (GetDimension())
|
||||
{
|
||||
case dimEnd:
|
||||
{
|
||||
a_IniFile.GetValueSet("Generator", "BiomeGen", "Constant");
|
||||
a_IniFile.GetValueSet("Generator", "ConstantBiome", "End");
|
||||
a_IniFile.GetValueSet("Generator", "HeightGen", "Biomal");
|
||||
a_IniFile.GetValueSet("Generator", "CompositionGen", "End");
|
||||
break;
|
||||
}
|
||||
case dimOverworld:
|
||||
{
|
||||
a_IniFile.GetValueSet("Generator", "BiomeGen", "MultiStepMap");
|
||||
a_IniFile.GetValueSet("Generator", "HeightGen", "DistortedHeightmap");
|
||||
a_IniFile.GetValueSet("Generator", "CompositionGen", "DistortedHeightmap");
|
||||
a_IniFile.GetValueSet("Generator", "Finishers", "Ravines, WormNestCaves, WaterLakes, WaterSprings, LavaLakes, LavaSprings, OreNests, Mineshafts, Trees, SprinkleFoliage, Ice, Snow, Lilypads, BottomLava, DeadBushes, PreSimulator");
|
||||
break;
|
||||
}
|
||||
case dimNether:
|
||||
{
|
||||
a_IniFile.GetValueSet("Generator", "BiomeGen", "Constant");
|
||||
a_IniFile.GetValueSet("Generator", "ConstantBiome", "Nether");
|
||||
a_IniFile.GetValueSet("Generator", "HeightGen", "Flat");
|
||||
a_IniFile.GetValueSet("Generator", "FlatHeight", "128");
|
||||
a_IniFile.GetValueSet("Generator", "CompositionGen", "Nether");
|
||||
a_IniFile.GetValueSet("Generator", "Finishers", "WormNestCaves, BottomLava, LavaSprings, NetherClumpFoliage, NetherForts, PreSimulator");
|
||||
a_IniFile.GetValueSet("Generator", "BottomLavaHeight", "30");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cWorld::InitialiseAndLoadMobSpawningValues(cIniFile & a_IniFile)
|
||||
{
|
||||
AString DefaultMonsters;
|
||||
switch (m_Dimension)
|
||||
{
|
||||
case dimOverworld: DefaultMonsters = "bat, cavespider, chicken, cow, creeper, enderman, horse, mooshroom, ocelot, pig, sheep, silverfish, skeleton, slime, spider, squid, wolf, zombie"; break;
|
||||
case dimNether: DefaultMonsters = "blaze, ghast, magmacube, skeleton, zombie, zombiepigman"; break;
|
||||
case dimEnd: DefaultMonsters = "enderman"; break;
|
||||
}
|
||||
|
||||
m_bAnimals = a_IniFile.GetValueSetB("Monsters", "AnimalsOn", true);
|
||||
AString AllMonsters = a_IniFile.GetValueSet("Monsters", "Types", DefaultMonsters);
|
||||
|
||||
if (!m_bAnimals)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
AStringVector SplitList = StringSplitAndTrim(AllMonsters, ",");
|
||||
for (AStringVector::const_iterator itr = SplitList.begin(), end = SplitList.end(); itr != end; ++itr)
|
||||
{
|
||||
cMonster::eType ToAdd = cMonster::StringToMobType(*itr);
|
||||
if (ToAdd != cMonster::mtInvalidType)
|
||||
{
|
||||
m_AllowedMobs.insert(ToAdd);
|
||||
LOGD("Allowed mob: %s", itr->c_str());
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG("World \"%s\": Unknown mob type: %s", m_WorldName.c_str(), itr->c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cWorld::Stop(void)
|
||||
{
|
||||
// Delete the clients that have been in this world:
|
||||
@ -703,6 +780,25 @@ void cWorld::Stop(void)
|
||||
} // for itr - m_Clients[]
|
||||
m_Clients.clear();
|
||||
}
|
||||
|
||||
// Write settings to file; these are all plugin changeable values - keep updated!
|
||||
cIniFile IniFile;
|
||||
IniFile.ReadFile(m_IniFileName);
|
||||
if (GetDimension() == dimOverworld)
|
||||
{
|
||||
IniFile.SetValue("LinkedWorlds", "NetherWorldName", m_NetherWorldName);
|
||||
IniFile.SetValue("LinkedWorlds", "EndWorldName", m_EndWorldName);
|
||||
}
|
||||
else
|
||||
{
|
||||
IniFile.SetValue("LinkedWorlds", "OverworldName", m_OverworldName);
|
||||
}
|
||||
IniFile.SetValueI("Physics", "TNTShrapnelLevel", (int)m_TNTShrapnelLevel);
|
||||
IniFile.SetValueB("Mechanics", "CommandBlocksEnabled", m_bCommandBlocksEnabled);
|
||||
IniFile.SetValueB("Mechanics", "UseChatPrefixes", m_bUseChatPrefixes);
|
||||
IniFile.SetValueI("General", "Weather", (int)m_Weather);
|
||||
IniFile.SetValueI("General", "TimeInTicks", m_TimeOfDay);
|
||||
IniFile.WriteFile(m_IniFileName);
|
||||
|
||||
m_TickThread.Stop();
|
||||
m_Lighting.Stop();
|
||||
@ -955,11 +1051,7 @@ void cWorld::TickClients(float a_Dt)
|
||||
// Add clients scheduled for adding:
|
||||
for (cClientHandleList::iterator itr = m_ClientsToAdd.begin(), end = m_ClientsToAdd.end(); itr != end; ++itr)
|
||||
{
|
||||
if (std::find(m_Clients.begin(), m_Clients.end(), *itr) != m_Clients.end())
|
||||
{
|
||||
ASSERT(!"Adding a client that is already in the clientlist");
|
||||
continue;
|
||||
}
|
||||
ASSERT(std::find(m_Clients.begin(), m_Clients.end(), *itr) == m_Clients.end());
|
||||
m_Clients.push_back(*itr);
|
||||
} // for itr - m_ClientsToRemove[]
|
||||
m_ClientsToAdd.clear();
|
||||
@ -2376,10 +2468,14 @@ void cWorld::AddPlayer(cPlayer * a_Player)
|
||||
|
||||
|
||||
|
||||
void cWorld::RemovePlayer(cPlayer * a_Player)
|
||||
void cWorld::RemovePlayer(cPlayer * a_Player, bool a_RemoveFromChunk)
|
||||
{
|
||||
|
||||
m_ChunkMap->RemoveEntity(a_Player);
|
||||
if (a_RemoveFromChunk)
|
||||
{
|
||||
// To prevent iterator invalidations when an entity goes through a portal and calls this function whilst being ticked by cChunk
|
||||
// we should not change cChunk's entity list if asked not to
|
||||
m_ChunkMap->RemoveEntity(a_Player);
|
||||
}
|
||||
{
|
||||
cCSLock Lock(m_CSPlayersToAdd);
|
||||
m_PlayersToAdd.remove(a_Player);
|
||||
@ -2882,15 +2978,6 @@ bool cWorld::HasEntity(int a_UniqueID)
|
||||
|
||||
|
||||
|
||||
void cWorld::RemoveEntity(cEntity * a_Entity)
|
||||
{
|
||||
m_ChunkMap->RemoveEntity(a_Entity);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
unsigned int cWorld::GetNumPlayers(void)
|
||||
{
|
||||
@ -3197,7 +3284,8 @@ void cWorld::AddQueuedPlayers(void)
|
||||
cCSLock Lock(m_CSPlayers);
|
||||
for (cPlayerList::iterator itr = PlayersToAdd.begin(), end = PlayersToAdd.end(); itr != end; ++itr)
|
||||
{
|
||||
ASSERT(std::find(m_Players.begin(), m_Players.end(), *itr) == m_Players.end()); // Is it already in the list? HOW?
|
||||
ASSERT(std::find(m_Players.begin(), m_Players.end(), *itr) == m_Players.end()); // Is it already in the list? HOW?
|
||||
LOGD("Adding player %s to world \"%s\".", (*itr)->GetName().c_str(), m_WorldName.c_str());
|
||||
|
||||
m_Players.push_back(*itr);
|
||||
(*itr)->SetWorld(this);
|
||||
@ -3227,6 +3315,9 @@ void cWorld::AddQueuedPlayers(void)
|
||||
if (Client != NULL)
|
||||
{
|
||||
Client->StreamChunks();
|
||||
Client->SendPlayerMoveLook();
|
||||
Client->SendHealth();
|
||||
Client->SendWholeInventory(*(*itr)->GetWindow());
|
||||
}
|
||||
} // for itr - PlayersToAdd[]
|
||||
}
|
||||
|
52
src/World.h
52
src/World.h
@ -185,7 +185,7 @@ public:
|
||||
virtual eDimension GetDimension(void) const { return m_Dimension; }
|
||||
|
||||
/** Returns the world height at the specified coords; waits for the chunk to get loaded / generated */
|
||||
int GetHeight(int a_BlockX, int a_BlockZ);
|
||||
virtual int GetHeight(int a_BlockX, int a_BlockZ);
|
||||
|
||||
// tolua_end
|
||||
|
||||
@ -281,8 +281,9 @@ public:
|
||||
|
||||
/** Removes the player from the world.
|
||||
Removes the player from the addition queue, too, if appropriate.
|
||||
If the player has a ClientHandle, the ClientHandle is removed from all chunks in the world and will not be ticked by this world anymore. */
|
||||
void RemovePlayer(cPlayer * a_Player);
|
||||
If the player has a ClientHandle, the ClientHandle is removed from all chunks in the world and will not be ticked by this world anymore.
|
||||
@param a_RemoveFromChunk determines if the entity should be removed from its chunk as well. Should be false when ticking from cChunk. */
|
||||
void RemovePlayer(cPlayer * a_Player, bool a_RemoveFromChunk);
|
||||
|
||||
/** Calls the callback for each player in the list; returns true if all players processed, false if the callback aborted by returning true */
|
||||
virtual bool ForEachPlayer(cPlayerListCallback & a_Callback) override; // >> EXPORTED IN MANUALBINDINGS <<
|
||||
@ -304,9 +305,6 @@ public:
|
||||
|
||||
bool HasEntity(int a_UniqueID);
|
||||
|
||||
/** Removes the entity, the entity ptr ownership is assumed taken by the caller */
|
||||
void RemoveEntity(cEntity * a_Entity);
|
||||
|
||||
/** Calls the callback for each entity in the entire world; returns true if all entities processed, false if the callback aborted by returning true */
|
||||
bool ForEachEntity(cEntityCallback & a_Callback); // Exported in ManualBindings.cpp
|
||||
|
||||
@ -622,6 +620,19 @@ public:
|
||||
|
||||
bool ShouldUseChatPrefixes(void) const { return m_bUseChatPrefixes; }
|
||||
void SetShouldUseChatPrefixes(bool a_Flag) { m_bUseChatPrefixes = a_Flag; }
|
||||
|
||||
bool ShouldBroadcastDeathMessages(void) const { return m_BroadcastDeathMessages; }
|
||||
bool ShouldBroadcastAchievementMessages(void) const { return m_BroadcastAchievementMessages; }
|
||||
|
||||
|
||||
AString GetNetherWorldName(void) const { return m_NetherWorldName; }
|
||||
void SetNetherWorldName(const AString & a_Name) { m_NetherWorldName = a_Name; }
|
||||
|
||||
AString GetEndWorldName(void) const { return m_EndWorldName; }
|
||||
void SetEndWorldName(const AString & a_Name) { m_EndWorldName = a_Name; }
|
||||
|
||||
AString GetLinkedOverworldName(void) const { return m_OverworldName; }
|
||||
void SetLinkedOverworldName(const AString & a_Name) { m_OverworldName = a_Name; }
|
||||
|
||||
// tolua_end
|
||||
|
||||
@ -705,7 +716,7 @@ public:
|
||||
|
||||
/** Returns true if the current weather is stormy */
|
||||
bool IsWeatherStorm(void) const { return (m_Weather == wStorm); }
|
||||
|
||||
|
||||
/** Returns true if the weather is stormy at the specified location. This takes into account biomes. */
|
||||
bool IsWeatherStormAt(int a_BlockX, int a_BlockZ)
|
||||
{
|
||||
@ -716,10 +727,11 @@ public:
|
||||
bool IsWeatherWet(void) const { return !IsWeatherSunny(); }
|
||||
|
||||
/** Returns true if it is raining, stormy or snowing at the specified location. This takes into account biomes. */
|
||||
bool IsWeatherWetAt(int a_BlockX, int a_BlockZ)
|
||||
virtual bool IsWeatherWetAt(int a_BlockX, int a_BlockZ)
|
||||
{
|
||||
return (IsWeatherWet() && !IsBiomeNoDownfall(GetBiomeAt(a_BlockX, a_BlockZ)));
|
||||
}
|
||||
|
||||
// tolua_end
|
||||
|
||||
cChunkGenerator & GetGenerator(void) { return m_Generator; }
|
||||
@ -824,6 +836,12 @@ private:
|
||||
|
||||
|
||||
AString m_WorldName;
|
||||
|
||||
/** The name of the world that a portal in this world should link to
|
||||
Only has effect if this world is a nether or end world, as it is used by entities to see which world to teleport to when in a portal
|
||||
*/
|
||||
AString m_OverworldName;
|
||||
|
||||
AString m_IniFileName;
|
||||
|
||||
/** Name of the storage schema used to load and save chunks */
|
||||
@ -842,6 +860,9 @@ private:
|
||||
double m_SpawnY;
|
||||
double m_SpawnZ;
|
||||
|
||||
bool m_BroadcastDeathMessages;
|
||||
bool m_BroadcastAchievementMessages;
|
||||
|
||||
double m_WorldAgeSecs; // World age, in seconds. Is only incremented, cannot be set by plugins.
|
||||
double m_TimeOfDaySecs; // Time of day in seconds. Can be adjusted. Is wrapped to zero each day.
|
||||
Int64 m_WorldAge; // World age in ticks, calculated off of m_WorldAgeSecs
|
||||
@ -908,6 +929,12 @@ private:
|
||||
See the eShrapnelLevel enumeration for details
|
||||
*/
|
||||
eShrapnelLevel m_TNTShrapnelLevel;
|
||||
|
||||
/** Name of the nether world */
|
||||
AString m_NetherWorldName;
|
||||
|
||||
/** Name of the end world */
|
||||
AString m_EndWorldName;
|
||||
|
||||
|
||||
cChunkGenerator m_Generator;
|
||||
@ -967,7 +994,7 @@ private:
|
||||
cSetChunkDataPtrs m_SetChunkDataQueue;
|
||||
|
||||
|
||||
cWorld(const AString & a_WorldName);
|
||||
cWorld(const AString & a_WorldName, eDimension a_Dimension = dimOverworld, const AString & a_OverworldName = "");
|
||||
virtual ~cWorld();
|
||||
|
||||
void Tick(float a_Dt, int a_LastTickDurationMSec);
|
||||
@ -1008,9 +1035,16 @@ private:
|
||||
Assumes it is called from the Tick thread. */
|
||||
void AddQueuedPlayers(void);
|
||||
|
||||
/** Sets generator values to dimension specific defaults, if those values do not exist */
|
||||
void InitialiseGeneratorDefaults(cIniFile & a_IniFile);
|
||||
|
||||
/** Sets mob spawning values if nonexistant to their dimension specific defaults */
|
||||
void InitialiseAndLoadMobSpawningValues(cIniFile & a_IniFile);
|
||||
|
||||
/** Sets the specified chunk data into the chunkmap. Called in the tick thread.
|
||||
Modifies the a_SetChunkData - moves the entities contained in it into the chunk. */
|
||||
void SetChunkData(cSetChunkData & a_SetChunkData);
|
||||
|
||||
}; // tolua_export
|
||||
|
||||
|
||||
|
@ -2475,10 +2475,7 @@ bool cWSSAnvil::LoadEntityBaseFromNBT(cEntity & a_Entity, const cParsedNBT & a_N
|
||||
|
||||
// Load health:
|
||||
int Health = a_NBT.FindChildByName(a_TagIdx, "Health");
|
||||
if (Health > 0)
|
||||
{
|
||||
a_Entity.SetHealth(a_NBT.GetShort(Health));
|
||||
}
|
||||
a_Entity.SetHealth(Health > 0 ? a_NBT.GetShort(Health) : a_Entity.GetMaxHealth());
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -2499,8 +2496,14 @@ bool cWSSAnvil::LoadMonsterBaseFromNBT(cMonster & a_Monster, const cParsedNBT &
|
||||
a_Monster.SetDropChanceChestplate(DropChance[2]);
|
||||
a_Monster.SetDropChanceLeggings(DropChance[3]);
|
||||
a_Monster.SetDropChanceBoots(DropChance[4]);
|
||||
bool CanPickUpLoot = (a_NBT.GetByte(a_NBT.FindChildByName(a_TagIdx, "CanPickUpLoot")) == 1);
|
||||
a_Monster.SetCanPickUpLoot(CanPickUpLoot);
|
||||
|
||||
int LootTag = a_NBT.FindChildByName(a_TagIdx, "CanPickUpLoot");
|
||||
if (LootTag > 0)
|
||||
{
|
||||
bool CanPickUpLoot = (a_NBT.GetByte(LootTag) == 1);
|
||||
a_Monster.SetCanPickUpLoot(CanPickUpLoot);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user