2fc86476ae
+ Boot the player out if the bed was destroyed
180 lines
4.7 KiB
C++
180 lines
4.7 KiB
C++
|
|
// BlockBed.cpp
|
|
|
|
#include "Globals.h"
|
|
#include "BlockBed.h"
|
|
|
|
#include "BroadcastInterface.h"
|
|
#include "../Entities/Player.h"
|
|
#include "../World.h"
|
|
#include "../BoundingBox.h"
|
|
#include "../Mobs/Monster.h"
|
|
#include "../BlockEntities/BedEntity.h"
|
|
|
|
|
|
|
|
|
|
|
|
void cBlockBedHandler::OnBroken(
|
|
cChunkInterface & a_ChunkInterface, cWorldInterface & a_WorldInterface,
|
|
const Vector3i a_BlockPos,
|
|
BLOCKTYPE a_OldBlockType, NIBBLETYPE a_OldBlockMeta,
|
|
const cEntity * a_Digger
|
|
) const
|
|
{
|
|
UNUSED(a_Digger);
|
|
auto Direction = MetaDataToDirection(a_OldBlockMeta & 0x03);
|
|
if ((a_OldBlockMeta & 0x08) != 0)
|
|
{
|
|
// Was pillow
|
|
Vector3i FootPos(a_BlockPos - Direction);
|
|
if (a_ChunkInterface.GetBlock(FootPos) == E_BLOCK_BED)
|
|
{
|
|
// First replace the bed with air
|
|
a_ChunkInterface.FastSetBlock(FootPos, E_BLOCK_AIR, 0);
|
|
|
|
// Then destroy the bed entity
|
|
a_ChunkInterface.SetBlock(FootPos, E_BLOCK_AIR, 0);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Was foot end
|
|
Vector3i PillowPos(a_BlockPos + Direction);
|
|
if (a_ChunkInterface.GetBlock(PillowPos) == E_BLOCK_BED)
|
|
{
|
|
// First replace the bed with air
|
|
a_ChunkInterface.FastSetBlock(PillowPos, E_BLOCK_AIR, 0);
|
|
|
|
// Then destroy the bed entity
|
|
a_ChunkInterface.SetBlock(PillowPos, E_BLOCK_AIR, 0);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool cBlockBedHandler::OnUse(
|
|
cChunkInterface & a_ChunkInterface,
|
|
cWorldInterface & a_WorldInterface,
|
|
cPlayer & a_Player,
|
|
Vector3i a_BlockPos,
|
|
eBlockFace a_BlockFace,
|
|
const Vector3i a_CursorPos
|
|
) const
|
|
{
|
|
// Source: https://minecraft.gamepedia.com/Bed#Sleeping
|
|
|
|
// Sleeping in bed only allowed in Overworld, beds explode elsewhere:
|
|
if (a_WorldInterface.GetDimension() != dimOverworld)
|
|
{
|
|
auto PosCopy = a_BlockPos;
|
|
a_WorldInterface.DoExplosionAt(5, a_BlockPos.x, a_BlockPos.y, a_BlockPos.z, true, esBed, &PosCopy);
|
|
return true;
|
|
}
|
|
|
|
auto Meta = a_ChunkInterface.GetBlockMeta(a_BlockPos);
|
|
|
|
if ((Meta & 0x8) == 0)
|
|
{
|
|
// Clicked on the foot of the bed, adjust to the head:
|
|
a_BlockPos += MetaDataToDirection(Meta & 0x03);
|
|
|
|
BLOCKTYPE Type;
|
|
a_ChunkInterface.GetBlockTypeMeta(a_BlockPos, Type, Meta);
|
|
if (Type != E_BLOCK_BED)
|
|
{
|
|
// Bed was incomplete, bail:
|
|
return true;
|
|
}
|
|
}
|
|
|
|
// Set the bed position to the pillow block:
|
|
a_Player.SetBedPos(a_BlockPos);
|
|
|
|
// Sleeping is allowed only during night and thunderstorms:
|
|
if (
|
|
!(((a_WorldInterface.GetTimeOfDay() > 12541_tick) && (a_WorldInterface.GetTimeOfDay() < 23458_tick)) ||
|
|
(a_Player.GetWorld()->GetWeather() == wThunderstorm))
|
|
)
|
|
{
|
|
a_Player.SendAboveActionBarMessage("You can only sleep at night and during thunderstorms");
|
|
return true;
|
|
}
|
|
|
|
// Check if the bed is occupied:
|
|
if ((Meta & 0x04) == 0x04)
|
|
{
|
|
a_Player.SendAboveActionBarMessage("This bed is occupied");
|
|
return true;
|
|
}
|
|
|
|
// Cannot sleep if there are hostile mobs nearby:
|
|
if (
|
|
!a_Player.GetWorld()->ForEachEntityInBox({ a_Player.GetPosition().addedY(-5), 8, 10 }, [](cEntity & a_Entity)
|
|
{
|
|
return a_Entity.IsMob() && (static_cast<cMonster&>(a_Entity).GetMobFamily() == cMonster::mfHostile);
|
|
})
|
|
)
|
|
{
|
|
a_Player.SendAboveActionBarMessage("You may not rest now, there are monsters nearby");
|
|
return true;
|
|
}
|
|
|
|
// This will broadcast "use bed" for the pillow block, if the player can sleep:
|
|
a_Player.SetIsInBed(true);
|
|
|
|
// Check sleeping was successful, if not, bail:
|
|
if (!a_Player.IsInBed())
|
|
{
|
|
return true;
|
|
}
|
|
|
|
// Occupy the bed, where 0x4 = occupied bit:
|
|
a_ChunkInterface.SetBlockMeta(a_BlockPos, Meta | 0x04);
|
|
a_Player.GetStatManager().AddValue(Statistic::SleepInBed);
|
|
|
|
// When sleeping, the player's bounding box moves to approximately where his head is.
|
|
// Set the player's position to somewhere close to the edge of the pillow block:
|
|
a_Player.SetPosition(Vector3f(0.4f, 1, 0.4f) * MetaDataToDirection(Meta & 0x03) + Vector3f(0.5f, 0.6875, 0.5f) + a_BlockPos);
|
|
|
|
// Fast-forward the time if all players in the world are in their beds:
|
|
if (a_WorldInterface.ForEachPlayer([](cPlayer & a_OtherPlayer) { return !a_OtherPlayer.IsInBed(); }))
|
|
{
|
|
a_WorldInterface.ForEachPlayer([&a_ChunkInterface](cPlayer & a_OtherPlayer)
|
|
{
|
|
VacateBed(a_ChunkInterface, a_OtherPlayer);
|
|
return false;
|
|
});
|
|
a_WorldInterface.SetTimeOfDay(0_tick);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void cBlockBedHandler::OnPlacedByPlayer(cChunkInterface & a_ChunkInterface, cWorldInterface & a_WorldInterface, cPlayer & a_Player, const sSetBlock & a_BlockChange) const
|
|
{
|
|
a_Player.GetWorld()->DoWithBlockEntityAt(a_BlockChange.GetAbsolutePos(), [&a_Player](cBlockEntity & a_BlockEntity)
|
|
{
|
|
ASSERT(a_BlockEntity.GetBlockType() == E_BLOCK_BED);
|
|
static_cast<cBedEntity &>(a_BlockEntity).SetColor(a_Player.GetEquippedItem().m_ItemDamage);
|
|
return false;
|
|
});
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
cItems cBlockBedHandler::ConvertToPickups(const NIBBLETYPE a_BlockMeta, const cItem * const a_Tool) const
|
|
{
|
|
// Drops handled by the block entity:
|
|
return {};
|
|
}
|