diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..dfe0770 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,2 @@ +# Auto detect text files and perform LF normalization +* text=auto diff --git a/depends.txt b/depends.txt new file mode 100644 index 0000000..331d858 --- /dev/null +++ b/depends.txt @@ -0,0 +1 @@ +default \ No newline at end of file diff --git a/exemple code canned food.lua b/exemple code canned food.lua new file mode 100644 index 0000000..9a6cde5 --- /dev/null +++ b/exemple code canned food.lua @@ -0,0 +1,167 @@ +-- [[..]] +-- code from : https://github.com/h-v-smacker/canned_food/blob/master/init.lua + +if minetest.get_modpath("unified_inventory") and unified_inventory.register_craft_type then + unified_inventory.register_craft_type("pickling", { + description = "Dark room, wooden shelf", + icon = "canned_food_pickling_icon.png", + width = 1, + height = 1, + uses_crafting_grid = false, + }) +end + + +-- [[..]] + +local lbm_list = {} + +-- creating all objects with one universal scheme +for product, def in pairs(canned_food_definitions) do + if minetest.get_modpath(def.found_in) then + --if minetest.global_exists(def.found_in) then + if def.sugar and minetest.get_modpath("farming") or not def.sugar then + + -- general description + + local nodetable = { + description = def.proper_name, + drawtype = "plantlike", + tiles = {"canned_food_" .. product .. ".png"}, + inventory_image = "canned_food_" .. product .. ".png", + wield_image = "canned_food_" .. product .. ".png", + paramtype = "light", + is_ground_content = false, + walkable = false, + selection_box = { + type = "fixed", + fixed = {-0.25, -0.5, -0.25, 0.25, 0.3, 0.25} + }, + groups = { canned_food = 1, + vessel = 1, + dig_immediate = 3, + attached_node = 1 }, + -- canned food prolongs shelf life IRL, but in minetest food never + -- goes bad. Here, we increase the nutritional value instead. + on_use = minetest.item_eat( + math.floor (def.orig_nutritional_value * def.amount * 1.33) + + (def.sugar and 1 or 0), "vessels:glass_bottle"), + -- the empty bottle stays, of course + sounds = default.node_sound_glass_defaults(), + } + + + if not def.transforms then + -- introducing a new item, a bit more nutricious than the source + -- material when sugar is used. Always stays the same. + minetest.register_node("canned_food:" .. product, nodetable) + + else + -- Some products involve marinating or salting, however there is no salt + -- or vingerar in minetest; instead we imitate this more complex process + -- by putting the jar on a wooden shelf in a dark room for a long while. + -- The effort is rewarded accordingly. + + -- adding transformation code + nodetable.on_construct = function(pos) + local t = minetest.get_node_timer(pos) + t:start(180) + end + + nodetable.on_timer = function(pos) + -- if light level is 11 or less, and wood is nearby, there is 1 in 10 chance... + if minetest.get_node_light(pos) > 11 or + not minetest.find_node_near(pos, 1, {"group:wood"}) + or math.random() > 0.1 then + return true + else + minetest.set_node(pos, {name = "canned_food:" .. product .."_plus"}) + return false + end + end + + minetest.register_node("canned_food:" .. product, nodetable) + + -- add node to the list for LBM + table.insert(lbm_list, "canned_food:" .. product) + + -- a better version + minetest.register_node("canned_food:" .. product .."_plus", { + description = def.transforms, + drawtype = "plantlike", + tiles = {"canned_food_" .. product .. ".png^canned_food_paper_lid_cover.png"}, + inventory_image = "canned_food_" .. product .. ".png^canned_food_paper_lid_cover.png", + wield_image = "canned_food_" .. product .. ".png^canned_food_paper_lid_cover.png", + paramtype = "light", + is_ground_content = false, + walkable = false, + selection_box = { + type = "fixed", + fixed = {-0.25, -0.5, -0.25, 0.25, 0.3, 0.25} + }, + groups = { canned_food = 1, + vessel = 1, + dig_immediate = 3, + attached_node = 1, + not_in_creative_inventory = 1 }, + -- the reward for putting the food in a cellar is even greater + -- than for merely canning it. + on_use = minetest.item_eat( + (math.floor(def.orig_nutritional_value * def.amount * 1.33) + + (def.sugar and 1 or 0))*2, "vessels:glass_bottle"), + -- the empty bottle stays, of course + sounds = default.node_sound_glass_defaults(), + }) + + -- register the recipe with unified inventory + if minetest.get_modpath("unified_inventory") and unified_inventory.register_craft then + unified_inventory.register_craft({ + type = "pickling", + output = "canned_food:" .. product .."_plus", + items = {"canned_food:" .. product}, + }) + end + + end + + -- a family of shapeless recipes, with sugar for jams + -- except for apple: there should be at least 1 jam guaranteed + -- to be available in vanilla game (and mushrooms are the guaranteed + -- regular - not sweet - canned food) + local ingredients = {"vessels:glass_bottle"} + local max = 8 + if def.sugar then + table.insert(ingredients, "farming:sugar") + max = 7 + end + -- prevent creation of a recipe with more items than there are slots + -- left in the 9-tile craft grid + if def.amount > max then + def.amount = max + end + for i=1,def.amount do + table.insert(ingredients, def.obj_name) + end + minetest.register_craft({ + type = "shapeless", + output = "canned_food:" .. product, + recipe = ingredients + }) + end + end +end + + +-- LBM to start timers on existing, ABM-driven nodes +minetest.register_lbm({ + name = "canned_food:timer_init", + nodenames = lbm_list, + run_at_every_load = false, + action = function(pos) + local t = minetest.get_node_timer(pos) + t:start(180) + end, +}) + +-- The Moor has done his duty, the Moor can go +canned_food_definitions = nil \ No newline at end of file diff --git a/furnace.lua b/furnace.lua new file mode 100644 index 0000000..d5703cd --- /dev/null +++ b/furnace.lua @@ -0,0 +1,380 @@ +-- default/furnace.lua + +-- support for MT game translation. +local S = default.get_translator + +-- +-- Formspecs +-- + +function default.get_furnace_active_formspec(fuel_percent, item_percent) + return "size[8,8.5]".. + "list[context;src;2.75,0.5;1,1;]".. + "list[context;fuel;2.75,2.5;1,1;]".. + "image[2.75,1.5;1,1;default_furnace_fire_bg.png^[lowpart:".. + (fuel_percent)..":default_furnace_fire_fg.png]".. + "image[3.75,1.5;1,1;gui_furnace_arrow_bg.png^[lowpart:".. + (item_percent)..":gui_furnace_arrow_fg.png^[transformR270]".. + "list[context;dst;4.75,0.96;2,2;]".. + "list[current_player;main;0,4.25;8,1;]".. + "list[current_player;main;0,5.5;8,3;8]".. + "listring[context;dst]".. + "listring[current_player;main]".. + "listring[context;src]".. + "listring[current_player;main]".. + "listring[context;fuel]".. + "listring[current_player;main]".. + default.get_hotbar_bg(0, 4.25) +end + +function default.get_furnace_inactive_formspec() + return "size[8,8.5]".. + "list[context;src;2.75,0.5;1,1;]".. + "list[context;fuel;2.75,2.5;1,1;]".. + "image[2.75,1.5;1,1;default_furnace_fire_bg.png]".. + "image[3.75,1.5;1,1;gui_furnace_arrow_bg.png^[transformR270]".. + "list[context;dst;4.75,0.96;2,2;]".. + "list[current_player;main;0,4.25;8,1;]".. + "list[current_player;main;0,5.5;8,3;8]".. + "listring[context;dst]".. + "listring[current_player;main]".. + "listring[context;src]".. + "listring[current_player;main]".. + "listring[context;fuel]".. + "listring[current_player;main]".. + default.get_hotbar_bg(0, 4.25) +end + +-- +-- Node callback functions that are the same for active and inactive furnace +-- + +local function can_dig(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 + +local function allow_metadata_inventory_put(pos, listname, index, stack, player) + if minetest.is_protected(pos, player:get_player_name()) then + return 0 + end + 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("Furnace is empty")) + 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 function allow_metadata_inventory_move(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) + return allow_metadata_inventory_put(pos, to_list, to_index, stack, player) +end + +local function allow_metadata_inventory_take(pos, listname, index, stack, player) + if minetest.is_protected(pos, player:get_player_name()) then + return 0 + end + return stack:get_count() +end + +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 furnace_node_timer(pos, elapsed) + -- + -- Initialize metadata + -- + local meta = minetest.get_meta(pos) + local fuel_time = meta:get_float("fuel_time") or 0 + local src_time = meta:get_float("src_time") or 0 + local fuel_totaltime = meta:get_float("fuel_totaltime") or 0 + + local inv = meta:get_inventory() + local srclist, fuellist + local dst_full = false + + local timer_elapsed = meta:get_int("timer_elapsed") or 0 + meta:set_int("timer_elapsed", timer_elapsed + 1) + + local cookable, cooked + local fuel + + local update = true + while elapsed > 0 and update do + update = false + + srclist = inv:get_list("src") + fuellist = inv:get_list("fuel") + + -- + -- Cooking + -- + + -- Check if we have cookable content + local aftercooked + cooked, aftercooked = minetest.get_craft_result({method = "cooking", width = 1, items = srclist}) + cookable = cooked.time ~= 0 + + local el = math.min(elapsed, fuel_totaltime - fuel_time) + if cookable then -- fuel lasts long enough, adjust el to cooking duration + el = math.min(el, cooked.time - src_time) + end + + -- Check if we have enough fuel to burn + if fuel_time < fuel_totaltime then + -- The furnace is currently active and has enough fuel + fuel_time = fuel_time + el + -- If there is a cookable item then check if it is ready yet + if cookable then + src_time = src_time + el + if src_time >= cooked.time then + -- Place result in dst list if possible + if inv:room_for_item("dst", cooked.item) then + inv:add_item("dst", cooked.item) + inv:set_stack("src", 1, aftercooked.items[1]) + src_time = src_time - cooked.time + update = true + else + dst_full = true + end + -- Play cooling sound + minetest.sound_play("default_cool_lava", + {pos = pos, max_hear_distance = 16, gain = 0.1}, true) + else + -- Item could not be cooked: probably missing fuel + update = true + end + end + else + -- Furnace ran out of fuel + if cookable then + -- We need to get new fuel + local afterfuel + fuel, afterfuel = minetest.get_craft_result({method = "fuel", width = 1, items = fuellist}) + + if fuel.time == 0 then + -- No valid fuel in fuel list + fuel_totaltime = 0 + src_time = 0 + else + -- Take fuel from fuel list + inv:set_stack("fuel", 1, afterfuel.items[1]) + -- Put replacements in dst list or drop them on the furnace. + local replacements = fuel.replacements + if replacements[1] then + local leftover = inv:add_item("dst", replacements[1]) + if not leftover:is_empty() then + local above = vector.new(pos.x, pos.y + 1, pos.z) + local drop_pos = minetest.find_node_near(above, 1, {"air"}) or above + minetest.item_drop(replacements[1], nil, drop_pos) + end + end + update = true + fuel_totaltime = fuel.time + (fuel_totaltime - fuel_time) + end + else + -- We don't need to get new fuel since there is no cookable item + fuel_totaltime = 0 + src_time = 0 + end + fuel_time = 0 + end + + elapsed = elapsed - el + end + + if fuel and fuel_totaltime > fuel.time then + fuel_totaltime = fuel.time + end + if srclist and srclist[1]:is_empty() then + src_time = 0 + end + + -- + -- Update formspec, infotext and node + -- + local formspec + local item_state + local item_percent = 0 + if cookable then + item_percent = math.floor(src_time / cooked.time * 100) + if dst_full then + item_state = S("100% (output full)") + else + item_state = S("@1%", item_percent) + end + else + if srclist and not srclist[1]:is_empty() then + item_state = S("Not cookable") + else + item_state = S("Empty") + end + end + + local fuel_state = S("Empty") + local active = false + local result = false + + if fuel_totaltime ~= 0 then + active = true + local fuel_percent = 100 - math.floor(fuel_time / fuel_totaltime * 100) + fuel_state = S("@1%", fuel_percent) + formspec = default.get_furnace_active_formspec(fuel_percent, item_percent) + swap_node(pos, "default:furnace_active") + -- make sure timer restarts automatically + result = true + + -- Play sound every 5 seconds while the furnace is active + if timer_elapsed == 0 or (timer_elapsed+1) % 5 == 0 then + minetest.sound_play("default_furnace_active", + {pos = pos, max_hear_distance = 16, gain = 0.5}, true) + end + else + if fuellist and not fuellist[1]:is_empty() then + fuel_state = S("@1%", 0) + end + formspec = default.get_furnace_inactive_formspec() + swap_node(pos, "default:furnace") + -- stop timer on the inactive furnace + minetest.get_node_timer(pos):stop() + meta:set_int("timer_elapsed", 0) + end + + + local infotext + if active then + infotext = S("Furnace active") + else + infotext = S("Furnace inactive") + end + infotext = infotext .. "\n" .. S("(Item: @1; Fuel: @2)", item_state, fuel_state) + + -- + -- Set meta values + -- + meta:set_float("fuel_totaltime", fuel_totaltime) + meta:set_float("fuel_time", fuel_time) + meta:set_float("src_time", src_time) + meta:set_string("formspec", formspec) + meta:set_string("infotext", infotext) + + return result +end + +-- +-- Node definitions +-- + +minetest.register_node("default:furnace", { + description = S("Furnace"), + tiles = { + "default_furnace_top.png", "default_furnace_bottom.png", + "default_furnace_side.png", "default_furnace_side.png", + "default_furnace_side.png", "default_furnace_front.png" + }, + paramtype2 = "facedir", + groups = {cracky=2}, + legacy_facedir_simple = true, + is_ground_content = false, + sounds = default.node_sound_stone_defaults(), + + can_dig = can_dig, + + on_timer = furnace_node_timer, + + on_construct = function(pos) + local meta = minetest.get_meta(pos) + local inv = meta:get_inventory() + inv:set_size('src', 1) + inv:set_size('fuel', 1) + inv:set_size('dst', 4) + furnace_node_timer(pos, 0) + end, + + on_metadata_inventory_move = function(pos) + minetest.get_node_timer(pos):start(1.0) + end, + on_metadata_inventory_put = function(pos) + -- start timer function, it will sort out whether furnace can burn or not. + minetest.get_node_timer(pos):start(1.0) + end, + on_metadata_inventory_take = function(pos) + -- check whether the furnace is empty or not. + minetest.get_node_timer(pos):start(1.0) + end, + on_blast = function(pos) + local drops = {} + default.get_inventory_drops(pos, "src", drops) + default.get_inventory_drops(pos, "fuel", drops) + default.get_inventory_drops(pos, "dst", drops) + drops[#drops+1] = "default:furnace" + minetest.remove_node(pos) + return drops + end, + + allow_metadata_inventory_put = allow_metadata_inventory_put, + allow_metadata_inventory_move = allow_metadata_inventory_move, + allow_metadata_inventory_take = allow_metadata_inventory_take, +}) + +minetest.register_node("default:furnace_active", { + description = S("Furnace"), + tiles = { + "default_furnace_top.png", "default_furnace_bottom.png", + "default_furnace_side.png", "default_furnace_side.png", + "default_furnace_side.png", + { + image = "default_furnace_front_active.png", + backface_culling = false, + animation = { + type = "vertical_frames", + aspect_w = 16, + aspect_h = 16, + length = 1.5 + }, + } + }, + paramtype2 = "facedir", + light_source = 8, + drop = "default:furnace", + groups = {cracky=2, not_in_creative_inventory=1}, + legacy_facedir_simple = true, + is_ground_content = false, + sounds = default.node_sound_stone_defaults(), + on_timer = furnace_node_timer, + + can_dig = can_dig, + + allow_metadata_inventory_put = allow_metadata_inventory_put, + allow_metadata_inventory_move = allow_metadata_inventory_move, + allow_metadata_inventory_take = allow_metadata_inventory_take, +}) + +minetest.register_craft({ + output = "default:furnace", + recipe = { + {"group:stone", "group:stone", "group:stone"}, + {"group:stone", "", "group:stone"}, + {"group:stone", "group:stone", "group:stone"}, + } +})