-
Notifications
You must be signed in to change notification settings - Fork 28
/
util_item_place_node.lua
180 lines (153 loc) · 7.19 KB
/
util_item_place_node.lua
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
-- 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 has_prefix(str, prefix)
return str:sub(1, string.len(prefix)) == prefix
end
digtron.whitelisted_on_place = function (item_name)
for listed_item, value in pairs(digtron.builder_on_place_items) do
if item_name == listed_item then return value end
end
for prefix, value in pairs(digtron.builder_on_place_prefixes) do
if has_prefix(item_name, prefix) then return value end
end
if minetest.get_item_group(item_name, "digtron_on_place") > 0 then return true end
return false
end
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 item_name = itemstack:get_name()
local def = itemstack:get_definition()
if (not def) or (param2 < 0) or (def.paramtype2 == "wallmounted" and param2 > 5) or (param2 > 23) then -- validate parameters
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 and def.on_place ~= minetest.nodedef_default.on_place and digtron.whitelisted_on_place(item_name) 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 and 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
if minetest.registered_nodes[item_name] == nil then
-- Permitted craft items are handled by the node-specific on_place call, above.
-- if we are a craft item and we get here then we're not whitelisted and we should fail.
-- Note that builder settings should be filtering out craft items like this before we get here,
-- but this will protect us just in case.
return itemstack, false
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, using genuine player for per-node definition.
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, using fake_player to take the blame.
-- Note that fake_player:update is called in the DigtronLayout class's "create" function,
-- which is called before Digtron does any of this building stuff, so it's not necessary
-- to update it here.
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, digtron.fake_player, oldnode_copy, itemstack, pointed_thing_copy) then
take_item = false
end
end
if take_item then
itemstack:take_item()
end
return itemstack, true
end