Aaron Suen 47ac931f34 Easy targeted stack placement via drop key + raycast.
Attempt to place stack node directly when player is pointing at
a node space, instead of tossing out an item ent.

This makes item organization less tedious in the early game, as
players are manipulating node positions instead of calculating
ent ballistics in their heads when trying to lay out inventories
on the ground, or using naive shelving.

(Note that wooden shelves still have an advantage in how quickly
items can be taken from them).

Items are still tossed as ents if a player is not looking at a
valid place in nodespace, so inventories can still be e.g. thrown
over a cliff or jettisoned in the ocean...
2019-04-05 07:42:48 -04:00

230 lines
6.8 KiB
Lua

-- LUALOCALS < ---------------------------------------------------------
local ItemStack, math, minetest, nodecore, setmetatable, type, vector
= ItemStack, math, minetest, nodecore, setmetatable, type, vector
local math_random
= math.random
-- LUALOCALS > ---------------------------------------------------------
local modname = minetest.get_current_modname()
minetest.register_node(modname .. ":stack", {
drawtype = "nodebox",
node_box = nodecore.fixedbox(
{-0.5, -0.5, -0.5, 0.5, -7/16, 0.5}
),
use_texture_alpha = true,
tiles = {
"nc_items_shadow.png",
"nc_items_blank.png",
},
walkable = true,
selection_box = nodecore.fixedbox(
{-0.4, -0.5, -0.4, 0.4, 0.3, 0.4}
),
collision_box = nodecore.fixedbox(),
drop = {},
groups = {
snappy = 1,
falling_repose = 1,
visinv = 1,
is_stack_only = 1
},
paramtype = "light",
sunlight_propagates = true,
repose_drop = function(posfrom, posto, node)
local stack = nodecore.stack_get(posfrom)
if stack and not stack:is_empty() then
nodecore.item_eject(posto, stack)
end
return minetest.remove_node(posfrom)
end,
on_rightclick = function(pos, node, whom, stack, pointed, ...)
if not nodecore.interact(whom) then return stack end
local def = nodecore.stack_get(pos):get_definition() or {}
if def.stack_rightclick then
local rtn = def.stack_rightclick(pos, node, whom, stack, pointed, ...)
if rtn then return rtn end
end
return nodecore.stack_add(pos, stack)
end,
on_construct = function(pos, ...)
minetest.after(0, function()
return nodecore.stack_sounds(pos, "place")
end)
return nodecore.visinv_on_construct(pos, ...)
end
})
function nodecore.place_stack(pos, stack, placer, pointed_thing)
stack = ItemStack(stack)
local name = stack:get_name()
local below = {x = pos.x, y = pos.y - 1, z = pos.z}
if nodecore.match(below, {name = name, count = false}) then
stack = nodecore.stack_add(below, stack)
if stack:is_empty() then return end
end
minetest.set_node(pos, {name = modname .. ":stack"})
nodecore.stack_set(pos, stack)
if placer and pointed_thing then
nodecore.craft_check(pos, {name = stack:get_name()}, {
action = "place",
crafter = placer,
pointed = pointed_thing
})
end
return minetest.after(0, function() minetest.check_for_falling(pos) end)
end
local bii = minetest.registered_entities["__builtin:item"]
local item = {
on_step = function(self, dtime, ...)
bii.on_step(self, dtime, ...)
local pos = self.object:getpos()
if not self.oldpos or not vector.equals(pos, self.oldpos) then
self.oldpos = pos
self.sitting = 0
return
end
self.sitting = (self.sitting or 0) + dtime
if self.sitting < 0.25 then return end
pos = vector.round(pos)
local i = ItemStack(self.itemstring)
pos = nodecore.scan_flood(pos, 5,
function(p)
if p.y > pos.y and math_random() < 0.95 then return end
if p.y > pos.y + 1 then return end
i = nodecore.stack_add(p, i)
if i:is_empty() then return p end
if nodecore.buildable_to(p) then return p end
end)
if not pos then return end
if not i:is_empty() then nodecore.place_stack(pos, i) end
self.itemstring = ""
self.object:remove()
end,
on_punch = function(self, whom, ...)
if not nodecore.interact(whom) then return end
local r = bii.on_punch(self, whom, ...)
if self.itemstring ~= "" then
local v = self.object:get_velocity()
v.x = v.x + math_random() * 5 - 2.5
v.y = v.y + math_random() * 5 - 2.5
v.z = v.z + math_random() * 5 - 2.5
self.object:set_velocity(v)
end
end
}
setmetatable(item, bii)
minetest.register_entity(":__builtin:item", item)
local bifn = minetest.registered_entities["__builtin:falling_node"]
local falling = {
set_node = function(self, node, meta, ...)
if node and node.name == modname .. ":stack"
and meta and meta.inventory and meta.inventory.solo then
local stack = ItemStack(meta.inventory.solo[1] or "")
if not stack:is_empty() then
minetest.add_item(self.object:getpos(), stack)
:set_velocity({x = 0, y = 0, z = 0})
return self.object:remove()
end
end
return bifn.set_node(self, node, meta, ...)
end
}
setmetatable(falling, bifn)
minetest.register_entity(":__builtin:falling_node", falling)
function minetest.item_place(itemstack, placer, pointed_thing, param2)
if not nodecore.interact(placer) then return end
if pointed_thing.type == "node" and placer and
not placer:get_player_control().sneak then
local n = minetest.get_node(pointed_thing.under)
local nn = n.name
local nd = minetest.registered_items[nn]
if nd and nd.on_rightclick then
return nd.on_rightclick(pointed_thing.under, n,
placer, itemstack, pointed_thing) or itemstack, false
end
end
local def = itemstack:get_definition()
if def.type == "node" and not def.place_as_item then
return minetest.item_place_node(itemstack, placer, pointed_thing, param2)
end
if not itemstack:is_empty() then
local above = minetest.get_pointed_thing_position(pointed_thing, true)
if above and nodecore.buildable_to(above) then
nodecore.place_stack(above, itemstack:take_item(), placer, pointed_thing)
end
end
return itemstack
end
if nodecore.loaded_mods().nc_fire then
nodecore.register_limited_abm({
label = "Flammable ItemStacks Ignite",
interval = 5,
chance = 1,
nodenames = {modname .. ":stack"},
neighbors = {"group:igniter"},
action = function(pos, node)
local stack = nodecore.stack_get(pos)
return nodecore.fire_check_ignite(pos, {name = stack:get_name()})
end
})
end
nodecore.register_cook_abm({nodenames = {modname .. ":stack"}})
if minetest.raycast then
local olddrop = minetest.item_drop
function minetest.item_drop(item, player, ...)
local oldadd = minetest.add_item
function minetest.add_item(pos, stack, ...)
if not minetest.raycast then
return oldadd(pos, stack, ...)
end
local start = player:get_pos()
local eyeheight = player:get_properties().eye_height or 1.625
start.y = start.y + eyeheight
local target = vector.add(start, vector.multiply(player:get_look_dir(), 4))
local pointed = minetest.raycast(start, target, false)()
if (not pointed) or pointed.type ~= "node" then
return oldadd(pos, stack, ...)
end
local dummyent = {}
setmetatable(dummyent, {__index = function(t, k)
return function() return {} end
end})
local name = stack:get_name()
local function tryplace(p)
if nodecore.match(p, {name = name, count = false}) then
stack = nodecore.stack_add(p, stack)
if stack:is_empty() then return dummyent end
end
if nodecore.buildable_to(p) then
nodecore.place_stack(p, stack, player, pointed)
return dummyent
end
end
return tryplace(pointed.under)
or tryplace(pointed.above)
or oldadd(pos, stack, ...)
end
local function helper(...)
minetest.add_item = oldadd
return ...
end
return helper(olddrop(item, player, ...))
end
end