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

// 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
	{
		// Prepare sound effect
		AString PlaceSound = cBlockInfo::GetPlaceSound(m_ItemType);
		float Volume = 1.0f, Pitch = 0.8f;

		// 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)
			)
			{
				a_World.BroadcastSoundEffect(PlaceSound, a_BlockX + 0.5, a_BlockY + 0.5, a_BlockZ + 0.5, Volume, Pitch);
				return a_Player.PlaceBlock(a_BlockX, a_BlockY, a_BlockZ, m_DoubleSlabBlockType, ClickedBlockMeta & 0x07);
			}

			// If clicking the bottom side of a top-half slab, combine into a doubleslab:
			if (
				(a_BlockFace == BLOCK_FACE_BOTTOM) &&
				((ClickedBlockMeta & 0x08) != 0)
			)
			{
				a_World.BroadcastSoundEffect(PlaceSound, a_BlockX + 0.5, a_BlockY + 0.5, a_BlockZ + 0.5, Volume, Pitch);
				return a_Player.PlaceBlock(a_BlockX, a_BlockY, a_BlockZ, m_DoubleSlabBlockType, ClickedBlockMeta & 0x07);
			}
		}

		// 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)
		)
		{
			a_World.BroadcastSoundEffect(PlaceSound, a_BlockX + 0.5, a_BlockY + 0.5, a_BlockZ + 0.5, Volume, Pitch);
			return a_Player.PlaceBlock(a_BlockX, a_BlockY, a_BlockZ, m_DoubleSlabBlockType, PlaceBlockMeta & 0x07);
		}

		// 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.: http://forum.mc-server.org/showthread.php?tid=434&pid=17388#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;
};