From 2d4822b95367a7f28fa3a6ebb36f49b1cc6452e2 Mon Sep 17 00:00:00 2001 From: Zenon Seth Date: Fri, 24 Nov 2023 11:10:44 +0000 Subject: [PATCH] Add Wireless Upgrader, with full difficult of two wave addition --- api/api.lua | 1 + api/synchronizer.lua | 67 ++++ item/craft_item.lua | 6 + logic/logic.lua | 1 + logic/synchronizer.lua | 373 +++++++++++++++++++++++ registration/lava_furnace_recipes.lua | 8 + textures/logistica_icon_combine.png | Bin 0 -> 1986 bytes textures/logistica_icon_fdn.png | Bin 0 -> 2955 bytes textures/logistica_icon_fup.png | Bin 0 -> 3464 bytes textures/logistica_icon_graph_back.png | Bin 0 -> 1869 bytes textures/logistica_icon_sl.png | Bin 0 -> 3259 bytes textures/logistica_icon_sr.png | Bin 0 -> 3245 bytes textures/logistica_synchronizer_side.png | Bin 0 -> 2266 bytes textures/logistica_wireless_crystal.png | Bin 0 -> 2834 bytes tools/tools.lua | 2 + tools/wireless_access_pad.lua | 44 ++- util/common.lua | 7 + 17 files changed, 506 insertions(+), 3 deletions(-) create mode 100644 api/synchronizer.lua create mode 100644 logic/synchronizer.lua create mode 100644 textures/logistica_icon_combine.png create mode 100644 textures/logistica_icon_fdn.png create mode 100644 textures/logistica_icon_fup.png create mode 100644 textures/logistica_icon_graph_back.png create mode 100644 textures/logistica_icon_sl.png create mode 100644 textures/logistica_icon_sr.png create mode 100644 textures/logistica_synchronizer_side.png create mode 100644 textures/logistica_wireless_crystal.png diff --git a/api/api.lua b/api/api.lua index f696b72..214f094 100644 --- a/api/api.lua +++ b/api/api.lua @@ -16,3 +16,4 @@ dofile(path.."/vaccuum_chest.lua") dofile(path.."/crafter_auto.lua") dofile(path.."/crafting_supplier.lua") dofile(path.."/cobble_supplier.lua") +dofile(path.."/synchronizer.lua") diff --git a/api/synchronizer.lua b/api/synchronizer.lua new file mode 100644 index 0000000..ec65335 --- /dev/null +++ b/api/synchronizer.lua @@ -0,0 +1,67 @@ + +-------------------------------- +-- callbacks +-------------------------------- + +local function on_rightclick_sync(pos, node, clicker, itemstack, pointed_thing) + if not clicker or not clicker:is_player() then return end + if minetest.is_protected(pos, clicker:get_player_name()) then return end + logistica.sync_on_rightclick(pos, node, clicker, itemstack, pointed_thing) +end + +local function after_place_sync(pos, placer, itemstack) + logistica.sync_after_place(pos, placer, itemstack) +end + +local function allow_storage_inv_put_sync(pos, listname, index, stack, player) + if minetest.is_protected(pos, player:get_player_name()) then return 0 end + return logistica.sync_allow_storage_inv_put(pos, listname, index, stack, player) +end + +local function allow_inv_take_sync(pos, listname, index, stack, player) + if minetest.is_protected(pos, player:get_player_name()) then return 0 end + return logistica.sync_allow_inv_take(pos, listname, index, stack, player) +end + +local function allow_inv_move_sync(_, _, _, _, _, _, _) + return 0 +end + + +---------------------------------------------------------------- +-- Public Registration API +---------------------------------------------------------------- + +-- `simpleName` is used for the description and for the name (can contain spaces) +function logistica.register_synchronizer(description, name, tiles) + local lname = string.lower(name:gsub(" ", "_")) + local syncName = "logistica:"..lname + + local grps = {oddly_breakable_by_hand = 3, cracky = 3 } + local def = { + description = description, + drawtype = "normal", + tiles = tiles, + paramtype2 = "facedir", + is_ground_content = false, + groups = grps, + drop = syncName, + sounds = logistica.node_sound_metallic(), + after_place_node = after_place_sync, + on_rightclick = on_rightclick_sync, + can_dig = logistica.sync_can_dig, + allow_metadata_inventory_put = allow_storage_inv_put_sync, + allow_metadata_inventory_take = allow_inv_take_sync, + allow_metadata_inventory_move = allow_inv_move_sync, + -- on_metadata_inventory_move = logistica.sync_on_inv_move, + on_metadata_inventory_put = logistica.sync_on_inv_put, + on_metadata_inventory_take = logistica.sync_on_inv_take, + logistica = {} + } + + minetest.register_node(syncName, def) +end + +logistica.register_synchronizer("Wireless Upgrader", "wireless_synchronizer", { + "logistica_synchronizer_side.png" +}) \ No newline at end of file diff --git a/item/craft_item.lua b/item/craft_item.lua index 80c8056..f474243 100644 --- a/item/craft_item.lua +++ b/item/craft_item.lua @@ -52,6 +52,12 @@ items[L("cobblegen_upgrade")] = { stack_max = 4, } +items[L("wireless_crystal")] = { + description = S("Wireless Crystal\nUsed for upgrading the Wireless Access Pad"), + inventory_image = "logistica_wireless_crystal.png", + stack_max = 99, +} + for name, info in pairs(items) do minetest.register_craftitem(name, { description = info.description, diff --git a/logic/logic.lua b/logic/logic.lua index 9ee63b3..7a1d4f2 100644 --- a/logic/logic.lua +++ b/logic/logic.lua @@ -17,3 +17,4 @@ dofile(path.."/trashcan.lua") dofile(path.."/vaccuum_chest.lua") dofile(path.."/autocrafting_logic.lua") dofile(path.."/crafting_supplier.lua") +dofile(path.."/synchronizer.lua") diff --git a/logic/synchronizer.lua b/logic/synchronizer.lua new file mode 100644 index 0000000..c0cab95 --- /dev/null +++ b/logic/synchronizer.lua @@ -0,0 +1,373 @@ +local S = logistica.TRANSLATOR + +local FORMSPEC_NAME = "logistica_sync" + +local META_LAVA = "loglav" +local META_F1 = "crf1" +local META_S1 = "crs1" +local META_F2 = "crf2" +local META_S2 = "crs2" +local META_TF1 = "tarf1" +local META_TS1 = "tars1" +local META_TF2 = "tarf2" +local META_TS2 = "tars2" + +local MAX_LAVA_AMOUNT = 2000 -- in millibuckets + +local FUP1_BTN = "FUP1" +local FDN1_BTN = "FDN1" +local SR1_BTN = "SR1" +local SL1_BTN = "SL1" +local FUP2_BTN = "FUP2" +local FDN2_BTN = "FDN2" +local SR2_BTN = "SR2" +local SL2_BTN = "SL2" +local APPLY_BUTTON = "APPLY" + +local INV_C1 = "invc1" +local INV_C2 = "invc2" +local INV_UPGRADE = "upgr" + +local THIRD_PI = 3.1415926 / 3 +local FREQ_MIN = 200 +local FREQ_MAX = 2000 +local FREQ_STEP = 50 +local SHIFT_MAX = 3000 +local SHIFT_MIN = -3000 +local SHIFT_STEP = 500 +local DISPLAY_FACTOR = 1000.0 +local GRAPH_XMIN = -35.0 +local GRAPH_XSIZE = 70.0 +local GRAPH_YMIN = -2.2 +local GRAPH_YSIZE = 4.4 +local GRAPH_STEP = 0.1 +-- derived +local SHIFT_NUM_STEPS = (SHIFT_MAX - SHIFT_MIN) / SHIFT_STEP +local FREQ_NUM_STEPS = (FREQ_MAX - FREQ_MIN) / FREQ_STEP +local BOX_SIZE = math.min(GRAPH_STEP / 2, 0.02) +local BOX_W = BOX_SIZE * 2 + +local ITEM_WIRELESS_CRYSTAL = "logistica:wireless_crystal" +local ITEM_WAP = "logistica:wireless_access_pad" + +local WAP_RANGE_INCREASE_STEP = 200 + +local COLOR_TARGET = "#6BA1FFFF" +local COLOR_PTS_NO_MATCH = "#FFB300FF" +local COLOR_PTS_MATCH = "#CCFF00FF" + +local forms = {} +local get_meta = minetest.get_meta + +---------------------------------------------------------------- +-- local funcs +---------------------------------------------------------------- + +-- returns true if there was enough lava (and was used), false if not +-- local function use_lava(meta, amount) +-- local currLevel = meta:get_int(META_LAVA) +-- if currLevel - amount >= 0 then +-- meta:set_int(META_LAVA, currLevel - amount) +-- return true +-- else +-- return false +-- end +-- end + +-- local function get_lava_level(meta) +-- return meta:get_int(META_LAVA) +-- end + +-- -- returns y value for f(x) with given params +local function f(x, freq, shift) + return math.sin( freq * x + shift * THIRD_PI ) +end + +local function both_crystals_present(inv) + return (not inv:is_empty(INV_C1)) and (not inv:is_empty(INV_C2)) +end + +local function change_freq(meta, crysNum, change) + local META_F = META_F1 ; if crysNum ~= 1 then META_F = META_F2 end + local curF = meta:get_int(META_F) + local newF = logistica.clamp(curF + change * FREQ_STEP, FREQ_MIN, FREQ_MAX) + meta:set_int(META_F, newF) +end + +local function change_shift(meta, crysNum, change) + local META_S = META_S1 ; if crysNum ~= 1 then META_S = META_S2 end + local curS = meta:get_int(META_S) + local newS = curS + change * SHIFT_STEP + if newS >= SHIFT_MAX then + meta:set_int(META_S, SHIFT_MIN) + elseif newS <= SHIFT_MIN then + meta:set_int(META_S, SHIFT_MAX) + else + meta:set_int(META_S, newS) + end +end + +local function waves_match(meta) + local freq1 = meta:get_int(META_F1) + local freq2 = meta:get_int(META_F2) + local shft1 = meta:get_int(META_S1) + local shft2 = meta:get_int(META_S2) + local freqT1 = meta:get_int(META_TF1) + local shftT1 = meta:get_int(META_TS1) + local freqT2 = meta:get_int(META_TF2) + local shftT2 = meta:get_int(META_TS2) + return (freq1 == freqT1 and shft1 == shftT1 and freq2 == freqT2 and shft2 == shftT2) or + (freq2 == freqT1 and shft2 == shftT1 and freq1 == freqT2 and shft1 == shftT2) +end + +local function apply_to_wap(pos, info) + local meta = get_meta(pos) + local inv = meta:get_inventory() + if inv:is_empty(INV_UPGRADE) then return end + if not both_crystals_present(inv) then return end + if not waves_match(meta) then return end + + local item = inv:get_stack(INV_UPGRADE, 1) + local itemMeta = item:get_meta() + if item:get_name() == ITEM_WAP then + local currRange = itemMeta:get_int(logistica.tools.wap.meta_range_key) + local newRange = currRange + WAP_RANGE_INCREASE_STEP + -- TODO: check max range against server-configurable range + itemMeta:set_int(logistica.tools.wap.meta_range_key, newRange) + itemMeta:set_string("description", logistica.tools.wap.get_description_with_range(newRange)) + inv:set_stack(INV_UPGRADE, 1, item) + inv:set_stack(INV_C1, 1, ItemStack("")) + inv:set_stack(INV_C2, 1, ItemStack("")) + end + +end + +local function set_new_targets(meta, valid) + local targetF1 = (valid and FREQ_MIN + math.random(0, FREQ_NUM_STEPS) * FREQ_STEP) or 0 + local targetF2 = (valid and FREQ_MIN + math.random(0, FREQ_NUM_STEPS) * FREQ_STEP) or 0 + local targetS1 = (valid and SHIFT_MIN + math.random(0, SHIFT_NUM_STEPS) * SHIFT_STEP) or (SHIFT_MIN - 1) + local targetS2 = (valid and SHIFT_MIN + math.random(0, SHIFT_NUM_STEPS) * SHIFT_STEP) or (SHIFT_MIN - 1) + meta:set_int(META_TF1, targetF1) + meta:set_int(META_TF2, targetF2) + meta:set_int(META_TS1, targetS1) + meta:set_int(META_TS2, targetS2) +end + +local function randomize_curr_values(meta, valid) + if not valid then return 0 end + local cF1 = FREQ_MIN + math.random(0, FREQ_NUM_STEPS) * FREQ_STEP + local cF2 = FREQ_MIN + math.random(0, FREQ_NUM_STEPS) * FREQ_STEP + local cS1 = SHIFT_MIN + math.random(0, SHIFT_NUM_STEPS) * SHIFT_STEP + local cS2 = SHIFT_MIN + math.random(0, SHIFT_NUM_STEPS) * SHIFT_STEP + meta:set_int(META_F1, cF1) + meta:set_int(META_F2, cF2) + meta:set_int(META_S1, cS1) + meta:set_int(META_S2, cS2) +end + +---------------------------------------------------------------- +-- formspec +---------------------------------------------------------------- + +local function box(xy, color) + return string.format( + "box[%s,%s;%s,%s;%s]", + tostring(xy.x - BOX_SIZE), + tostring(xy.y - BOX_SIZE), + tostring(BOX_W), + tostring(BOX_W), + color + ) +end + +-- returns {x=x, y=y} +local function map(x, y, rx, ry, rw, rh) + return { + x = rx + ((x - GRAPH_XMIN) / GRAPH_XSIZE) * rw, + y = ry + ((y - GRAPH_YMIN) / GRAPH_YSIZE) * rh, + } +end + +local function get_point_boxes(x, y, w, h, f1, s1, f2, s2, color) + if not color then return "" end + local pts = {} + for ptX = GRAPH_XMIN, GRAPH_XMIN + GRAPH_XSIZE, GRAPH_STEP do + pts[ptX] = f(ptX, f1, s1) + f(ptX, f2, s2) + end + return table.concat( + logistica.table_to_list_indexed( + pts, + function(ptX, ptY) + return box(map(ptX, ptY, x, y, w, h), color) + end + ) + ) +end + +local function get_formspec_sync(pos, playerName, optMeta) + local posForm = "nodemeta:"..pos.x..","..pos.y..","..pos.z + local meta = optMeta or get_meta(pos) + local tf1 = meta:get_int(META_TF1) / DISPLAY_FACTOR + local ts1 = meta:get_int(META_TS1) / DISPLAY_FACTOR + local tf2 = meta:get_int(META_TF2) / DISPLAY_FACTOR + local ts2 = meta:get_int(META_TS2) / DISPLAY_FACTOR + local f1 = meta:get_int(META_F1) / DISPLAY_FACTOR + local f2 = meta:get_int(META_F2) / DISPLAY_FACTOR + local s1 = meta:get_int(META_S1) / DISPLAY_FACTOR + local s2 = meta:get_int(META_S2) / DISPLAY_FACTOR + local valid = both_crystals_present(meta:get_inventory()) + local matching = waves_match(meta) + local ptsColor = nil + local tarColor = nil + local applyBtn = "" + if valid then + if matching then + ptsColor = COLOR_PTS_MATCH + applyBtn = "button[3.2,6.8;2,0.8;"..APPLY_BUTTON..";"..S("Upgrade").."]" + else + tarColor = COLOR_TARGET + ptsColor = COLOR_PTS_NO_MATCH + end + end + + local targBoxes = get_point_boxes(0.3,2.6,10,4, tf1, ts1, tf2, ts2, tarColor) + local currBoxes = get_point_boxes(0.3,2.6,10,4, f1, s1, f2, s2, ptsColor) + + return "formspec_version[4]".. + "size[10.5,13]".. + logistica.ui.background.. + "list[current_player;main;0.4,8;8,4;0]".. + "list["..posForm..";"..INV_C1..";2.9,1.1;1,1;0]".. + "list["..posForm..";"..INV_C2..";6.6,1.1;1,1;0]".. + "image_button[2.1,0.8;0.8,0.8;logistica_icon_fup.png;"..FUP1_BTN..";]".. + "image_button[2.1,1.6;0.8,0.8;logistica_icon_fdn.png;"..FDN1_BTN..";]".. + "image_button[3.9,0.8;0.8,0.8;logistica_icon_sr.png;"..SR1_BTN..";]".. + "image_button[3.9,1.6;0.8,0.8;logistica_icon_sl.png;"..SL1_BTN..";]".. + "image_button[5.8,0.8;0.8,0.8;logistica_icon_fup.png;"..FUP2_BTN..";]".. + "image_button[5.8,1.6;0.8,0.8;logistica_icon_fdn.png;"..FDN2_BTN..";]".. + "image_button[7.6,0.8;0.8,0.8;logistica_icon_sr.png;"..SR2_BTN..";]".. + "image_button[7.6,1.6;0.8,0.8;logistica_icon_sl.png;"..SL2_BTN..";]".. + "image[4.75,0.6;1,2;logistica_icon_combine.png]".. + "image[0.3,2.6;10,4;logistica_icon_graph_back.png]".. + targBoxes.. + currBoxes.. + applyBtn.. + "list["..posForm..";"..INV_UPGRADE..";5.3,6.7;1,1;0]".. + "label[3.0,0.5;Crystal 1]".. + "label[6.6,0.5;Crystal 2]".. + "label[6.4,7.2;"..S("Wireless Access Pad").."]" +end + +local function show_formspec_sync(playerName, pos, optMeta) + minetest.show_formspec(playerName, FORMSPEC_NAME, get_formspec_sync(pos, optMeta)) +end + +---------------------------------------------------------------- +-- callbacks +---------------------------------------------------------------- + +function logistica.sync_on_player_receive_fields(player, formname, fields) + if not player or not player:is_player() then return false end + if formname ~= FORMSPEC_NAME then return false end + local playerName = player:get_player_name() + if not forms[playerName] then return false end + local info = forms[playerName] + local pos = info.position + if not pos then return false end + if minetest.is_protected(pos, playerName) then return true end + local meta = get_meta(pos) + + if fields.quit then forms[playerName] = nil; return true + elseif fields[FUP1_BTN] then change_freq(meta, 1, 1) + elseif fields[FDN1_BTN] then change_freq(meta, 1, -1) + elseif fields[SR1_BTN] then change_shift(meta, 1, -1) + elseif fields[SL1_BTN] then change_shift(meta, 1, 1) + elseif fields[FUP2_BTN] then change_freq(meta, 2, 1) + elseif fields[FDN2_BTN] then change_freq(meta, 2, -1) + elseif fields[SR2_BTN] then change_shift(meta, 2, -1) + elseif fields[SL2_BTN] then change_shift(meta, 2, 1) + elseif fields[APPLY_BUTTON] then apply_to_wap(pos, info) + end + show_formspec_sync(playerName, pos, meta) + return true +end + +function logistica.sync_on_rightclick(pos, node, clicker, itemstack, pointed_thing) + for k, _ in pairs(forms) do + logistica.show_popup(clicker:get_player_name(), S("Someone is currently using this Upgrader!")) + return + end + forms[clicker:get_player_name()] = { + position = pos + } + show_formspec_sync(clicker:get_player_name(), pos) +end + +function logistica.sync_after_place(pos, placer, itemstack) + local meta = minetest.get_meta(pos) + if placer and placer:is_player() then + meta:set_string("owner", placer:get_player_name()) + end + logistica.set_infotext(pos, S("Wireless Upgrader")) + local inv = meta:get_inventory() + inv:set_size(INV_C1, 1) + inv:set_size(INV_C2, 1) + inv:set_size(INV_UPGRADE, 1) + set_new_targets(meta, false) +end + +function logistica.sync_allow_storage_inv_put(pos, listname, index, stack, player) + local inv = minetest.get_meta(pos):get_inventory() + if listname == INV_C1 or listname == INV_C2 then + if not inv:get_stack(listname, index):is_empty() then return 0 end + if stack:get_name() ~= ITEM_WIRELESS_CRYSTAL then return 0 + else return 1 end + elseif listname == INV_UPGRADE then + if stack:get_name() ~= ITEM_WAP then return 0 + else return 1 end + end + return 0 +end + +function logistica.sync_allow_inv_take(pos, listname, index, stack, player) + return 1 +end + +function logistica.sync_on_inv_put(pos, listname, index, stack, player) + if not player then return end + if listname == INV_C1 or listname == INV_C2 then + if not forms[player:get_player_name()] then return end + local meta = get_meta(pos) + if both_crystals_present(meta:get_inventory()) then + set_new_targets(meta, true) + randomize_curr_values(meta, true) + end + show_formspec_sync(player:get_player_name(), pos) + end +end + +function logistica.sync_on_inv_take(pos, listname, index, stack, player) + if not player then return end + if listname == INV_C1 or listname == INV_C2 then + if not forms[player:get_player_name()] then return end + set_new_targets(get_meta(pos), false) + show_formspec_sync(player:get_player_name(), pos) + end +end + +function logistica.sync_can_dig(pos) + local inv = get_meta(pos):get_inventory() + return inv:is_empty(INV_C1) and inv:is_empty(INV_C2) and inv:is_empty(INV_UPGRADE) +end + +---------------------------------------------------------------- +-- Minetest registration +---------------------------------------------------------------- + +minetest.register_on_player_receive_fields(logistica.sync_on_player_receive_fields) + +minetest.register_on_leaveplayer(function(objRef, _) + if objRef:is_player() then + forms[objRef:get_player_name()] = nil + end +end) diff --git a/registration/lava_furnace_recipes.lua b/registration/lava_furnace_recipes.lua index e62c09f..3784180 100644 --- a/registration/lava_furnace_recipes.lua +++ b/registration/lava_furnace_recipes.lua @@ -31,3 +31,11 @@ logistica.register_lava_furnace_recipe("default:glass", { additive_use_chance = 100, time = 4 }) + +logistica.register_lava_furnace_recipe(L("silverin"), { + output = L("wireless_crystal"), + lava = 120, + additive = "default:mese_crystal", + additive_use_chance = 100, + time = 6 +}) diff --git a/textures/logistica_icon_combine.png b/textures/logistica_icon_combine.png new file mode 100644 index 0000000000000000000000000000000000000000..234aba5d4554ae493eb9ec3b2881cc828fce6fd3 GIT binary patch literal 1986 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 VLE_V{0PTP10KZ`BF`u}f{sL;ipxFQb literal 0 HcmV?d00001 diff --git a/textures/logistica_icon_fup.png b/textures/logistica_icon_fup.png new file mode 100644 index 0000000000000000000000000000000000000000..9536dfe2ed48d5b2f2daf36824cec7070d02c07a GIT binary patch literal 3464 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 MPVSCp*9Ry5A6avMo&W#< literal 0 HcmV?d00001 diff --git a/textures/logistica_icon_graph_back.png b/textures/logistica_icon_graph_back.png new file mode 100644 index 0000000000000000000000000000000000000000..de30a4d964e2200471e259b9f7fa1a884a139933 GIT binary patch literal 1869 zcmaJ?eQXnD7(XNoH=Upm8B^KiPyw}fZCM9Bdt3T3w#IgA%f`Ag2glvp_U!fUyt}UL zLJR^EU4RH7QTYfP4n$@M5cmUujLaw@ME<~y7>pqi6a$2hNl+jI-@9(z$_9U2d(ZoO zp5ODl&)0qCtEnn1C@w${q|jaM3c&xV+VgB4{4M$C5se^|++1By4SGE!BgS!>6;}W} z6_+56AhwE>L^CmbrF~-Vb73BrC z6bT99K&8=CoEH=_MPWKG3E#D20z-8bHAZ1UuMc&KGC(c31vg^l1*lDC!(_mRN?K6qltEBtYZMs{7f=|PlT=O=P>qvb zAtqD`!yt|hk+V#eTS~~h&df3d;6WS+szR7>6ERvJq_~l21Z5~)FmZ&2Ky_n|y2^#0 zpspcf)TOAXib_~?HZM4jh z?l>I*4IHaRvs@$R9w0;>2eioegw_i60tfG69^nd(U=+5%OpJ2VI4+c}1F1*n6ho(lW3|U^`KmJsjp>@zSVIn-dhyrV-EdQPv_E z1(0prOcJASF&veR7!!Bq(xY|<03*!#|PY?;*Gk=JOrKTy?iH&vU@6^7bY z@7=x3IXLM^-p_X5bo=4+O?`m@l9>3$K+}z*J0G05^6Cw0#&?_6Z|;9358ZYq*4c59 zDtTX8P=4uTTU*U_Q)NS>ciM|rFPz%pYXAMtS4Xa%8jNlHbM5|ByWJ^||H$!MUAOLp znl0(2zwYj0&h&oM+w8^Xu8dv1UC{I8_IxvSN2zU%M&nKRqNj~?BvuYKX(Tm3seI#4n5r=?R$1^ZF9Xy>|5{N>O5 zHoNKazI)x1jRSb=xBJp}I};Pn;@zQ@lg}>byky_N9V|Kh<}X`4z1Q*&yE+Gpx6XK* zdO%(IZx(iKpuXY2vB@W1y7>K_!ddqtKQ>?Jz6n2L`BHVGf*{XL)1C>)*6l^`@HMx~ wUbk=q!!BRGH`0L^{kzhAtNare6&88m$FT}Sb{cB;hPAJtyRyc$-?4o4|2=1Vo&W#< literal 0 HcmV?d00001 diff --git a/textures/logistica_icon_sl.png b/textures/logistica_icon_sl.png new file mode 100644 index 0000000000000000000000000000000000000000..24713f50364a39d20091d28184793cfe25f7636d GIT binary patch literal 3259 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 S+H%={4j)f{k4m@b^#23Jpc|h6 literal 0 HcmV?d00001 diff --git a/textures/logistica_icon_sr.png b/textures/logistica_icon_sr.png new file mode 100644 index 0000000000000000000000000000000000000000..3360e42f6174d99d679b10d9b95b903d7ea56ea9 GIT binary patch literal 3245 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 Kf9eTxRO;V(USECy literal 0 HcmV?d00001 diff --git a/tools/tools.lua b/tools/tools.lua index 2225944..02ca203 100644 --- a/tools/tools.lua +++ b/tools/tools.lua @@ -1,4 +1,6 @@ local path = logistica.MODPATH.."/tools" +logistica.tools = {} + dofile(path.."/misc.lua") dofile(path.."/wireless_access_pad.lua") diff --git a/tools/wireless_access_pad.lua b/tools/wireless_access_pad.lua index 2d79f9f..c4cfa0f 100644 --- a/tools/wireless_access_pad.lua +++ b/tools/wireless_access_pad.lua @@ -1,7 +1,18 @@ local S = logistica.TRANSLATOR local META_ACCESS_POINT_POSITION = "logacptps" -local WAP_MAX_DIST_DEF = 200 -- in nodes +local META_RANGE = "log_range" + +local STR_INIT_TIP = S("Put in a Wireless Upgrader to initialize") + +logistica.tools.wap = { + meta_range_key = META_RANGE, + description_default = S("Wireless Access Pad").."\n"..STR_INIT_TIP, + get_description_with_range = function (range) + return S("Wireless Access Pad\nSneak+Punch an Access Point to Sync\nMax range: @1", range) + end +} + -- we need this because default tostring(number) function returns scientific representation which loses accuracy local str = function(anInt) return string.format("%.0f", anInt) end @@ -14,12 +25,22 @@ local function on_wireless_pad_primary(itemstack, user, pointed_thing) if minetest.get_item_group(node.name, logistica.TIER_ACCESS_POINT) <= 0 then return end local playerName = user:get_player_name() + local itemMeta = itemstack:get_meta() + local range = itemMeta:get_int(META_RANGE) + + if range <= 0 then + logistica.show_popup( + playerName, + S("This Wireless Access Pad is not initialized").."\n"..STR_INIT_TIP + ) + return + end + if minetest.is_protected(pos, playerName) then logistica.show_popup(playerName, S("This Access Point is in a protected area!")) return end - local itemMeta = itemstack:get_meta() local posHashStr = str(minetest.hash_node_position(pos)) itemMeta:set_string(META_ACCESS_POINT_POSITION, posHashStr) @@ -35,12 +56,28 @@ local function on_wireless_pad_secondary(itemstack, placer, pointed_thing) local itemMeta = itemstack:get_meta() local posHashStr = itemMeta:get_string(META_ACCESS_POINT_POSITION) + local range = itemMeta:get_int(META_RANGE) + if range <= 0 then + logistica.show_popup( + playerName, + S("This Wireless Access Pad is not initialized").."\n"..STR_INIT_TIP + ) + return + end + if posHashStr == "" then logistica.show_popup(playerName, S("This WAP is not synced to any Access Point.")) return end local targetPos = minetest.get_position_from_hash(tonumber(posHashStr)) + + local dist = vector.length(vector.subtract(placer:get_pos(), targetPos)) + if not dist or dist > range then + logistica.show_popup(playerName, S("The synced Access Point is too far away!")) + return + end + logistica.load_position(targetPos) logistica.try_to_wake_up_network(targetPos) @@ -71,10 +108,11 @@ end -- end) minetest.register_craftitem("logistica:wireless_access_pad",{ - description = S("Wireless Access Pad\nSneak+Punch an Access Point to Sync"), + description = logistica.tools.wap.description_default, inventory_image = "logistica_wap.png", wield_image = "logistica_wap.png", stack_max = 1, on_use = on_wireless_pad_primary, on_secondary_use = on_wireless_pad_secondary, + on_place = on_wireless_pad_secondary, }) diff --git a/util/common.lua b/util/common.lua index 5c68ae3..b97f3ae 100644 --- a/util/common.lua +++ b/util/common.lua @@ -202,3 +202,10 @@ function logistica.table_map(table, func) for k,v in pairs(table) do t[k] = func(v) end return t end + +function logistica.table_to_list_indexed(table, func) + local t = {} + local index = 0 + for k,v in pairs(table) do index = index + 1; t[index] = func(k, v) end + return t +end