From 2bf30a11b6614adb3f41499f20e3aa67f6f28a56 Mon Sep 17 00:00:00 2001 From: Zenon Seth Date: Sun, 26 Nov 2023 23:11:47 +0000 Subject: [PATCH] Add working reservoirs implementation --- api/api.lua | 1 + api/reservoir.lua | 176 ++++++++++++++++++++++ logic/logic.lua | 1 + logic/reservoir.lua | 140 +++++++++++++++++ registration/machines_api_reg.lua | 8 + registration/node_recipes.lua | 18 +++ settingtypes.txt | 1 + textures/logistica_reservoir_obsidian.png | Bin 0 -> 2593 bytes textures/logistica_reservoir_silverin.png | Bin 0 -> 2717 bytes util/settings.lua | 2 + 10 files changed, 347 insertions(+) create mode 100644 api/reservoir.lua create mode 100644 logic/reservoir.lua create mode 100644 textures/logistica_reservoir_obsidian.png create mode 100644 textures/logistica_reservoir_silverin.png 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 0000000000000000000000000000000000000000..acf7520e1c10f929f1d7322b373828a6b21287d3 GIT binary patch literal 2593 zcmaJ@c|a3)79LPQkW)d#B232$*d-^|I%L3vfUqSPQ3y~uLNZB4a*<3Vp|&1~2U@DE zT@(+Z1w@V%P`Zk^B5K70wJus~MZ17U#R_F97+iEGK@qC$ACu($zW2WOe(#!W;l?d? zu$yBC0DwbGG&`R722Ia2YvNO#n>z>qRxE6J5}w3~g#`*3nJ-jiqGYX1N#Fp$2-hn4 zf-DpVGf^=nN1(nFryvj$A`mZ#L**zVPze^Dt3sFM#w{1*W(gJwq3|#}h889WWGK!D zwKAz(4Qmm|%nK90O~(`nG^^lQ2$aO(f)NT83I>sb$W$oI4rHi=A~>EM^~#u#5J-aK zN|-{)$;lz-1dtUfF@?5p;X(?PPNCCDga%2iljD3XNv@t}q8Q^~qiTT)Q{tFH4w^Xm znFhx6y^GTEeSsPV{EMB-i* zVNy)59jKS|%g63v|`2tfd5J;&~2sHv!Xuc>B{}`%Pi0~Y~3T29k zK0+X-NQ4np(}7&mfPlnMQpv%SEpj9%!S>D+QYPNwbyN(>TXBzFdcx8m?8eTBWkhT> z%J^C%gz?3Q%86~LBG#VORfj+Tuvmu?g@3Ey0N}Snv*Xl9tfj8!!&keA#Oo=ENoj??dlvYe8m6Up#r8aCZCG{b3Cox594@bz?b|}l+g9LR z;p9Eq@KxN4mL)IztBa~P^A;SeXB7nMnh(w@%Vpgxdt~e3Q6PHxawuVmqw&KV`#!4+ z_xSc>r$9OLxNvsqrj1L(EPtH0qNmDOZ9^R(=N_)o4IR)-xk9c^S#5JAFx9s%3WnAZ1x;^~z*Yo358`We# z&=oz?q*9*f+?q0vFN7IAa?$TtDq>1juvKN#8wP=E0~IqS3` zY<2VhrULrZ4XTQZzC-p5FL`#`aP_9ty{X-gS1UpxA4`4erTgEP_8|b+4Sl-k2oGcf zpi=y8_3&@H+e={2zHH;uld|IXrcC?r%XxmDg{O+`*V;nNkr#in0$5Z3x+pOFf#bDg zP-q{Ggq%1Q^larGOLr*Bk`y@OesO}@ zR{tZ>=`J;Wz0>{7g5ws4g;5^<*XwRq{l_QE=SuHcYry58PX91(7K_^@$DDqfpB=qs zRct*cG~yec+Z~^^O3RYM6k8AWqV~HzU9)IM{hhaV{Xl0vuuUjwYrA+^o>`xNB=o_T z)H|VhM{&=L)1$nPVwjRtM=qB?lketO^qtqt-Swe+tuy9Up04E?hl>tHrEl))>cadT z+^wehCA<79rFKf;<;tqSdY=LPrwxh2t-ES=wW*7Gp|k6|_BR#R-uE&nZ(CHNZOA{T z4oR|it`~f9`uQ>DuFu(9;h(;ev{`=fle0%}-?=F7A07dfnt8&G{qlQgpWmT&G0yUL z*p_xT2AW@LnK>s|cvAFpIWB3O(X_qef6CCF}JhEZFQNmRa3KTL++8bARA?e%VI?5Ui0^6Y0})nwf@s*NErdnhQmkav%0o> z-&tz?eq$Q2qAbgE0I3))Gb+yf7ti-aW*5nu9ji;-=C*X+^Ue#Cf4#(|luoYz(?7eI$T+rvm9hT+hGW&3 literal 0 HcmV?d00001 diff --git a/textures/logistica_reservoir_silverin.png b/textures/logistica_reservoir_silverin.png new file mode 100644 index 0000000000000000000000000000000000000000..c6b5b37cf22dcfd8f99beac3f6106719b7f9102e GIT binary patch literal 2717 zcmaJ@d0Z2B79RxUP#_*vz);7i8-SPtfECY=8-jm>`p0}S{)wif3L3(;RHm^Bt1S`M*?isNj_;fuW+5M3JYM9g;9|TQDTTf4(d6D z@d`D@B$03&d`m7CjdLs2Dw%;Npa1vjNl6o~fnvVv@Wj0> z!i1RKIY2{oN*D@(QH5G1f_>NF*_&@joIWpf0iuOvkz52%DvDQwNphwA35@y+kfV?( zR4ef&gqb84gN;6ObY7A$0ggb#m~@=${doW^Pxv#Sk4&c4w?dd4!S6EW-(VB@9%GU` zXwV04qCN?Y*MV+`j;|2o!MPfOdk^tA9Dzz9L1Z|FhOA(NJYSBxi>JH0GmSFl0WTUI zw?!m6t`NhuXjGc3vx|!}%{`Pxqq{AoyShaiNDYek*ocLg@NZQ0eGf9kB07d(GT0!E z%>he>jY(so4AD0~T_%(#FiBcxF)R_PWf;j@q!LQ7I0S=}PzI!*7a8)F#2~$6VL%gH zF!1R3e7YZkVhWY+1FtLL!w@vOIfL?rQuLRET6JyL*o4b%ep;<75#Y^ zqZiBs5(}!l!Wswc!>>e#N5*E83l=Y`d*%{%E1>62!|`=357}fc!AE}3mRwIw-<~!1 zpv~OT;|Bs?*9X1+qH+04o?&NGXXImHcalO-N#@?Be$didn zlW+RlD(^U5$SI_QzI#&DQ<>%W9L+{M_Fh&MZ!v8ReB6iq92d5Ux%_JKh}HAm%{iN_ zeP3wePQ1L-r7)p7kl#d`WS-GJ8G=&F$;heYFNVJ9I(@eGSH{B^?NeemzA*-}yRGBc zPag#Nd_M^|wPAq2%Bkr1u8MQfaX(*89z6ToobU5oiAqQ?E4-sKrbcZ{ElBqH@6!8O zqdh-ICjkxp7dlqIV*RnMcR@@4o%4qGM^9@Y`)#gA$$Zc$B9P#R& zW6A~6ENfB;BSYkQ=Gff@I*3OIT;0|3UkXfZfNgH| z)miPJ_{l}$&<=KwYG%{7*YP?r$?fh5&ml&U@?I6A*gM82f9Tw( zHhf8Fy)Ro(`AX_%cC+DIbW_pJkZ|w5j+`{WUGt0*ZF!McbNR@$RF(DFXHre?zI&Da zzz%Khx)|P9DNpT}%_L4*bVwjomz1t~w9;mqhX({H-V8N;Y1^LV_b=(AU)QJmI90c< zv&ogI+-%NQ85KLWmes7Ecc8Myq4s8ck2<-Ib^M0^zQ$R=mkt*g7Y2?mJW!qfwdlJN zTKV&m)>-pQ>{D~6BW-qTyZfFD9@+3}*e>e|-S*mrFkq;|>EG08w-5Wly9epP)|b8t zG7tGr|LV?L3sG4jNmc*ZU;sGJIYg3RUojm089~C#aI)z1Si192o^U$*f6L4tePKFp z>lrJwIk(yKSzp^gVVe>7$N4{ubkf@>WD!q1XodEltgxETkEkvF>5!0l@w@}k_Eut_ z+v&hlb@w)#n`m06)|8B{t-V*cVNdVtrW34|+Jdsk`b{+okKpXCqSdhjeO;>#cK5Vs z8WZv&I#$OOiQ9JHnwEO7dpN&&z2~U^H=`-9edJvo?EJK4v-STfdA@<%qwF