1
0
Fork 0

Merged branch 'master' into NameToUUID.

This commit is contained in:
madmaxoft 2014-07-31 23:17:49 +02:00
commit 6d02fce9a2
71 changed files with 1568 additions and 442 deletions

2
.gitignore vendored
View File

@ -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

View File

@ -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

View File

@ -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()))

View File

@ -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);

View File

@ -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})

View File

@ -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))

View File

@ -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)
{

View File

@ -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);

View File

@ -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;
}

View File

@ -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

View File

@ -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))

View File

@ -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);
}
}
} ;

View File

@ -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

View File

@ -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();
}

View File

@ -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)

View File

@ -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;

View File

@ -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")

View File

@ -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();
}
}

View File

@ -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);
}

View File

@ -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);

View File

@ -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);

View File

@ -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;

View File

@ -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));
}

View File

@ -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;

View File

@ -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);

View File

@ -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)
{

View File

@ -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

View File

@ -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

View File

@ -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;

View File

@ -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

View File

@ -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)

View File

@ -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--)
{

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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;
} ;

View File

@ -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
}

View File

@ -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

View 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
));
}

View 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;
};

View File

@ -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));
}

View File

@ -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
{

View File

@ -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;

View File

@ -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);
}

View File

@ -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;
}

View File

@ -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);

View File

@ -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;
}
} ;

View File

@ -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

View File

@ -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();
}
}
} ;

View File

@ -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

View File

@ -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;
}
} ;

View File

@ -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 (

View File

@ -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

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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;
}

View File

@ -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;

View File

@ -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();
}

View File

@ -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;

View File

@ -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;
}

View File

@ -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;

View File

@ -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);
}

View File

@ -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;

View File

@ -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;

View File

@ -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)

View File

@ -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

View File

@ -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);

View File

@ -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[]
}

View File

@ -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

View File

@ -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;
}