summaryrefslogtreecommitdiffstats
path: root/src/Blocks/BlockBed.cpp
blob: 68f45b518797b0a8945be7a23211dc2b41981900 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179

// 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 {};
}