summaryrefslogtreecommitdiffstats
path: root/src/Items/ItemSlab.h
blob: 7741f5f30dcc445c5cb48362425de52a78cd180f (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

// ItemSlab.h

// Declares the cItemSlabHandler responsible for handling slabs, when in their item form.





#pragma once

#include "ItemHandler.h"
#include "../Blocks/BlockSlab.h"





class cItemSlabHandler:
	public cItemHandler
{
	typedef cItemHandler super;

public:

	/** Creates a new handler for the specified slab item type.
	Sets the handler to use the specified doubleslab block type for combining self into doubleslabs. */
	cItemSlabHandler(int a_ItemType, BLOCKTYPE a_DoubleSlabBlockType):
		super(a_ItemType),
		m_DoubleSlabBlockType(a_DoubleSlabBlockType)
	{
	}


	// cItemHandler overrides:
	virtual bool OnPlayerPlace(
		cWorld & a_World, cPlayer & a_Player, const cItem & a_EquippedItem,
		int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace,
		int a_CursorX, int a_CursorY, int a_CursorZ
	) override
	{
		// Special slab handling - placing a slab onto another slab produces a dblslab instead:
		BLOCKTYPE ClickedBlockType;
		NIBBLETYPE ClickedBlockMeta;
		a_World.GetBlockTypeMeta(a_BlockX, a_BlockY, a_BlockZ, ClickedBlockType, ClickedBlockMeta);
		// If clicked on a half slab directly
		if (
			(ClickedBlockType == m_ItemType) &&                         // Placing the same slab material
			((ClickedBlockMeta & 0x07) == a_EquippedItem.m_ItemDamage)  // Placing the same slab sub-kind (and existing slab is single)
		)
		{
			// If clicking the top side of a bottom-half slab, combine into a doubleslab:
			if (
				(a_BlockFace == BLOCK_FACE_TOP) &&
				((ClickedBlockMeta & 0x08) == 0)
			)
			{
				if (!a_Player.PlaceBlock(a_BlockX, a_BlockY, a_BlockZ, m_DoubleSlabBlockType, ClickedBlockMeta & 0x07))
				{
					return false;
				}
				if (a_Player.IsGameModeSurvival())
				{
					a_Player.GetInventory().RemoveOneEquippedItem();
				}
				return true;
			}

			// If clicking the bottom side of a top-half slab, combine into a doubleslab:
			if (
				(a_BlockFace == BLOCK_FACE_BOTTOM) &&
				((ClickedBlockMeta & 0x08) != 0)
			)
			{
				if (!a_Player.PlaceBlock(a_BlockX, a_BlockY, a_BlockZ, m_DoubleSlabBlockType, ClickedBlockMeta & 0x07))
				{
					return false;
				}
				if (a_Player.IsGameModeSurvival())
				{
					a_Player.GetInventory().RemoveOneEquippedItem();
				}
				return true;
			}
		}

		// Checking the type of block that should be placed
		AddFaceDirection(a_BlockX, a_BlockY, a_BlockZ, a_BlockFace);
		BLOCKTYPE PlaceBlockType;
		NIBBLETYPE PlaceBlockMeta;
		a_World.GetBlockTypeMeta(a_BlockX, a_BlockY, a_BlockZ, PlaceBlockType, PlaceBlockMeta);
		// If it's a slab combine into a doubleslab (means that clicked on side, top or bottom of a block adjacent to a half slab)
		if (
			(PlaceBlockType == m_ItemType) &&                         // Placing the same slab material
			((PlaceBlockMeta & 0x07) == a_EquippedItem.m_ItemDamage)  // Placing the same slab sub-kind (and existing slab is single)
		)
		{
			if (!a_Player.PlaceBlock(a_BlockX, a_BlockY, a_BlockZ, m_DoubleSlabBlockType, PlaceBlockMeta & 0x07))
			{
				return false;
			}
			if (a_Player.IsGameModeSurvival())
			{
				a_Player.GetInventory().RemoveOneEquippedItem();
			}
			return true;
		}

		// The slabs didn't combine, use the default handler to place the slab:
		AddFaceDirection(a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, true);
		bool res = super::OnPlayerPlace(a_World, a_Player, a_EquippedItem, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ);

		/*
		The client has a bug when a slab replaces snow and there's a slab above it.
		The client then combines the slab above, rather than replacing the snow.
		We send the block above the currently placed block back to the client to fix the bug.
		Ref.: https://forum.cuberite.org/thread-434-post-17388.html#pid17388
		*/
		if ((a_BlockFace == BLOCK_FACE_TOP) && (a_BlockY < cChunkDef::Height - 1))
		{
			a_Player.SendBlocksAround(a_BlockX, a_BlockY + 1, a_BlockZ, 1);
		}
		return res;
	}

protected:
	/** The block type to use when the slab combines into a doubleslab block. */
	BLOCKTYPE m_DoubleSlabBlockType;
};