summaryrefslogtreecommitdiffstats
path: root/src/Blocks/BlockBed.cpp
blob: e333c0700be1943d30949a3cb65b3dc100b92c1b (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
180
181
182
183
184
185
186
187
188
189
190
191

// 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
)
{
	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,
	const Vector3i a_BlockPos,
	eBlockFace a_BlockFace,
	const Vector3i a_CursorPos
)
{
	// 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;
	}

	// Sleeping is allowed only during night and thunderstorms:
	if (
		!(((a_WorldInterface.GetTimeOfDay() > 12541) && (a_WorldInterface.GetTimeOfDay() < 23458)) ||
		(a_Player.GetWorld()->GetWeather() == wThunderstorm))
	)  // Source: https://minecraft.gamepedia.com/Bed#Sleeping
	{
		a_Player.SendAboveActionBarMessage("You can only sleep at night and during thunderstorms");

		// Try to set home position anyway:
		SetBedPos(a_Player, a_BlockPos);
		return true;
	}

	// Check if the bed is occupied:
	auto Meta = a_ChunkInterface.GetBlockMeta(a_BlockPos);
	if ((Meta & 0x04) == 0x04)
	{
		a_Player.SendMessageFailure("This bed is occupied");
		return true;
	}

	// Cannot sleep if there are hostile mobs nearby:
	auto FindMobs = [](cEntity & a_Entity)
	{
		return (
			(a_Entity.GetEntityType() == cEntity::etMonster) &&
			(static_cast<cMonster&>(a_Entity).GetMobFamily() == cMonster::mfHostile)
		);
	};
	if (!a_Player.GetWorld()->ForEachEntityInBox(cBoundingBox(a_Player.GetPosition() - Vector3i(0, 5, 0), 8, 10), FindMobs))
	{
		a_Player.SendAboveActionBarMessage("You may not rest now, there are monsters nearby");
		return true;
	}

	// Broadcast the "Use bed" for the pillow block:
	if ((Meta & 0x8) == 0x8)
	{
		// Is pillow
		a_WorldInterface.GetBroadcastManager().BroadcastUseBed(a_Player, a_BlockPos);
	}
	else
	{
		// Is foot end
		VERIFY((Meta & 0x04) != 0x04);  // Occupied flag should never be set, else our compilator (intended) is broken

		auto PillowPos = a_BlockPos + MetaDataToDirection(Meta & 0x03);
		if (a_ChunkInterface.GetBlock(PillowPos) == E_BLOCK_BED)  // Must always use pillow location for sleeping
		{
			a_WorldInterface.GetBroadcastManager().BroadcastUseBed(a_Player, PillowPos);
		}
	}

	// Occupy the bed:
	SetBedPos(a_Player, a_BlockPos);
	SetBedOccupationState(a_ChunkInterface, a_Player.GetLastBedPos(), true);
	a_Player.SetIsInBed(true);

	// Fast-forward the time if all players in the world are in their beds:
	auto TimeFastForwardTester = [](cPlayer & a_OtherPlayer)
	{
		return !a_OtherPlayer.IsInBed();
	};
	if (a_WorldInterface.ForEachPlayer(TimeFastForwardTester))
	{
		a_WorldInterface.ForEachPlayer([&](cPlayer & a_OtherPlayer)
			{
				cBlockBedHandler::SetBedOccupationState(a_ChunkInterface, a_OtherPlayer.GetLastBedPos(), false);
				a_OtherPlayer.SetIsInBed(false);
				return false;
			}
		);
		a_WorldInterface.SetTimeOfDay(0);
		a_ChunkInterface.SetBlockMeta(a_BlockPos, Meta & 0x0b);  // Clear the "occupied" bit of the bed's block
	}
	return true;
}





void cBlockBedHandler::OnPlacedByPlayer(cChunkInterface & a_ChunkInterface, cWorldInterface & a_WorldInterface, cPlayer & a_Player, const sSetBlock & a_BlockChange)
{
	a_Player.GetWorld()->DoWithBedAt(a_BlockChange.GetX(), a_BlockChange.GetY(), a_BlockChange.GetZ(), [&](cBedEntity & a_Bed)
		{
			a_Bed.SetColor(a_Player.GetEquippedItem().m_ItemDamage);
			return true;
		}
	);
}





cItems cBlockBedHandler::ConvertToPickups(NIBBLETYPE a_BlockMeta, cBlockEntity * a_BlockEntity, const cEntity * a_Digger, const cItem * a_Tool)
{
	short color = E_META_WOOL_RED;
	if (a_BlockEntity != nullptr)
	{
		color = reinterpret_cast<cBedEntity *>(a_BlockEntity)->GetColor();
	}
	return cItem(E_ITEM_BED, 1, color);
}





void cBlockBedHandler::SetBedPos(cPlayer & a_Player, const Vector3i a_BedPosition)
{
	if (a_Player.GetLastBedPos() != a_BedPosition)
	{
		a_Player.SetBedPos(a_BedPosition);
		a_Player.SendMessageSuccess("Home position set successfully");
	}
}