From 888b0ebfec8c2eff9015163549a7e47443cb8665 Mon Sep 17 00:00:00 2001 From: Vanessa Ezekowitz Date: Fri, 1 Apr 2016 21:00:20 -0400 Subject: "explode" all modpacks into their individual components (you can't have a modpack buried inside a modpack) --- homedecor/handlers/expansion.lua | 362 +++++++++++++++++++++++++++++++++ homedecor/handlers/furnaces.lua | 279 +++++++++++++++++++++++++ homedecor/handlers/init.lua | 22 ++ homedecor/handlers/inventory.lua | 173 ++++++++++++++++ homedecor/handlers/nodeboxes.lua | 62 ++++++ homedecor/handlers/registration.lua | 95 +++++++++ homedecor/handlers/sit.lua | 32 +++ homedecor/handlers/water_particles.lua | 115 +++++++++++ 8 files changed, 1140 insertions(+) create mode 100644 homedecor/handlers/expansion.lua create mode 100644 homedecor/handlers/furnaces.lua create mode 100644 homedecor/handlers/init.lua create mode 100644 homedecor/handlers/inventory.lua create mode 100644 homedecor/handlers/nodeboxes.lua create mode 100644 homedecor/handlers/registration.lua create mode 100644 homedecor/handlers/sit.lua create mode 100644 homedecor/handlers/water_particles.lua (limited to 'homedecor/handlers') diff --git a/homedecor/handlers/expansion.lua b/homedecor/handlers/expansion.lua new file mode 100644 index 0000000..0767837 --- /dev/null +++ b/homedecor/handlers/expansion.lua @@ -0,0 +1,362 @@ +local S = homedecor.gettext + +-- vectors to place one node next to or behind another + +homedecor.fdir_to_right = { + { 1, 0 }, + { 0, -1 }, + { -1, 0 }, + { 0, 1 }, +} + +homedecor.fdir_to_left = { + { -1, 0 }, + { 0, 1 }, + { 1, 0 }, + { 0, -1 }, +} + +homedecor.fdir_to_fwd = { + { 0, 1 }, + { 1, 0 }, + { 0, -1 }, + { -1, 0 }, +} + +local placeholder_node = "homedecor:expansion_placeholder" +minetest.register_node(placeholder_node, { + description = "Expansion placeholder (you hacker you!)", + groups = { not_in_creative_inventory=1 }, + drawtype = "airlike", + paramtype = "light", + walkable = false, + selection_box = { type = "fixed", fixed = { 0, 0, 0, 0, 0, 0 } }, + is_ground_content = false, + sunlight_propagates = true, + buildable_to = false, +}) + +--- select which node was pointed at based on it being known, not ignored, buildable_to +-- returns nil if no node could be selected +local function select_node(pointed_thing) + local pos = pointed_thing.under + local node = minetest.get_node_or_nil(pos) + local def = node and minetest.registered_nodes[node.name] + + if not def or not def.buildable_to then + pos = pointed_thing.above + node = minetest.get_node_or_nil(pos) + def = node and minetest.registered_nodes[node.name] + end + return def and pos, def +end + +--- check if all nodes can and may be build to +local function is_buildable_to(placer_name, ...) + for _, pos in ipairs({...}) do + local node = minetest.get_node_or_nil(pos) + local def = node and minetest.registered_nodes[node.name] + if not (def and def.buildable_to) or minetest.is_protected(pos, placer_name) then + return false + end + end + return true +end + +-- place one or two nodes if and only if both can be placed +local function stack(itemstack, placer, fdir, pos, def, pos2, node1, node2) + local placer_name = placer:get_player_name() or "" + if is_buildable_to(placer_name, pos, pos2) then + local fdir = fdir or minetest.dir_to_facedir(placer:get_look_dir()) + minetest.set_node(pos, { name = node1, param2 = fdir }) + node2 = node2 or "air" -- this can be used to clear buildable_to nodes even though we are using a multinode mesh + -- do not assume by default, as we still might want to allow overlapping in some cases + local has_facedir = node2 ~= "air" + if node2 == "placeholder" then + has_facedir = false + node2 = placeholder_node + end + minetest.set_node(pos2, { name = node2, param2 = (has_facedir and fdir) or nil }) + + -- call after_place_node of the placed node if available + local ctrl_node_def = minetest.registered_nodes[node1] + if ctrl_node_def and ctrl_node_def.after_place_node then + ctrl_node_def.after_place_node(pos, placer) + end + + if not homedecor.expect_infinite_stacks then + itemstack:take_item() + end + end + return itemstack +end + +local function rightclick_pointed_thing(pos, placer, itemstack) + local node = minetest.get_node_or_nil(pos) + if not node then return false end + local def = minetest.registered_nodes[node.name] + if not def or not def.on_rightclick then return false end + return def.on_rightclick(pos, node, placer, itemstack) or itemstack +end + +-- Stack one node above another +-- leave the last argument nil if it's one 2m high node +function homedecor.stack_vertically(itemstack, placer, pointed_thing, node1, node2) + local rightclick_result = rightclick_pointed_thing(pointed_thing.under, placer, itemstack) + if rightclick_result then return rightclick_result end + + local pos, def = select_node(pointed_thing) + if not pos then return itemstack end + + local top_pos = { x=pos.x, y=pos.y+1, z=pos.z } + + return stack(itemstack, placer, nil, pos, def, top_pos, node1, node2) +end + +-- Stack one door node above another +-- like homedecor.stack_vertically but tests first if it was placed as a right wing, then uses node1_right and node2_right instead + +function homedecor.stack_wing(itemstack, placer, pointed_thing, node1, node2, node1_right, node2_right) + local rightclick_result = rightclick_pointed_thing(pointed_thing.under, placer, itemstack) + if rightclick_result then return rightclick_result end + + local pos, def = select_node(pointed_thing) + if not pos then return itemstack end + + local forceright = placer:get_player_control()["sneak"] + local fdir = minetest.dir_to_facedir(placer:get_look_dir()) + + local is_right_wing = node1 == minetest.get_node({ x = pos.x + homedecor.fdir_to_left[fdir+1][1], y=pos.y, z = pos.z + homedecor.fdir_to_left[fdir+1][2] }).name + if forceright or is_right_wing then + node1, node2 = node1_right, node2_right + end + + local top_pos = { x=pos.x, y=pos.y+1, z=pos.z } + return stack(itemstack, placer, fdir, pos, def, top_pos, node1, node2) +end + +function homedecor.stack_sideways(itemstack, placer, pointed_thing, node1, node2, dir) + local rightclick_result = rightclick_pointed_thing(pointed_thing.under, placer, itemstack) + if rightclick_result then return rightclick_result end + + local pos, def = select_node(pointed_thing) + if not pos then return itemstack end + + local fdir = minetest.dir_to_facedir(placer:get_look_dir()) + local fdir_transform = dir and homedecor.fdir_to_right or homedecor.fdir_to_fwd + + local pos2 = { x = pos.x + fdir_transform[fdir+1][1], y=pos.y, z = pos.z + fdir_transform[fdir+1][2] } + + return stack(itemstack, placer, fdir, pos, def, pos2, node1, node2) +end + +function homedecor.bed_expansion(pos, placer, itemstack, pointed_thing, color) + + local thisnode = minetest.get_node(pos) + local fdir = thisnode.param2 + + local fxd = homedecor.fdir_to_fwd[fdir+1][1] + local fzd = homedecor.fdir_to_fwd[fdir+1][2] + + local forwardpos = {x=pos.x+fxd, y=pos.y, z=pos.z+fzd} + local forwardnode = minetest.get_node(forwardpos) + + local def = minetest.registered_nodes[forwardnode.name] + local placer_name = placer:get_player_name() + + if not (def and def.buildable_to) then + minetest.chat_send_player( placer:get_player_name(), "Not enough room - the space for the headboard is occupied!" ) + minetest.set_node(pos, {name = "air"}) + return true + end + + if minetest.is_protected(forwardpos, placer_name) then + minetest.chat_send_player( placer:get_player_name(), "Someone already owns the spot where the headboard goes." ) + return true + end + + minetest.set_node(forwardpos, {name = "air"}) + + local lxd = homedecor.fdir_to_left[fdir+1][1] + local lzd = homedecor.fdir_to_left[fdir+1][2] + local leftpos = {x=pos.x+lxd, y=pos.y, z=pos.z+lzd} + local leftnode = minetest.get_node(leftpos) + + local rxd = homedecor.fdir_to_right[fdir+1][1] + local rzd = homedecor.fdir_to_right[fdir+1][2] + local rightpos = {x=pos.x+rxd, y=pos.y, z=pos.z+rzd} + local rightnode = minetest.get_node(rightpos) + + if leftnode.name == "homedecor:bed_"..color.."_regular" then + local newname = string.gsub(thisnode.name, "_regular", "_kingsize") + minetest.set_node(pos, {name = "air"}) + minetest.set_node(leftpos, { name = newname, param2 = fdir}) + elseif rightnode.name == "homedecor:bed_"..color.."_regular" then + local newname = string.gsub(thisnode.name, "_regular", "_kingsize") + minetest.set_node(rightpos, {name = "air"}) + minetest.set_node(pos, { name = newname, param2 = fdir}) + end + + local topnode = minetest.get_node({x=pos.x, y=pos.y+1.0, z=pos.z}) + local bottomnode = minetest.get_node({x=pos.x, y=pos.y-1.0, z=pos.z}) + + if string.find(topnode.name, "homedecor:bed_.*_regular$") then + if fdir == topnode.param2 then + local newname = string.gsub(thisnode.name, "_regular", "_extended") + minetest.set_node(pos, { name = newname, param2 = fdir}) + end + end + + if string.find(bottomnode.name, "homedecor:bed_.*_regular$") then + if fdir == bottomnode.param2 then + local newname = string.gsub(bottomnode.name, "_regular", "_extended") + minetest.set_node({x=pos.x, y=pos.y-1.0, z=pos.z}, { name = newname, param2 = fdir}) + end + end +end + +function homedecor.unextend_bed(pos, color) + local bottomnode = minetest.get_node({x=pos.x, y=pos.y-1.0, z=pos.z}) + local fdir = bottomnode.param2 + if string.find(bottomnode.name, "homedecor:bed_.*_extended$") then + local newname = string.gsub(bottomnode.name, "_extended", "_regular") + minetest.set_node({x=pos.x, y=pos.y-1.0, z=pos.z}, { name = newname, param2 = fdir}) + end +end + +function homedecor.place_banister(itemstack, placer, pointed_thing) + local rightclick_result = rightclick_pointed_thing(pointed_thing.under, placer, itemstack) + if rightclick_result then return rightclick_result end + + local pos, def = select_node(pointed_thing) + if not pos then return itemstack end + + local fdir = minetest.dir_to_facedir(placer:get_look_dir()) + + local abovepos = { x=pos.x, y=pos.y+1, z=pos.z } + local abovenode = minetest.get_node(abovepos) + + local adef = minetest.registered_nodes[abovenode.name] + local placer_name = placer:get_player_name() + + if not (adef and adef.buildable_to) then + minetest.chat_send_player(placer_name, "Not enough room - the upper space is occupied!" ) + return itemstack + end + + if minetest.is_protected(abovepos, placer_name) then + minetest.chat_send_player(placer_name, "Someone already owns that spot." ) + return itemstack + end + + local lxd = homedecor.fdir_to_left[fdir+1][1] + local lzd = homedecor.fdir_to_left[fdir+1][2] + + local rxd = homedecor.fdir_to_right[fdir+1][1] + local rzd = homedecor.fdir_to_right[fdir+1][2] + + local fxd = homedecor.fdir_to_fwd[fdir+1][1] + local fzd = homedecor.fdir_to_fwd[fdir+1][2] + + local below_pos = { x=pos.x, y=pos.y-1, z=pos.z } + local fwd_pos = { x=pos.x+fxd, y=pos.y, z=pos.z+fzd } + local left_pos = { x=pos.x+lxd, y=pos.y, z=pos.z+lzd } + local right_pos = { x=pos.x+rxd, y=pos.y, z=pos.z+rzd } + local left_fwd_pos = { x=pos.x+lxd+fxd, y=pos.y, z=pos.z+lzd+fzd } + local right_fwd_pos = { x=pos.x+rxd+fxd, y=pos.y, z=pos.z+rzd+fzd } + local right_fwd_above_pos = { x=pos.x+rxd+fxd, y=pos.y+1, z=pos.z+rzd+fzd } + local left_fwd_above_pos = { x=pos.x+lxd+fxd, y=pos.y+1, z=pos.z+lzd+fzd } + local right_fwd_below_pos = { x=pos.x+rxd+fxd, y=pos.y-1, z=pos.z+rzd+fzd } + local left_fwd_below_pos = { x=pos.x+lxd+fxd, y=pos.y-1, z=pos.z+lzd+fzd } + + local below_node = minetest.get_node(below_pos) + local fwd_node = minetest.get_node(fwd_pos) + local left_node = minetest.get_node(left_pos) + local right_node = minetest.get_node(right_pos) + local left_fwd_node = minetest.get_node(left_fwd_pos) + local right_fwd_node = minetest.get_node(right_fwd_pos) + local left_below_node = minetest.get_node({x=left_pos.x, y=left_pos.y-1, z=left_pos.z}) + local right_below_node = minetest.get_node({x=right_pos.x, y=right_pos.y-1, z=right_pos.z}) + local right_fwd_above_node = minetest.get_node(right_fwd_above_pos) + local left_fwd_above_node = minetest.get_node(left_fwd_above_pos) + local right_fwd_below_node = minetest.get_node(right_fwd_below_pos) + local left_fwd_below_node = minetest.get_node(left_fwd_below_pos) + + local new_place_name = itemstack:get_name() + local n = 0 + + -- try to place a diagonal one on the side of blocks stacked like stairs + -- or follow an existing diagonal with another. + if (left_below_node and string.find(left_below_node.name, "banister_.-_diagonal_right") + and below_node and is_buildable_to(placer_name, below_pos, below_pos)) + or not is_buildable_to(placer_name, right_fwd_above_pos, right_fwd_above_pos) then + new_place_name = string.gsub(new_place_name, "_horizontal", "_diagonal_right") + elseif (right_below_node and string.find(right_below_node.name, "banister_.-_diagonal_left") + and below_node and is_buildable_to(placer_name, below_pos, below_pos)) + or not is_buildable_to(placer_name, left_fwd_above_pos, left_fwd_above_pos) then + new_place_name = string.gsub(new_place_name, "_horizontal", "_diagonal_left") + + -- try to follow a diagonal with the corresponding horizontal + -- from the top of a diagonal... + elseif left_below_node and string.find(left_below_node.name, "homedecor:banister_.*_diagonal") then + fdir = left_below_node.param2 + new_place_name = string.gsub(left_below_node.name, "_diagonal_.-$", "_horizontal") + elseif right_below_node and string.find(right_below_node.name, "homedecor:banister_.*_diagonal") then + fdir = right_below_node.param2 + new_place_name = string.gsub(right_below_node.name, "_diagonal_.-$", "_horizontal") + + -- try to place a horizontal in-line with the nearest diagonal, at the top + elseif left_fwd_below_node and string.find(left_fwd_below_node.name, "homedecor:banister_.*_diagonal") + and is_buildable_to(placer_name, fwd_pos, fwd_pos) then + fdir = left_fwd_below_node.param2 + pos = fwd_pos + new_place_name = string.gsub(left_fwd_below_node.name, "_diagonal_.-$", "_horizontal") + elseif right_fwd_below_node and string.find(right_fwd_below_node.name, "homedecor:banister_.*_diagonal") + and is_buildable_to(placer_name, fwd_pos, fwd_pos) then + fdir = right_fwd_below_node.param2 + pos = fwd_pos + new_place_name = string.gsub(right_fwd_below_node.name, "_diagonal_.-$", "_horizontal") + + -- try to follow a diagonal with a horizontal, at the bottom of the diagonal + elseif left_node and string.find(left_node.name, "homedecor:banister_.*_diagonal") then + fdir = left_node.param2 + new_place_name = string.gsub(left_node.name, "_diagonal_.-$", "_horizontal") + elseif right_node and string.find(right_node.name, "homedecor:banister_.*_diagonal") then + fdir = right_node.param2 + new_place_name = string.gsub(right_node.name, "_diagonal_.-$", "_horizontal") + + -- try to place a horizontal in-line with the nearest diagonal, at the bottom + elseif left_fwd_node and string.find(left_fwd_node.name, "homedecor:banister_.*_diagonal") + and is_buildable_to(placer_name, fwd_pos, fwd_pos) then + fdir = left_fwd_node.param2 + pos = fwd_pos + new_place_name = string.gsub(left_fwd_node.name, "_diagonal_.-$", "_horizontal") + elseif right_fwd_node and string.find(right_fwd_node.name, "homedecor:banister_.*_diagonal") + and is_buildable_to(placer_name, fwd_pos, fwd_pos) then + fdir = right_fwd_node.param2 + pos = fwd_pos + new_place_name = string.gsub(right_fwd_node.name, "_diagonal_.-$", "_horizontal") + + -- try to follow a horizontal with another of the same + elseif left_node and string.find(left_node.name, "homedecor:banister_.*_horizontal") then + fdir = left_node.param2 + new_place_name = left_node.name + elseif right_node and string.find(right_node.name, "homedecor:banister_.*_horizontal") then + fdir = right_node.param2 + new_place_name = right_node.name + end + + -- manually invert left-right orientation + if placer:get_player_control()["sneak"] then + if string.find(new_place_name, "banister_.*_diagonal") then + new_place_name = string.gsub(new_place_name, "_left", "_right") + else + new_place_name = string.gsub(new_place_name, "_right", "_left") + end + end + + minetest.set_node(pos, {name = new_place_name, param2 = fdir}) + itemstack:take_item() + return itemstack +end + diff --git a/homedecor/handlers/furnaces.lua b/homedecor/handlers/furnaces.lua new file mode 100644 index 0000000..aaa2fef --- /dev/null +++ b/homedecor/handlers/furnaces.lua @@ -0,0 +1,279 @@ +-- This code supplies an oven/stove. Basically it's just a copy of the default furnace with different textures. + +local S = homedecor.gettext + +local function swap_node(pos, name) + local node = minetest.get_node(pos) + if node.name == name then return end + node.name = name + minetest.swap_node(pos, node) +end + +local function make_formspec(furnacedef, percent) + local fire + + if percent and (percent > 0) then + fire = ("%s^[lowpart:%d:%s"):format( + furnacedef.fire_bg, + (100-percent), + furnacedef.fire_fg + ) + else + fire = "default_furnace_fire_bg.png" + end + + local w = furnacedef.output_width + local h = math.ceil(furnacedef.output_slots / furnacedef.output_width) + + return "size["..math.max(8, 6 + w)..",9]".. + "image[2,2;1,1;"..fire.."]".. + "list[current_name;fuel;2,3;1,1;]".. + "list[current_name;src;2,1;1,1;]".. + "list[current_name;dst;5,1;"..w..","..h..";]".. + "list[current_player;main;0,5;8,4;]" +end + +--[[ +furnacedef = { + description = "Oven", + tiles = { ... }, + tiles_active = { ... }, + ^ +Y -Y +X -X +Z -Z + tile_format = "oven_%s%s.png", + ^ First '%s' replaced by one of "top", "bottom", "side", "front". + ^ Second '%s' replaced by "" for inactive, and "_active" for active "front" + ^ "side" is used for left, right and back. + ^ tiles_active for front is set + output_slots = 4, + output_width = 2, + cook_speed = 1, + ^ Higher values cook stuff faster. + extra_nodedef_fields = { ... }, + ^ Stuff here is copied verbatim into both active and inactive nodedefs + ^ Useful for overriding drawtype, etc. +} +]] + +local function make_tiles(tiles, fmt, active) + if not fmt then return tiles end + tiles = { } + for i,side in ipairs{"top", "bottom", "side", "side", "side", "front"} do + if active and (i == 6) then + tiles[i] = fmt:format(side, "_active") + else + tiles[i] = fmt:format(side, "") + end + end + return tiles +end + +local furnace_can_dig = function(pos,player) + local meta = minetest.get_meta(pos); + local inv = meta:get_inventory() + return inv:is_empty("fuel") + and inv:is_empty("dst") + and inv:is_empty("src") +end + +function homedecor.register_furnace(name, furnacedef) + furnacedef.fire_fg = furnacedef.fire_bg or "default_furnace_fire_fg.png" + furnacedef.fire_bg = furnacedef.fire_bg or "default_furnace_fire_bg.png" + + furnacedef.output_slots = furnacedef.output_slots or 4 + furnacedef.output_width = furnacedef.output_width or 2 + + furnacedef.cook_speed = furnacedef.cook_speed or 1 + + local description = furnacedef.description or "Furnace" + + local furnace_construct = function(pos) + local meta = minetest.get_meta(pos) + meta:set_string("formspec", make_formspec(furnacedef, 0)) + meta:set_string("infotext", description) + local inv = meta:get_inventory() + inv:set_size("fuel", 1) + inv:set_size("src", 1) + inv:set_size("dst", furnacedef.output_slots) + end + + local furnace_allow_put = function(pos, listname, index, stack, player) + local meta = minetest.get_meta(pos) + local inv = meta:get_inventory() + if listname == "fuel" then + if minetest.get_craft_result({method="fuel",width=1,items={stack}}).time ~= 0 then + if inv:is_empty("src") then + meta:set_string("infotext", S("%s is empty"):format(description)) + end + return stack:get_count() + else + return 0 + end + elseif listname == "src" then + return stack:get_count() + elseif listname == "dst" then + return 0 + end + end + local furnace_allow_move = function(pos, from_list, from_index, to_list, to_index, count, player) + local meta = minetest.get_meta(pos) + local inv = meta:get_inventory() + local stack = inv:get_stack(from_list, from_index) + if to_list == "fuel" then + if minetest.get_craft_result({method="fuel",width=1,items={stack}}).time ~= 0 then + if inv:is_empty("src") then + meta:set_string("infotext", S("%s is empty"):format(description)) + end + return count + else + return 0 + end + elseif to_list == "src" then + return count + elseif to_list == "dst" then + return 0 + end + end + + local def = { + description = description, + tiles = make_tiles(furnacedef.tiles, furnacedef.tile_format, false), + groups = furnacedef.groups or {cracky=2}, + sounds = furnacedef.sounds or default.node_sound_wood_defaults(), + on_construct = furnace_construct, + can_dig = furnace_can_dig, + allow_metadata_inventory_put = furnace_allow_put, + allow_metadata_inventory_move = furnace_allow_move, + inventory = { lockable = true } + } + + local def_active = { + description = description .. " (active)", + tiles = make_tiles(furnacedef.tiles_active, furnacedef.tile_format, true), + light_source = 8, + drop = "homedecor:" .. name, + groups = furnacedef.groups or {cracky=2, not_in_creative_inventory=1}, + sounds = furnacedef.sounds or default.node_sound_stone_defaults(), + on_construct = furnace_construct, + can_dig = furnace_can_dig, + allow_metadata_inventory_put = furnace_allow_put, + allow_metadata_inventory_move = furnace_allow_move, + inventory = { lockable = true } + } + + if furnacedef.extra_nodedef_fields then + for k, v in pairs(furnacedef.extra_nodedef_fields) do + def[k] = v + def_active[k] = v + end + end + + local name_active = name.."_active" + + homedecor.register(name, def) + homedecor.register(name_active, def_active) + + local name, name_active = "homedecor:"..name, "homedecor:"..name_active + + minetest.register_abm({ + nodenames = {name, name_active, name.."_locked", name_active.."_locked"}, + label = "furnaces", + interval = 1.0, + chance = 1, + action = function(pos, node, active_object_count, active_object_count_wider) + local meta = minetest.get_meta(pos) + for i, name in ipairs({ + "fuel_totaltime", + "fuel_time", + "src_totaltime", + "src_time" + }) do + if meta:get_string(name) == "" then + meta:set_float(name, 0.0) + end + end + + local inv = meta:get_inventory() + + local srclist = inv:get_list("src") + local cooked = nil + local aftercooked + + if srclist then + cooked, aftercooked = minetest.get_craft_result({method = "cooking", width = 1, items = srclist}) + end + + local was_active = false + + if meta:get_float("fuel_time") < meta:get_float("fuel_totaltime") then + was_active = true + meta:set_float("fuel_time", meta:get_float("fuel_time") + 1) + meta:set_float("src_time", meta:get_float("src_time") + furnacedef.cook_speed) + if cooked and cooked.item and meta:get_float("src_time") >= cooked.time then + -- check if there's room for output in "dst" list + if inv:room_for_item("dst",cooked.item) then + -- Put result in "dst" list + inv:add_item("dst", cooked.item) + -- take stuff from "src" list + inv:set_stack("src", 1, aftercooked.items[1]) + end + meta:set_string("src_time", 0) + end + end + + -- XXX: Quick patch, make it better in the future. + local locked = node.name:find("_locked$") and "_locked" or "" + local desc = minetest.registered_nodes[name..locked].description + + if meta:get_float("fuel_time") < meta:get_float("fuel_totaltime") then + local percent = math.floor(meta:get_float("fuel_time") / + meta:get_float("fuel_totaltime") * 100) + meta:set_string("infotext",S("%s active: %d%%"):format(desc,percent)) + swap_node(pos,name_active..locked) + meta:set_string("formspec", make_formspec(furnacedef, percent)) + return + end + + local fuel = nil + local afterfuel + local cooked = nil + local fuellist = inv:get_list("fuel") + local srclist = inv:get_list("src") + + if srclist then + cooked = minetest.get_craft_result({method = "cooking", width = 1, items = srclist}) + end + if fuellist then + fuel, afterfuel = minetest.get_craft_result({method = "fuel", width = 1, items = fuellist}) + end + + if (not fuel) or (fuel.time <= 0) then + meta:set_string("infotext",desc..S(": Out of fuel")) + swap_node(pos, name..locked) + meta:set_string("formspec", make_formspec(furnacedef, 0)) + return + end + + if cooked.item:is_empty() then + if was_active then + meta:set_string("infotext",S("%s is empty"):format(desc)) + swap_node(pos, name..locked) + meta:set_string("formspec", make_formspec(furnacedef, 0)) + end + return + end + + if not inv:room_for_item("dst", cooked.item) then + meta:set_string("infotext", desc..S(": output bins are full")) + swap_node(pos, name..locked) + meta:set_string("formspec", make_formspec(furnacedef, 0)) + return + end + + meta:set_string("fuel_totaltime", fuel.time) + meta:set_string("fuel_time", 0) + + inv:set_stack("fuel", 1, afterfuel.items[1]) + end, + }) + +end diff --git a/homedecor/handlers/init.lua b/homedecor/handlers/init.lua new file mode 100644 index 0000000..12003ca --- /dev/null +++ b/homedecor/handlers/init.lua @@ -0,0 +1,22 @@ +local handlerpath = homedecor.modpath .. "/handlers/" + +-- nodebox arithmetics and helpers +-- (please keep non-generic nodeboxes with their node definition) +dofile(handlerpath.."nodeboxes.lua") + +-- expand and unexpand decor +dofile(handlerpath.."expansion.lua") + +-- register nodes that cook stuff +dofile(handlerpath.."furnaces.lua") + +-- inventory related functionality, like initialization, ownership and spawning locked versions +dofile(handlerpath.."inventory.lua") + +-- glue it all together into a registration function +dofile(handlerpath.."registration.lua") + +-- some nodes have particle spawners +dofile(handlerpath.."water_particles.lua") + +dofile(handlerpath.."sit.lua") diff --git a/homedecor/handlers/inventory.lua b/homedecor/handlers/inventory.lua new file mode 100644 index 0000000..51ac70f --- /dev/null +++ b/homedecor/handlers/inventory.lua @@ -0,0 +1,173 @@ +local S = homedecor.gettext + +local default_can_dig = function(pos,player) + local meta = minetest.get_meta(pos) + return meta:get_inventory():is_empty("main") +end + +local background = default.gui_bg .. default.gui_bg_img .. default.gui_slots +local default_inventory_formspecs = { + ["4"]="size[8,6]".. background .. + "list[context;main;2,0;4,1;]".. + "list[current_player;main;0,2;8,4;]", + + ["6"]="size[8,6]".. background .. + "list[context;main;1,0;6,1;]".. + "list[current_player;main;0,2;8,4;]", + + ["8"]="size[8,6]".. background .. + "list[context;main;0,0;8,1;]".. + "list[current_player;main;0,2;8,4;]", + + ["12"]="size[8,7]".. background .. + "list[context;main;1,0;6,2;]".. + "list[current_player;main;0,3;8,4;]", + + ["16"]="size[8,7]".. background .. + "list[context;main;0,0;8,2;]".. + "list[current_player;main;0,3;8,4;]", + + ["24"]="size[8,8]".. background .. + "list[context;main;0,0;8,3;]".. + "list[current_player;main;0,4;8,4;]", + + ["32"]="size[8,9]".. background .. + "list[context;main;0,0.3;8,4;]".. + "list[current_player;main;0,4.85;8,1;]".. + "list[current_player;main;0,6.08;8,3;8]".. + default.get_hotbar_bg(0,4.85), + + ["50"]="size[10,10]".. background .. + "list[context;main;0,0;10,5;]".. + "list[current_player;main;1,6;8,4;]", +} + +local function get_formspec_by_size(size) + --TODO heuristic to use the "next best size" + local formspec = default_inventory_formspecs[tostring(size)] + return formspec or default_inventory_formspecs +end + +---- +-- handle inventory setting +-- inventory = { +-- size = 16, +-- formspec = …, +-- locked = false, +-- lockable = true, +-- } +-- +function homedecor.handle_inventory(name, def, original_def) + local inventory = def.inventory + if not inventory then return end + def.inventory = nil + + if inventory.size then + local on_construct = def.on_construct + def.on_construct = function(pos) + local size = inventory.size + local meta = minetest.get_meta(pos) + meta:get_inventory():set_size("main", size) + meta:set_string("formspec", inventory.formspec or get_formspec_by_size(size)) + if on_construct then on_construct(pos) end + end + end + + def.can_dig = def.can_dig or default_can_dig + def.on_metadata_inventory_move = def.on_metadata_inventory_move or function(pos, from_list, from_index, to_list, to_index, count, player) + minetest.log("action", S("%s moves stuff in %s at %s"):format( + player:get_player_name(), name, minetest.pos_to_string(pos) + )) + end + def.on_metadata_inventory_put = def.on_metadata_inventory_put or function(pos, listname, index, stack, player) + minetest.log("action", S("%s moves stuff to %s at %s"):format( + player:get_player_name(), name, minetest.pos_to_string(pos) + )) + end + def.on_metadata_inventory_take = def.on_metadata_inventory_take or function(pos, listname, index, stack, player) + minetest.log("action", S("%s takes stuff from %s at %s"):format( + player:get_player_name(), name, minetest.pos_to_string(pos) + )) + end + + local locked = inventory.locked + if locked then + local after_place_node = def.after_place_node + def.after_place_node = function(pos, placer) + local meta = minetest.get_meta(pos) + local owner = placer:get_player_name() or "" + + meta:set_string("owner", owner) + meta:set_string("infotext", S("%s (owned by %s)"):format(def.infotext or def.description, owner)) + return after_place_node and after_place_node(pos, placer) + end + + local allow_move = def.allow_metadata_inventory_move + def.allow_metadata_inventory_move = function(pos, from_list, from_index, to_list, to_index, count, player) + local meta = minetest.get_meta(pos) + local owner = meta:get_string("owner") + local playername = player:get_player_name() + + if (playername ~= owner) then + minetest.log("action", string.format("%s tried to access a %s belonging to %s at %s", + playername, name, owner, minetest.pos_to_string(pos) + )) + return 0 + end + + return allow_move and allow_move(pos, from_list, from_index, to_list, to_index, count, player) + or count + end + + local allow_put = def.allow_metadata_inventory_put + def.allow_metadata_inventory_put = function(pos, listname, index, stack, player) + local meta = minetest.get_meta(pos) + local owner = meta:get_string("owner") + local playername = player:get_player_name() + + if (playername ~= owner) then + minetest.log("action", string.format("%s tried to access a %s belonging to %s at %s", + playername, name, owner, minetest.pos_to_string(pos) + )) + return 0 + end + return allow_put and allow_put(pos, listname, index, stack, player) + or stack:get_count() + end + + local allow_take = def.allow_metadata_inventory_take + def.allow_metadata_inventory_take = function(pos, listname, index, stack, player) + local meta = minetest.get_meta(pos) + local owner = meta:get_string("owner") + local playername = player:get_player_name() + + if (playername ~= owner) then + minetest.log("action", string.format("%s tried to access a %s belonging to %s at %s", + playername, name, owner, minetest.pos_to_string(pos) + )) + return 0 + end + return allow_take and allow_take(pos, listname, index, stack, player) + or stack:get_count() + end + end + + local lockable = inventory.lockable + if lockable then + local locked_def = table.copy(original_def) + locked_def.description = S("Locked %s"):format(def.description or name) + + local locked_inventory = locked_def.inventory + locked_inventory.locked = true + locked_inventory.lockable = nil -- avoid loops of locked locked stuff + + local locked_name = name .. "_locked" + homedecor.register(locked_name, locked_def) + minetest.register_craft({ + type = "shapeless", + output = "homedecor:" .. locked_name, + recipe = { "homedecor:" .. name, "default:steel_ingot" } + }) + end + +end diff --git a/homedecor/handlers/nodeboxes.lua b/homedecor/handlers/nodeboxes.lua new file mode 100644 index 0000000..0d8306e --- /dev/null +++ b/homedecor/handlers/nodeboxes.lua @@ -0,0 +1,62 @@ +-- please keep any non-generic nodeboxe with its node definition +-- this file should not accumulate any left over nodeboxes +-- but is meant to host any abstractions or calculations based on nodeboxes + +-- a box is defined as {x1, y1, z1, x2, y2, z2} +homedecor.box = { + -- slab starting from -x (after rotation: left) + slab_x = function(depth) return { -0.5, -0.5, -0.5, -0.5+depth, 0.5, 0.5 } end, + -- bottom slab (starting from -y) with height optionally shifted upwards + slab_y = function(height, shift) return { -0.5, -0.5+(shift or 0), -0.5, 0.5, -0.5+height+(shift or 0), 0.5 } end, + -- slab starting from -z (+z with negative depth) + slab_z = function(depth) + -- for consistency with the other functions here, we have to assume that a "z" slab starts from -z and extends by depth, + -- but since conventionally a lot of nodes place slabs against +z for player convenience, we define + -- a "negative" depth as a depth extending from the other side, i.e. +z + if depth > 0 then + -- slab starting from -z + return { -0.5, -0.5, -0.5, 0.5, 0.5, -0.5+depth } + else + -- slab starting from +z (z1=0.5-(-depth)) + return { -0.5, -0.5, 0.5+depth, 0.5, 0.5, 0.5 } + end + end, + bar_y = function(radius) return {-radius, -0.5, -radius, radius, 0.5, radius} end, + cuboid = function(radius_x, radius_y, radius_z) return {-radius_x, -radius_y, -radius_z, radius_x, radius_y, radius_z} end, +} + +homedecor.nodebox = { + -- { -0.5, -0.5, -0.5, 0.5, 0.5, 0.5 }, + -- can be used in-place as: + -- { type="regular" }, + regular = { type="regular" }, + null = { type = "fixed", fixed = { 0, 0, 0, 0, 0, 0 } }, + corner_xz = function(depth_x, depth_z) return { + type="fixed", + fixed={ + homedecor.box.slab_x(depth_x), + homedecor.box.slab_z(depth_z), + -- { -0.5, -0.5, -0.5, 0.5-depth, 0.5, -0.5+depth } -- slab_x without the overlap, but actually looks a bit worse + } + } end, +} + +local mt = {} +mt.__index = function(table, key) + local ref = homedecor.box[key] + local ref_type = type(ref) + if ref_type == "function" then + return function(...) + return { type = "fixed", fixed = ref(...) } + end + elseif ref_type == "table" then + return { type = "fixed", fixed = ref } + elseif ref_type == "nil" then + error(key .. "could not be found among nodebox presets and functions") + end + error("unexpected datatype " .. tostring(type(ref)) .. " while looking for " .. key) +end +setmetatable(homedecor.nodebox, mt) + + + diff --git a/homedecor/handlers/registration.lua b/homedecor/handlers/registration.lua new file mode 100644 index 0000000..b25378f --- /dev/null +++ b/homedecor/handlers/registration.lua @@ -0,0 +1,95 @@ +homedecor = homedecor or {} +local S = homedecor.gettext +local placeholder_node = "homedecor:expansion_placeholder" + +--wrapper around minetest.register_node that sets sane defaults and interprets some specialized settings +function homedecor.register(name, original_def) + local def = table.copy(original_def) + + def.drawtype = def.drawtype + or (def.mesh and "mesh") + or (def.node_box and "nodebox") + + def.paramtype = def.paramtype or "light" + + -- avoid facedir for some drawtypes as they might be used internally for something else + -- even if undocumented + if not (def.drawtype == "glasslike_framed" + or def.drawtype == "raillike" + or def.drawtype == "plantlike" + or def.drawtype == "firelike") then + + def.paramtype2 = def.paramtype2 or "facedir" + end + + homedecor.handle_inventory(name, def, original_def) + + local infotext = def.infotext + --def.infotext = nil -- currently used to set locked refrigerator infotexts + + if infotext then + local on_construct = def.on_construct + def.on_construct = function(pos) + local meta = minetest.get_meta(pos) + meta:set_string("infotext", infotext) + if on_construct then on_construct(pos) end + end + end + + local expand = def.expand + def.expand = nil + local after_unexpand = def.after_unexpand + def.after_unexpand = nil + + if expand then + -- dissallow rotating only half the expanded node by default + -- unless we know better + def.on_rotate = def.on_rotate + or (def.mesh and expand.top and screwdriver.rotate_simple) + or screwdriver.disallow + + def.on_place = def.on_place or function(itemstack, placer, pointed_thing) + if expand.top then + return homedecor.stack_vertically(itemstack, placer, pointed_thing, itemstack:get_name(), expand.top) + elseif expand.right then + return homedecor.stack_sideways(itemstack, placer, pointed_thing, itemstack:get_name(), expand.right, true) + elseif expand.forward then + return homedecor.stack_sideways(itemstack, placer, pointed_thing, itemstack:get_name(), expand.forward, false) + end + end + def.after_dig_node = def.after_dig_node or function(pos, oldnode, oldmetadata, digger) + if expand.top and expand.forward ~= "air" then + local top_pos = { x=pos.x, y=pos.y+1, z=pos.z } + local node = minetest.get_node(top_pos).name + if node == expand.top or node == placeholder_node then + minetest.remove_node(top_pos) + end + end + + local fdir = oldnode.param2 + if not fdir or fdir > 3 then return end + + if expand.right and expand.forward ~= "air" then + local right_pos = { x=pos.x+homedecor.fdir_to_right[fdir+1][1], y=pos.y, z=pos.z+homedecor.fdir_to_right[fdir+1][2] } + local node = minetest.get_node(right_pos).name + if node == expand.right or node == placeholder_node then + minetest.remove_node(right_pos) + end + end + if expand.forward and expand.forward ~= "air" then + local forward_pos = { x=pos.x+homedecor.fdir_to_fwd[fdir+1][1], y=pos.y, z=pos.z+homedecor.fdir_to_fwd[fdir+1][2] } + local node = minetest.get_node(forward_pos).name + if node == expand.forward or node == placeholder_node then + minetest.remove_node(forward_pos) + end + end + + if after_unexpand then + after_unexpand(pos) + end + end + end + + -- register the actual minetest node + minetest.register_node("homedecor:" .. name, def) +end diff --git a/homedecor/handlers/sit.lua b/homedecor/handlers/sit.lua new file mode 100644 index 0000000..636a585 --- /dev/null +++ b/homedecor/handlers/sit.lua @@ -0,0 +1,32 @@ +function homedecor.sit(pos, node, clicker) + do return end -- delete it when the engine is stabler for the player's physics + local meta = minetest.get_meta(pos) + local param2 = node.param2 + local name = clicker:get_player_name() + + if name == meta:get_string("is_sit") then + meta:set_string("is_sit", "") + pos.y = pos.y-0.5 + clicker:setpos(pos) + clicker:set_eye_offset({x=0,y=0,z=0}, {x=0,y=0,z=0}) + clicker:set_physics_override(1, 1, 1) + default.player_attached[name] = false + default.player_set_animation(clicker, "stand", 30) + else + meta:set_string("is_sit", clicker:get_player_name()) + clicker:set_eye_offset({x=0,y=-7,z=2}, {x=0,y=0,z=0}) + clicker:set_physics_override(0, 0, 0) + clicker:setpos(pos) + default.player_attached[name] = true + default.player_set_animation(clicker, "sit", 30) + if param2 == 0 then + clicker:set_look_yaw(3.15) + elseif param2 == 1 then + clicker:set_look_yaw(7.9) + elseif param2 == 2 then + clicker:set_look_yaw(6.28) + elseif param2 == 3 then + clicker:set_look_yaw(4.75) + else return end + end +end diff --git a/homedecor/handlers/water_particles.lua b/homedecor/handlers/water_particles.lua new file mode 100644 index 0000000..a3334b0 --- /dev/null +++ b/homedecor/handlers/water_particles.lua @@ -0,0 +1,115 @@ +-- variables taken by the start... function +-- +-- pos and node are as usual, from e.g. on_rightclick. +-- +-- in the { particledef } table: +-- +-- outletx/y/z are the exact coords of the starting point +-- for the spawner, relative to the center of the node +-- +-- velocityx/y/z are the speed of the particles, +-- (x and z are relative to a node placed while looking north/facedir 0) +-- negative Y values flow downward. +-- +-- spread is the radius from the starting point, +-- along X and Z only, to randomly spawn particles. +-- +-- soundname is the filename (without .ogg) of the sound file +-- to be played along with the particle stream + +function homedecor.start_particle_spawner(pos, node, particledef, soundname) + + local this_spawner_meta = minetest.get_meta(pos) + local id = this_spawner_meta:get_int("active") + local s_handle = this_spawner_meta:get_int("sound") + + if id ~= 0 then + if s_handle then + minetest.after(0, function(s_handle) + minetest.sound_stop(s_handle) + end, s_handle) + end + minetest.delete_particlespawner(id) + this_spawner_meta:set_int("active", nil) + this_spawner_meta:set_int("sound", nil) + return + end + + local fdir = node.param2 + + if fdir and fdir < 4 and (not id or id == 0) then + + local outletx = particledef.outlet.x + local outlety = particledef.outlet.y + local outletz = particledef.outlet.z + local velocityx = particledef.velocity_x + local velocityy = particledef.velocity_y + local velocityz = particledef.velocity_z + local spread = particledef.spread + + local minx_t = { outletx - spread, -outletz - spread, outletx - spread, outletz - spread } + local maxx_t = { outletx + spread, -outletz + spread, outletx + spread, outletz + spread } + local minz_t = { -outletz - spread, outletx - spread, outletz - spread, outletx - spread } + local maxz_t = { -outletz + spread, outletx + spread, outletz + spread, outletx + spread } + + local minvelx_t = { velocityx.min, velocityz.min, -velocityx.max, -velocityz.max } + local maxvelx_t = { velocityx.max, velocityz.max, -velocityx.min, -velocityz.min } + local minvelz_t = { velocityz.min, velocityx.min, -velocityz.max, velocityx.min } + local maxvelz_t = { velocityz.max, velocityx.max, -velocityz.min, velocityx.max } + + local minx = minx_t[fdir + 1] + local maxx = maxx_t[fdir + 1] + local minz = minz_t[fdir + 1] + local maxz = maxz_t[fdir + 1] + + local minvelx = minvelx_t[fdir + 1] + local minvelz = minvelz_t[fdir + 1] + local maxvelx = maxvelx_t[fdir + 1] + local maxvelz = maxvelz_t[fdir + 1] + + id = minetest.add_particlespawner({ + amount = 60, + time = 0, + collisiondetection = true, + minpos = {x=pos.x - minx, y=pos.y + outlety, z=pos.z - minz}, + maxpos = {x=pos.x - maxx, y=pos.y + outlety, z=pos.z - maxz}, + minvel = {x = minvelx, y = velocityy, z = minvelz}, + maxvel = {x = maxvelx, y = velocityy, z = maxvelz}, + minacc = {x=0, y=0, z=0}, + maxacc = {x=0, y=-0.05, z=0}, + minexptime = 2, + maxexptime = 4, + minsize = 0.5, + maxsize = 1, + texture = "homedecor_water_particle.png", + }) + s_handle = minetest.sound_play(soundname, { + pos = pos, + max_hear_distance = 5, + loop = true + }) + this_spawner_meta:set_int("active", id) + this_spawner_meta:set_int("sound", s_handle) + return + end +end + +function homedecor.stop_particle_spawner(pos) + local this_spawner_meta = minetest.get_meta(pos) + local id = this_spawner_meta:get_int("active") + local s_handle = this_spawner_meta:get_int("sound") + + if id ~= 0 then + minetest.delete_particlespawner(id) + end + + if s_handle then + minetest.after(0, function(s_handle) + minetest.sound_stop(s_handle) + end, s_handle) + end + + this_spawner_meta:set_int("active", nil) + this_spawner_meta:set_int("sound", nil) +end + -- cgit v1.2.3