diff --git a/api/api.lua b/api/api.lua index 214f094..d272b1c 100644 --- a/api/api.lua +++ b/api/api.lua @@ -17,3 +17,4 @@ dofile(path.."/crafter_auto.lua") dofile(path.."/crafting_supplier.lua") dofile(path.."/cobble_supplier.lua") dofile(path.."/synchronizer.lua") +dofile(path.."/reservoir.lua") diff --git a/api/reservoir.lua b/api/reservoir.lua new file mode 100644 index 0000000..d090518 --- /dev/null +++ b/api/reservoir.lua @@ -0,0 +1,176 @@ +local S = logistica.TRANSLATOR +local function L(str) return "logistica:"..str end + +local META_LIQUID_LEVEL = "liquidLevel" +local META_LIQUID_NAME = "liquidName" + +local EMPTY_SUFFIX = "_empty" + +local LIQUID_NONE = "" + +local VAR_SMALL = "silverin" +local VAR_LARGE = "obsidian" + +local SMALL_MAX = 32 +local LARGE_MAX = 128 + +local variants = {VAR_SMALL} +if logistica.settings.large_liquid_tank_enabled then table.insert(variants, VAR_LARGE) end + +local variantSpecificDefs = { + [VAR_SMALL] = { + description = logistica.reservoir_get_description(0, SMALL_MAX, ""), + tiles = {"logistica_reservoir_silverin.png"}, + sounds = logistica.node_sound_metallic(), + logistica = { + maxBuckets = SMALL_MAX, + }, + }, + [VAR_LARGE] = { + description = logistica.reservoir_get_description(0, LARGE_MAX, ""), + tiles = {"logistica_reservoir_obsidian.png"}, + sounds = default.node_sound_stone_defaults(), + logistica = { + maxBuckets = LARGE_MAX, + }, + } +} + +---------------------------------------------------------------- +-- general helper functions +---------------------------------------------------------------- + +-- local function drop_item(pos, stack) +-- minetest.add_item(pos, stack) +-- end + +local function give_item_to_player(pos, player, stack) + local inv = player:get_inventory() + local leftover = inv:add_item("main", stack) + if leftover and not leftover:is_empty() then + -- print("leftover not empty, size = " .. leftover:get_count()) + minetest.item_drop(leftover, player, player:get_pos()) + end +end + +---------------------------------------------------------------- +-- callbacks +---------------------------------------------------------------- + + +local function after_place_node(pos, placer, itemstack, pointed_thing) + local nodeMeta = minetest.get_meta(pos) + local node = minetest.get_node(pos) + local stackMeta = itemstack:get_meta() + local nodeDef = minetest.registered_nodes[node.name] + if not nodeDef or not nodeDef.logistica then return end + + local liquidLevel = stackMeta:get_int(META_LIQUID_LEVEL) + local liquidDesc = nodeDef.logistica.liquidDesc + local maxBuckets = nodeDef.logistica.maxBuckets + + nodeMeta:set_int(META_LIQUID_LEVEL, liquidLevel) + node.param2 = logistica.reservoir_make_param2(liquidLevel, maxBuckets) + minetest.swap_node(pos, node) + nodeMeta:set_string("infotext", logistica.reservoir_get_description(liquidLevel, maxBuckets, liquidDesc)) +end + + +local function preserve_metadata(pos, oldnode, oldmeta, drops) + if not drops or not drops[1] then return end + local nodeDef = minetest.registered_nodes[oldnode.name] + if not nodeDef or not nodeDef.logistica then return end + + local meta = minetest.get_meta(pos) + local drop = drops[1] + local dropMeta = drop:get_meta() + local liquidDesc = nodeDef.logistica.liquidDesc + local maxBuckets = nodeDef.logistica.maxBuckets + local liquidLevel = meta:get_int(META_LIQUID_LEVEL) + + dropMeta:set_int(META_LIQUID_LEVEL, liquidLevel) + dropMeta:set_string("description", logistica.reservoir_get_description(liquidLevel, maxBuckets, liquidDesc)) +end + +local function on_rightclick(pos, node, player, itemstack, pointed_thing, max) + if not player or not player:is_player() or minetest.is_protected(pos, player:get_player_name()) then return end + + local usedItem = logistica.reservoir_use_item_on(pos, itemstack, node) + + if not usedItem then return end + + if itemstack:get_count() == 1 then + return usedItem + else + give_item_to_player(pos, player, usedItem) + itemstack:take_item(1) + return itemstack + end +end + +-------------------------------- +-- registration helpers +-------------------------------- + +local commonDef = { + drawtype = "glasslike_framed_optional", + paramtype = "light", + paramtype2 = "glasslikeliquidlevel", + is_ground_content = false, + sunlight_propagates = false, + groups = {cracky = 3, level = 1}, + preserve_metadata = preserve_metadata, + after_place_node = after_place_node, + on_rightclick = on_rightclick, + stack_max = 1, + backface_culling = false, +} + +local function get_variant_def(variantName) + if not variantSpecificDefs[variantName] then return nil end + local vDef = variantSpecificDefs[variantName] + local def = table.copy(commonDef) + for k,v in pairs(vDef) do def[k] = v end + return def +end + +-------------------------------- +-- minetest registration +-------------------------------- + +-- register empty tanks, always +for _, variantName in ipairs(variants) do + local def = table.copy(get_variant_def(variantName)) + local nodeName = L("reservoir_"..variantName..EMPTY_SUFFIX) + def.drops = nodeName + def.logistica.liquidName = LIQUID_NONE + def.logistica.liquidDesc = LIQUID_NONE + minetest.register_node(nodeName, def) +end + +--[[ + `liquidName`: the name used to register the reservoir, should have no spaces and all lowercase
+ `liquidDesc`: a human readable liquid description, e.g. "Water"
+ `bucketItemName` : the name of the bucket that holds the liquid
+ `liquidTexture` : a single texture to use for the liquid
+ `optLight` : optional, if nil assumed 0. How much a non-empty reservoir will glow + `emptyBucketName` : optional, if nil, bucket:bucket_empty will be used - the "empty" container to use
+]] +function logistica.register_reservoir(liquidName, liquidDesc, bucketItemName, liquidTexture, optLight, optEmptyBucketName) + local lname = string.lower(liquidName:gsub(" ", "_")) + + for _, variantName in ipairs(variants) do + local nodeName = L("reservoir_"..variantName.."_"..lname) + local def = table.copy(get_variant_def(variantName)) + def.drops = nodeName + def.special_tiles = {liquidTexture} + def.logistica.liquidName = lname + def.logistica.liquidDesc = liquidDesc + def.groups.not_in_creative_inventory = 1 + def.light_source = optLight + + minetest.register_node(nodeName, def) + + logistica.reservoir_register_names(lname, bucketItemName, optEmptyBucketName) + end +end diff --git a/logic/logic.lua b/logic/logic.lua index 7a1d4f2..312c231 100644 --- a/logic/logic.lua +++ b/logic/logic.lua @@ -18,3 +18,4 @@ dofile(path.."/vaccuum_chest.lua") dofile(path.."/autocrafting_logic.lua") dofile(path.."/crafting_supplier.lua") dofile(path.."/synchronizer.lua") +dofile(path.."/reservoir.lua") diff --git a/logic/reservoir.lua b/logic/reservoir.lua new file mode 100644 index 0000000..15e65c7 --- /dev/null +++ b/logic/reservoir.lua @@ -0,0 +1,140 @@ +local S = logistica.TRANSLATOR + +local BUCKET_TO_NAME = {} +local NAME_TO_BUCKET = {} +local NAME_TO_EMPTY_BUCKET = {} +local EMPTY_BUCKET = "bucket:bucket_empty" +local EMPTY_SUFFIX = "_empty" + +local META_LIQUID_LEVEL = "liquidLevel" +local LIQUID_NONE = "" +local BUCKET_ANY = "BUCKET_ANY" + +local strDescription = S("Reservoir") +local strEmpty = S("Empty") +local getStrContains = function(number, max, ofWhat) + if number == 0 then + return S("@1 / @2 buckets", number, max) + else + return S("@1 / @2 buckets of @3", number, max, ofWhat) + end +end + +local function ends_with(str, ending) + return ending == "" or string.sub(str, -#ending) == ending +end + +local function get_empty_bucket_needed_for(liquidName) + local savedBucket = NAME_TO_EMPTY_BUCKET[liquidName] + if savedBucket then return savedBucket + else return EMPTY_BUCKET end +end + +local function get_full_bucket_needed_for(liquidName) + if liquidName == LIQUID_NONE then return BUCKET_ANY end + return NAME_TO_BUCKET[liquidName] +end + +local function get_empty_reservoir_name(nodeName, liquidName) + if not ends_with(nodeName, liquidName) then return nodeName end + if ends_with(nodeName, EMPTY_SUFFIX) then return nodeName end + local nodeBase = string.sub(nodeName, 1, (#nodeName) - (#liquidName) - 1) + return nodeBase..EMPTY_SUFFIX +end + +local function get_liquid_reservoir_name_for(nodeName, liquidName) + if not ends_with(nodeName, EMPTY_SUFFIX) then return nodeName end + local nodeBase = string.sub(nodeName, 1, (#nodeName) - (#EMPTY_SUFFIX)) + local newName = nodeBase.."_"..liquidName + if not minetest.registered_nodes[newName] then return nodeName + else return newName end +end + +-------------------------------- +-- public functions +-------------------------------- + +function logistica.reservoir_make_param2(val, max) + local ret = math.floor(63*(val/max)) + if val > 0 and ret == 0 then + ret = 1 -- this ensures we always have at least 1 visible liquid level + end + return ret +end + +function logistica.reservoir_get_description(currBuckets, maxBuckets, liquidName) + return strDescription.."\n"..getStrContains(currBuckets, maxBuckets, liquidName) +end + +function logistica.reservoir_register_names(liquidName, bucketName, emptyBucketName) + BUCKET_TO_NAME[bucketName] = liquidName + NAME_TO_BUCKET[liquidName] = bucketName + if emptyBucketName then + NAME_TO_EMPTY_BUCKET[liquidName] = emptyBucketName + end +end + +-- returns nil if item had no effect
+-- returns an ItemStack to replace the item, if it had effect (e.g. took or stored liquid) +function logistica.reservoir_use_item_on(pos, itemstack, optNode) + local node = optNode or minetest.get_node(pos) + local nodeDef = minetest.registered_nodes[node.name] + if not nodeDef or not nodeDef.logistica or not nodeDef.logistica.liquidName or not nodeDef.logistica.maxBuckets then return end + + local itemStackName = itemstack:get_name() + local meta = minetest.get_meta(pos) + local nodeLiquidLevel = meta:get_int(META_LIQUID_LEVEL) + local liquidName = nodeDef.logistica.liquidName + local maxBuckets = nodeDef.logistica.maxBuckets + local liquidDesc = nodeDef.logistica.liquidDesc + + local emptyBucket = get_empty_bucket_needed_for(liquidName) + local fullBucket = get_full_bucket_needed_for(liquidName) + + if itemStackName == emptyBucket then + if nodeLiquidLevel == 0 then + -- make sure we swap this for the empty reservoir + logistica.swap_node(pos, get_empty_reservoir_name(node.name, liquidName)) + return nil + end + if not fullBucket then return nil end + + nodeLiquidLevel = nodeLiquidLevel - 1 + if nodeLiquidLevel == 0 then + node.param2 = 0 + else + node.param2 = logistica.reservoir_make_param2(nodeLiquidLevel, maxBuckets) + end + minetest.swap_node(pos, node) + meta:set_int(META_LIQUID_LEVEL, nodeLiquidLevel) + if nodeLiquidLevel == 0 then + local newNodeName = get_empty_reservoir_name(node.name, liquidName) + if not minetest.registered_nodes[newNodeName] then return nil end + logistica.swap_node(pos, newNodeName) + end + meta:set_string("infotext", logistica.reservoir_get_description(nodeLiquidLevel, maxBuckets, liquidDesc)) + + return ItemStack(fullBucket) + elseif fullBucket == BUCKET_ANY or itemStackName == fullBucket then + local newLiquidName = BUCKET_TO_NAME[itemStackName] + if not newLiquidName then return nil end -- wasn't a bucket we can use + local newEmptyBucket = get_empty_bucket_needed_for(newLiquidName) + if not newEmptyBucket then return nil end + + nodeLiquidLevel = nodeLiquidLevel + 1 + if nodeLiquidLevel > maxBuckets then return nil end + node.param2 = logistica.reservoir_make_param2(nodeLiquidLevel, maxBuckets) + minetest.swap_node(pos, node) + local newNodeName = get_liquid_reservoir_name_for(node.name, newLiquidName) + + if not minetest.registered_nodes[newNodeName] then return nil end + if nodeLiquidLevel == 1 then -- first bucket we added, swap to that reservoir type + logistica.swap_node(pos, newNodeName) + end + local newLiquidDesc = minetest.registered_nodes[newNodeName].logistica.liquidDesc + meta:set_string("infotext", logistica.reservoir_get_description(nodeLiquidLevel, maxBuckets, newLiquidDesc)) + meta:set_int(META_LIQUID_LEVEL, nodeLiquidLevel) + return ItemStack(newEmptyBucket) + end + return nil +end diff --git a/registration/machines_api_reg.lua b/registration/machines_api_reg.lua index 86ad176..54d21b8 100644 --- a/registration/machines_api_reg.lua +++ b/registration/machines_api_reg.lua @@ -185,6 +185,14 @@ local function ins_tiles(lname) return { logistica.register_requester("Item Request Inserter\nInserts 1 item at a time", "requester_item", 1, ins_tiles("item")) logistica.register_requester("Bulk Request Inserter\nInserts up to 64 items at a time", "requester_stack", 64, ins_tiles("stack")) +-------------------------------- +-- Reservoirs +-------------------------------- + +logistica.register_reservoir("lava", "Lava", "bucket:bucket_lava", "default_lava.png", 8) +logistica.register_reservoir("water", "Water", "bucket:bucket_water", "default_water.png") +logistica.register_reservoir("river_water", "River Water", "bucket:bucket_river_water", "default_river_water.png") + -------------------------------- -- Passive Supply Chest -------------------------------- diff --git a/registration/node_recipes.lua b/registration/node_recipes.lua index 89de123..836ea99 100644 --- a/registration/node_recipes.lua +++ b/registration/node_recipes.lua @@ -147,3 +147,21 @@ minetest.register_craft({ {L("silverin_plate"), L("wireless_crystal"), L("silverin_plate")}, } }) + +minetest.register_craft({ + output = L("reservoir_silverin_empty"), + recipe = { + {L("silverin_plate"), "", L("silverin_plate")}, + {L("optic_cable"), "bucket:bucket_empty", L("photonizer")}, + {L("silverin_plate"), "", L("silverin_plate")}, + } +}) + +minetest.register_craft({ + output = L("reservoir_obsidian_empty"), + recipe = { + {"default:obsidianbrick", L("silverin_plate"), "default:obsidianbrick"}, + {L("optic_cable"), "bucket:bucket_empty", L("photonizer")}, + {"default:obsidianbrick", L("silverin_plate"), "default:obsidianbrick"}, + } +}) diff --git a/settingtypes.txt b/settingtypes.txt index 6be1963..408144a 100644 --- a/settingtypes.txt +++ b/settingtypes.txt @@ -3,3 +3,4 @@ logistica_wap_max_range (Max range for the Wirless Access Pad, in nodes.) int 64 logistica_wap_upgrade_step (How much distance each upgrade to the WAP adds) int 250 10 5000 logistica_wifi_upgrader_hard_mode (Make Wireless Upgrader's minigame harder) bool false logistica_cable_size (Changes the visual/hitbox size of cables) enum Medium Small,Medium,Large,XLarge +logistica_enable_large_liquid_tank (Enable the Large Liquid Tank) bool true diff --git a/textures/logistica_reservoir_obsidian.png b/textures/logistica_reservoir_obsidian.png new file mode 100644 index 0000000..acf7520 Binary files /dev/null and b/textures/logistica_reservoir_obsidian.png differ diff --git a/textures/logistica_reservoir_silverin.png b/textures/logistica_reservoir_silverin.png new file mode 100644 index 0000000..c6b5b37 Binary files /dev/null and b/textures/logistica_reservoir_silverin.png differ diff --git a/util/settings.lua b/util/settings.lua index 43a2098..6d5aaeb 100644 --- a/util/settings.lua +++ b/util/settings.lua @@ -32,3 +32,5 @@ logistica.settings.wap_upgrade_step = get_int("wap_upgrade_step", 250) logistica.settings.wifi_upgrader_hard_mode = get_bool("wifi_upgrader_hard_mode", false) logistica.settings.cable_size = get_cable_size_from_settings() + +logistica.settings.large_liquid_tank_enabled = get_bool("enable_large_liquid_tank", true)