Skip to content

Commit

Permalink
Stairs: Big simplification of slabs combination
Browse files Browse the repository at this point in the history
Combine slabs if identical based on orientations using a simple lookup
table if the nodes are identical.

Otherwise relies on place_node() to place the node, which properly
handles rotation compared to adjacent nodes already, and can orient
based on look_dir as well.

Initial slabs placed are oriented based on (1) the orientation of
the pointed "face" (assumes nodes are cubic, of course), and uses
the player look direction to orient the node n/e/w/s if the slab
is horizontal or upside-down. If placed against a vertical face,
the slab is placed against the face without rotation around the axis
perpendicular to that vertical face. This allows upside down placement
and vertical placement without screwdriver.

If a slab is placed on top of an upside down slab, or below a normally
placed slab, the rotation is inverted so that no "floating" slab
is created.

Largely based on kilbith's #807 PR. Slab combining and place_node()
usage by sofar.

Since this relies entirely on `on_place` mechanics, this fails to
combine slabs into a plain node if the space *above* is occupied.
This is unavoidable due to the fact that on_place() happens after
the checks required to see if pointed_thing.above is empty or not.
  • Loading branch information
sofar authored and paramat committed Sep 14, 2016
1 parent 0cbb516 commit b848e35
Showing 1 changed file with 51 additions and 71 deletions.
122 changes: 51 additions & 71 deletions mods/stairs/init.lua
Expand Up @@ -110,6 +110,11 @@ function stairs.register_stair(subname, recipeitem, groups, images, description,
end


-- Slab facedir to placement 6d matching table
local slab_trans_dir = {[0] = 8, 0, 2, 1, 3, 4}
-- Slab facedir when placing initial slab against other surface
local slab_trans_dir_place = {[0] = 0, 20, 12, 16, 4, 8}

-- Register slabs.
-- Node will be called stairs:slab_<subname>

Expand All @@ -129,86 +134,61 @@ function stairs.register_slab(subname, recipeitem, groups, images, description,
fixed = {-0.5, -0.5, -0.5, 0.5, 0, 0.5},
},
on_place = function(itemstack, placer, pointed_thing)
if pointed_thing.type ~= "node" then
return itemstack
end
local under = minetest.get_node(pointed_thing.under)
local wield_item = itemstack:get_name()

-- If it's being placed on an another similar one, replace it with
-- a full block
local slabpos = nil
local slabnode = nil
local p0 = pointed_thing.under
local p1 = pointed_thing.above
local n0 = minetest.get_node(p0)
local n1 = minetest.get_node(p1)
local param2 = 0
if under and wield_item == under.name then

This comment has been minimized.

Copy link
@HybridDog

HybridDog Sep 14, 2016

Contributor

@sofar
minetest.get_node ↑ either returns a table or it crashes due to wrong argument

What is the meaning of testing if under exists?

This comment has been minimized.

Copy link
@paramat

paramat Sep 14, 2016

Contributor

'if under' was to cope with unknown nodes.

This comment has been minimized.

Copy link
@t4im

t4im Sep 14, 2016

Contributor

Unknown nodes aren't nil with get_node though. get_node_or_nil could be nil for CONTENT_IGNORE . Unknown nodes are only nil when looking up their definitions.

Also see my explanation here

This comment has been minimized.

Copy link
@paramat

paramat Sep 15, 2016

Contributor

Ok, i was confused, this code here then only prevents combining an unknown slab with itself, so probably isn't necessary.

This comment has been minimized.

Copy link
@sofar

sofar Sep 15, 2016

Author Contributor

under here will be either ignore or (some node) or stairs:slab_wood.

If it's ignore then it's impossible that the player also wields a ignore node and so this section is never executed, and the slab is placed above the ignore node (line 182 and beyond) which uses minetest.item_place(), and that one will succeed placing the wielded slab(*).

Even though technically we should check for node.name == "ignore", the check can be omitted in this particular case due to the fact that the game will likely blow up badly if the player manages to get an ignore node in their inventory.

This comment has been minimized.

Copy link
@HybridDog

HybridDog Sep 15, 2016

Contributor

ignore does not wreak havoc in my experience, l disabled the "fucks up stuff" prevention and placed it without problems

Anyway, a crash caused by placing ignore doesn't need to be stymied and
the under test should be removed to not bemuse reviewing people.

This comment has been minimized.

Copy link
@paramat

paramat Sep 15, 2016

Contributor

I would prefer the minimum code necessary that still avoids a crash in the case of an unknown or ignore node.

-- place slab using under node orientation
local dir = minetest.dir_to_facedir(vector.subtract(
pointed_thing.above, pointed_thing.under), true)

local n0_is_upside_down = (n0.name == "stairs:slab_" .. subname and
n0.param2 >= 20)
local p2 = under.param2

if n0.name == "stairs:slab_" .. subname and not n0_is_upside_down and
p0.y + 1 == p1.y then
slabpos = p0
slabnode = n0
elseif n1.name == "stairs:slab_" .. subname then
slabpos = p1
slabnode = n1
end
if slabpos then
-- Remove the slab at slabpos
minetest.remove_node(slabpos)
-- Make a fake stack of a single item and try to place it
local fakestack = ItemStack(recipeitem)
fakestack:set_count(itemstack:get_count())

pointed_thing.above = slabpos
local success
fakestack, success = minetest.item_place(fakestack, placer,
pointed_thing)
-- If the item was taken from the fake stack, decrement original
if success then
itemstack:set_count(fakestack:get_count())
-- Else put old node back
else
minetest.set_node(slabpos, slabnode)
end
return itemstack
end

-- Upside down slabs
if p0.y - 1 == p1.y then
-- Turn into full block if pointing at a existing slab
if n0_is_upside_down then
-- Remove the slab at the position of the slab
minetest.remove_node(p0)
-- Make a fake stack of a single item and try to place it
local fakestack = ItemStack(recipeitem)
fakestack:set_count(itemstack:get_count())

pointed_thing.above = p0
local success
fakestack, success = minetest.item_place(fakestack, placer,
pointed_thing)
-- If the item was taken from the fake stack, decrement original
if success then
itemstack:set_count(fakestack:get_count())
-- Else put old node back
else
minetest.set_node(p0, n0)
-- combine two slabs if possible
if slab_trans_dir[math.floor(p2 / 4)] == dir then
if not recipeitem then
return itemstack
end
local player_name = placer:get_player_name()
if minetest.is_protected(pointed_thing.under, player_name) and not
minetest.check_player_privs(placer, "protection_bypass") then
minetest.record_protection_violation(pointed_thing.under,
player_name)
return
end
minetest.set_node(pointed_thing.under, {name = recipeitem, param2 = p2})
if not minetest.setting_getbool("creative_mode") then
itemstack:take_item()
end
return itemstack
end

-- Place upside down slab
param2 = 20
end
-- Placing a slab on an upside down slab should make it right-side up.
if p2 >= 20 and dir == 8 then
p2 = p2 - 20
-- same for the opposite case: slab below normal slab
elseif p2 <= 3 and dir == 4 then
p2 = p2 + 20
end

-- If pointing at the side of a upside down slab
if n0_is_upside_down and p0.y + 1 ~= p1.y then
param2 = 20
end
-- else attempt to place node with proper param2
minetest.item_place_node(ItemStack(wield_item), placer, pointed_thing, p2)
if not minetest.setting_getbool("creative_mode") then
itemstack:take_item()
end
return itemstack
else
-- place slab using look direction of player
local dir = minetest.dir_to_wallmounted(vector.subtract(
pointed_thing.above, pointed_thing.under), true)

local rot = slab_trans_dir_place[dir]
if rot == 0 or rot == 20 then
rot = rot + minetest.dir_to_facedir(placer:get_look_dir())
end

return minetest.item_place(itemstack, placer, pointed_thing, param2)
return minetest.item_place(itemstack, placer, pointed_thing, rot)
end
end,
})

Expand Down

0 comments on commit b848e35

Please sign in to comment.