summaryrefslogtreecommitdiff
path: root/util_item_place_node.lua
blob: 77c499b5479722fe2e0054b9e69361aaff1ee809 (plain)
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
-- The default minetest.item_place_node from item.lua was hard to work with given some of the details
-- of how it handled pointed_thing. It also didn't work right with default:torch and seeds. It was simpler to
-- just copy it here and chop out the special cases that were causing problems, and add some special handling.
-- for nodes that define on_place

-- This specific file is therefore licensed under the LGPL 2.1

--GNU Lesser General Public License, version 2.1
--Copyright (C) 2011-2016 celeron55, Perttu Ahola <celeron55@gmail.com>
--Copyright (C) 2011-2016 Various Minetest developers and contributors

--This program is free software; you can redistribute it and/or modify it under the terms
--of the GNU Lesser General Public License as published by the Free Software Foundation;
--either version 2.1 of the License, or (at your option) any later version.

--This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
--without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
--See the GNU Lesser General Public License for more details:
--https://www.gnu.org/licenses/old-licenses/lgpl-2.1.html

-- Mapping from facedir value to index in facedir_to_dir.
digtron.facedir_to_dir_map = {
	[0]=1, 2, 3, 4,
	5, 2, 6, 4,
	6, 2, 5, 4,
	1, 5, 3, 6,
	1, 6, 3, 5,
	1, 4, 3, 2,
}

local function copy_pointed_thing(pointed_thing)
	return {
		type  = pointed_thing.type,
		above = vector.new(pointed_thing.above),
		under = vector.new(pointed_thing.under),
	}
end

local function check_attached_node(p, n)
	local def = minetest.registered_nodes[n.name]
	local d = {x = 0, y = 0, z = 0}
	if def.paramtype2 == "wallmounted" then
		-- The fallback vector here is in case 'wallmounted to dir' is nil due
		-- to voxelmanip placing a wallmounted node without resetting a
		-- pre-existing param2 value that is out-of-range for wallmounted.
		-- The fallback vector corresponds to param2 = 0.
		d = minetest.wallmounted_to_dir(n.param2) or {x = 0, y = 1, z = 0}
	else
		d.y = -1
	end
	local p2 = vector.add(p, d)
	local nn = minetest.get_node(p2).name
	local def2 = minetest.registered_nodes[nn]
	if def2 and not def2.walkable then
		return false
	end
	return true
end

digtron.item_place_node = function(itemstack, placer, place_to, param2)
	local def = itemstack:get_definition()
	if not def or def.type ~= "node" then
		return itemstack, false
	end

	local pointed_thing = {}
	pointed_thing.type = "node"
	pointed_thing.above = {x=place_to.x, y=place_to.y, z=place_to.z}
	pointed_thing.under = {x=place_to.x, y=place_to.y - 1, z=place_to.z}
	
	-- Handle node-specific on_place calls as best we can.
	if def.on_place then
		if def.paramtype2 == "facedir" then
			pointed_thing.under = vector.add(place_to, minetest.facedir_to_dir(param2))
		elseif def.paramtype2 == "wallmounted" then
			pointed_thing.under = vector.add(place_to, minetest.wallmounted_to_dir(param2))
		end
	
		-- pass a copy of the item stack parameter because on_place might modify it directly and then we can't tell if we succeeded or not
		-- though note that some mods do "creative_mode" handling within their own on_place methods, which makes it impossible for Digtron
		-- to know what to do in that case - if you're in creative_mode Digtron will place such items but it will think it failed and not
		-- deduct them from inventory no matter what Digtron's settings are. Unfortunate, but not very harmful and I have no workaround.
		local returnstack, success = def.on_place(ItemStack(itemstack), placer, pointed_thing)
		if returnstack:get_count() < itemstack:get_count() then success = true end -- some mods neglect to return a success condition
		if success then
			-- Override the param2 value to force it to be what Digtron wants
			local placed_node = minetest.get_node(place_to)
			placed_node.param2 = param2
			minetest.set_node(place_to, placed_node)
		end
		
		return returnstack, success
	end
	
	local oldnode = minetest.get_node_or_nil(place_to)

	--this should never happen, digtron is testing for adjacent unloaded nodes before getting here.
	if not oldnode then
		minetest.log("info", placer:get_player_name() .. " tried to place"
			.. " node in unloaded position " .. minetest.pos_to_string(place_to)
			.. " using a digtron.")
		return itemstack, false
	end

	local newnode = {name = def.name, param1 = 0, param2 = param2}
	if def.place_param2 ~= nil then
		newnode.param2 = def.place_param2
	end

	-- Check if the node is attached and if it can be placed there
	if minetest.get_item_group(def.name, "attached_node") ~= 0 and
		not check_attached_node(place_to, newnode) then
		minetest.log("action", "attached node " .. def.name ..
			" can not be placed at " .. minetest.pos_to_string(place_to))
		return itemstack, false
	end
	
	-- Add node and update
	minetest.add_node(place_to, newnode)

	local take_item = true

	-- Run callback
	if def.after_place_node then
		-- Deepcopy place_to and pointed_thing because callback can modify it
		local place_to_copy = {x=place_to.x, y=place_to.y, z=place_to.z}
		local pointed_thing_copy = copy_pointed_thing(pointed_thing)
		if def.after_place_node(place_to_copy, placer, itemstack,
				pointed_thing_copy) then
			take_item = false
		end
	end

	-- Run script hook
	local _, callback
	for _, callback in ipairs(minetest.registered_on_placenodes) do
		-- Deepcopy pos, node and pointed_thing because callback can modify them
		local place_to_copy = {x=place_to.x, y=place_to.y, z=place_to.z}
		local newnode_copy = {name=newnode.name, param1=newnode.param1, param2=newnode.param2}
		local oldnode_copy = {name=oldnode.name, param1=oldnode.param1, param2=oldnode.param2}
		local pointed_thing_copy = copy_pointed_thing(pointed_thing)
		if callback(place_to_copy, newnode_copy, placer, oldnode_copy, itemstack, pointed_thing_copy) then
			take_item = false
		end
	end

	if take_item then
		itemstack:take_item()
	end
	return itemstack, true
end