diff --git a/fish_bot/LICENSE b/fish_bot/LICENSE new file mode 100644 index 0000000..fdddb29 --- /dev/null +++ b/fish_bot/LICENSE @@ -0,0 +1,24 @@ +This is free and unencumbered software released into the public domain. + +Anyone is free to copy, modify, publish, use, compile, sell, or +distribute this software, either in source code form or as a compiled +binary, for any purpose, commercial or non-commercial, and by any +means. + +In jurisdictions that recognize copyright laws, the author or authors +of this software dedicate any and all copyright interest in the +software to the public domain. We make this dedication for the benefit +of the public at large and to the detriment of our heirs and +successors. We intend this dedication to be an overt act of +relinquishment in perpetuity of all present and future rights to this +software under copyright law. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR +OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. + +For more information, please refer to diff --git a/fish_bot/README.md b/fish_bot/README.md new file mode 100644 index 0000000..fc12136 --- /dev/null +++ b/fish_bot/README.md @@ -0,0 +1,2 @@ +# fishbot +Basic mineclone fishing bot for dragonfire. Put a fishing rod in the hotbar, aim at water and activate FishBot. diff --git a/fish_bot/init.lua b/fish_bot/init.lua new file mode 100644 index 0000000..95d8f92 --- /dev/null +++ b/fish_bot/init.lua @@ -0,0 +1,45 @@ +local function get_bobber_pos() + local obs=minetest.get_objects_inside_radius(minetest.localplayer:get_pos(),10) + for k,v in ipairs(obs) do + local txt=v:get_item_textures() + if txt:find("bobber") then + return v:get_pos() + end + end + return false +end + +local fb_state=0 +local fb_obpos=vector.new() + +minetest.register_globalstep(function() + if not minetest.settings:get_bool("fishbot") then return end + if not minetest.localplayer then return end + if not minetest.switch_to_item('mcl_fishing:fishing_rod_enchanted') then + minetest.switch_to_item('mcl_fishing:fishing_rod') + end + local bpos=get_bobber_pos() + if not bpos then fb_state=0 end + if fb_state == 0 then --init + minetest.interact("activate",{type="nothing"}) + fb_state=1 + elseif fb_state == 1 then --waiting for bobber to settle + if vector.distance(bpos,fb_obpos) == 0 then + fb_state=2 + end + elseif fb_state == 2 then --waiting for bobber to move + if vector.distance(bpos,fb_obpos) > 0 then + minetest.after('0.1',function() + minetest.interact("activate",{type="nothing"}) + end) + fb_state=3 + end + elseif fb_state == 3 then --waiting til bobber is gone + if not bpos then fb_state=0 end + else + fb_state=0 + end + if bpos then fb_obpos=bpos end +end) + +minetest.register_cheat('FishBot','Bots','fishbot') diff --git a/frenemies/init.lua b/frenemies/init.lua new file mode 100644 index 0000000..e69de29 diff --git a/frenemies/mod.conf b/frenemies/mod.conf new file mode 100644 index 0000000..e69de29 diff --git a/goddessmode/init.lua b/goddessmode/init.lua new file mode 100644 index 0000000..ca2b856 --- /dev/null +++ b/goddessmode/init.lua @@ -0,0 +1,162 @@ +-- +-- cora's defensive combat hax + + + +local karange=14 +local tping=false +local dodged=false + +local function checkair(pos) + local n=minetest.get_node_or_nil(pos) + if n==nil or n['name'] == 'air' then return true end + return false +end + +local function checkbadblocks(pos) + local n=minetest.find_node_near(pos, 2, {'mcl_core:gravel','mcl_core:sand','mcl_core:lava_source','mcl_core:lava_flowing','mcl_core:water_source','mcl_core:water_flowing', + 'mcl_core:obsidian','mcl_core:bedrock'}, true) + if n == nil then return false end + return true +end +local function checktrap() + local lp=minetest.localplayer:get_pos() + local air,nd=minetest.line_of_sight(vector.add(lp,{x=0,y=-2,z=0}), vector.add(lp,{x=0,y=50,z=0})) + if(not air) then + local tn=minetest.get_node_or_nil(nd) + if(tn == nil) then return false end + for k,v in ipairs({'mcl_core:lava_source','mcl_core:lava_flowing','mcl_core:water_source','mcl_core:water_flowing'}) do + if tn.name == v then return true end + end + end + return false +end + +local function checkhead() + local ppos=vector.add(minetest.localplayer:get_pos(),{x=0,y=1,z=0}) + if (checkair(ppos)) then return true end + return false +end + + +local function checkprojectile() + for k, v in ipairs(minetest.localplayer.get_nearby_objects(karange)) do + if ( v:get_item_textures():sub(-9) == "arrow_box") or ( v:get_item_textures():sub(-7) == "_splash") or v:get_item_textures():sub(-17) == "shulkerbullet.png" then + local lp=minetest.localplayer:get_pos() + local vel=v:get_velocity() + local dst=vector.distance(lp,v:get_pos()) + if dst > 4 then return false end + if (vel.x == 0 and vel.y == 0 and vel.z ==0 ) then return false end + return true + end + end + return false +end + + +local function amautotool(pos) + local node=minetest.get_node_or_nil(pos) + minetest.select_best_tool(node.name) +end + +local function get_2dpos_from_yaw(r,yaw) + local tg={x=0,y=0,z=0} + tg.x= r * math.sin(yaw) + tg.z= r * math.cos(yaw) + return tg +end +local function get_3dpos_from_yaw_and_pitch(r,yaw,pitch) + local tg={x=0,y=0,z=0} + tg.x= r * math.sin(yaw) + tg.y= r * math.sin(pitch) + tg.z= r * math.cos(yaw) + return tg +end +local function dhfree() + if not minetest.localplayer then return end + local n=vector.add(minetest.localplayer:get_pos(),{x=0,y=2,z=0}) + local nd=minetest.get_node_or_nil(n) + if nd == nil then return end + while nd.name ~= "air" do + amautotool(n) + minetest.dig_node(n) + minetest.dig_node(vector.add(n,{x=0,y=-1,z=0})) + nd=minetest.get_node_or_nil(n) + end + tping=false +end +local lastwrp=0 +local function mwarp(pos) + if tping then return end + --if os.time() < lastwrp+1 then return end + --lastwrp=os.time(); + tping=true + minetest.after("0.1",function() dhfree() end) + minetest.localplayer:set_pos(pos) +end + +local function get_target(epos) + math.randomseed(os.time()) + local t=vector.add(epos,get_3dpos_from_yaw_and_pitch(karange+1,math.random(90,240),math.random(90,135))) + if (checkbadblocks(t)) then + return get_target(epos) + elseif checkair(t) then + return t + else + amautotool(t) + end + return t +end + + +local function evade(ppos) + mwarp(get_target(ppos)) +end +local function dodge() + if dodged then return end + dodged=true + local t=turtle.dircoord(math.random(0,2)-1,0,math.random(0,2)-1) + local opos=minetest.localplayer:get_pos() + mwarp(t) + minetest.after("0.5",function() mwarp(opos) dodged=false end ) +end + +local function rro() -- reverse restraining order + + for k, v in ipairs(minetest.localplayer.get_nearby_objects(karange+5)) do + local name=v:get_name() + if (v:is_player() and name ~= minetest.localplayer:get_name()) then + if fren.is_friend(name) then + return end + local pos = v:get_pos() + pos.y = pos.y - 1 + local mpos=minetest.localplayer:get_pos() + local distance=vector.distance(mpos,pos) + if distance < karange then + local trg=get_target(pos) + mwarp(trg) + minetest.after("0.2",function() autofly.aim(pos) end) + return + end + end + end +end + +minetest.register_globalstep(function() + if minetest.settings:get_bool("goddess") then + local ppos=minetest.localplayer:get_pos() + --rro() + if(checkprojectile()) then dodge(ppos) end + --if(checktrap()) then evade(ppos) end + if(not checkhead()) then dhfree() end + end +end) +minetest.register_chatcommand("dhf", { description = "", func = dhfree }) + + +-- REG cheats on DF +if (_G["minetest"]["register_cheat"] ~= nil) then + minetest.register_cheat("Goddess Mode", "Combat", "goddess") +else + minetest.settings:set_bool('goddess',true) +end diff --git a/goddessmode/mod.conf b/goddessmode/mod.conf new file mode 100644 index 0000000..5b23e56 --- /dev/null +++ b/goddessmode/mod.conf @@ -0,0 +1,3 @@ +name = goddessmode +author = cora +description = defensive combat hax diff --git a/goddessmode/settingtypes.txt b/goddessmode/settingtypes.txt new file mode 100644 index 0000000..898ccd5 --- /dev/null +++ b/goddessmode/settingtypes.txt @@ -0,0 +1 @@ +goddess (Goddess Mode) bool true diff --git a/hignore/init.lua b/hignore/init.lua new file mode 100644 index 0000000..e69de29 diff --git a/hignore/mod.conf b/hignore/mod.conf new file mode 100644 index 0000000..e69de29 diff --git a/hignore/settingtypes.txt b/hignore/settingtypes.txt new file mode 100644 index 0000000..e69de29 diff --git a/hpchange/init.lua b/hpchange/init.lua new file mode 100644 index 0000000..e69de29 diff --git a/hpchange/mod.conf b/hpchange/mod.conf new file mode 100644 index 0000000..e69de29 diff --git a/incrementaltp/init.lua b/incrementaltp/init.lua new file mode 100644 index 0000000..9ae6cc7 --- /dev/null +++ b/incrementaltp/init.lua @@ -0,0 +1,127 @@ +-- CC0/Unlicense Emilia 2021 + +incremental_tp = {} + +incremental_tp.fudge = 0.8 -- cause the tp time isn't synced with the server +incremental_tp.tpactive=false + +-- for Clamity +incremental_tp.max_instantaneous_tp = { + x = 5, + y = 45, + z = 5 +} + +function incremental_tp.get_actual_speed() + local po=minetest.localplayer:get_physics_override() + local rt=vector.new() + rt.x=incremental_tp.max_instantaneous_tp.x * po.speed + rt.y=incremental_tp.max_instantaneous_tp.y * po.speed + rt.z=incremental_tp.max_instantaneous_tp.z * po.speed + return rt +end +local wason=false +local function sign(n) + if n == 0 then + return 0 + end + + return n / math.abs(n) +end + +local function max_dist_per(vec, time) + local mitp = vector.multiply(incremental_tp.get_actual_speed(), + incremental_tp.fudge) + local nvec = {x = 0, y = 0, z = 0} + nvec.x = sign(vec.x) * math.min(math.abs(vec.x), mitp.x * time) + nvec.z = sign(vec.z) * math.min(math.abs(vec.z), mitp.z * time) + -- negative y speed cap is infinity, so if y < 0 it is always allowed + nvec.y = math.min(vec.y, mitp.y * time) + return nvec +end + +local function tpstep(target, time, second, variance,sfunc) + local pos = minetest.localplayer:get_pos() + local vec = vector.subtract(target, pos) + minetest.settings:set_bool("free_move",true) + if not incremental_tp.tpactive and wason then + wason=false + return + end + wason=true + incremental_tp.tpactive=true + if vector.distance(pos,target) < 3 then + minetest.localplayer:set_pos(target) + incremental_tp.tpactive=false + --minetest.display_chat_message("Arrived at " .. minetest.pos_to_string(target)) + if sfunc then + minetest.after(time, function() + sfunc(target) + end) + end + return + end + + if second < 0.001 then + second = 1 + end + + local intime = math.min(time, second) + if variance then + -- you can't move faster than 1 second of distance instantaneously + intime = math.min(1, math.random() * variance - variance / 2 + intime) + end + + local nvec = max_dist_per(vec, intime) + local trg=vector.add(pos, nvec) + --local safe=ws.find_closest_reachable_airpocket(trg) + minetest.localplayer:set_pos(trg) + minetest.after(intime, function() + tpstep(target, time, second - intime, variance,sfunc) + end) +end + +function incremental_tp.tpstep(target, time, variance,sfunc) + if incremental_tp.tpactive then return end + tpstep(target, time, 1, variance,sfunc) +end + +function incremental_tp.tp(target, time, variance) + incremental_tp.tpactive=false + minetest.after(time,function() + tpstep(target,time,1,variance) + end) +end + +function incremental_tp.tpafter(target,time,variance,sfunc) + incremental_tp.tpactive=false + minetest.after(time,function() + tpstep(target,time,1,variance,sfunc) + end) +end + +if autofly then autofly.register_transport('itp',function(pos,name) incremental_tp.tp(pos,1) end) end + +if autofly then autofly.register_transport('jitp',function(pos,name) incremental_tp.tp(pos,0.5,0.4) end) end + +minetest.register_chatcommand("itp", { + description = "Teleport to destination with fixed increments.", + params = "", + func = function(params) + local pos = minetest.string_to_pos(params) + + incremental_tp.tp(pos, 1) + end +}) + +minetest.register_chatcommand("jittertp", { + description = "Teleport to destination with jittery increments.", + params = "", + func = function(params) + local pos = minetest.string_to_pos(params) + + incremental_tp.tp(pos, 0.5, 0.4) + end +}) + +-- chunk_rand diff --git a/invrefill/init.lua b/invrefill/init.lua new file mode 100644 index 0000000..e69de29 diff --git a/invutil/LICENSE b/invutil/LICENSE new file mode 100644 index 0000000..e69de29 diff --git a/invutil/README b/invutil/README new file mode 100644 index 0000000..e69de29 diff --git a/invutil/init.lua b/invutil/init.lua new file mode 100644 index 0000000..e69de29 diff --git a/invutil/mod.conf b/invutil/mod.conf new file mode 100644 index 0000000..e69de29 diff --git a/invutil/settingtypes.txt b/invutil/settingtypes.txt new file mode 100644 index 0000000..e69de29 diff --git a/itemcount/init.lua b/itemcount/init.lua new file mode 100644 index 0000000..8b2d187 --- /dev/null +++ b/itemcount/init.lua @@ -0,0 +1,80 @@ +local last_count +local last_item + +local icon_widget +local count_widget + +local epoch = 0 + +local function display_widgets() + if minetest.localplayer ~= nil then + if icon_widget == nil then + icon_widget = minetest.localplayer:hud_add({ + hud_elem_type = "image", + name = "Item count icon", + scale = {x = 1, y = 1}, + alignment = {x = 0.5, y = 1}, + position = {x = 0.85, y = 0.5} + }) + end + if count_widget == nil then + count_widget = minetest.localplayer:hud_add({ + hud_elem_tyoe = "text", + name = "Item count", + scale = {x = 1, y = 1}, + alignment = {x = 0.5, y = 0}, + position = {x = 0.85, y = 0.5}, + text = "0", + number = 0xFFFFFF + }) + end + end +end + +local function update_count() + if minetest.localplayer ~= nil then + display_widgets() + + local wielded = minetest.localplayer:get_wielded_item() + + local texture = "" --wielded:get_definition().inventory_image + + local wear = wielded:get_wear() + local count = 0 + + local num = "" + + if wear == 0 then + for k, v in ipairs(minetest.get_inventory("current_player").main) do + if v:get_name() == wielded:get_name() then + count = count + v:get_count() + end + end + num = tostring(count) + else + num = tostring(((65535 - wear) / 65535) * 100) .. "%" + end + + last_count = count + last_item = wielded.name + + + minetest.localplayer:hud_change(icon_widget, "text", texture) + minetest.localplayer:hud_change(count_widget, "text", num) + end +end + +minetest.register_on_placenode(function(item, pointed_thing) + update_count() +end) + +minetest.register_on_item_use(function(item, pointed_thing) + update_count() +end) + +minetest.register_globalstep(function() + if os.time() > epoch then + update_count() + epoch = os.time() + end +end) diff --git a/itemcount/mod.conf b/itemcount/mod.conf new file mode 100644 index 0000000..8ffa089 --- /dev/null +++ b/itemcount/mod.conf @@ -0,0 +1,2 @@ +name = itemcount +description = Display currently held item's total count. diff --git a/kamikaze/init.lua b/kamikaze/init.lua new file mode 100644 index 0000000..4f73810 --- /dev/null +++ b/kamikaze/init.lua @@ -0,0 +1,192 @@ +kamikaze={} +kamikaze.active=false +local fnd=false +local cpos=vector.new(0,0,0) +local hud_wp=nil +local zz=vector.new(42,42,42) +local badnodes={'mcl_tnt:tnt','mcl_fire:basic_flame','mcl_fire:fire','mcl_banners:hanging_banner','mcl_banners:standing_banner','mcl_fire:fire_charge','mcl_sponges:sponge','mcl_sponges:sponge_wet','mcl_nether:soul_sand','mcl_heads:wither_skeleton'} +local badobs={'mcl_end_crystal','arrow_box','mobs_mc_wither.png'} +local searchtxt=nil +local searchheight=64 +local tob=nil + +local function set_kwp(name,pos) + if hud_wp then + minetest.localplayer:hud_change(hud_wp, 'world_pos', pos) + minetest.localplayer:hud_change(hud_wp, 'name', name) + else + hud_wp = minetest.localplayer:hud_add({ + hud_elem_type = 'waypoint', + name = name, + text = 'm', + number = 0x00ff00, + world_pos = pos + }) + end +end +local nextzz=0 +local function randomzz() + if nextzz > os.clock() then return false end + math.randomseed(os.time()) + zz.x=math.random(-128,129) + zz.y=math.random(0,searchheight) + zz.z=math.random(-128,128) + nextzz=os.clock()+ 30 +end +local function find_ob(txts) + local odst=500 + local rt=nil + local obs=minetest.localplayer.get_nearby_objects(500) + for k, v in ipairs(obs) do + for kk,txt in pairs(txts) do + if ( v:get_item_textures():find(txt) ) then + local npos=v:get_pos() + local dst=vector.distance(npos,minetest.localplayer:get_pos()) + if odst > dst then + searchtxt=v:get_item_textures() + cpos=npos + set_kwp(searchtxt,v:get_pos()) + odst=dst + fnd=true + tob=v + rt=v + end + end + end + end + return rt +end + +local function find_nd(names) + local lp=minetest.localplayer:get_pos() + local epos=minetest.find_nodes_near(lp,60,names,true) + local rt=nil + local odst=500 + if epos then + for k,v in pairs(epos) do + local node=minetest.get_node_or_nil(v) + local lp=minetest.localplayer:get_pos() + local dst=vector.distance(lp,v) + if odst > dst then + odst=dst + cpos=vv + rt=vv + fnd=true + end + end + end + return rt +end + + +local function find_bad_things() + if fnd then return true end + local lp=minetest.localplayer:get_pos() + local ob=find_ob(badobs) + if not ob then ob=find_nd(badnodes) end + if not ob then + set_kwp('nothing found',zz) + randomzz() + fnd=false + return false + end + return true +end + + + +local function flythere() + if not minetest.localplayer then return end + if not cpos then return end + ws.aim(cpos) + minetest.settings:set_bool("killaura",false) + if incremental_tp.tpactive then return end + local lp=minetest.localplayer:get_pos() + local dst=vector.distance(lp,cpos) + if tob and tob:get_item_textures() == searchtxt then + dst=vector.distance(lp,tob:get_pos()) + cpos=tob:get_pos() + set_kwp(searchtxt,cpos) + end + minetest.settings:set_bool("continuous_forward",true) +end + + +local function stopflight() + local lp = minetest.localplayer:get_pos() + local dst=vector.distance(lp,cpos) + minetest.settings:set_bool("continuous_forward",false) + if tob and tob:get_item_textures():find(searchtxt) then + if searchtxt == 'mcl_end_crystal.png' then + minetest.dig_node(cpos) + tob:punch() + minetest.interact('start_digging') + searchtxt="" + tob=nil + else + minetest.settings:set_bool("killaura",true) + end + end + fnd=false + tob=nil +end + + + +ws.rg('Kamikaze','Bots','kamikaze', function() + local lp = minetest.localplayer:get_pos() + local dst=vector.distance(lp,cpos) + + if not find_bad_things() then + if vector.distance(lp,zz) < 1 then + stopflight() + else + cpos=zz + flythere() + end + elseif dst < 1 then + stopflight() + else + flythere() + end + + -- ws.dignodes(minetest.find_nodes_near(minetest.localplayer:get_pos(),5,badnodes,true)) + if cpos then + minetest.dig_node(cpos) + --minetest.interact('start_digging') + + end + +end,function() + core.set_keypress("special1", true) + kamikaze.active=true +end, function() + kamikaze.active=false + core.set_keypress("special1", false) + fnd=false + if hud_wp then + minetest.localplayer:hud_remove(hud_wp) + hud_wp=nil + end +end,{"noclip","pitch_move","dighead","digbadnodes"}) + + + +minetest.register_on_death(function() + if not minetest.settings:get_bool("kamikaze") then return end + tob=nil +-- incremental_tp.tpactive=false + minetest.after("5.0",function() + fnd=false + end) +end) + +ws.on_connect(function() +minetest.settings:set_bool("kamikaze",false) + --if minetest.localplayer and minetest.localplayer:get_name():find("kamikaze") then + -- minetest.settings:set_bool("kamikaze",true) + --else minetest.settings:set_bool("kamikaze",false) + --end +end) + +minetest.register_cheat('KamiCrystals','Bots','kamikaze_crystals') diff --git a/muse/init.lua b/muse/init.lua new file mode 100644 index 0000000..482848f --- /dev/null +++ b/muse/init.lua @@ -0,0 +1,146 @@ +-- CC0/Unlicense Emilia 2020 + +-- TODO: support noteblocks on different blocktypes +-- denoted with S/W/O/etc. then the tone in a string? +-- TODO: polyphony? +-- TODO: multiple mod support + +muse = {} +muse.playing = nil + +-- assumes tone in param2 +muse.noteblocks = { + "mesecons_noteblock:noteblock" +} + +muse.tracks = { + ["test"] = { + name = "example", + interval = 0.25, -- in seconds + notes = { + 10, + 10 + } + } +} + +local toneblocks = {} + +local tones = {} + +function muse.find_toneblocks(radius) + radius = radius or 20 + + local pos = vector.round(minetest.localplayer:get_pos()) + local nodes = minetest.find_nodes_near(pos, radius, muse.noteblocks) + + for i, v in ipairs(nodes) do + local p2 = minetest.get_node_or_nil(v).param2 + local i = tostring(p2) + + if not toneblocks[i] then + toneblocks[i] = {} + end + + toneblocks[i][#toneblocks[i] + 1] = v + end +end + +function muse.play(note) + if not note then + return + end + + if note == -1 then + return + end + + if type(note) == "string" then + note = tones[note] + end + + local poses = toneblocks[tostring(note)] + if poses then + local pos = poses[1] + + minetest.localplayer:set_pos(pos) + minetest.interact("start_digging", pos) + minetest.interact("stop_digging", pos) + end +end + +function muse.play_track(track) + muse.playing = track + muse.playing.idx = 1 + muse.playing.last = 0 +end + +minetest.register_globalstep(function() + local now = os.clock() + if muse.playing and muse.playing.last + muse.playing.interval <= now then + muse.playing.last = now + local note = muse.playing.notes[muse.playing.idx] + if note then + muse.play(note) + muse.playing.idx = muse.playing.idx + 1 + else + muse.playing = nil + end + end +end) + +local function timestring(seconds) + seconds = math.floor(seconds + 0.5) + local mins = math.floor(seconds / 60) + local secs = seconds % 60 + return string.format("%02d:%02d", mins, secs) +end + +function muse.get_playing_string() + if muse.playing then + return string.format("Currently playing: %s, %d/%d (%s/%s)", + muse.playing.name, + muse.playing.idx, + #muse.playing.notes, + timestring(muse.playing.idx * muse.playing.interval), + timestring(#muse.playing.notes * muse.playing.interval)) + else + return "Nothing is playing" + end +end + +minetest.register_chatcommand("findtones", { + description = "Find tone blocks in the vicinity.", + func = function() + toneblocks = {} + muse.find_toneblocks() + + local len = 0 + for k, v in pairs(toneblocks) do + len = len + 1 + end + + minetest.display_chat_message("Found " .. tostring(len) .. " out of 25 tones") + end +}) + +minetest.register_chatcommand("play", { + description = "Play a musical track", + params = "", + func = function(params) + local track = muse.tracks[params] + if track then + muse.play_track(track) + minetest.display_chat_message("Now playing " .. muse.playing.name) + else + minetest.display_chat_message("Track not found.") + end + end +}) + +minetest.register_chatcommand("playing", { + description = "Show currently playing track and its progress", + func = function() + minetest.display_chat_message(muse.get_playing_string()) + end +}) diff --git a/nlist/forms.lua b/nlist/forms.lua new file mode 100644 index 0000000..8018918 --- /dev/null +++ b/nlist/forms.lua @@ -0,0 +1,981 @@ + +ws = {} +ws.registered_globalhacks = {} +ws.displayed_wps={} + +ws.c = core + +ws.range=4 +ws.target=nil +ws.targetpos=nil + +local nextact = {} +local ghwason={} + +local nodes_this_tick=0 + +function ws.s(name,value) + if value == nil then + return ws.c.settings:get(name) + else + ws.c.settings:set(name,value) + return ws.c.settings:get(name) + end +end +function ws.sb(name,value) + if value == nil then + return ws.c.settings:get_bool(name) + else + ws.c.settings:set_bool(name,value) + return ws.c.settings:get_bool(name) + end +end + +function ws.dcm(msg) + return minetest.display_chat_message(msg) +end +function ws.set_bool_bulk(settings,value) + if type(settings) ~= 'table' then return false end + for k,v in pairs(settings) do + minetest.settings:set_bool(v,value) + end + return true +end + +function ws.shuffle(tbl) + for i = #tbl, 2, -1 do + local j = math.random(i) + tbl[i], tbl[j] = tbl[j], tbl[i] + end + return tbl +end + +function ws.in_list(val, list) + if type(list) ~= "table" then return false end + for i, v in pairs(list) do + if v == val then + return true + end + end + return false +end + +function ws.random_table_element(tbl) + local ks = {} + for k in pairs(tbl) do + table.insert(ks, k) + end + return tbl[ks[math.random(#ks)]] +end + +function ws.center() + --local lp=ws.dircoord(0,0,0) + --minetest.localplayer:set_pos(lp) +end + +function ws.globalhacktemplate(setting,func,funcstart,funcstop,daughters,delay) + funcstart = funcstart or function() end + funcstop = funcstop or function() end + delay = delay or 0.5 + return function() + if not minetest.localplayer then return end + if minetest.settings:get_bool(setting) then + if tps_client and tps_client.ping and tps_client.ping > 1000 then return end + nodes_this_tick = 0 + if nextact[setting] and nextact[setting] > os.clock() then return end + nextact[setting] = os.clock() + delay + if not ghwason[setting] then + if not funcstart() then + ws.set_bool_bulk(daughters,true) + ghwason[setting] = true + --ws.dcm(setting.. " activated") + ws.center() + minetest.settings:set('last-dir',ws.getdir()) + minetest.settings:set('last-y',ws.dircoord(0,0,0).y) + else minetest.settings:set_bool(setting,false) + end + else + func() + end + + elseif ghwason[setting] then + ghwason[setting] = false + ws.set_bool_bulk(daughters,false) + funcstop() + --ws.dcm(setting.. " deactivated") + end + end +end + +function ws.register_globalhack(func) + table.insert(ws.registered_globalhacks,func) +end + +function ws.register_globalhacktemplate(name,category,setting,func,funcstart,funcstop,daughters) + ws.register_globalhack(ws.globalhacktemplate(setting,func,funcstart,funcstop,daughters)) + minetest.register_cheat(name,category,setting) +end + +ws.rg=ws.register_globalhacktemplate + +function ws.step_globalhacks(dtime) + for i, v in ipairs(ws.registered_globalhacks) do + v(dtime) + end +end + +minetest.register_globalstep(function(dtime) ws.step_globalhacks(dtime) end) +minetest.settings:set_bool('continuous_forward',false) + +function ws.on_connect(func) + if not minetest.localplayer then minetest.after(0,function() ws.on_connect(func) end) return end + if func then func() end +end + +ws.on_connect(function() + local ldir =minetest.settings:get('last-dir') + if ldir then ws.setdir(ldir) end +end) + + +-- COORD MAGIC + +function ws.is_same_pos(pos1,pos2) + return vector.distance(vector.round(pos1),vector.round(pos2)) == 0 +end +function ws.get_reachable_positions(range,under) + under=under or false + range=range or 4 + local rt={} + local lp=vector.round(minetest.localplayer:get_pos()) + local ylim=range + if under then ylim=-1 end + for x = -range,range,1 do + for y = -range,ylim,1 do + for z = -range,range,1 do + table.insert(rt,vector.round(vector.add(lp,vector.new(x,y,z)))) + end + end + end + return rt +end + +function ws.do_area(radius,func,plane) + for k,v in pairs(ws.get_reachable_positions(range)) do + if not plane or v.y == minetest.localplayer:get_pos().y -1 then + func(v) + end + end +end + +function ws.get_hud_by_texture(texture) + local def + local i = -1 + repeat + i = i + 1 + def = minetest.localplayer:hud_get(i) + until not def or def.text == texture + if def then + return def + end + def.number=0 + return def +end + +function ws.find_player(name) + for k, v in ipairs(minetest.localplayer.get_nearby_objects(500)) do + if v:get_name() == name then + return v:get_pos(),v + end + end +end + +function ws.display_wp(pos,name) + local ix = #ws.displayed_wps + 1 + ws.displayed_wps[ix] = minetest.localplayer:hud_add({ + hud_elem_type = 'waypoint', + name = name, + text = name, + number = 0x00ff00, + world_pos = pos + }) + return ix +end + +function ws.clear_wp(ix) + table.remove(ws.displayed_wps,ix) +end + +function ws.clear_wps() + for k,v in ipairs(ws.displayed_wps) do + minetest.localplayer:hud_remove(v) + table.remove(ws.displayed_wps,k) + end +end + +function ws.register_chatcommand_alias(old, ...) + local def = assert(minetest.registered_chatcommands[old]) + def.name = nil + for i = 1, select('#', ...) do + minetest.register_chatcommand(select(i, ...), table.copy(def)) + end +end + +function ws.round2(num, numDecimalPlaces) + return tonumber(string.format("%." .. (numDecimalPlaces or 0) .. "f", num)) +end + +function ws.pos_to_string(pos) + if type(pos) == 'table' then + pos = minetest.pos_to_string(vector.round(pos)) + end + if type(pos) == 'string' then + return pos + end + return pos +end + +function ws.string_to_pos(pos) + if type(pos) == 'string' then + pos = minetest.string_to_pos(pos) + end + if type(pos) == 'table' then + return vector.round(pos) + end + return pos +end + + + +--ITEMS +function ws.find_item_in_table(items,rnd) + if type(items) == 'string' then + return minetest.find_item(items) + end + if type(items) ~= 'table' then return end + if rnd then items=ws.shuffle(items) end + for i, v in pairs(items) do + local n = minetest.find_item(v) + if n then + return n + end + end + return false +end + +function ws.find_empty(inv) + for i, v in ipairs(inv) do + if v:is_empty() then + return i + end + end + return false +end + +function ws.find_named(inv, name) + if not inv then return -1 end + if not name then return end + for i, v in ipairs(inv) do + if v:get_name():find(name) then + return i + end + end +end + +function ws.itemnameformat(description) + description = description:gsub(string.char(0x1b) .. "%(.@[^)]+%)", "") + description = description:match("([^\n]*)") + return description +end + +function ws.find_nametagged(list, name) + for i, v in ipairs(list) do + if ws.itemnameformat(v:get_description()) == name then + return i + end + end +end + +local hotbar_slot=8 +function ws.to_hotbar(it,hslot) + local tpos=nil + local plinv = minetest.get_inventory("current_player") + if hslot and hslot < 10 then + tpos=hslot + else + for i, v in ipairs(plinv.main) do + if i<10 and v:is_empty() then + tpos = i + break + end + end + end + if tpos == nil then tpos=hotbar_slot end + local mv = InventoryAction("move") + mv:from("current_player", "main", it) + mv:to("current_player", "main", tpos) + mv:apply() + return tpos +end + +function ws.switch_to_item(itname,hslot) + if not minetest.localplayer then return false end + local plinv = minetest.get_inventory("current_player") + for i, v in ipairs(plinv.main) do + if i<10 and v:get_name() == itname then + minetest.localplayer:set_wield_index(i) + return true + end + end + local pos = ws.find_named(plinv.main, itname) + if pos then + minetest.localplayer:set_wield_index(ws.to_hotbar(pos,hslot)) + return true + end + return false +end +function ws.in_inv(itname) + if not minetest.localplayer then return false end + local plinv = minetest.get_inventory("current_player") + local pos = ws.find_named(plinv.main, itname) + if pos then + return true + end +end + +function core.switch_to_item(item) return ws.switch_to_item(item) end + +function ws.switch_inv_or_echest(name,max_count,hslot) + if not minetest.localplayer then return false end + local plinv = minetest.get_inventory("current_player") + if ws.switch_to_item(name) then return true end + + local epos = ws.find_named(plinv.enderchest, name) + if epos then + local tpos + for i, v in ipairs(plinv.main) do + if i < 9 and v:is_empty() then + tpos = i + break + end + end + if not tpos then tpos=hotbar_slot end + + if tpos then + local mv = InventoryAction("move") + mv:from("current_player", "enderchest", epos) + mv:to("current_player", "main", tpos) + if max_count then + mv:set_count(max_count) + end + mv:apply() + minetest.localplayer:set_wield_index(tpos) + return true + end + end + return false +end + +local function posround(n) + return math.floor(n + 0.5) +end + +local function fmt(c) + return tostring(posround(c.x))..","..tostring(posround(c.y))..","..tostring(posround(c.z)) +end + +local function map_pos(value) + if value.x then + return value + else + return {x = value[1], y = value[2], z = value[3]} + end +end + +function ws.invparse(location) + if type(location) == "string" then + if string.match(location, "^[-]?[0-9]+,[-]?[0-9]+,[-]?[0-9]+$") then + return "nodemeta:" .. location + else + return location + end + elseif type(location) == "table" then + return "nodemeta:" .. fmt(map_pos(location)) + end +end + +function ws.invpos(p) + return "nodemeta:"..p.x..","..p.y..","..p.z +end + + +-- TOOLS + + +local function check_tool(stack, node_groups, old_best_time) + local toolcaps = stack:get_tool_capabilities() + if not toolcaps then return end + local best_time = old_best_time + for group, groupdef in pairs(toolcaps.groupcaps) do + local level = node_groups[group] + if level then + local this_time = groupdef.times[level] + if this_time and this_time < best_time then + best_time = this_time + end + end + end + return best_time < old_best_time, best_time +end + +local function find_best_tool(nodename, switch) + local player = minetest.localplayer + local inventory = minetest.get_inventory("current_player") + local node_groups = minetest.get_node_def(nodename).groups + local new_index = player:get_wield_index() + local is_better, best_time = false, math.huge + + is_better, best_time = check_tool(player:get_wielded_item(), node_groups, best_time) + if inventory.hand then + is_better, best_time = check_tool(inventory.hand[1], node_groups, best_time) + end + + for index, stack in ipairs(inventory.main) do + is_better, best_time = check_tool(stack, node_groups, best_time) + if is_better then + new_index = index + end + end + + return new_index,best_time +end + +function ws.get_digtime(nodename) + local idx,tm=find_best_tool(nodename) + return tm +end + +function ws.select_best_tool(pos) + local nd=minetest.get_node_or_nil(pos) + local nodename='air' + if nd then nodename=nd.name end + local t=find_best_tool(nodename) + minetest.localplayer:set_wield_index(ws.to_hotbar(t,hotbar_slot)) + --minetest.localplayer:set_wield_index(find_best_tool(nodename)) +end + +--- COORDS +function ws.coord(x, y, z) + return vector.new(x,y,z) +end +function ws.ordercoord(c) + if c.x == nil then + return {x = c[1], y = c[2], z = c[3]} + else + return c + end +end + +-- x or {x,y,z} or {x=x,y=y,z=z} +function ws.optcoord(x, y, z) + if y and z then + return ws.coord(x, y, z) + else + return ws.ordercoord(x) + end +end +function ws.cadd(c1, c2) + return vector.add(c1,c2) + --return ws.coord(c1.x + c2.x, c1.y + c2.y, c1.z + c2.z) +end + +function ws.relcoord(x, y, z, rpos) + local pos = rpos or minetest.localplayer:get_pos() + pos.y=math.ceil(pos.y) + --math.floor(pos.y) + 0.5 + return ws.cadd(pos, ws.optcoord(x, y, z)) +end + +local function between(x, y, z) -- x is between y and z (inclusive) + return y <= x and x <= z +end + +function ws.getdir(yaw) -- + local rot = yaw or minetest.localplayer:get_yaw() % 360 + if between(rot, 315, 360) or between(rot, 0, 45) then + return "north" + elseif between(rot, 135, 225) then + return "south" + elseif between(rot, 225, 315) then + return "east" + elseif between(rot, 45, 135) then + return "west" + end +end + +function ws.getaxis() + local dir=ws.getdir() + if dir == "north" or dir == "south" then return "z" end + return "x" +end +function ws.setdir(dir) -- + if dir == "north" then + minetest.localplayer:set_yaw(0) + elseif dir == "south" then + minetest.localplayer:set_yaw(180) + elseif dir == "east" then + minetest.localplayer:set_yaw(270) + elseif dir == "west" then + minetest.localplayer:set_yaw(90) + end +end + +function ws.dircoord(f, y, r ,rpos, rdir) + local dir= ws.getdir(rdir) + local coord = ws.optcoord(f, y, r) + local f = coord.x + local y = coord.y + local r = coord.z + local lp= rpos or minetest.localplayer:get_pos() + if dir == "north" then + return ws.relcoord(r, y, f,rpos) + elseif dir == "south" then + return ws.relcoord(-r, y, -f,rpos) + elseif dir == "east" then + return ws.relcoord(f, y, -r,rpos) + elseif dir== "west" then + return ws.relcoord(-f, y, r,rpos) + end + return ws.relcoord(0, 0, 0,rpos) +end + +function ws.get_dimension(pos) + if pos.y > -65 then return "overworld" + elseif pos.y > -8000 then return "void" + elseif pos.y > -27000 then return "end" + elseif pos.y > -28930 then return "void" + elseif pos.y > -31000 then return "nether" + else return "void" + end +end + +function ws.aim(tpos) + local ppos=minetest.localplayer:get_pos() + local dir=vector.direction(ppos,tpos) + local yyaw=0; + local pitch=0; + if dir.x < 0 then + yyaw = math.atan2(-dir.x, dir.z) + (math.pi * 2) + else + yyaw = math.atan2(-dir.x, dir.z) + end + yyaw = ws.round2(math.deg(yyaw),2) + pitch = ws.round2(math.deg(math.asin(-dir.y) * 1),2); + minetest.localplayer:set_yaw(yyaw) + minetest.localplayer:set_pitch(pitch) +end + +function ws.gaim(tpos,v,g) + local v = v or 40 + local g = g or 9.81 + local ppos=minetest.localplayer:get_pos() + local dir=vector.direction(ppos,tpos) + local yyaw=0; + local pitch=0; + if dir.x < 0 then + yyaw = math.atan2(-dir.x, dir.z) + (math.pi * 2) + else + yyaw = math.atan2(-dir.x, dir.z) + end + yyaw = ws.round2(math.deg(yyaw),2) + local y = dir.y + dir.y = 0 + local x = vector.length(dir) + pitch=math.atan(math.pow(v, 2) / (g * x) + math.sqrt(math.pow(v, 4)/(math.pow(g, 2) * math.pow(x, 2)) - 2 * math.pow(v, 2) * y/(g * math.pow(x, 2)) - 1)) + --pitch = ws.round2(math.deg(math.asin(-dir.y) * 1),2); + minetest.localplayer:set_yaw(yyaw) + minetest.localplayer:set_pitch(math.deg(pitch)) +end + +function ws.buildable_to(pos) + local node=minetest.get_node_or_nil(pos) + if node then + return minetest.get_node_def(node.name).buildable_to + end +end + +function ws.tplace(p,n,stay) + if not p then return end + if n then ws.switch_to_item(n) end + local opos=ws.dircoord(0,0,0) + local tpos=vector.add(p,vector.new(0,1,0)) + minetest.localplayer:set_pos(tpos) + ws.place(p,{n}) + if not stay then + minetest.after(0.1,function() + minetest.localplayer:set_pos(opos) + end) + end +end + +minetest.register_chatcommand("tplace", { + description = "tp-place", + param = "Y", + func = function(param) + return ws.tplace(minetest.string_to_pos(param)) + end +}) + +function ws.ytp(param) + local y=tonumber(param) + local lp=ws.dircoord(0,0,0) + if lp.y < y + 50 then return false,"Can't TP up." end + if y < -30912 then return false,"Don't TP into the void lol." end + minetest.localplayer:set_pos(vector.new(lp.x,y,lp.z)) +end + +local function tablearg(arg) + local tb={} + if type(arg) == 'string' then + tb={arg} + elseif type(arg) == 'table' then + tb=arg + elseif type(arg) == 'function' then + tb=arg() + end + return tb +end + +function ws.isnode(pos,arg)--arg is either an itemstring, a table of itemstrings or a function returning an itemstring + local nodename=tablearg(arg) + local nd=minetest.get_node_or_nil(pos) + if nd and nodename and ws.in_list(nd.name,nodename) then + return true + end +end + +function ws.can_place_at(pos) + local node = minetest.get_node_or_nil(pos) + return (node and (node.name == "air" or node.name=="mcl_core:water_source" or node.name=="mcl_core:water_flowing" or node.name=="mcl_core:lava_source" or node.name=="mcl_core:lava_flowing" or minetest.get_node_def(node.name).buildable_to)) +end + +-- should check if wield is placeable +-- minetest.get_node(wielded:get_name()) ~= nil should probably work +-- otherwise it equips armor and eats food +function ws.can_place_wielded_at(pos) + local wield_empty = minetest.localplayer:get_wielded_item():is_empty() + return not wield_empty and ws.can_place_at(pos) +end + + +function ws.find_any_swap(items,hslot) + hslot=hslot or 8 + for i, v in ipairs(items) do + local n = minetest.find_item(v) + if n then + ws.switch_to_item(v,hslot) + return true + end + end + return false +end + + +-- swaps to any of the items and places if need be +-- returns true if placed and in inventory or already there, false otherwise + +local lastact=0 +local lastplc=0 +local lastdig=0 +local actint=10 +function ws.place(pos,items,hslot, place) + --if nodes_this_tick > 8 then return end + --nodes_this_tick = nodes_this_tick + 1 + --if not inside_constraints(pos) then return end + if not pos then return end + if not ws.can_place_at(pos) then return end + items=tablearg(items) + + place = place or minetest.place_node + + local node = minetest.get_node_or_nil(pos) + if not node then return end + -- already there + if ws.isnode(pos,items) then + return true + else + if ws.find_any_swap(items,hslot) then + place(pos) + return true + end + end +end + +function ws.place_if_able(pos) + if not pos then return end + if not inside_constraints(pos) then return end + if ws.can_place_wielded_at(pos) then + minetest.place_node(pos) + end +end + +function ws.is_diggable(pos) + if not pos then return false end + local nd=minetest.get_node_or_nil(pos) + if not nd then return false end + local n = minetest.get_node_def(nd.name) + if n and n.diggable then return true end + return false +end + +function ws.dig(pos,condition,autotool) + --if not inside_constraints(pos) then return end + if autotool == nil then autotool = true end + if condition and not condition(pos) then return false end + if not ws.is_diggable(pos) then return end + local nd=minetest.get_node_or_nil(pos) + if nd and minetest.get_node_def(nd.name).diggable then + if autotool then ws.select_best_tool(pos) end + minetest.dig_node(pos) + end + return true +end + +function ws.chunk_loaded() + local ign=minetest.find_nodes_near(ws.dircoord(0,0,0),10,{'ignore'},true) + if #ign == 0 then return true end + return false +end + +function ws.get_near(nodes,range) + range=range or 5 + local nds=minetest.find_nodes_near(ws.dircoord(0,0,0),rang,nodes,true) + if #nds > 0 then return nds end + return false +end + +function ws.is_laggy() + if tps_client and tps_client.ping and tps_client.ping > 1000 then return true end +end + + +function ws.donodes(poss,func,condition) + if ws.is_laggy() then return end + local dn_i=0 + for k,v in pairs(poss) do + if dn_i > 8 then return end + --local nd=minetest.get_node_or_nil(v) + if condition == nil or condition(v) then + func(v) + dn_i = dn_i + 1 + end + end +end + +function ws.dignodes(poss,condition) + local func=function(p) ws.dig(p) end + ws.donodes(poss,func,condition) +end + + +function ws.replace(pos,arg) + arg=tablearg(arg) + local nd=minetest.get_node_or_nil(pos) + if nd and not ws.in_list(nd.name,arg) and ws.buildable_to(pos) then + local tm=ws.get_digtime(nd.name) or 0 + ws.dig(pos) + minetest.after(tm + 0.1,function() + ws.place(pos,arg) + end) + return tm + else + return ws.place(pos,arg) + end +end + +function ws.playeron(p) + local pls=minetest.get_player_names() + for k,v in pairs(pls) do + if v == p then return true end + end + return false +end + + +function ws.between(x, y, z) -- x is between y and z (inclusive) + return y <= x and x <= z +end + + +local wall_pos1={x=-1255,y=6,z=792} +local wall_pos2={x=-1452,y=80,z=981} +local iwall_pos1={x=-1266,y=6,z=802} +local iwall_pos2={x=-1442,y=80,z=971} + +function ws.in_cube(tpos,wpos1,wpos2) + local xmax=wpos2.x + local xmin=wpos1.x + + local ymax=wpos2.y + local ymin=wpos1.y + + local zmax=wpos2.z + local zmin=wpos1.z + if wpos1.x > wpos2.x then + xmax=wpos1.x + xmin=wpos2.x + end + if wpos1.y > wpos2.y then + ymax=wpos1.y + ymin=wpos2.y + end + if wpos1.z > wpos2.z then + zmax=wpos1.z + zmin=wpos2.z + end + if ws.between(tpos.x,xmin,xmax) and ws.between(tpos.y,ymin,ymax) and ws.between(tpos.z,zmin,zmax) then + return true + end + return false +end + +function ws.in_wall(pos) + if ws.in_cube(pos,wall_pos1,wall_pos2) and not in_cube(pos,iwall_pos1,iwall_pos2) then + return true end + return false +end + +function ws.inside_wall(pos) + local p1=iwall_pos1 + local p2=iwall_pos2 + if ws.in_cube(pos,p1,p2) then return true end + return false +end + +function ws.find_closest_reachable_airpocket(pos) + local lp=ws.dircoord(0,0,0) + local nds=minetest.find_nodes_near(lp,5,{'air'}) + local odst=10 + local rt=lp + for k,v in ipairs(nds) do + local dst=vector.distance(pos,v) + if dst < odst then odst=dst rt=v end + end + if odst==10 then return false end + return vector.add(rt,vector.new(0,-1,0)) +end + + +-- DEBUG +local function printwieldedmeta() + ws.dcm(dump(minetest.localplayer:get_wielded_item():get_meta():to_table())) +end +minetest.register_cheat('ItemMeta','Test',printwieldedmeta) +local selected_name = false +local formspec_list = {} +function ws.display_list_formspec(fname,list,funcs) + funcs={} + local formspec = 'size[6.25,9]' .. + 'label[0,0;NodeLists]' .. + 'button_exit[0,7.5;1,0.5;display;Show]' .. + 'button[3.625,7.5;1.3,0.5;rename;Rename]' .. + 'button[4.9375,7.5;1.3,0.5;delete;Delete]' + local sp=0 + + for k,v in pairs(funcs) do + formspec=formspec..'button_exit['..sp..',8.5;1,0.5;'..v.name..';'..v.name..']' + sp=sp+0.8 + end + + formspec=formspec..'textlist[0,0.75;6,6;marker;' + local selected = 1 + formspec_list = {} + if not list then list={} end + for id, name in ipairs(list) do + if id > 1 then + formspec = formspec .. ',' + end + if not selected_name then + selected_name = name + end + if name == selected_name then + selected = id + end + formspec_list[#formspec_list + 1] = name + formspec = formspec .. '##' .. minetest.formspec_escape(name) + end + + formspec = formspec .. ';' .. tostring(selected) .. ']' + + if selected_name then + local val=list[selected] + if val then + formspec = formspec .. 'label[0,6.75;' .. selected_name .. ']' + end + else + formspec = formspec .. 'button_exit[0,7.5;5.25,0.5;quit;Close dialog]' .. + 'label[0,6.75;No Entries.]' + end + + return minetest.show_formspec(fname, formspec) +end + +minetest.register_on_formspec_input(function(formname, fields) + local fname="NodeLists" + if formname == 'NodeLists-ignore' then + return true + elseif formname ~= "NodeLists" then + return + end + local name = selected_name + + if name then + for k,v in pairs(autofly.registered_transports) do + if fields[v.name] then + if not v.func(autofly.get_waypoint(name),name) then + minetest.display_chat_message('Error with '..v.name) + end + end + end + if fields.display then + if not autofly.display_waypoint(name) then + minetest.display_chat_message('Error displaying waypoint!') + end + elseif fields.rename then + minetest.show_formspec(fname, 'size[6,3]' .. + 'label[0.35,0.2;Rename waypoint]' .. + 'field[0.3,1.3;6,1;new_name;New name;' .. + minetest.formspec_escape(name) .. ']' .. + 'button[0,2;3,1;cancel;Cancel]' .. + 'button[3,2;3,1;rename_confirm;Rename]') + elseif fields.rename_confirm then + if fields.new_name and #fields.new_name > 0 then + if nlist.rename(name, fields.new_name) then + selected_name = fields.new_name + else + minetest.display_chat_message('Error renaming!') + end + ws.display_list_formspec() + else + minetest.display_chat_message('Please enter a new name for the entry.') + end + elseif fields.delete then + minetest.show_formspec(fname, 'size[6,2]' .. + 'label[0.35,0.25;Are you sure you want to delete this waypoint?]' .. + 'button[0,1;3,1;cancel;Cancel]' .. + 'button[3,1;3,1;delete_confirm;Delete]') + elseif fields.delete_confirm then + autofly.delete_waypoint(name) + selected_name = false + ws.display_list_formspec() + elseif fields.cancel then + ws.display_list_formspec() + elseif name ~= selected_name then + selected_name = name + ws.display_list_formspec() + end + elseif fields.display or fields.delete then + minetest.display_chat_message('Please select a waypoint.') + end + return true +end) diff --git a/nlist/init.lua b/nlist/init.lua new file mode 100644 index 0000000..0a8a344 --- /dev/null +++ b/nlist/init.lua @@ -0,0 +1,2109 @@ + +ws = {} +ws.registered_globalhacks = {} +ws.displayed_wps={} + +ws.c = core + +ws.range=4 +ws.target=nil +ws.targetpos=nil + +local nextact = {} +local ghwason={} + +local nodes_this_tick=0 + +function ws.s(name,value) + if value == nil then + return ws.c.settings:get(name) + else + ws.c.settings:set(name,value) + return ws.c.settings:get(name) + end +end +function ws.sb(name,value) + if value == nil then + return ws.c.settings:get_bool(name) + else + ws.c.settings:set_bool(name,value) + return ws.c.settings:get_bool(name) + end +end + +function ws.dcm(msg) + return minetest.display_chat_message(msg) +end +function ws.set_bool_bulk(settings,value) + if type(settings) ~= 'table' then return false end + for k,v in pairs(settings) do + minetest.settings:set_bool(v,value) + end + return true +end + +function ws.shuffle(tbl) + for i = #tbl, 2, -1 do + local j = math.random(i) + tbl[i], tbl[j] = tbl[j], tbl[i] + end + return tbl +end + +function ws.in_list(val, list) + if type(list) ~= "table" then return false end + for i, v in pairs(list) do + if v == val then + return true + end + end + return false +end + +function ws.random_table_element(tbl) + local ks = {} + for k in pairs(tbl) do + table.insert(ks, k) + end + return tbl[ks[math.random(#ks)]] +end + +function ws.center() + --local lp=ws.dircoord(0,0,0) + --minetest.localplayer:set_pos(lp) +end + +function ws.globalhacktemplate(setting,func,funcstart,funcstop,daughters,delay) + funcstart = funcstart or function() end + funcstop = funcstop or function() end + delay = delay or 0.5 + return function() + if not minetest.localplayer then return end + if minetest.settings:get_bool(setting) then + if tps_client and tps_client.ping and tps_client.ping > 1000 then return end + nodes_this_tick = 0 + if nextact[setting] and nextact[setting] > os.clock() then return end + nextact[setting] = os.clock() + delay + if not ghwason[setting] then + if not funcstart() then + ws.set_bool_bulk(daughters,true) + ghwason[setting] = true + --ws.dcm(setting.. " activated") + ws.center() + minetest.settings:set('last-dir',ws.getdir()) + minetest.settings:set('last-y',ws.dircoord(0,0,0).y) + else minetest.settings:set_bool(setting,false) + end + else + func() + end + + elseif ghwason[setting] then + ghwason[setting] = false + ws.set_bool_bulk(daughters,false) + funcstop() + --ws.dcm(setting.. " deactivated") + end + end +end + +function ws.register_globalhack(func) + table.insert(ws.registered_globalhacks,func) +end + +function ws.register_globalhacktemplate(name,category,setting,func,funcstart,funcstop,daughters) + ws.register_globalhack(ws.globalhacktemplate(setting,func,funcstart,funcstop,daughters)) + minetest.register_cheat(name,category,setting) +end + +ws.rg=ws.register_globalhacktemplate + +function ws.step_globalhacks(dtime) + for i, v in ipairs(ws.registered_globalhacks) do + v(dtime) + end +end + +minetest.register_globalstep(function(dtime) ws.step_globalhacks(dtime) end) +minetest.settings:set_bool('continuous_forward',false) + +function ws.on_connect(func) + if not minetest.localplayer then minetest.after(0,function() ws.on_connect(func) end) return end + if func then func() end +end + +ws.on_connect(function() + local ldir =minetest.settings:get('last-dir') + if ldir then ws.setdir(ldir) end +end) + + +-- COORD MAGIC + +function ws.is_same_pos(pos1,pos2) + return vector.distance(vector.round(pos1),vector.round(pos2)) == 0 +end +function ws.get_reachable_positions(range,under) + under=under or false + range=range or 4 + local rt={} + local lp=vector.round(minetest.localplayer:get_pos()) + local ylim=range + if under then ylim=-1 end + for x = -range,range,1 do + for y = -range,ylim,1 do + for z = -range,range,1 do + table.insert(rt,vector.round(vector.add(lp,vector.new(x,y,z)))) + end + end + end + return rt +end + +function ws.do_area(radius,func,plane) + for k,v in pairs(ws.get_reachable_positions(range)) do + if not plane or v.y == minetest.localplayer:get_pos().y -1 then + func(v) + end + end +end + +function ws.get_hud_by_texture(texture) + local def + local i = -1 + repeat + i = i + 1 + def = minetest.localplayer:hud_get(i) + until not def or def.text == texture + if def then + return def + end + def.number=0 + return def +end + +function ws.find_player(name) + for k, v in ipairs(minetest.localplayer.get_nearby_objects(500)) do + if v:get_name() == name then + return v:get_pos(),v + end + end +end + +function ws.display_wp(pos,name) + local ix = #ws.displayed_wps + 1 + ws.displayed_wps[ix] = minetest.localplayer:hud_add({ + hud_elem_type = 'waypoint', + name = name, + text = name, + number = 0x00ff00, + world_pos = pos + }) + return ix +end + +function ws.clear_wp(ix) + table.remove(ws.displayed_wps,ix) +end + +function ws.clear_wps() + for k,v in ipairs(ws.displayed_wps) do + minetest.localplayer:hud_remove(v) + table.remove(ws.displayed_wps,k) + end +end + +function ws.register_chatcommand_alias(old, ...) + local def = assert(minetest.registered_chatcommands[old]) + def.name = nil + for i = 1, select('#', ...) do + minetest.register_chatcommand(select(i, ...), table.copy(def)) + end +end + +function ws.round2(num, numDecimalPlaces) + return tonumber(string.format("%." .. (numDecimalPlaces or 0) .. "f", num)) +end + +function ws.pos_to_string(pos) + if type(pos) == 'table' then + pos = minetest.pos_to_string(vector.round(pos)) + end + if type(pos) == 'string' then + return pos + end + return pos +end + +function ws.string_to_pos(pos) + if type(pos) == 'string' then + pos = minetest.string_to_pos(pos) + end + if type(pos) == 'table' then + return vector.round(pos) + end + return pos +end + + + +--ITEMS +function ws.find_item_in_table(items,rnd) + if type(items) == 'string' then + return minetest.find_item(items) + end + if type(items) ~= 'table' then return end + if rnd then items=ws.shuffle(items) end + for i, v in pairs(items) do + local n = minetest.find_item(v) + if n then + return n + end + end + return false +end + +function ws.find_empty(inv) + for i, v in ipairs(inv) do + if v:is_empty() then + return i + end + end + return false +end + +function ws.find_named(inv, name) + if not inv then return -1 end + if not name then return end + for i, v in ipairs(inv) do + if v:get_name():find(name) then + return i + end + end +end + +function ws.itemnameformat(description) + description = description:gsub(string.char(0x1b) .. "%(.@[^)]+%)", "") + description = description:match("([^\n]*)") + return description +end + +function ws.find_nametagged(list, name) + for i, v in ipairs(list) do + if ws.itemnameformat(v:get_description()) == name then + return i + end + end +end + +local hotbar_slot=8 +function ws.to_hotbar(it,hslot) + local tpos=nil + local plinv = minetest.get_inventory("current_player") + if hslot and hslot < 10 then + tpos=hslot + else + for i, v in ipairs(plinv.main) do + if i<10 and v:is_empty() then + tpos = i + break + end + end + end + if tpos == nil then tpos=hotbar_slot end + local mv = InventoryAction("move") + mv:from("current_player", "main", it) + mv:to("current_player", "main", tpos) + mv:apply() + return tpos +end + +function ws.switch_to_item(itname,hslot) + if not minetest.localplayer then return false end + local plinv = minetest.get_inventory("current_player") + for i, v in ipairs(plinv.main) do + if i<10 and v:get_name() == itname then + minetest.localplayer:set_wield_index(i) + return true + end + end + local pos = ws.find_named(plinv.main, itname) + if pos then + minetest.localplayer:set_wield_index(ws.to_hotbar(pos,hslot)) + return true + end + return false +end +function ws.in_inv(itname) + if not minetest.localplayer then return false end + local plinv = minetest.get_inventory("current_player") + local pos = ws.find_named(plinv.main, itname) + if pos then + return true + end +end + +function core.switch_to_item(item) return ws.switch_to_item(item) end + +function ws.switch_inv_or_echest(name,max_count,hslot) + if not minetest.localplayer then return false end + local plinv = minetest.get_inventory("current_player") + if ws.switch_to_item(name) then return true end + + local epos = ws.find_named(plinv.enderchest, name) + if epos then + local tpos + for i, v in ipairs(plinv.main) do + if i < 9 and v:is_empty() then + tpos = i + break + end + end + if not tpos then tpos=hotbar_slot end + + if tpos then + local mv = InventoryAction("move") + mv:from("current_player", "enderchest", epos) + mv:to("current_player", "main", tpos) + if max_count then + mv:set_count(max_count) + end + mv:apply() + minetest.localplayer:set_wield_index(tpos) + return true + end + end + return false +end + +local function posround(n) + return math.floor(n + 0.5) +end + +local function fmt(c) + return tostring(posround(c.x))..","..tostring(posround(c.y))..","..tostring(posround(c.z)) +end + +local function map_pos(value) + if value.x then + return value + else + return {x = value[1], y = value[2], z = value[3]} + end +end + +function ws.invparse(location) + if type(location) == "string" then + if string.match(location, "^[-]?[0-9]+,[-]?[0-9]+,[-]?[0-9]+$") then + return "nodemeta:" .. location + else + return location + end + elseif type(location) == "table" then + return "nodemeta:" .. fmt(map_pos(location)) + end +end + +function ws.invpos(p) + return "nodemeta:"..p.x..","..p.y..","..p.z +end + + +-- TOOLS + + +local function check_tool(stack, node_groups, old_best_time) + local toolcaps = stack:get_tool_capabilities() + if not toolcaps then return end + local best_time = old_best_time + for group, groupdef in pairs(toolcaps.groupcaps) do + local level = node_groups[group] + if level then + local this_time = groupdef.times[level] + if this_time and this_time < best_time then + best_time = this_time + end + end + end + return best_time < old_best_time, best_time +end + +local function find_best_tool(nodename, switch) + local player = minetest.localplayer + local inventory = minetest.get_inventory("current_player") + local node_groups = minetest.get_node_def(nodename).groups + local new_index = player:get_wield_index() + local is_better, best_time = false, math.huge + + is_better, best_time = check_tool(player:get_wielded_item(), node_groups, best_time) + if inventory.hand then + is_better, best_time = check_tool(inventory.hand[1], node_groups, best_time) + end + + for index, stack in ipairs(inventory.main) do + is_better, best_time = check_tool(stack, node_groups, best_time) + if is_better then + new_index = index + end + end + + return new_index,best_time +end + +function ws.get_digtime(nodename) + local idx,tm=find_best_tool(nodename) + return tm +end + +function ws.select_best_tool(pos) + local nd=minetest.get_node_or_nil(pos) + local nodename='air' + if nd then nodename=nd.name end + local t=find_best_tool(nodename) + minetest.localplayer:set_wield_index(ws.to_hotbar(t,hotbar_slot)) + --minetest.localplayer:set_wield_index(find_best_tool(nodename)) +end + +--- COORDS +function ws.coord(x, y, z) + return vector.new(x,y,z) +end +function ws.ordercoord(c) + if c.x == nil then + return {x = c[1], y = c[2], z = c[3]} + else + return c + end +end + +-- x or {x,y,z} or {x=x,y=y,z=z} +function ws.optcoord(x, y, z) + if y and z then + return ws.coord(x, y, z) + else + return ws.ordercoord(x) + end +end +function ws.cadd(c1, c2) + return vector.add(c1,c2) + --return ws.coord(c1.x + c2.x, c1.y + c2.y, c1.z + c2.z) +end + +function ws.relcoord(x, y, z, rpos) + local pos = rpos or minetest.localplayer:get_pos() + pos.y=math.ceil(pos.y) + --math.floor(pos.y) + 0.5 + return ws.cadd(pos, ws.optcoord(x, y, z)) +end + +local function between(x, y, z) -- x is between y and z (inclusive) + return y <= x and x <= z +end + +function ws.getdir(yaw) -- + local rot = yaw or minetest.localplayer:get_yaw() % 360 + if between(rot, 315, 360) or between(rot, 0, 45) then + return "north" + elseif between(rot, 135, 225) then + return "south" + elseif between(rot, 225, 315) then + return "east" + elseif between(rot, 45, 135) then + return "west" + end +end + +function ws.getaxis() + local dir=ws.getdir() + if dir == "north" or dir == "south" then return "z" end + return "x" +end +function ws.setdir(dir) -- + if dir == "north" then + minetest.localplayer:set_yaw(0) + elseif dir == "south" then + minetest.localplayer:set_yaw(180) + elseif dir == "east" then + minetest.localplayer:set_yaw(270) + elseif dir == "west" then + minetest.localplayer:set_yaw(90) + end +end + +function ws.dircoord(f, y, r ,rpos, rdir) + local dir= ws.getdir(rdir) + local coord = ws.optcoord(f, y, r) + local f = coord.x + local y = coord.y + local r = coord.z + local lp= rpos or minetest.localplayer:get_pos() + if dir == "north" then + return ws.relcoord(r, y, f,rpos) + elseif dir == "south" then + return ws.relcoord(-r, y, -f,rpos) + elseif dir == "east" then + return ws.relcoord(f, y, -r,rpos) + elseif dir== "west" then + return ws.relcoord(-f, y, r,rpos) + end + return ws.relcoord(0, 0, 0,rpos) +end + +function ws.get_dimension(pos) + if pos.y > -65 then return "overworld" + elseif pos.y > -8000 then return "void" + elseif pos.y > -27000 then return "end" + elseif pos.y > -28930 then return "void" + elseif pos.y > -31000 then return "nether" + else return "void" + end +end + +function ws.aim(tpos) + local ppos=minetest.localplayer:get_pos() + local dir=vector.direction(ppos,tpos) + local yyaw=0; + local pitch=0; + if dir.x < 0 then + yyaw = math.atan2(-dir.x, dir.z) + (math.pi * 2) + else + yyaw = math.atan2(-dir.x, dir.z) + end + yyaw = ws.round2(math.deg(yyaw),2) + pitch = ws.round2(math.deg(math.asin(-dir.y) * 1),2); + minetest.localplayer:set_yaw(yyaw) + minetest.localplayer:set_pitch(pitch) +end + +function ws.gaim(tpos,v,g) + local v = v or 40 + local g = g or 9.81 + local ppos=minetest.localplayer:get_pos() + local dir=vector.direction(ppos,tpos) + local yyaw=0; + local pitch=0; + if dir.x < 0 then + yyaw = math.atan2(-dir.x, dir.z) + (math.pi * 2) + else + yyaw = math.atan2(-dir.x, dir.z) + end + yyaw = ws.round2(math.deg(yyaw),2) + local y = dir.y + dir.y = 0 + local x = vector.length(dir) + pitch=math.atan(math.pow(v, 2) / (g * x) + math.sqrt(math.pow(v, 4)/(math.pow(g, 2) * math.pow(x, 2)) - 2 * math.pow(v, 2) * y/(g * math.pow(x, 2)) - 1)) + --pitch = ws.round2(math.deg(math.asin(-dir.y) * 1),2); + minetest.localplayer:set_yaw(yyaw) + minetest.localplayer:set_pitch(math.deg(pitch)) +end + +function ws.buildable_to(pos) + local node=minetest.get_node_or_nil(pos) + if node then + return minetest.get_node_def(node.name).buildable_to + end +end + +function ws.tplace(p,n,stay) + if not p then return end + if n then ws.switch_to_item(n) end + local opos=ws.dircoord(0,0,0) + local tpos=vector.add(p,vector.new(0,1,0)) + minetest.localplayer:set_pos(tpos) + ws.place(p,{n}) + if not stay then + minetest.after(0.1,function() + minetest.localplayer:set_pos(opos) + end) + end +end + +minetest.register_chatcommand("tplace", { + description = "tp-place", + param = "Y", + func = function(param) + return ws.tplace(minetest.string_to_pos(param)) + end +}) + +function ws.ytp(param) + local y=tonumber(param) + local lp=ws.dircoord(0,0,0) + if lp.y < y + 50 then return false,"Can't TP up." end + if y < -30912 then return false,"Don't TP into the void lol." end + minetest.localplayer:set_pos(vector.new(lp.x,y,lp.z)) +end + +local function tablearg(arg) + local tb={} + if type(arg) == 'string' then + tb={arg} + elseif type(arg) == 'table' then + tb=arg + elseif type(arg) == 'function' then + tb=arg() + end + return tb +end + +function ws.isnode(pos,arg)--arg is either an itemstring, a table of itemstrings or a function returning an itemstring + local nodename=tablearg(arg) + local nd=minetest.get_node_or_nil(pos) + if nd and nodename and ws.in_list(nd.name,nodename) then + return true + end +end + +function ws.can_place_at(pos) + local node = minetest.get_node_or_nil(pos) + return (node and (node.name == "air" or node.name=="mcl_core:water_source" or node.name=="mcl_core:water_flowing" or node.name=="mcl_core:lava_source" or node.name=="mcl_core:lava_flowing" or minetest.get_node_def(node.name).buildable_to)) +end + +-- should check if wield is placeable +-- minetest.get_node(wielded:get_name()) ~= nil should probably work +-- otherwise it equips armor and eats food +function ws.can_place_wielded_at(pos) + local wield_empty = minetest.localplayer:get_wielded_item():is_empty() + return not wield_empty and ws.can_place_at(pos) +end + + +function ws.find_any_swap(items,hslot) + hslot=hslot or 8 + for i, v in ipairs(items) do + local n = minetest.find_item(v) + if n then + ws.switch_to_item(v,hslot) + return true + end + end + return false +end + + +-- swaps to any of the items and places if need be +-- returns true if placed and in inventory or already there, false otherwise + +local lastact=0 +local lastplc=0 +local lastdig=0 +local actint=10 +function ws.place(pos,items,hslot, place) + --if nodes_this_tick > 8 then return end + --nodes_this_tick = nodes_this_tick + 1 + --if not inside_constraints(pos) then return end + if not pos then return end + if not ws.can_place_at(pos) then return end + items=tablearg(items) + + place = place or minetest.place_node + + local node = minetest.get_node_or_nil(pos) + if not node then return end + -- already there + if ws.isnode(pos,items) then + return true + else + if ws.find_any_swap(items,hslot) then + place(pos) + return true + end + end +end + +function ws.place_if_able(pos) + if not pos then return end + if not inside_constraints(pos) then return end + if ws.can_place_wielded_at(pos) then + minetest.place_node(pos) + end +end + +function ws.is_diggable(pos) + if not pos then return false end + local nd=minetest.get_node_or_nil(pos) + if not nd then return false end + local n = minetest.get_node_def(nd.name) + if n and n.diggable then return true end + return false +end + +function ws.dig(pos,condition,autotool) + --if not inside_constraints(pos) then return end + if autotool == nil then autotool = true end + if condition and not condition(pos) then return false end + if not ws.is_diggable(pos) then return end + local nd=minetest.get_node_or_nil(pos) + if nd and minetest.get_node_def(nd.name).diggable then + if autotool then ws.select_best_tool(pos) end + minetest.dig_node(pos) + end + return true +end + +function ws.chunk_loaded() + local ign=minetest.find_nodes_near(ws.dircoord(0,0,0),10,{'ignore'},true) + if #ign == 0 then return true end + return false +end + +function ws.get_near(nodes,range) + range=range or 5 + local nds=minetest.find_nodes_near(ws.dircoord(0,0,0),rang,nodes,true) + if #nds > 0 then return nds end + return false +end + +function ws.is_laggy() + if tps_client and tps_client.ping and tps_client.ping > 1000 then return true end +end + + +function ws.donodes(poss,func,condition) + if ws.is_laggy() then return end + local dn_i=0 + for k,v in pairs(poss) do + if dn_i > 8 then return end + --local nd=minetest.get_node_or_nil(v) + if condition == nil or condition(v) then + func(v) + dn_i = dn_i + 1 + end + end +end + +function ws.dignodes(poss,condition) + local func=function(p) ws.dig(p) end + ws.donodes(poss,func,condition) +end + + +function ws.replace(pos,arg) + arg=tablearg(arg) + local nd=minetest.get_node_or_nil(pos) + if nd and not ws.in_list(nd.name,arg) and ws.buildable_to(pos) then + local tm=ws.get_digtime(nd.name) or 0 + ws.dig(pos) + minetest.after(tm + 0.1,function() + ws.place(pos,arg) + end) + return tm + else + return ws.place(pos,arg) + end +end + +function ws.playeron(p) + local pls=minetest.get_player_names() + for k,v in pairs(pls) do + if v == p then return true end + end + return false +end + + +function ws.between(x, y, z) -- x is between y and z (inclusive) + return y <= x and x <= z +end + + +local wall_pos1={x=-1255,y=6,z=792} +local wall_pos2={x=-1452,y=80,z=981} +local iwall_pos1={x=-1266,y=6,z=802} +local iwall_pos2={x=-1442,y=80,z=971} + +function ws.in_cube(tpos,wpos1,wpos2) + local xmax=wpos2.x + local xmin=wpos1.x + + local ymax=wpos2.y + local ymin=wpos1.y + + local zmax=wpos2.z + local zmin=wpos1.z + if wpos1.x > wpos2.x then + xmax=wpos1.x + xmin=wpos2.x + end + if wpos1.y > wpos2.y then + ymax=wpos1.y + ymin=wpos2.y + end + if wpos1.z > wpos2.z then + zmax=wpos1.z + zmin=wpos2.z + end + if ws.between(tpos.x,xmin,xmax) and ws.between(tpos.y,ymin,ymax) and ws.between(tpos.z,zmin,zmax) then + return true + end + return false +end + +function ws.in_wall(pos) + if ws.in_cube(pos,wall_pos1,wall_pos2) and not in_cube(pos,iwall_pos1,iwall_pos2) then + return true end + return false +end + +function ws.inside_wall(pos) + local p1=iwall_pos1 + local p2=iwall_pos2 + if ws.in_cube(pos,p1,p2) then return true end + return false +end + +function ws.find_closest_reachable_airpocket(pos) + local lp=ws.dircoord(0,0,0) + local nds=minetest.find_nodes_near(lp,5,{'air'}) + local odst=10 + local rt=lp + for k,v in ipairs(nds) do + local dst=vector.distance(pos,v) + if dst < odst then odst=dst rt=v end + end + if odst==10 then return false end + return vector.add(rt,vector.new(0,-1,0)) +end + + +-- DEBUG +local function printwieldedmeta() + ws.dcm(dump(minetest.localplayer:get_wielded_item():get_meta():to_table())) +end +minetest.register_cheat('ItemMeta','Test',printwieldedmeta) +nlist = {} +ws.on_connect(function() + ws.lp=minetest.localplayer +end) +local storage=minetest.get_mod_storage() +local sl="default" +local mode=1 --1:add, 2:remove +local nled_hud +local edmode_wason=false +nlist.selected=sl +nlist.dumppos={} + +local modname = minetest.get_current_modname() +local modpath = minetest.get_modpath(modname) +dofile(modpath .. "/forms.lua") +minetest.register_cheat('Lists','nList',function()ws.display_list_formspec("NodeLists",nlist.get_lists(),{}) end) + +ws.rg('NlEdMode','nList','nlist_edmode', function()nlist.show_list(sl,true) end,function() end,function()nlist.hide() end) + + + + +minetest.register_on_punchnode(function(p, n) + if not minetest.settings:get_bool('nlist_edmode') then return end + if mode == 1 then + nlist.add(sl,n.name) + elseif mode ==2 then + nlist.remove(sl,n.name) + end + +end) + + +function nlist.add(list,node) + if node == "" then mode=1 return end + local tb=nlist.get(list) + local str='' + for k,v in pairs(tb) do + str=str..','..v + if v == node then return end + end + str=str..','..node + storage:set_string(list,str) + ws.dcm('added '..str..' to list '..list) +end + +function nlist.remove(list,node) + if node == "" then mode=2 return end + local tb=nlist.get(list) + local rstr='' + for k,v in pairs(tb) do + if v ~= node then rstr = rstr .. ',' .. v end + end + storage:set_string(list, rstr) +end + +function nlist.get(list) + local arr=storage:get_string(list):split(',') + if not arr then arr={} end + return arr +end + +function nlist.get_dumppos() + local arr=minetest.deserialize(storage:get_string("dumppos")) + if not arr then arr={} end + nlist.dumppos=arr + return arr +end +function nlist.set_dumppos(list,pos) + nlist.dumppos=nlist.get_dumppos() + nlist.dumppos[list]=pos + storage:set_string('dumppos',minetest.serialize(nlist.dumppos)) +end + + + +function nlist.get_lists() + local ret={} + for name, _ in pairs(storage:to_table().fields) do + table.insert(ret, name) + end + table.sort(ret) + return ret +end + +function nlist.rename(oldname, newname) + oldname, newname = tostring(oldname), tostring(newname) + local list = storage:get_string(oldname) + if not list or not storage:set_string(newname,list)then return end + if oldname ~= newname then + storage:set_string(list,'') + end + return true +end + +function nlist.clear(list) + storage:set_string(list,'') +end + + +function nlist.getd() + return nlist.get_string(minetest.get_current_modname()) +end + +function nlist.show_list(list,hlp) + if not list then return end + local act="add" + if mode == 2 then act="remove" end + local txt=list .. "\n --\n" .. table.concat(nlist.get(list),"\n") + local htxt="Nodelist edit mode\n .nla/.nlr to switch\n punch node to ".. act .. "\n.nlc to clear\n" + if hlp then txt=htxt .. txt end + set_nled_hud(txt) +end + +function nlist.hide() + if nled_hud then minetest.localplayer:hud_remove(nled_hud) nled_hud=nil end +end + +function nlist.random(list) + local str=storage:get(list) + local tb=str:split(',') + local kk = {} + for k in pairs(tb) do + table.insert(kk, k) + end + return tb[kk[math.random(#kk)]] +end + + +function set_nled_hud(ttext) + if not minetest.localplayer then return end + if type(ttext) ~= "string" then return end + + + local dtext ="List: ".. ttext + + if nled_hud then + minetest.localplayer:hud_change(nled_hud,'text',dtext) + else + nled_hud = minetest.localplayer:hud_add({ + hud_elem_type = 'text', + name = "Nodelist", + text = dtext, + number = 0x00ff00, + direction = 0, + position = {x=0.8,y=0.40}, + alignment ={x=1,y=1}, + offset = {x=0, y=0} + }) + end + return true +end + +local function todflist(list) + --if not minetest.settings:get(list) then return end + minetest.settings:set(list,table.concat(nlist.get(nlist.selected),",")) +end + +minetest.register_chatcommand('nls',{func=function(list) sl=list nlist.selected=list end}) +minetest.register_chatcommand('nlshow',{func=function() nlist.show_list(sl) end}) +minetest.register_chatcommand('nla',{func=function(el) nlist.add(sl,el) end}) +minetest.register_chatcommand('nlr',{func=function(el) nlist.remove(sl,el) end}) +minetest.register_chatcommand('nlc',{func=function(el) nlist.clear(sl) end}) + +minetest.register_chatcommand('nlawi',{func=function() nlist.add(sl,minetest.localplayer:get_wielded_item():get_name()) end}) +minetest.register_chatcommand('nlrwi',{func=function() nlist.remove(sl,minetest.localplayer:get_wielded_item():get_name()) end}) + +minetest.register_chatcommand('nltodf',{func=function(p) todflist(tostring(p)) end}) + + +minetest.register_cheat("NlToDfXray",'nList',function() + todflist('xray_nodes') +end) +minetest.register_cheat("NlToDfSearch",'nList',function() + todflist('search_nodes') +end) +minetest.register_cheat("NlToDfEject",'nList',function() + todflist('eject_nodes') +end) + +function nlist.get_mtnodes() + local arr= { + "default:3dtorch", + "default:acacia_bush", + "default:acacia_bush_leaves", + "default:acacia_bush_sapling", + "default:acacia_bush_stem", + "default:acacia_leaves", + "default:acacia_log", + "default:acacia_sapling", + "default:acacia_tree", + "default:acacia_wood", + "default:apple", + "default:apple_log", + "default:apple_mark", + "default:apple_tree", + "default:aspen_leaves", + "default:aspen_log", + "default:aspen_sapling", + "default:aspen_tree", + "default:aspen_wood", + "default:axe_", + "default:axe_bronze", + "default:axe_diamond", + "default:axe_mese", + "default:axe_steel", + "default:axe_stone", + "default:axe_wood", + "default:blueberries", + "default:blueberry_bush", + "default:blueberry_bush_leaves", + "default:blueberry_bush_leaves_with_berries", + "default:blueberry_bush_sapling", + "default:book", + "default:bookshelf", + "default:book_written", + "default:brick", + "default:bronzeblock", + "default:bronze_ingot", + "default:bush", + "default:bush_leaves", + "default:bush_sapling", + "default:bush_stem", + "default:cactus", + "default:cave_ice", + "default:chest", + "default:chest_locked", + "default:clay", + "default:clay_brick", + "default:clay_lump", + "default:cloud", + "default:coalblock", + "default:coal_lump", + "default:cobble", + "default:cobble]", + "default:convert_saplings_to_node_timer", + "default:copperblock", + "default:copper_ingot", + "default:copper_lump", + "default:coral_brown", + "default:coral_cyan", + "default:coral_green", + "default:coral_orange", + "default:coral_pink", + "default:corals", + "default:coral_skeleton", + "default:desert_cobble", + "default:desert_sand", + "default:desert_sandstone", + "default:desert_sandstone_block", + "default:desert_sandstone_brick", + "default:desert_stone", + "default:desert_stone_block", + "default:desert_stonebrick", + "default:diamond", + "default:diamondblock", + "default:dirt", + "default:dirt_with_coniferous_litter", + "default:dirt_with_dry_grass", + "default:dirt_with_grass", + "default:dirt_with_grass_footsteps", + "default:dirt_with_rainforest_litter", + "default:dirt_with_snow", + "default:dry_dirt", + "default:dry_dirt_with_dry_grass", + "default:dry_grass_", + "default:dry_grass_1", + "default:dry_shrub", + "default:emergent_jungle_sapling", + "default:emergent_jungle_tree", + "default:fence_acacia_wood", + "default:fence_aspen_wood", + "default:fence_junglewood", + "default:fence_pine_wood", + "default:fence_rail_acacia_wood", + "default:fence_rail_aspen_wood", + "default:fence_rail_junglewood", + "default:fence_rail_pine_wood", + "default:fence_rail_wood", + "default:fence_wood", + "default:fern_", + "default:fern_1", + "default:flint", + "default:furnace", + "default:furnace_active", + "default:glass", + "default:goldblock", + "default:gold_ingot", + "default:gold_lump", + "default:grass_", + "default:grass_1", + "default:gravel", + "default:ice", + "default:iron_lump", + "default:junglegrass", + "default:jungleleaves", + "default:jungle_log", + "default:junglesapling", + "default:jungle_tree", + "default:jungletree", + "default:jungle_tree(swamp)", + "default:junglewood", + "default:kelp", + "default:key", + "default:ladder", + "default:ladder_steel", + "default:ladder_wood", + "default:large_cactus", + "default:large_cactus_seedling", + "default:lava_flowing", + "default:lava_source", + "default:leaves", + "default:marram_grass", + "default:marram_grass_", + "default:marram_grass_1", + "default:marram_grass_2", + "default:marram_grass_3", + "default:mese", + "default:mese_block", + "default:mese_crystal", + "default:mese_crystal_fragment", + "default:meselamp", + "default:mese_post_light", + "default:mese_post_light_acacia", + "default:mese_post_light_aspen_wood", + "default:mese_post_light_junglewood", + "default:mese_post_light_pine_wood", + "default:mossycobble", + "default:obsidian", + "default:obsidian_block", + "default:obsidianbrick", + "default:obsidian_glass", + "default:obsidian_shard", + "default:paper", + "default:papyrus", + "default:papyrus_on_dirt", + "default:papyrus_on_dry_dirt", + "default:permafrost", + "default:permafrost_with_moss", + "default:permafrost_with_stones", + "default:pick_", + "default:pick_bronze", + "default:pick_diamond", + "default:pick_mese", + "default:pick_steel", + "default:pick_stone", + "default:pick_wood", + "default:pine_bush", + "default:pine_bush_needles", + "default:pine_bush_sapling", + "default:pine_bush_stem", + "default:pine_log", + "default:pine_needles", + "default:pine_sapling", + "default:pine_tree", + "default:pinetree", + "default:pine_wood", + "default:pinewood", + "default:rail", + "default:river_water_flowing", + "default:river_water_source", + "default:sand", + "default:sandstone", + "default:sandstone_block", + "default:sandstonebrick", + "default:sand_with_kelp", + "default:sapling", + "default:shovel_", + "default:shovel_bronze", + "default:shovel_diamond", + "default:shovel_mese", + "default:shovel_steel", + "default:shovel_stone", + "default:shovel_wood", + "default:sign_wall", + "default:sign_wall_", + "default:sign_wall_steel", + "default:sign_wall_wood", + "default:silver_sand", + "default:silver_sandstone", + "default:silver_sandstone_block", + "default:silver_sandstone_brick", + "default:skeleton_key", + "default:small_pine_tree", + "default:snow", + "default:snowblock", + "default:steelblock", + "default:steel_ingot", + "default:stick", + "default:stone", + "default:stone_block", + "default:stonebrick", + "default:stone_with_coal", + "default:stone_with_copper", + "default:stone_with_diamond", + "default:stone_with_gold", + "default:stone_with_iron", + "default:stone_with_mese", + "default:stone_with_tin", + "default:sword_", + "default:sword_bronze", + "default:sword_diamond", + "default:sword_mese", + "default:sword_steel", + "default:sword_stone", + "default:sword_wood", + "default:tinblock", + "default:tin_ingot", + "default:tin_lump", + "default:torch", + "default:torch_ceiling", + "default:torch_wall", + "default:tree", + "default:upgrade_", + "default:water_flowing", + "default:waterlily", + "default:water_source", + "default:wood" + } + return arr +end + +function nlist.get_mclnodes() + local arr={ + "mcl_anvils:anvil", + 'mcl_anvils:anvil', + 'mcl_anvils:anvil_damage_1', + 'mcl_anvils:anvil_damage_2', + 'mcl_anvils:update_formspec_0_60_0', + 'mcl_armor:boots_', + 'mcl_armor:boots_chain', + 'mcl_armor:boots_diamond', + 'mcl_armor:boots_gold', + 'mcl_armor:boots_iron', + 'mcl_armor:boots_leather', + 'mcl_armor:chestplate_', + 'mcl_armor:chestplate_chain', + 'mcl_armor:chestplate_diamond', + 'mcl_armor:chestplate_gold', + 'mcl_armor:chestplate_iron', + 'mcl_armor:chestplate_leather', + 'mcl_armor:helmet_', + 'mcl_armor:helmet_chain', + 'mcl_armor:helmet_diamond', + 'mcl_armor:helmet_gold', + 'mcl_armor:helmet_iron', + 'mcl_armor:helmet_leather', + 'mcl_armor:leggings_', + 'mcl_armor:leggings_chain', + 'mcl_armor:leggings_diamond', + 'mcl_armor:leggings_gold', + 'mcl_armor:leggings_iron', + 'mcl_armor:leggings_leather', + 'mcl_banners:banner_item_', + 'mcl_banners:banner_item_white', + 'mcl_banners:hanging_banner', + 'mcl_banners:respawn_entities', + 'mcl_banners:standing_banner', + 'mcl_beds:bed_', + 'mcl_beds:bed_red_bottom', + 'mcl_beds:bed_red_top', + 'mcl_beds:bed_white_bottom', + 'mcl_beds:sleeping', + 'mcl_beds:spawn', + 'mcl_biomes:chorus_plant', + 'mcl_boats:boat', + 'mcl_books:book', + 'mcl_books:bookshelf', + 'mcl_books:signing', + 'mcl_books:writable_book', + 'mcl_books:written_book', + 'mcl_bows:arrow', + 'mcl_bows:arrow_box', + 'mcl_bows:arrow_entity', + 'mcl_bows:bow', + 'mcl_bows:bow_', + 'mcl_bows:bow_0', + 'mcl_bows:bow_1', + 'mcl_bows:bow_2', + 'mcl_bows:use_bow', + 'mcl_brewing:stand', + 'mcl_brewing:stand_', + 'mcl_brewing:stand_000', + 'mcl_brewing:stand_001', + 'mcl_brewing:stand_010', + 'mcl_brewing:stand_011', + 'mcl_brewing:stand_100', + 'mcl_brewing:stand_101', + 'mcl_brewing:stand_110', + 'mcl_brewing:stand_111', + 'mcl_buckets:bucket_empty', + 'mcl_buckets:bucket_lava', + 'mcl_buckets:bucket_river_water', + 'mcl_buckets:bucket_water', + 'mcl_cake:cake', + 'mcl_cake:cake_', + 'mcl_cake:cake_1', + 'mcl_cake:cake_6', + 'mcl_cauldrons:cauldron', + 'mcl_cauldrons:cauldron_', + 'mcl_cauldrons:cauldron_1', + 'mcl_cauldrons:cauldron_1r', + 'mcl_cauldrons:cauldron_2', + 'mcl_cauldrons:cauldron_2r', + 'mcl_cauldrons:cauldron_3', + 'mcl_cauldrons:cauldron_3r', + 'mcl_chests:chest', + 'mcl_chests:ender_chest', + 'mcl_chests:reset_trapped_chests', + 'mcl_chests:trapped_chest', + 'mcl_chests:trapped_chest_', + 'mcl_chests:trapped_chest_left', + 'mcl_chests:trapped_chest_on', + 'mcl_chests:trapped_chest_on_left', + 'mcl_chests:trapped_chest_on_right', + 'mcl_chests:trapped_chest_right', + 'mcl_chests:update_ender_chest_formspecs_0_60_0', + 'mcl_chests:update_formspecs_0_51_0', + 'mcl_chests:update_shulker_box_formspecs_0_60_0', + 'mcl_chests:violet_shulker_box', + 'mcl_clock:clock', + 'mcl_clock:clock_', + 'mcl_cocoas:cocoa_1', + 'mcl_cocoas:cocoa_2', + 'mcl_cocoas:cocoa_3', + 'mcl_colorblocks:concrete_', + 'mcl_colorblocks:concrete_powder_', + 'mcl_colorblocks:glazed_terracotta_', + 'mcl_colorblocks:glazed_terracotta_black', + 'mcl_colorblocks:glazed_terracotta_blue', + 'mcl_colorblocks:glazed_terracotta_brown', + 'mcl_colorblocks:glazed_terracotta_cyan', + 'mcl_colorblocks:glazed_terracotta_green', + 'mcl_colorblocks:glazed_terracotta_grey', + 'mcl_colorblocks:glazed_terracotta_light_blue', + 'mcl_colorblocks:glazed_terracotta_lime', + 'mcl_colorblocks:glazed_terracotta_magenta', + 'mcl_colorblocks:glazed_terracotta_orange', + 'mcl_colorblocks:glazed_terracotta_pink', + 'mcl_colorblocks:glazed_terracotta_purple', + 'mcl_colorblocks:glazed_terracotta_red', + 'mcl_colorblocks:glazed_terracotta_silver', + 'mcl_colorblocks:glazed_terracotta_white', + 'mcl_colorblocks:glazed_terracotta_yellow', + 'mcl_colorblocks:hardened_clay', + 'mcl_colorblocks:hardened_clay_', + 'mcl_colorblocks:hardened_clay_orange', + 'mcl_comparators:comparator_', + 'mcl_comparators:comparator_off_', + 'mcl_comparators:comparator_off_comp', + 'mcl_comparators:comparator_off_sub', + 'mcl_comparators:comparator_on_', + 'mcl_comparators:comparator_on_comp', + 'mcl_comparators:comparator_on_sub', + 'mcl_compass:compass', + 'mcl_core:acacialeaves', + 'mcl_core:acaciasapling', + 'mcl_core:acaciatree', + 'mcl_core:acaciawood', + 'mcl_core:andesite', + 'mcl_core:andesite_smooth', + 'mcl_core:apple', + 'mcl_core:apple_gold', + 'mcl_core:axe_diamond', + 'mcl_core:axe_gold', + 'mcl_core:axe_iron', + 'mcl_core:axe_stone', + 'mcl_core:axe_wood', + 'mcl_core:barrier', + 'mcl_core:bedrock', + 'mcl_core:birchsapling', + 'mcl_core:birchtree', + 'mcl_core:birchwood', + 'mcl_core:bone_block', + 'mcl_core:bowl', + 'mcl_core:brick', + 'mcl_core:brick_block', + 'mcl_core:cactus', + 'mcl_core:charcoal_lump', + 'mcl_core:clay', + 'mcl_core:clay_lump', + 'mcl_core:coalblock', + 'mcl_core:coal_lump', + 'mcl_core:coarse_dirt', + 'mcl_core:cobble', + 'mcl_core:cobblestone', + 'mcl_core:cobweb', + 'mcl_core:darksapling', + 'mcl_core:darktree', + 'mcl_core:darkwood', + 'mcl_core:deadbush', + 'mcl_core:diamond', + 'mcl_core:diamondblock', + 'mcl_core:diorite', + 'mcl_core:diorite_smooth', + 'mcl_core:dirt', + 'mcl_core:dirt_with_dry_grass', + 'mcl_core:dirt_with_dry_grass_snow', + 'mcl_core:dirt_with_grass', + 'mcl_core:dirt_with_grass_snow', + 'mcl_core:emerald', + 'mcl_core:emeraldblock', + 'mcl_core:flint', + 'mcl_core:frosted_ice_', + 'mcl_core:frosted_ice_0', + 'mcl_core:glass', + 'mcl_core:glass_', + 'mcl_core:glass_black', + 'mcl_core:glass_blue', + 'mcl_core:glass_brown', + 'mcl_core:glass_cyan', + 'mcl_core:glass_gray', + 'mcl_core:glass_green', + 'mcl_core:glass_light_blue', + 'mcl_core:glass_lime', + 'mcl_core:glass_magenta', + 'mcl_core:glass_orange', + 'mcl_core:glass_pink', + 'mcl_core:glass_purple', + 'mcl_core:glass_red', + 'mcl_core:glass_silver', + 'mcl_core:glass_white', + 'mcl_core:glass_yellow', + 'mcl_core:goldblock', + 'mcl_core:gold_ingot', + 'mcl_core:gold_nugget', + 'mcl_core:granite', + 'mcl_core:granite_smooth', + 'mcl_core:grass_path', + 'mcl_core:gravel', + 'mcl_core:ice', + 'mcl_core:ironblock', + 'mcl_core:iron_ingot', + 'mcl_core:iron_nugget', + 'mcl_core:jungleleaves', + 'mcl_core:junglesapling', + 'mcl_core:jungletree', + 'mcl_core:junglewood', + 'mcl_core:ladder', + 'mcl_core:lapisblock', + 'mcl_core:lava_flowing', + 'mcl_core:lava_source', + 'mcl_core:leaves', + 'mcl_core:mat', + 'mcl_core:mossycobble', + 'mcl_core:mycelium', + 'mcl_core:mycelium_snow', + 'mcl_core:obsidian', + 'mcl_core:packed_ice', + 'mcl_core:paper', + 'mcl_core:pick_diamond', + 'mcl_core:pick_gold', + 'mcl_core:pick_iron', + 'mcl_core:pick_stone', + 'mcl_core:pick_wood', + 'mcl_core:podzol', + 'mcl_core:podzol_snow', + 'mcl_core:realm_barrier', + 'mcl_core:redsand', + 'mcl_core:redsandstone', + 'mcl_core:redsandstonecarved', + 'mcl_core:redsandstonesmooth', + 'mcl_core:redsandstonesmooth2', + 'mcl_core:reeds', + 'mcl_core:replace_legacy_dry_grass_0_65_0', + 'mcl_core:sand', + 'mcl_core:sandstone', + 'mcl_core:sandstonecarved', + 'mcl_core:sandstonesmooth', + 'mcl_core:sandstonesmooth2', + 'mcl_core:sapling', + 'mcl_core:shears', + 'mcl_core:shovel_diamond', + 'mcl_core:shovel_gold', + 'mcl_core:shovel_iron', + 'mcl_core:shovel_stone', + 'mcl_core:shovel_wood', + 'mcl_core:slimeblock', + 'mcl_core:snow', + 'mcl_core:snow_', + 'mcl_core:snowblock', + 'mcl_core:spruceleaves', + 'mcl_core:sprucesapling', + 'mcl_core:sprucetree', + 'mcl_core:sprucewood', + 'mcl_core:stick', + 'mcl_core:stone', + 'mcl_core:stonebrick', + 'mcl_core:stonebrickcarved', + 'mcl_core:stonebrickcracked', + 'mcl_core:stonebrickmossy', + 'mcl_core:stone_smooth', + 'mcl_core:stone_with_coal', + 'mcl_core:stone_with_diamond', + 'mcl_core:stone_with_emerald', + 'mcl_core:stone_with_gold', + 'mcl_core:stone_with_iron', + 'mcl_core:stone_with_lapis', + 'mcl_core:stone_with_redstone', + 'mcl_core:stone_with_redstone_lit', + 'mcl_core:sugar', + 'mcl_core:sword_diamond', + 'mcl_core:sword_gold', + 'mcl_core:sword_iron', + 'mcl_core:sword_stone', + 'mcl_core:sword_wood', + 'mcl_core:tallgrass', + 'mcl_core:torch', + 'mcl_core:tree', + 'mcl_core:vine', + 'mcl_core:void', + 'mcl_core:water_flowing', + 'mcl_core:water_source', + 'mcl_core:wood', + 'mcl_dispenser:dispenser_down', + 'mcl_dispenser:dispenser_up', + 'mcl_dispensers:dispenser', + 'mcl_dispensers:dispenser_down', + 'mcl_dispensers:dispenser_up', + 'mcl_dispensers:update_formspecs_0_60_0', + 'mcl_doors:acacia_door', + 'mcl_doors:birch_door', + 'mcl_doors:dark_oak_door', + 'mcl_doors:iron_door', + 'mcl_doors:iron_trapdoor', + 'mcl_doors:iron_trapdoor_open', + 'mcl_doors:jungle_door', + 'mcl_doors:register_door', + 'mcl_doors:register_trapdoor', + 'mcl_doors:spruce_door', + 'mcl_doors:trapdoor', + 'mcl_doors:trapdoor_open', + 'mcl_doors:wooden_door', + 'mcl_droppers:dropper', + 'mcl_droppers:dropper_down', + 'mcl_droppers:dropper_up', + 'mcl_droppers:update_formspecs_0_51_0', + 'mcl_droppers:update_formspecs_0_60_0', + 'mcl_dye:black', + 'mcl_dye:blue', + 'mcl_dye:brown', + 'mcl_dye:cyan', + 'mcl_dye:dark_green', + 'mcl_dye:dark_grey', + 'mcl_dye:green', + 'mcl_dye:grey', + 'mcl_dye:lightblue', + 'mcl_dye:magenta', + 'mcl_dye:orange', + 'mcl_dye:pink', + 'mcl_dye:red', + 'mcl_dye:violet', + 'mcl_dye:white', + 'mcl_dye:yellow', + 'mcl_end:chorus_flower', + 'mcl_end:chorus_flower_dead', + 'mcl_end:chorus_fruit', + 'mcl_end:chorus_fruit_popped', + 'mcl_end:chorus_plant', + 'mcl_end:dragon_egg', + 'mcl_end:end_bricks', + 'mcl_end:ender_eye', + 'mcl_end:end_rod', + 'mcl_end:end_stone', + 'mcl_end:purpur_block', + 'mcl_end:purpur_pillar', + 'mcl_farming:add_gourd', + 'mcl_farming:add_plant', + 'mcl_farming:beetroot', + 'mcl_farming:beetroot_', + 'mcl_farming:beetroot_0', + 'mcl_farming:beetroot_1', + 'mcl_farming:beetroot_2', + 'mcl_farming:beetroot_item', + 'mcl_farming:beetroot_seeds', + 'mcl_farming:beetroot_soup', + 'mcl_farming:bread', + 'mcl_farming:carrot', + 'mcl_farming:carrot_', + 'mcl_farming:carrot_1', + 'mcl_farming:carrot_2', + 'mcl_farming:carrot_3', + 'mcl_farming:carrot_4', + 'mcl_farming:carrot_5', + 'mcl_farming:carrot_6', + 'mcl_farming:carrot_7', + 'mcl_farming:carrot_item', + 'mcl_farming:carrot_item_gold', + 'mcl_farming:cookie', + 'mcl_farming:grow_plant', + 'mcl_farming:growth', + 'mcl_farming:hay_block', + 'mcl_farming:hoe_diamond', + 'mcl_farming:hoe_gold', + 'mcl_farming:hoe_iron', + 'mcl_farming:hoe_stone', + 'mcl_farming:hoe_wood', + 'mcl_farming:melon', + 'mcl_farming:melon_item', + 'mcl_farming:melon_seeds', + 'mcl_farming:melontige_', + 'mcl_farming:melontige_1', + 'mcl_farming:melontige_2', + 'mcl_farming:melontige_3', + 'mcl_farming:melontige_4', + 'mcl_farming:melontige_5', + 'mcl_farming:melontige_6', + 'mcl_farming:melontige_7', + 'mcl_farming:melontige_linked', + 'mcl_farming:melontige_unconnect', + 'mcl_farming:mushroom_brown', + 'mcl_farming:mushroom_red', + 'mcl_farming:place_seed', + 'mcl_farming:potato', + 'mcl_farming:potato_', + 'mcl_farming:potato_1', + 'mcl_farming:potato_2', + 'mcl_farming:potato_3', + 'mcl_farming:potato_4', + 'mcl_farming:potato_5', + 'mcl_farming:potato_6', + 'mcl_farming:potato_7', + 'mcl_farming:potato_item', + 'mcl_farming:potato_item_baked', + 'mcl_farming:potato_item_poison', + 'mcl_farming:pumkin_seeds', + 'mcl_farming:pumpkin', + 'mcl_farming:pumpkin_', + 'mcl_farming:pumpkin_1', + 'mcl_farming:pumpkin_2', + 'mcl_farming:pumpkin_3', + 'mcl_farming:pumpkin_4', + 'mcl_farming:pumpkin_5', + 'mcl_farming:pumpkin_6', + 'mcl_farming:pumpkin_7', + 'mcl_farming:pumpkin_face', + 'mcl_farming:pumpkin_face_light', + 'mcl_farming:pumpkin_pie', + 'mcl_farming:pumpkin_seeds', + 'mcl_farming:pumpkintige_linked', + 'mcl_farming:pumpkintige_unconnect', + 'mcl_farming:soil', + 'mcl_farming:soil_wet', + 'mcl_farming:stem_color', + 'mcl_farming:wheat', + 'mcl_farming:wheat_', + 'mcl_farming:wheat_1', + 'mcl_farming:wheat_2', + 'mcl_farming:wheat_3', + 'mcl_farming:wheat_4', + 'mcl_farming:wheat_5', + 'mcl_farming:wheat_6', + 'mcl_farming:wheat_7', + 'mcl_farming:wheat_item', + 'mcl_farming:wheat_seeds', + 'mcl_fences:dark_oak_fence', + 'mcl_fences:fence', + 'mcl_fences:nether_brick_fence', + 'mcl_fire:basic_flame', + 'mcl_fire:eternal_fire', + 'mcl_fire:fire', + 'mcl_fire:fire_charge', + 'mcl_fire:flint_and_steel', + 'mcl_fire:smoke', + 'mcl_fishing:bobber', + 'mcl_fishing:bobber_entity', + 'mcl_fishing:clownfish_raw', + 'mcl_fishing:fish_cooked', + 'mcl_fishing:fishing_rod', + 'mcl_fishing:fish_raw', + 'mcl_fishing:pufferfish_raw', + 'mcl_fishing:salmon_cooked', + 'mcl_fishing:salmon_raw', + 'mcl_flowerpots:flower_pot', + 'mcl_flowerpots:flower_pot_', + 'mcl_flowers:allium', + 'mcl_flowers:azure_bluet', + 'mcl_flowers:blue_orchid', + 'mcl_flowers:dandelion', + 'mcl_flowers:double_fern', + 'mcl_flowers:double_fern_top', + 'mcl_flowers:double_grass', + 'mcl_flowers:double_grass_top', + 'mcl_flowers:fern', + 'mcl_flowers:lilac', + 'mcl_flowers:lilac_top', + 'mcl_flowers:oxeye_daisy', + 'mcl_flowers:peony', + 'mcl_flowers:peony_top', + 'mcl_flowers:poppy', + 'mcl_flowers:rose_bush', + 'mcl_flowers:rose_bush_top', + 'mcl_flowers:sunflower', + 'mcl_flowers:sunflower_top', + 'mcl_flowers:tallgrass', + 'mcl_flowers:tulip_orange', + 'mcl_flowers:tulip_pink', + 'mcl_flowers:tulip_red', + 'mcl_flowers:tulip_white', + 'mcl_flowers:waterlily', + 'mcl_furnaces:flames', + 'mcl_furnaces:furnace', + 'mcl_furnaces:furnace_active', + 'mcl_furnaces:update_formspecs_0_60_0', + 'mcl_heads:creeper', + 'mcl_heads:skeleton', + 'mcl_heads:wither_skeleton', + 'mcl_heads:zombie', + 'mcl_hoppers:hopper', + 'mcl_hoppers:hopper_disabled', + 'mcl_hoppers:hopper_item', + 'mcl_hoppers:hopper_side', + 'mcl_hoppers:hopper_side_disabled', + 'mcl_hoppers:update_formspec_0_60_0', + 'mcl_hunger:exhaustion', + 'mcl_hunger:hunger', + 'mcl_hunger:saturation', + 'mcl_inventory:workbench', + 'mcl_itemframes:item', + 'mcl_itemframes:item_frame', + 'mcl_itemframes:respawn_entities', + 'mcl_itemframes:update_legacy_item_frames', + 'mcl_jukebox:jukebox', + 'mcl_jukebox:record_', + 'mcl_jukebox:record_1', + 'mcl_jukebox:record_2', + 'mcl_jukebox:record_3', + 'mcl_jukebox:record_4', + 'mcl_jukebox:record_5', + 'mcl_jukebox:record_6', + 'mcl_jukebox:record_7', + 'mcl_jukebox:record_8', + 'mcl_jukebox:record_9', + 'mcl_maps:empty_map', + 'mcl_maps:filled_map', + 'mcl_meshhand:hand', + 'mcl_minecarts:activator_rail', + 'mcl_minecarts:activator_rail_on', + 'mcl_minecarts:check_front_up_down', + 'mcl_minecarts:chest_minecart', + 'mcl_minecarts:command_block_minecart', + 'mcl_minecarts:detector_rail', + 'mcl_minecarts:detector_rail_on', + 'mcl_minecarts:furnace_minecart', + 'mcl_minecarts:get_rail_direction', + 'mcl_minecarts:get_sign', + 'mcl_minecarts:golden_rail', + 'mcl_minecarts:golden_rail_on', + 'mcl_minecarts:hopper_minecart', + 'mcl_minecarts:is_rail', + 'mcl_minecarts:minecart', + 'mcl_minecarts:rail', + 'mcl_minecarts:tnt_minecart', + 'mcl_minecarts:velocity_to_dir', + 'mcl_mobitems:beef', + 'mcl_mobitems:blaze_powder', + 'mcl_mobitems:blaze_rod', + 'mcl_mobitems:bone', + 'mcl_mobitems:carrot_on_a_stick', + 'mcl_mobitems:chicken', + 'mcl_mobitems:cooked_beef', + 'mcl_mobitems:cooked_chicken', + 'mcl_mobitems:cooked_mutton', + 'mcl_mobitems:cooked_porkchop', + 'mcl_mobitems:cooked_rabbit', + 'mcl_mobitems:ender_eye', + 'mcl_mobitems:feather', + 'mcl_mobitems:ghast_tear', + 'mcl_mobitems:gunpowder', + 'mcl_mobitems:leather', + 'mcl_mobitems:magma_cream', + 'mcl_mobitems:milk_bucket', + 'mcl_mobitems:mutton', + 'mcl_mobitems:nether_star', + 'mcl_mobitems:porkchop', + 'mcl_mobitems:rabbit', + 'mcl_mobitems:rabbit_foot', + 'mcl_mobitems:rabbit_hide', + 'mcl_mobitems:rabbit_stew', + 'mcl_mobitems:rotten_flesh', + 'mcl_mobitems:saddle', + 'mcl_mobitems:shulker_shell', + 'mcl_mobitems:slimeball', + 'mcl_mobitems:spider_eye', + 'mcl_mobitems:string', + 'mcl_mobs:nametag', + 'mcl_mobspawners:doll', + 'mcl_mobspawners:respawn_entities', + 'mcl_mobspawners:spawner', + 'mcl_mushrooms:brown_mushroom_block_cap_corner', + 'mcl_mushrooms:brown_mushroom_block_cap_side', + 'mcl_mushrooms:mushroom_brown', + 'mcl_mushrooms:mushroom_red', + 'mcl_mushrooms:mushroom_stew', + 'mcl_mushrooms:red_mushroom_block_cap_corner', + 'mcl_mushrooms:red_mushroom_block_cap_side', + 'mcl_mushrooms:replace_legacy_mushroom_caps', + 'mcl_nether:glowstone', + 'mcl_nether:glowstone_dust', + 'mcl_nether:magma', + 'mcl_nether:nether_brick', + 'mcl_observers:observer_down', + 'mcl_observers:observer_down_off', + 'mcl_observers:observer_down_on', + 'mcl_observers:observer_off', + 'mcl_observers:observer_on', + 'mcl_observers:observer_up', + 'mcl_observers:observer_up_off', + 'mcl_observers:observer_up_on', + 'mcl_ocean:dead_', + 'mcl_ocean:dead_brain_coral_block', + 'mcl_ocean:dried_kelp', + 'mcl_ocean:dried_kelp_block', + 'mcl_ocean:kelp', + 'mcl_ocean:kelp_', + 'mcl_ocean:kelp_dirt', + 'mcl_ocean:kelp_gravel', + 'mcl_ocean:kelp_redsand', + 'mcl_ocean:kelp_sand', + 'mcl_ocean:prismarine', + 'mcl_ocean:prismarine_brick', + 'mcl_ocean:prismarine_crystals', + 'mcl_ocean:prismarine_dark', + 'mcl_ocean:prismarine_shard', + 'mcl_ocean:seagrass', + 'mcl_ocean:seagrass_', + 'mcl_ocean:seagrass_dirt', + 'mcl_ocean:seagrass_gravel', + 'mcl_ocean:seagrass_redsand', + 'mcl_ocean:seagrass_sand', + 'mcl_ocean:sea_lantern', + 'mcl_ocean:sea_pickle_', + 'mcl_ocean:sea_pickle_1_', + 'mcl_ocean:sea_pickle_1_dead_brain_coral_block', + 'mcl_ocean:sea_pickle_1_off_', + 'mcl_ocean:sea_pickle_1_off_dead_brain_coral_block', + 'mcl_paintings:painting', + 'mcl_playerplus:surface', + 'mcl_player:preview', + 'mcl_portals:end_portal_frame', + 'mcl_portals:end_portal_frame_eye', + 'mcl_portals:portal', + 'mcl_portals:portal_end', + 'mcl_potions:awkward', + 'mcl_potions:dragon_breath', + 'mcl_potions:fermented_spider_eye', + 'mcl_potions:fire_resistance', + 'mcl_potions:glass_bottle', + 'mcl_potions:harming', + 'mcl_potions:harming_2', + 'mcl_potions:harming_2_splash', + 'mcl_potions:harming_splash', + 'mcl_potions:healing', + 'mcl_potions:healing_2', + 'mcl_potions:healing_2_splash', + 'mcl_potions:healing_splash', + 'mcl_potions:invisibility', + 'mcl_potions:invisibility_plus', + 'mcl_potions:invisibility_plus_splash', + 'mcl_potions:invisibility_splash', + 'mcl_potions:leaping', + 'mcl_potions:leaping_plus', + 'mcl_potions:leaping_plus_splash', + 'mcl_potions:leaping_splash', + 'mcl_potions:mundane', + 'mcl_potions:night_vision', + 'mcl_potions:night_vision_arrow', + 'mcl_potions:night_vision_lingering', + 'mcl_potions:night_vision_plus', + 'mcl_potions:night_vision_plus_arrow', + 'mcl_potions:night_vision_plus_lingering', + 'mcl_potions:night_vision_plus_splash', + 'mcl_potions:night_vision_splash', + 'mcl_potions:poison', + 'mcl_potions:poison_2', + 'mcl_potions:poison_2_splash', + 'mcl_potions:poison_splash', + 'mcl_potions:regeneration', + 'mcl_potions:river_water', + 'mcl_potions:slowness', + 'mcl_potions:slowness_plus', + 'mcl_potions:slowness_plus_splash', + 'mcl_potions:slowness_splash', + 'mcl_potions:speckled_melon', + 'mcl_potions:strength', + 'mcl_potions:strength_2', + 'mcl_potions:strength_2_lingering', + 'mcl_potions:strength_2_splash', + 'mcl_potions:strength_lingering', + 'mcl_potions:strength_plus', + 'mcl_potions:strength_plus_lingering', + 'mcl_potions:strength_plus_splash', + 'mcl_potions:strength_splash', + 'mcl_potions:swiftness', + 'mcl_potions:swiftness_plus', + 'mcl_potions:swiftness_plus_splash', + 'mcl_potions:swiftness_splash', + 'mcl_potions:thick', + 'mcl_potions:water', + 'mcl_potions:water_breathing', + 'mcl_potions:water_splash', + 'mcl_potions:weakness', + 'mcl_potions:weakness_lingering', + 'mcl_potions:weakness_plus', + 'mcl_potions:weakness_plus_lingering', + 'mcl_potions:weakness_plus_splash', + 'mcl_potions:weakness_splash', + 'mcl_signs:respawn_entities', + 'mcl_signs:set_text_', + 'mcl_signs:standing_sign', + 'mcl_signs:standing_sign22_5', + 'mcl_signs:standing_sign45', + 'mcl_signs:standing_sign67_5', + 'mcl_signs:text', + 'mcl_signs:wall_sign', + 'mcl_skins:skin_id', + 'mcl_skins:skin_select', + 'mcl_sponges:sponge', + 'mcl_sponges:sponge_wet', + 'mcl_sponges:sponge_wet_river_water', + 'mcl_sprint:sprint', + 'mcl_stairs:slab_', + 'mcl_stairs:slab_concrete_', + 'mcl_stairs:slab_purpur_block', + 'mcl_stairs:slab_quartzblock', + 'mcl_stairs:slab_redsandstone', + 'mcl_stairs:slab_sandstone', + 'mcl_stairs:slab_stone', + 'mcl_stairs:slab_stonebrick', + 'mcl_stairs:slab_stone_double', + 'mcl_stairs:slab_wood', + 'mcl_stairs:stair_', + 'mcl_stairs:stair_cobble', + 'mcl_stairs:stair_concrete_', + 'mcl_stairs:stair_sandstone', + 'mcl_stairs:stair_stonebrick', + 'mcl_stairs:stair_stonebrickcracked', + 'mcl_stairs:stair_stonebrickcracked_inner', + 'mcl_stairs:stair_stonebrickcracked_outer', + 'mcl_stairs:stair_stonebrick_inner', + 'mcl_stairs:stair_stonebrickmossy', + 'mcl_stairs:stair_stonebrickmossy_inner', + 'mcl_stairs:stair_stonebrickmossy_outer', + 'mcl_stairs:stair_stonebrick_outer', + 'mcl_stairs:stairs_wood', + 'mcl_supplemental:nether_brick_fence_gate', + 'mcl_supplemental:nether_brick_fence_gate_open', + 'mcl_supplemental:red_nether_brick_fence', + 'mcl_supplemental:red_nether_brick_fence_gate', + 'mcl_supplemental:red_nether_brick_fence_gate_open', + 'mcl_throwing:arrow', + 'mcl_throwing:bow', + 'mcl_throwing:egg', + 'mcl_throwing:egg_entity', + 'mcl_throwing:ender_pearl', + 'mcl_throwing:ender_pearl_entity', + 'mcl_throwing:flying_bobber', + 'mcl_throwing:flying_bobber_entity', + 'mcl_throwing:snowball', + 'mcl_throwing:snowball_entity', + 'mcl_tnt:tnt', + 'mcl_tools:axe_diamond', + 'mcl_tools:axe_gold', + 'mcl_tools:axe_iron', + 'mcl_tools:axe_stone', + 'mcl_tools:axe_wood', + 'mcl_tools:pick_diamond', + 'mcl_tools:pick_gold', + 'mcl_tools:pick_iron', + 'mcl_tools:pick_stone', + 'mcl_tools:pick_wood', + 'mcl_tools:shears', + 'mcl_tools:shovel_diamond', + 'mcl_tools:shovel_gold', + 'mcl_tools:shovel_iron', + 'mcl_tools:shovel_stone', + 'mcl_tools:shovel_wood', + 'mcl_tools:sword_diamond', + 'mcl_tools:sword_gold', + 'mcl_tools:sword_iron', + 'mcl_tools:sword_stone', + 'mcl_tools:sword_wood', + 'mcl_torches:flames', + 'mcl_torches:torch', + 'mcl_torches:torch_wall', + 'mcl_walls:andesite', + 'mcl_walls:brick', + 'mcl_walls:cobble', + 'mcl_walls:diorite', + 'mcl_walls:endbricks', + 'mcl_walls:granite', + 'mcl_walls:mossycobble', + 'mcl_walls:netherbrick', + 'mcl_walls:prismarine', + 'mcl_walls:rednetherbrick', + 'mcl_walls:redsandstone', + 'mcl_walls:sandstone', + 'mcl_walls:stonebrick', + 'mcl_walls:stonebrickmossy', + 'mcl_wool:black', + 'mcl_wool:black_carpet', + 'mcl_wool:blue', + 'mcl_wool:blue_carpet', + 'mcl_wool:brown', + 'mcl_wool:brown_carpet', + 'mcl_wool:cyan', + 'mcl_wool:cyan_carpet', + 'mcl_wool:dark_blue', + 'mcl_wool:gold', + 'mcl_wool:green', + 'mcl_wool:green_carpet', + 'mcl_wool:grey', + 'mcl_wool:grey_carpet', + 'mcl_wool:light_blue', + 'mcl_wool:light_blue_carpet', + 'mcl_wool:lime', + 'mcl_wool:lime_carpet', + 'mcl_wool:magenta', + 'mcl_wool:magenta_carpet', + 'mcl_wool:orange', + 'mcl_wool:orange_carpet', + 'mcl_wool:pink', + 'mcl_wool:pink_carpet', + 'mcl_wool:purple', + 'mcl_wool:purple_carpet', + 'mcl_wool:red', + 'mcl_wool:red_carpet', + 'mcl_wool:silver', + 'mcl_wool:silver_carpet', + 'mcl_wool:white', + 'mcl_wool:white_carpet', + 'mcl_wool:yellow', + 'mcl_wool:yellow_carpet' + } + return arr +end diff --git a/notebook/README.md b/notebook/README.md new file mode 100644 index 0000000..b995a5c --- /dev/null +++ b/notebook/README.md @@ -0,0 +1,2 @@ +The chatcommand `notebook` is added. +It opens a formspec in that you can write anything. \ No newline at end of file diff --git a/notebook/depends.txt b/notebook/depends.txt new file mode 100644 index 0000000..68fc3b1 --- /dev/null +++ b/notebook/depends.txt @@ -0,0 +1 @@ +worldstorage \ No newline at end of file diff --git a/notebook/init.lua b/notebook/init.lua new file mode 100644 index 0000000..6865f0b --- /dev/null +++ b/notebook/init.lua @@ -0,0 +1,49 @@ +--[[ + __ ___. __ + ____ _____/ |_ ____\_ |__ ____ ____ | | __ + / \ / _ \ __\/ __ \| __ \ / _ \ / _ \| |/ / +| | ( <_> ) | \ ___/| \_\ ( <_> | <_> ) < +|___| /\____/|__| \___ >___ /\____/ \____/|__|_ \ + \/ \/ \/ \/ +--]] + +local load_time_start = os.clock() +local modname = minetest.get_current_modname() + + +minetest.register_chatcommand("notebook", { + params = "", + description = "Opens a formspec.", + func = function(param) + if not worldstorage.get_current_worldname() then + return false, "Missing worldname." + end + minetest.show_formspec("notebook", "size[5,4]".. + "textarea[0.05,0;5.5,4;text;Write here anything, you want:;".. + (worldstorage.get_string("notebook:notes") or "").."]".. + "button_exit[2,3.5;1,1;save;Save]".. + "button_exit[4.5,-0.5;0.5,1;discard;X]" + ) + return true, "Opening notebook..." + end, +}) + +minetest.register_on_formspec_input(function(formname, fields) + if formname ~= "notebook" then + return + end + if not fields.text or fields.discard then + return true + end + worldstorage.set_string("notebook:notes", fields.text) + return true +end) + + +local time = math.floor(tonumber(os.clock()-load_time_start)*100+0.5)/100 +local msg = "["..modname.."] loaded after ca. "..time +if time > 0.05 then + print(msg) +else + minetest.log("info", msg) +end diff --git a/notebook/mod.conf b/notebook/mod.conf new file mode 100644 index 0000000..7256eab --- /dev/null +++ b/notebook/mod.conf @@ -0,0 +1 @@ +name = notebook \ No newline at end of file diff --git a/optimize/init.lua b/optimize/init.lua new file mode 100644 index 0000000..27ca8d4 --- /dev/null +++ b/optimize/init.lua @@ -0,0 +1,70 @@ +-- CC0/Unlicense Emilia 2020 + +-- Optimizes stuff. + + +woptimize={} + +function woptimize.countents() + local obj = minetest.localplayer.get_nearby_objects(10000) + ws.dcm("Entity count: "..#obj) +end + + +-- texture is a prefix +local function remove_ents(texture) + if not minetest.localplayer then return end + local obj = minetest.localplayer.get_nearby_objects(10000) + + for i, v in ipairs(obj) do + -- CAOs with water/lava textures are droplets + --minetest.log("ERROR",v:get_item_textures()) + --ws.dcm(v:get_item_textures()) + local txt=v:get_item_textures() + if type(txt) == "string" and txt:find(texture) then + v:set_visible(false) + v:remove(true) + end + end +end + + + +local function remove_hud(name) + local player = minetest.localplayer + local def + local i = -1 + if not player then return end + repeat + i = i + 1 + def = player:hud_get(i) + until not def or def.text:find(name) + if def then + minetest.localplayer:hud_remove(i) + end +end + + +core.register_on_spawn_particle(function(particle) + if minetest.settings:get_bool("noparticles") then return true end +end) + +local epoch = os.clock() + +minetest.register_globalstep(function() + if not minetest.localplayer then return end + if os.clock() > epoch + 1 then + if minetest.settings:get_bool("optimize_water_drops") then + remove_ents("default_water_source") + end + if minetest.settings:get_bool("optimize_burning") then + remove_hud('mcl_burning_hud_flame_animated.png') + end + epoch = os.clock() + end +end) + + +minetest.register_cheat("NoParticles", "Render", "noparticles") +minetest.register_cheat("NoDroplets", "Render", "optimize_water_drops") +minetest.register_cheat("NoBurning", "Render", "optimize_burning") diff --git a/optimize/settingtypes.txt b/optimize/settingtypes.txt new file mode 100644 index 0000000..db7e74e --- /dev/null +++ b/optimize/settingtypes.txt @@ -0,0 +1,3 @@ +optimize_water_drops (Hide water drops, improves framerates in watery areas) bool false +optimize_hearts (Hide damage hearts) bool false +noparticles (Do not show particles) bool false diff --git a/quint/init.lua b/quint/init.lua new file mode 100644 index 0000000..3de9ca1 --- /dev/null +++ b/quint/init.lua @@ -0,0 +1,288 @@ +-- CC0/Unlicense Emilia 2020 + +--[[ +invaction +place +dig +interact +--]] + +-- invaction stuff needs to change indices to 1 indexed soon(tm) + + +-- Queue Interact +-- think of it like minetest.after() but with automatic time calculation and a bit more parallel +quint = {} + +-- New empty invaction quint +function quint.invaction_new() + return {start = {}, q = {}, current = {}, last = 0, index = 1} +end + +-- Get a global index for an inventory location from part of an invaction +local function format_inv(taction) + return taction.location .. ";" .. taction.inventory +end + +-- Split a global inventory index +local function parse_inv(inv) + local spl = string.split(inv, ";") + return {location = spl[1], inventory = spl[2]} +end + +-- Get some useful things from an invaction +local function parse_invaction(lists, taction) + local idx = format_inv(taction) + local slot = taction.slot + local itemstack = lists[idx][slot] + + return idx, slot, itemstack +end + +-- Simulates an inventory action, performing the collateral operations +local function simulate_invaction(lists, invaction) + local fidx, fslot, fis = parse_invaction(lists, invaction:to_table().from) + local tidx, tslot, tis = parse_invaction(lists, invaction:to_table().to) + + local tcount = invaction:to_table().count + if tcount == 0 then + tcount = fis:get_count() + end + + -- can't do anything + if fis:is_empty() then + return + end + + -- dump + if tis:is_empty() then + lists[tidx][tslot] = fis + lists[fidx][fslot] = ItemStack() + return + end + + -- swap + if ((fis:get_name() ~= tis:get_name()) + or (fis:get_name() == tis:get_name() + and tcount > tis:get_free_space()) + or (tcount > tis:get_free_space())) then + local t = fis + lists[fidx][fslot] = tis + lists[tidx][tslot] = t + return + end + + -- fill + if fis:get_name() == tis:get_name() and tcount <= tis:get_free_space() then + local count = math.min(fis:get_count(), tis:get_free_space(), tcount) + + lists[tidx][tslot]:set_count(tis:get_count() + count) + + if fis:get_count() - count == 0 then + lists[fidx][fslot] = ItemStack() + else + lists[fidx][fslot]:set_count(fis:get_count() - count) + end + + return + end +end + +-- Deepcopy an inventory list +local function invlist_copy(list) + local o = {} + + for i, v in ipairs(list) do + o[i] = ItemStack(v:to_string()) + end + + return o +end + +-- Add an invlist to a invaction quint if not there already +local function insert_invlist(q, taction) + local idx = format_inv(taction) + if q.start[idx] == nil then + local mdata = minetest.get_inventory(taction.location) + if mdata then + q.start[idx] = mdata[taction.inventory] + q.current[idx] = invlist_copy(mdata[taction.inventory]) + end + end +end + +-- Enqueue and preview an InventoryAction +function quint.invaction_enqueue(q, invaction) + insert_invlist(q, invaction:to_table().from) + insert_invlist(q, invaction:to_table().to) + + table.insert(q.q, invaction) + + simulate_invaction(q.current, invaction) +end + +-- Dump a slot into destination, perform another dump if there is extra +local function invaction_dump_slot(q, src, dst, srci, dstbounds) + local empty + local matching + + local sinv = q.current[format_inv(src)] + local dinv = q.current[format_inv(dst)] + + if sinv[srci]:is_empty() then + return true + end + + for i = dstbounds.min, dstbounds.max do + if not empty and dinv[i]:is_empty() then + empty = i + end + + if not matching and dinv[i]:get_name() == sinv[srci]:get_name() then + if dinv[i]:get_free_space() ~= 0 then + matching = i + end + end + + if matching and empty then + break + end + end + + if matching then + local free = dinv[matching]:get_free_space() + local scount = sinv[srci]:get_count() + local count = math.min(free, scount) + + local act = InventoryAction("move") + act:from(src.location, src.inventory, srci) + act:to(dst.location, dst.inventory, matching) + act:set_count(count) + + quint.invaction_enqueue(q, act) + + if scount > free then + return invaction_dump_slot(q, src, dst, srci, dstbounds) + end + + return true + elseif empty then + local act = InventoryAction("move") + act:from(src.location, src.inventory, srci) + act:to(dst.location, dst.inventory, empty) + quint.invaction_enqueue(q, act) + + return true + else + return false + end +end + +local function rebind(lists, inv, bounds) + local invlist = lists[format_inv(inv)] + + if not bounds then + bounds = {min = 0, max = 0} + end + + if bounds.max == 0 then + bounds.max = #invlist + end + + bounds.min = math.max(bounds.min, 1) + bounds.max = math.min(bounds.max, #invlist) + + return bounds +end + +-- Dump from src to dst +-- src and dest are in the format of {location = "", inventory = ""} +-- like {location = "current_player", inventory = "main"} +-- srcbounds and dstbounds are inclusive beginning and ends to the selectible invslots +-- in the form {min = n, max = n}, if max is 0 it is changed to the maximum index +function quint.invaction_dump(q, src, dst, srcbounds, dstbounds) + if src.location .. src.inventory == dst.location .. dst.inventory then + return + end + + insert_invlist(q, src) + insert_invlist(q, dst) + + srcbounds = rebind(q.current, src, srcbounds) + dstbounds = rebind(q.current, dst, dstbounds) + + for i = srcbounds.min, srcbounds.max do + if not invaction_dump_slot(q, src, dst, i, dstbounds) then + return + end + end + + return +end + +-- Remake a invaction quint up to index, refreshing the starts +function quint.invaction_remake(q, index) + local t = quint.invaction_new() + + for i = 1, index do + invaction_enqueue(t, q.q[i]) + end + + return t +end + +-- Preview an invaction quint after the first n (index) actions +function quint.invaction_view_state_at(q, index) + local t = quint.invaction_remake(q, index) + + return t.current +end + +-- Refresh starts and get new currents +function quint.invaction_refresh(q) + local t = quint.invaction_remake(q, #q.q) + q.start = t.start + q.current = t.current +end + +quint.invaction_gsteps = {} + +-- Apply an invaction quint, with optional delay +function quint.invaction_apply(q, delay) + q.delay = delay + + if not delay or delay == 0 then + for i, v in ipairs(q.q) do + v:apply() + end + + return + end + + table.insert(quint.invaction_gsteps, q) +end + +minetest.register_globalstep(function() + local dead = {} + + local ctime = os.clock() + + for i, v in ipairs(quint.invaction_gsteps) do + if not v.delay or ctime >= v.last + v.delay then + if v.delay then + v.last = v.last + v.delay + end + + if v.q[v.index] then + v.q[v.index]:apply() + v.index = v.index + 1 + else + table.insert(dead, 1, i) + end + end + end + + for i, v in ipairs(dead) do + table.remove(quint.invaction_gsteps, v) + end +end) diff --git a/quotebot/init.lua b/quotebot/init.lua new file mode 100644 index 0000000..b6a6c79 --- /dev/null +++ b/quotebot/init.lua @@ -0,0 +1,399 @@ +-- CC0/Unlicense system32 2020 + +--[[ +Commands: + +.qb_add_commander player +.qb_list_commanders +.qb_del_commander player + +.qb_set ID quote +.qb_list + +.qb_say +.qb_direct player + +.qb_chance num/player + +.qb_enable +.qb_disable + +-- requires unsafe +.qb_export file +.qb_import file +--]] + + +local storage = minetest.get_mod_storage() + +local function storage_init_table(key) + if storage:get(key) == nil or storage:get(key) == "null" then + storage:set_string(key, "{}") + end + + return minetest.parse_json(storage:get_string(key)) +end + +local function storage_save_json(key, value) + storage:set_string(key, minetest.write_json(value)) +end + + +quotebot = {} +quotebot.quotes = storage_init_table("quotebot_quotes") +quotebot.chance = tonumber(storage:get_string("quotebot_chance")) or 0 +quotebot.chances = storage_init_table("quotebot_chances") +quotebot.commanders = storage_init_table("quotebot_commanders") +quotebot.enabled = false + +function quotebot.save() + storage_save_json("quotebot_quotes", quotebot.quotes) + storage:set_string("quotebot_chance", tostring(quotebot.chance)) + storage_save_json("quotebot_chances", quotebot.chances) + storage_save_json("quotebot_commanders", quotebot.commanders) + storage:set_bool("quotebot_enabled", quotebot.enabled) +end + + +local function localize_player(player) + if player == nil then + return nil + end + + local info = minetest.get_server_info() + + local name = info.ip + if info.address ~= "" then + name = info.address + end + + return player .. "@" .. info.ip .. ":" .. info.port +end + + +local function say(id, prefix) + if quotebot.enabled then + minetest.send_chat_message((prefix or "") .. quotebot.quotes[id]) + end +end + +local function index_table(t, ti) + local i = 1 + for k, v in pairs(t) do + if i == ti then + return k, v + end + i = i + 1 + end +end + +local function in_list(list, value) + for i, v in ipairs(list) do + if v == value then + return true + end + end + return false +end + +local function table_size(t) + local n = 0 + for k, v in pairs(t) do + n = n + 1 + end + return n +end + +local function rand_say(trigger, id, prefix) + id = id or index_table(quotebot.quotes, math.random(table_size(quotebot.quotes))) + + if trigger.type == "message" or trigger.type == "api" then + local chance = quotebot.chances[trigger.player] or quotebot.chance + + if math.random() <= chance then + say(id, prefix) + end + elseif trigger.type == "command" or trigger.type == "api_command" then + say(id, prefix) + elseif trigger.type == "dm" and trigger.content == "say" and in_list(trigger.player, quotebot.commanders) then + say(id, prefix) + end +end + +function quotebot.force_say(id, prefix) + rand_say({ + type = "api_command", + player = "", + content = "" + }, id, prefix) +end + +function quotebot.say(id, prefix) + rand_say({ + type = "api", + player = "", + content = "" + }, id, prefix) +end + + +minetest.register_on_mods_loaded(function() + math.randomseed(os.time()) +end) + + +minetest.register_on_receiving_chat_message(function(message) + local dm_player, dm_content = message:match(".*rom (.-): (.*)") + local message_player, message_content= message:match("<(.-)> (.*)") + + local mtype = "message" + local player = localize_player(message_player) + local content = message_content + + if dm_player then + mtype = "dm" + player = localize_player(dm_player) + content = dm_content + end + + rand_say({ + type = mtype, + player = player, + content = content + }) +end) + + +local function set_append(set, value) + if not in_list(set, value) then + set[#set + 1] = value + end +end + +local function list_remove(list, value) + local out = {} + for i, v in ipairs(list) do + if v ~= value then + out[#out] = value + end + end + return out +end + + +local function noplayer() + minetest.display_chat_message("No player specified.") +end + +local function parse_player(params) + local player = string.split(params, " ")[1] + if player == nil then + noplayer() + return + end + return player +end + +local function get_keys(t) + local out = {} + for k, v in pairs(t) do + out[#out + 1] = k + end + return out +end + + +minetest.register_chatcommand("qb_add_commander", { + params = "", + description = "Add a user who can DM 'say' to the bot to force it to say a random quote.", + func = function(params) + local player = parse_player(params) + if player then + set_append(quotebot.commanders, player) + + quotebot.save() + end + end +}) +minetest.register_chatcommand("qb_list_commanders", { + description = "List users who can force the bot to say a random quote.", + func = function(params) + minetest.display_chat_message(table.concat(quotebot.commanders, ", ")) + end +}) +minetest.register_chatcommand("qb_del_commander", { + params = "", + description = "Remove a commander.", + func = function(params) + local player = parse_player(params) + if player then + list_remove(quotebot.commanders, player) + + quotebot.save() + end + end +}) + +minetest.register_chatcommand("qb_set", { + params = " ", + description = "Set a quote ID to a value, omit quote to remove the quote.", + func = function(params) + local id, quote = params:match("(.-) (.*)") + if id == "" then + minetest.display_chat_message("ID not specified.") + return + end + + if quote == "" then + quote = nil + end + + quotebot.quotes[id] = quote + + quotebot.save() + end +}) +minetest.register_chatcommand("qb_list", { + description = "List quote IDs.", + func = function(params) + minetest.display_chat_message(table.concat(get_keys(quotebot.quotes), ", ")) + end +}) +minetest.register_chatcommand("qb_show", { + params = "", + description = "Show a quote", + func = function(params) + minetest.display_chat_message(quotebot.quotes[params] or "No quote with specified ID.") + end +}) + + +minetest.register_chatcommand("qb_say", { + params = "", + description = "Say a random quote (or specific if ID is specified).", + func = function(params) + if params ~= "" then + rand_say({ + type = "command", + player = "", + content = "" + }, params) + else + rand_say({ + type = "command", + player = "", + content = "" + }) + end + end +}) +minetest.register_chatcommand("qb_direct", { + params = " ", + description = "Say a random quote (or specific if ID is specified) directed towards a player.", + func = function(params) + local player = parse_player(params) + + if player then + if params ~= "" then + rand_say({ + type = "command", + player = "", + content = "" + }, params, player .. ": ") + else + rand_say({ + type = "command", + player = "", + content = "" + }, nil, player .. ": ") + end + end + end +}) + +minetest.register_chatcommand("qb_chance", { + params = " ", + description = "Set the say chance to number (0.percentage). If first is num, set global chance. If first is player and second is num then set player's chance. If first is player and second num is not set, clear player chance.", + func = function(params) + local plist = string.split(params, " ") + + local first_num = tonumber(plist[1] or "") + local player = plist[1] + local second_num = tonumber(plist[2] or "") + + if first_num then + quotebot.chance = first_num + elseif player then + quotebot.chances[player] = second_num + end + + quotebot.save() + end +}) + +minetest.register_chatcommand("qb_enable", { + description = "Enable the quotebot.", + func = function(params) + quotebot.enable = true + quotebot.save() + end +}) +minetest.register_chatcommand("qb_disable", { + description = "Disable the quotebot.", + func = function(params) + quotebot.enable = false + quotebot.save() + end +}) + + +if minetest.settings:get_bool("quotebot_export") then + if minetest.request_insecure_environment == nil then + error("Request insecure environment not accessible. Apply patches or disable quotebot_export.") + end + + local env = minetest.request_insecure_environment() + + if env == nil then + error("Could not get an insecure environment, is quotebot in trusted mods?") + end + + function quotebot.export(file) + fdesc = io.open(file, "w") + fdesc:write(minetest.write_json({ + quotes = quotebot.quotes, + chance = quotebot.chance, + chances = quotebot.chances, + commanders = quotebot.commanders, + enabled = quotebot.enabled + })) + fdesc:close() + end + + function quotebot.import(file) + fdesc = io.open(file, "r") + local t = minetest.parse_json(fdesc:read("*all")) or {} + fdesc:close() + quotebot.quotes = t.quotes or {} + quotebot.chance = t.chance or 0 + quotebot.chances = t.chances or {} + quotebot.commanders = t.commanders or {} + quotebot.enabled = t.enabled or false + quotebot.save() + end + + minetest.register_chatcommand("qb_export", { + params = "", + description = "Export quote setup to a file.", + func = function(params) + quotebot.export(params) + end + }) + minetest.register_chatcommand("qb_import", { + params = "", + description = "Import quote setup from a file.", + func = function(params) + quotebot.import(params) + end + }) +end diff --git a/quotebot/mod.conf b/quotebot/mod.conf new file mode 100644 index 0000000..944a6f0 --- /dev/null +++ b/quotebot/mod.conf @@ -0,0 +1,2 @@ +name = quotebot +description = Randomly say quotes when users send messages. diff --git a/randomscreenshot/init.lua b/randomscreenshot/init.lua new file mode 100644 index 0000000..d7e94f8 --- /dev/null +++ b/randomscreenshot/init.lua @@ -0,0 +1,45 @@ +--- +-- random screenshots + + +randomscreenshot = {} + +local function init_settings(setting_table) + for k, v in pairs(setting_table) do + if minetest.settings:get(k) == nil then + if type(v) == "boolean" then + minetest.settings:set_bool(k, v) + else + minetest.settings:set(k, v) + end + end + end +end + +init_settings({ + randomscreenshot_interval = 10, + randomscreenshot_rnd = 10 +}) + +local nextsc=0 + +minetest.register_globalstep(function() + if not minetest.settings:get_bool("randomsc") then return end + if os.time() < nextsc then return end + math.randomseed(os.clock()) + nextsc=os.time() + ( minetest.settings:get('randomscreenshot_interval') * 60 ) + math.random(minetest.settings:get('randomscreenshot_rnd') * 60) + minetest.after("15.0",function() + minetest.hide_huds() + --minetest.display_chat_message("\n\n\n\n\n\n\n\n\n") + minetest.after("0.05",minetest.take_screenshot) + minetest.after("0.1",function() + minetest.show_huds() + end) + end) +end) + +if (_G["minetest"]["register_cheat"] ~= nil) then + minetest.register_cheat("Random Screenshot", "World", "randomsc") +else + minetest.settings:set_bool('randomsc',true) +end diff --git a/randomscreenshot/mod.conf b/randomscreenshot/mod.conf new file mode 100644 index 0000000..190a0b3 --- /dev/null +++ b/randomscreenshot/mod.conf @@ -0,0 +1,3 @@ +name = randomscreenshot +author = cora +description = take screenshots at random intervals diff --git a/randomscreenshot/settingtypes.txt b/randomscreenshot/settingtypes.txt new file mode 100644 index 0000000..bd60298 --- /dev/null +++ b/randomscreenshot/settingtypes.txt @@ -0,0 +1,4 @@ +randomsc (Enable Autoscreenshot) bool false + +randomscreenshot_interval ( Minimum time to wait between screenshots in minutes ) int 10 +randomscreenshot_rnd (Random time between screenshots in minutes ) int 10 diff --git a/scaffold/atower.lua b/scaffold/atower.lua new file mode 100644 index 0000000..65fe075 --- /dev/null +++ b/scaffold/atower.lua @@ -0,0 +1,881 @@ + +ws = {} +ws.registered_globalhacks = {} +ws.displayed_wps={} + +ws.c = core + +ws.range=4 +ws.target=nil +ws.targetpos=nil + +local nextact = {} +local ghwason={} + +local nodes_this_tick=0 + +function ws.s(name,value) + if value == nil then + return ws.c.settings:get(name) + else + ws.c.settings:set(name,value) + return ws.c.settings:get(name) + end +end +function ws.sb(name,value) + if value == nil then + return ws.c.settings:get_bool(name) + else + ws.c.settings:set_bool(name,value) + return ws.c.settings:get_bool(name) + end +end + +function ws.dcm(msg) + return minetest.display_chat_message(msg) +end +function ws.set_bool_bulk(settings,value) + if type(settings) ~= 'table' then return false end + for k,v in pairs(settings) do + minetest.settings:set_bool(v,value) + end + return true +end + +function ws.shuffle(tbl) + for i = #tbl, 2, -1 do + local j = math.random(i) + tbl[i], tbl[j] = tbl[j], tbl[i] + end + return tbl +end + +function ws.in_list(val, list) + if type(list) ~= "table" then return false end + for i, v in pairs(list) do + if v == val then + return true + end + end + return false +end + +function ws.random_table_element(tbl) + local ks = {} + for k in pairs(tbl) do + table.insert(ks, k) + end + return tbl[ks[math.random(#ks)]] +end + +function ws.center() + --local lp=ws.dircoord(0,0,0) + --minetest.localplayer:set_pos(lp) +end + +function ws.globalhacktemplate(setting,func,funcstart,funcstop,daughters,delay) + funcstart = funcstart or function() end + funcstop = funcstop or function() end + delay = delay or 0.5 + return function() + if not minetest.localplayer then return end + if minetest.settings:get_bool(setting) then + if tps_client and tps_client.ping and tps_client.ping > 1000 then return end + nodes_this_tick = 0 + if nextact[setting] and nextact[setting] > os.clock() then return end + nextact[setting] = os.clock() + delay + if not ghwason[setting] then + if not funcstart() then + ws.set_bool_bulk(daughters,true) + ghwason[setting] = true + --ws.dcm(setting.. " activated") + ws.center() + minetest.settings:set('last-dir',ws.getdir()) + minetest.settings:set('last-y',ws.dircoord(0,0,0).y) + else minetest.settings:set_bool(setting,false) + end + else + func() + end + + elseif ghwason[setting] then + ghwason[setting] = false + ws.set_bool_bulk(daughters,false) + funcstop() + --ws.dcm(setting.. " deactivated") + end + end +end + +function ws.register_globalhack(func) + table.insert(ws.registered_globalhacks,func) +end + +function ws.register_globalhacktemplate(name,category,setting,func,funcstart,funcstop,daughters) + ws.register_globalhack(ws.globalhacktemplate(setting,func,funcstart,funcstop,daughters)) + minetest.register_cheat(name,category,setting) +end + +ws.rg=ws.register_globalhacktemplate + +function ws.step_globalhacks(dtime) + for i, v in ipairs(ws.registered_globalhacks) do + v(dtime) + end +end + +minetest.register_globalstep(function(dtime) ws.step_globalhacks(dtime) end) +minetest.settings:set_bool('continuous_forward',false) + +function ws.on_connect(func) + if not minetest.localplayer then minetest.after(0,function() ws.on_connect(func) end) return end + if func then func() end +end + +ws.on_connect(function() + local ldir =minetest.settings:get('last-dir') + if ldir then ws.setdir(ldir) end +end) + + +-- COORD MAGIC + +function ws.is_same_pos(pos1,pos2) + return vector.distance(vector.round(pos1),vector.round(pos2)) == 0 +end +function ws.get_reachable_positions(range,under) + under=under or false + range=range or 4 + local rt={} + local lp=vector.round(minetest.localplayer:get_pos()) + local ylim=range + if under then ylim=-1 end + for x = -range,range,1 do + for y = -range,ylim,1 do + for z = -range,range,1 do + table.insert(rt,vector.round(vector.add(lp,vector.new(x,y,z)))) + end + end + end + return rt +end + +function ws.do_area(radius,func,plane) + for k,v in pairs(ws.get_reachable_positions(range)) do + if not plane or v.y == minetest.localplayer:get_pos().y -1 then + func(v) + end + end +end + +function ws.get_hud_by_texture(texture) + local def + local i = -1 + repeat + i = i + 1 + def = minetest.localplayer:hud_get(i) + until not def or def.text == texture + if def then + return def + end + def.number=0 + return def +end + +function ws.find_player(name) + for k, v in ipairs(minetest.localplayer.get_nearby_objects(500)) do + if v:get_name() == name then + return v:get_pos(),v + end + end +end + +function ws.display_wp(pos,name) + local ix = #ws.displayed_wps + 1 + ws.displayed_wps[ix] = minetest.localplayer:hud_add({ + hud_elem_type = 'waypoint', + name = name, + text = name, + number = 0x00ff00, + world_pos = pos + }) + return ix +end + +function ws.clear_wp(ix) + table.remove(ws.displayed_wps,ix) +end + +function ws.clear_wps() + for k,v in ipairs(ws.displayed_wps) do + minetest.localplayer:hud_remove(v) + table.remove(ws.displayed_wps,k) + end +end + +function ws.register_chatcommand_alias(old, ...) + local def = assert(minetest.registered_chatcommands[old]) + def.name = nil + for i = 1, select('#', ...) do + minetest.register_chatcommand(select(i, ...), table.copy(def)) + end +end + +function ws.round2(num, numDecimalPlaces) + return tonumber(string.format("%." .. (numDecimalPlaces or 0) .. "f", num)) +end + +function ws.pos_to_string(pos) + if type(pos) == 'table' then + pos = minetest.pos_to_string(vector.round(pos)) + end + if type(pos) == 'string' then + return pos + end + return pos +end + +function ws.string_to_pos(pos) + if type(pos) == 'string' then + pos = minetest.string_to_pos(pos) + end + if type(pos) == 'table' then + return vector.round(pos) + end + return pos +end + + + +--ITEMS +function ws.find_item_in_table(items,rnd) + if type(items) == 'string' then + return minetest.find_item(items) + end + if type(items) ~= 'table' then return end + if rnd then items=ws.shuffle(items) end + for i, v in pairs(items) do + local n = minetest.find_item(v) + if n then + return n + end + end + return false +end + +function ws.find_empty(inv) + for i, v in ipairs(inv) do + if v:is_empty() then + return i + end + end + return false +end + +function ws.find_named(inv, name) + if not inv then return -1 end + if not name then return end + for i, v in ipairs(inv) do + if v:get_name():find(name) then + return i + end + end +end + +function ws.itemnameformat(description) + description = description:gsub(string.char(0x1b) .. "%(.@[^)]+%)", "") + description = description:match("([^\n]*)") + return description +end + +function ws.find_nametagged(list, name) + for i, v in ipairs(list) do + if ws.itemnameformat(v:get_description()) == name then + return i + end + end +end + +local hotbar_slot=8 +function ws.to_hotbar(it,hslot) + local tpos=nil + local plinv = minetest.get_inventory("current_player") + if hslot and hslot < 10 then + tpos=hslot + else + for i, v in ipairs(plinv.main) do + if i<10 and v:is_empty() then + tpos = i + break + end + end + end + if tpos == nil then tpos=hotbar_slot end + local mv = InventoryAction("move") + mv:from("current_player", "main", it) + mv:to("current_player", "main", tpos) + mv:apply() + return tpos +end + +function ws.switch_to_item(itname,hslot) + if not minetest.localplayer then return false end + local plinv = minetest.get_inventory("current_player") + for i, v in ipairs(plinv.main) do + if i<10 and v:get_name() == itname then + minetest.localplayer:set_wield_index(i) + return true + end + end + local pos = ws.find_named(plinv.main, itname) + if pos then + minetest.localplayer:set_wield_index(ws.to_hotbar(pos,hslot)) + return true + end + return false +end +function ws.in_inv(itname) + if not minetest.localplayer then return false end + local plinv = minetest.get_inventory("current_player") + local pos = ws.find_named(plinv.main, itname) + if pos then + return true + end +end + +function core.switch_to_item(item) return ws.switch_to_item(item) end + +function ws.switch_inv_or_echest(name,max_count,hslot) + if not minetest.localplayer then return false end + local plinv = minetest.get_inventory("current_player") + if ws.switch_to_item(name) then return true end + + local epos = ws.find_named(plinv.enderchest, name) + if epos then + local tpos + for i, v in ipairs(plinv.main) do + if i < 9 and v:is_empty() then + tpos = i + break + end + end + if not tpos then tpos=hotbar_slot end + + if tpos then + local mv = InventoryAction("move") + mv:from("current_player", "enderchest", epos) + mv:to("current_player", "main", tpos) + if max_count then + mv:set_count(max_count) + end + mv:apply() + minetest.localplayer:set_wield_index(tpos) + return true + end + end + return false +end + +local function posround(n) + return math.floor(n + 0.5) +end + +local function fmt(c) + return tostring(posround(c.x))..","..tostring(posround(c.y))..","..tostring(posround(c.z)) +end + +local function map_pos(value) + if value.x then + return value + else + return {x = value[1], y = value[2], z = value[3]} + end +end + +function ws.invparse(location) + if type(location) == "string" then + if string.match(location, "^[-]?[0-9]+,[-]?[0-9]+,[-]?[0-9]+$") then + return "nodemeta:" .. location + else + return location + end + elseif type(location) == "table" then + return "nodemeta:" .. fmt(map_pos(location)) + end +end + +function ws.invpos(p) + return "nodemeta:"..p.x..","..p.y..","..p.z +end + + +-- TOOLS + + +local function check_tool(stack, node_groups, old_best_time) + local toolcaps = stack:get_tool_capabilities() + if not toolcaps then return end + local best_time = old_best_time + for group, groupdef in pairs(toolcaps.groupcaps) do + local level = node_groups[group] + if level then + local this_time = groupdef.times[level] + if this_time and this_time < best_time then + best_time = this_time + end + end + end + return best_time < old_best_time, best_time +end + +local function find_best_tool(nodename, switch) + local player = minetest.localplayer + local inventory = minetest.get_inventory("current_player") + local node_groups = minetest.get_node_def(nodename).groups + local new_index = player:get_wield_index() + local is_better, best_time = false, math.huge + + is_better, best_time = check_tool(player:get_wielded_item(), node_groups, best_time) + if inventory.hand then + is_better, best_time = check_tool(inventory.hand[1], node_groups, best_time) + end + + for index, stack in ipairs(inventory.main) do + is_better, best_time = check_tool(stack, node_groups, best_time) + if is_better then + new_index = index + end + end + + return new_index,best_time +end + +function ws.get_digtime(nodename) + local idx,tm=find_best_tool(nodename) + return tm +end + +function ws.select_best_tool(pos) + local nd=minetest.get_node_or_nil(pos) + local nodename='air' + if nd then nodename=nd.name end + local t=find_best_tool(nodename) + minetest.localplayer:set_wield_index(ws.to_hotbar(t,hotbar_slot)) + --minetest.localplayer:set_wield_index(find_best_tool(nodename)) +end + +--- COORDS +function ws.coord(x, y, z) + return vector.new(x,y,z) +end +function ws.ordercoord(c) + if c.x == nil then + return {x = c[1], y = c[2], z = c[3]} + else + return c + end +end + +-- x or {x,y,z} or {x=x,y=y,z=z} +function ws.optcoord(x, y, z) + if y and z then + return ws.coord(x, y, z) + else + return ws.ordercoord(x) + end +end +function ws.cadd(c1, c2) + return vector.add(c1,c2) + --return ws.coord(c1.x + c2.x, c1.y + c2.y, c1.z + c2.z) +end + +function ws.relcoord(x, y, z, rpos) + local pos = rpos or minetest.localplayer:get_pos() + pos.y=math.ceil(pos.y) + --math.floor(pos.y) + 0.5 + return ws.cadd(pos, ws.optcoord(x, y, z)) +end + +local function between(x, y, z) -- x is between y and z (inclusive) + return y <= x and x <= z +end + +function ws.getdir(yaw) -- + local rot = yaw or minetest.localplayer:get_yaw() % 360 + if between(rot, 315, 360) or between(rot, 0, 45) then + return "north" + elseif between(rot, 135, 225) then + return "south" + elseif between(rot, 225, 315) then + return "east" + elseif between(rot, 45, 135) then + return "west" + end +end + +function ws.getaxis() + local dir=ws.getdir() + if dir == "north" or dir == "south" then return "z" end + return "x" +end +function ws.setdir(dir) -- + if dir == "north" then + minetest.localplayer:set_yaw(0) + elseif dir == "south" then + minetest.localplayer:set_yaw(180) + elseif dir == "east" then + minetest.localplayer:set_yaw(270) + elseif dir == "west" then + minetest.localplayer:set_yaw(90) + end +end + +function ws.dircoord(f, y, r ,rpos, rdir) + local dir= ws.getdir(rdir) + local coord = ws.optcoord(f, y, r) + local f = coord.x + local y = coord.y + local r = coord.z + local lp= rpos or minetest.localplayer:get_pos() + if dir == "north" then + return ws.relcoord(r, y, f,rpos) + elseif dir == "south" then + return ws.relcoord(-r, y, -f,rpos) + elseif dir == "east" then + return ws.relcoord(f, y, -r,rpos) + elseif dir== "west" then + return ws.relcoord(-f, y, r,rpos) + end + return ws.relcoord(0, 0, 0,rpos) +end + +function ws.get_dimension(pos) + if pos.y > -65 then return "overworld" + elseif pos.y > -8000 then return "void" + elseif pos.y > -27000 then return "end" + elseif pos.y > -28930 then return "void" + elseif pos.y > -31000 then return "nether" + else return "void" + end +end + +function ws.aim(tpos) + local ppos=minetest.localplayer:get_pos() + local dir=vector.direction(ppos,tpos) + local yyaw=0; + local pitch=0; + if dir.x < 0 then + yyaw = math.atan2(-dir.x, dir.z) + (math.pi * 2) + else + yyaw = math.atan2(-dir.x, dir.z) + end + yyaw = ws.round2(math.deg(yyaw),2) + pitch = ws.round2(math.deg(math.asin(-dir.y) * 1),2); + minetest.localplayer:set_yaw(yyaw) + minetest.localplayer:set_pitch(pitch) +end + +function ws.gaim(tpos,v,g) + local v = v or 40 + local g = g or 9.81 + local ppos=minetest.localplayer:get_pos() + local dir=vector.direction(ppos,tpos) + local yyaw=0; + local pitch=0; + if dir.x < 0 then + yyaw = math.atan2(-dir.x, dir.z) + (math.pi * 2) + else + yyaw = math.atan2(-dir.x, dir.z) + end + yyaw = ws.round2(math.deg(yyaw),2) + local y = dir.y + dir.y = 0 + local x = vector.length(dir) + pitch=math.atan(math.pow(v, 2) / (g * x) + math.sqrt(math.pow(v, 4)/(math.pow(g, 2) * math.pow(x, 2)) - 2 * math.pow(v, 2) * y/(g * math.pow(x, 2)) - 1)) + --pitch = ws.round2(math.deg(math.asin(-dir.y) * 1),2); + minetest.localplayer:set_yaw(yyaw) + minetest.localplayer:set_pitch(math.deg(pitch)) +end + +function ws.buildable_to(pos) + local node=minetest.get_node_or_nil(pos) + if node then + return minetest.get_node_def(node.name).buildable_to + end +end + +function ws.tplace(p,n,stay) + if not p then return end + if n then ws.switch_to_item(n) end + local opos=ws.dircoord(0,0,0) + local tpos=vector.add(p,vector.new(0,1,0)) + minetest.localplayer:set_pos(tpos) + ws.place(p,{n}) + if not stay then + minetest.after(0.1,function() + minetest.localplayer:set_pos(opos) + end) + end +end + +minetest.register_chatcommand("tplace", { + description = "tp-place", + param = "Y", + func = function(param) + return ws.tplace(minetest.string_to_pos(param)) + end +}) + +function ws.ytp(param) + local y=tonumber(param) + local lp=ws.dircoord(0,0,0) + if lp.y < y + 50 then return false,"Can't TP up." end + if y < -30912 then return false,"Don't TP into the void lol." end + minetest.localplayer:set_pos(vector.new(lp.x,y,lp.z)) +end + +local function tablearg(arg) + local tb={} + if type(arg) == 'string' then + tb={arg} + elseif type(arg) == 'table' then + tb=arg + elseif type(arg) == 'function' then + tb=arg() + end + return tb +end + +function ws.isnode(pos,arg)--arg is either an itemstring, a table of itemstrings or a function returning an itemstring + local nodename=tablearg(arg) + local nd=minetest.get_node_or_nil(pos) + if nd and nodename and ws.in_list(nd.name,nodename) then + return true + end +end + +function ws.can_place_at(pos) + local node = minetest.get_node_or_nil(pos) + return (node and (node.name == "air" or node.name=="mcl_core:water_source" or node.name=="mcl_core:water_flowing" or node.name=="mcl_core:lava_source" or node.name=="mcl_core:lava_flowing" or minetest.get_node_def(node.name).buildable_to)) +end + +-- should check if wield is placeable +-- minetest.get_node(wielded:get_name()) ~= nil should probably work +-- otherwise it equips armor and eats food +function ws.can_place_wielded_at(pos) + local wield_empty = minetest.localplayer:get_wielded_item():is_empty() + return not wield_empty and ws.can_place_at(pos) +end + + +function ws.find_any_swap(items,hslot) + hslot=hslot or 8 + for i, v in ipairs(items) do + local n = minetest.find_item(v) + if n then + ws.switch_to_item(v,hslot) + return true + end + end + return false +end + + +-- swaps to any of the items and places if need be +-- returns true if placed and in inventory or already there, false otherwise + +local lastact=0 +local lastplc=0 +local lastdig=0 +local actint=10 +function ws.place(pos,items,hslot, place) + --if nodes_this_tick > 8 then return end + --nodes_this_tick = nodes_this_tick + 1 + --if not inside_constraints(pos) then return end + if not pos then return end + if not ws.can_place_at(pos) then return end + items=tablearg(items) + + place = place or minetest.place_node + + local node = minetest.get_node_or_nil(pos) + if not node then return end + -- already there + if ws.isnode(pos,items) then + return true + else + if ws.find_any_swap(items,hslot) then + place(pos) + return true + end + end +end + +function ws.place_if_able(pos) + if not pos then return end + if not inside_constraints(pos) then return end + if ws.can_place_wielded_at(pos) then + minetest.place_node(pos) + end +end + +function ws.is_diggable(pos) + if not pos then return false end + local nd=minetest.get_node_or_nil(pos) + if not nd then return false end + local n = minetest.get_node_def(nd.name) + if n and n.diggable then return true end + return false +end + +function ws.dig(pos,condition,autotool) + --if not inside_constraints(pos) then return end + if autotool == nil then autotool = true end + if condition and not condition(pos) then return false end + if not ws.is_diggable(pos) then return end + local nd=minetest.get_node_or_nil(pos) + if nd and minetest.get_node_def(nd.name).diggable then + if autotool then ws.select_best_tool(pos) end + minetest.dig_node(pos) + end + return true +end + +function ws.chunk_loaded() + local ign=minetest.find_nodes_near(ws.dircoord(0,0,0),10,{'ignore'},true) + if #ign == 0 then return true end + return false +end + +function ws.get_near(nodes,range) + range=range or 5 + local nds=minetest.find_nodes_near(ws.dircoord(0,0,0),rang,nodes,true) + if #nds > 0 then return nds end + return false +end + +function ws.is_laggy() + if tps_client and tps_client.ping and tps_client.ping > 1000 then return true end +end + + +function ws.donodes(poss,func,condition) + if ws.is_laggy() then return end + local dn_i=0 + for k,v in pairs(poss) do + if dn_i > 8 then return end + --local nd=minetest.get_node_or_nil(v) + if condition == nil or condition(v) then + func(v) + dn_i = dn_i + 1 + end + end +end + +function ws.dignodes(poss,condition) + local func=function(p) ws.dig(p) end + ws.donodes(poss,func,condition) +end + + +function ws.replace(pos,arg) + arg=tablearg(arg) + local nd=minetest.get_node_or_nil(pos) + if nd and not ws.in_list(nd.name,arg) and ws.buildable_to(pos) then + local tm=ws.get_digtime(nd.name) or 0 + ws.dig(pos) + minetest.after(tm + 0.1,function() + ws.place(pos,arg) + end) + return tm + else + return ws.place(pos,arg) + end +end + +function ws.playeron(p) + local pls=minetest.get_player_names() + for k,v in pairs(pls) do + if v == p then return true end + end + return false +end + + +function ws.between(x, y, z) -- x is between y and z (inclusive) + return y <= x and x <= z +end + + +local wall_pos1={x=-1255,y=6,z=792} +local wall_pos2={x=-1452,y=80,z=981} +local iwall_pos1={x=-1266,y=6,z=802} +local iwall_pos2={x=-1442,y=80,z=971} + +function ws.in_cube(tpos,wpos1,wpos2) + local xmax=wpos2.x + local xmin=wpos1.x + + local ymax=wpos2.y + local ymin=wpos1.y + + local zmax=wpos2.z + local zmin=wpos1.z + if wpos1.x > wpos2.x then + xmax=wpos1.x + xmin=wpos2.x + end + if wpos1.y > wpos2.y then + ymax=wpos1.y + ymin=wpos2.y + end + if wpos1.z > wpos2.z then + zmax=wpos1.z + zmin=wpos2.z + end + if ws.between(tpos.x,xmin,xmax) and ws.between(tpos.y,ymin,ymax) and ws.between(tpos.z,zmin,zmax) then + return true + end + return false +end + +function ws.in_wall(pos) + if ws.in_cube(pos,wall_pos1,wall_pos2) and not in_cube(pos,iwall_pos1,iwall_pos2) then + return true end + return false +end + +function ws.inside_wall(pos) + local p1=iwall_pos1 + local p2=iwall_pos2 + if ws.in_cube(pos,p1,p2) then return true end + return false +end + +function ws.find_closest_reachable_airpocket(pos) + local lp=ws.dircoord(0,0,0) + local nds=minetest.find_nodes_near(lp,5,{'air'}) + local odst=10 + local rt=lp + for k,v in ipairs(nds) do + local dst=vector.distance(pos,v) + if dst < odst then odst=dst rt=v end + end + if odst==10 then return false end + return vector.add(rt,vector.new(0,-1,0)) +end + + +-- DEBUG +local function printwieldedmeta() + ws.dcm(dump(minetest.localplayer:get_wielded_item():get_meta():to_table())) +end +minetest.register_cheat('ItemMeta','Test',printwieldedmeta) +ws.rg('AutoTower','Scaffold','atower',function() + local it=minetest.localplayer:get_wielded_item():get_name() + local lp=ws.dircoord(0,0,0) + local nds=minetest.find_nodes_near_under_air(lp,4,{it},false) + for k,v in ipairs(nds) do + ws.place(vector.add(v,vector.new(0,1,0)),it) + end +end,function() end,function() end, {'autorefill'}) diff --git a/scaffold/autofarm.lua b/scaffold/autofarm.lua new file mode 100644 index 0000000..e604227 --- /dev/null +++ b/scaffold/autofarm.lua @@ -0,0 +1,950 @@ + +ws = {} +ws.registered_globalhacks = {} +ws.displayed_wps={} + +ws.c = core + +ws.range=4 +ws.target=nil +ws.targetpos=nil + +local nextact = {} +local ghwason={} + +local nodes_this_tick=0 + +function ws.s(name,value) + if value == nil then + return ws.c.settings:get(name) + else + ws.c.settings:set(name,value) + return ws.c.settings:get(name) + end +end +function ws.sb(name,value) + if value == nil then + return ws.c.settings:get_bool(name) + else + ws.c.settings:set_bool(name,value) + return ws.c.settings:get_bool(name) + end +end + +function ws.dcm(msg) + return minetest.display_chat_message(msg) +end +function ws.set_bool_bulk(settings,value) + if type(settings) ~= 'table' then return false end + for k,v in pairs(settings) do + minetest.settings:set_bool(v,value) + end + return true +end + +function ws.shuffle(tbl) + for i = #tbl, 2, -1 do + local j = math.random(i) + tbl[i], tbl[j] = tbl[j], tbl[i] + end + return tbl +end + +function ws.in_list(val, list) + if type(list) ~= "table" then return false end + for i, v in pairs(list) do + if v == val then + return true + end + end + return false +end + +function ws.random_table_element(tbl) + local ks = {} + for k in pairs(tbl) do + table.insert(ks, k) + end + return tbl[ks[math.random(#ks)]] +end + +function ws.center() + --local lp=ws.dircoord(0,0,0) + --minetest.localplayer:set_pos(lp) +end + +function ws.globalhacktemplate(setting,func,funcstart,funcstop,daughters,delay) + funcstart = funcstart or function() end + funcstop = funcstop or function() end + delay = delay or 0.5 + return function() + if not minetest.localplayer then return end + if minetest.settings:get_bool(setting) then + if tps_client and tps_client.ping and tps_client.ping > 1000 then return end + nodes_this_tick = 0 + if nextact[setting] and nextact[setting] > os.clock() then return end + nextact[setting] = os.clock() + delay + if not ghwason[setting] then + if not funcstart() then + ws.set_bool_bulk(daughters,true) + ghwason[setting] = true + --ws.dcm(setting.. " activated") + ws.center() + minetest.settings:set('last-dir',ws.getdir()) + minetest.settings:set('last-y',ws.dircoord(0,0,0).y) + else minetest.settings:set_bool(setting,false) + end + else + func() + end + + elseif ghwason[setting] then + ghwason[setting] = false + ws.set_bool_bulk(daughters,false) + funcstop() + --ws.dcm(setting.. " deactivated") + end + end +end + +function ws.register_globalhack(func) + table.insert(ws.registered_globalhacks,func) +end + +function ws.register_globalhacktemplate(name,category,setting,func,funcstart,funcstop,daughters) + ws.register_globalhack(ws.globalhacktemplate(setting,func,funcstart,funcstop,daughters)) + minetest.register_cheat(name,category,setting) +end + +ws.rg=ws.register_globalhacktemplate + +function ws.step_globalhacks(dtime) + for i, v in ipairs(ws.registered_globalhacks) do + v(dtime) + end +end + +minetest.register_globalstep(function(dtime) ws.step_globalhacks(dtime) end) +minetest.settings:set_bool('continuous_forward',false) + +function ws.on_connect(func) + if not minetest.localplayer then minetest.after(0,function() ws.on_connect(func) end) return end + if func then func() end +end + +ws.on_connect(function() + local ldir =minetest.settings:get('last-dir') + if ldir then ws.setdir(ldir) end +end) + + +-- COORD MAGIC + +function ws.is_same_pos(pos1,pos2) + return vector.distance(vector.round(pos1),vector.round(pos2)) == 0 +end +function ws.get_reachable_positions(range,under) + under=under or false + range=range or 4 + local rt={} + local lp=vector.round(minetest.localplayer:get_pos()) + local ylim=range + if under then ylim=-1 end + for x = -range,range,1 do + for y = -range,ylim,1 do + for z = -range,range,1 do + table.insert(rt,vector.round(vector.add(lp,vector.new(x,y,z)))) + end + end + end + return rt +end + +function ws.do_area(radius,func,plane) + for k,v in pairs(ws.get_reachable_positions(range)) do + if not plane or v.y == minetest.localplayer:get_pos().y -1 then + func(v) + end + end +end + +function ws.get_hud_by_texture(texture) + local def + local i = -1 + repeat + i = i + 1 + def = minetest.localplayer:hud_get(i) + until not def or def.text == texture + if def then + return def + end + def.number=0 + return def +end + +function ws.find_player(name) + for k, v in ipairs(minetest.localplayer.get_nearby_objects(500)) do + if v:get_name() == name then + return v:get_pos(),v + end + end +end + +function ws.display_wp(pos,name) + local ix = #ws.displayed_wps + 1 + ws.displayed_wps[ix] = minetest.localplayer:hud_add({ + hud_elem_type = 'waypoint', + name = name, + text = name, + number = 0x00ff00, + world_pos = pos + }) + return ix +end + +function ws.clear_wp(ix) + table.remove(ws.displayed_wps,ix) +end + +function ws.clear_wps() + for k,v in ipairs(ws.displayed_wps) do + minetest.localplayer:hud_remove(v) + table.remove(ws.displayed_wps,k) + end +end + +function ws.register_chatcommand_alias(old, ...) + local def = assert(minetest.registered_chatcommands[old]) + def.name = nil + for i = 1, select('#', ...) do + minetest.register_chatcommand(select(i, ...), table.copy(def)) + end +end + +function ws.round2(num, numDecimalPlaces) + return tonumber(string.format("%." .. (numDecimalPlaces or 0) .. "f", num)) +end + +function ws.pos_to_string(pos) + if type(pos) == 'table' then + pos = minetest.pos_to_string(vector.round(pos)) + end + if type(pos) == 'string' then + return pos + end + return pos +end + +function ws.string_to_pos(pos) + if type(pos) == 'string' then + pos = minetest.string_to_pos(pos) + end + if type(pos) == 'table' then + return vector.round(pos) + end + return pos +end + + + +--ITEMS +function ws.find_item_in_table(items,rnd) + if type(items) == 'string' then + return minetest.find_item(items) + end + if type(items) ~= 'table' then return end + if rnd then items=ws.shuffle(items) end + for i, v in pairs(items) do + local n = minetest.find_item(v) + if n then + return n + end + end + return false +end + +function ws.find_empty(inv) + for i, v in ipairs(inv) do + if v:is_empty() then + return i + end + end + return false +end + +function ws.find_named(inv, name) + if not inv then return -1 end + if not name then return end + for i, v in ipairs(inv) do + if v:get_name():find(name) then + return i + end + end +end + +function ws.itemnameformat(description) + description = description:gsub(string.char(0x1b) .. "%(.@[^)]+%)", "") + description = description:match("([^\n]*)") + return description +end + +function ws.find_nametagged(list, name) + for i, v in ipairs(list) do + if ws.itemnameformat(v:get_description()) == name then + return i + end + end +end + +local hotbar_slot=8 +function ws.to_hotbar(it,hslot) + local tpos=nil + local plinv = minetest.get_inventory("current_player") + if hslot and hslot < 10 then + tpos=hslot + else + for i, v in ipairs(plinv.main) do + if i<10 and v:is_empty() then + tpos = i + break + end + end + end + if tpos == nil then tpos=hotbar_slot end + local mv = InventoryAction("move") + mv:from("current_player", "main", it) + mv:to("current_player", "main", tpos) + mv:apply() + return tpos +end + +function ws.switch_to_item(itname,hslot) + if not minetest.localplayer then return false end + local plinv = minetest.get_inventory("current_player") + for i, v in ipairs(plinv.main) do + if i<10 and v:get_name() == itname then + minetest.localplayer:set_wield_index(i) + return true + end + end + local pos = ws.find_named(plinv.main, itname) + if pos then + minetest.localplayer:set_wield_index(ws.to_hotbar(pos,hslot)) + return true + end + return false +end +function ws.in_inv(itname) + if not minetest.localplayer then return false end + local plinv = minetest.get_inventory("current_player") + local pos = ws.find_named(plinv.main, itname) + if pos then + return true + end +end + +function core.switch_to_item(item) return ws.switch_to_item(item) end + +function ws.switch_inv_or_echest(name,max_count,hslot) + if not minetest.localplayer then return false end + local plinv = minetest.get_inventory("current_player") + if ws.switch_to_item(name) then return true end + + local epos = ws.find_named(plinv.enderchest, name) + if epos then + local tpos + for i, v in ipairs(plinv.main) do + if i < 9 and v:is_empty() then + tpos = i + break + end + end + if not tpos then tpos=hotbar_slot end + + if tpos then + local mv = InventoryAction("move") + mv:from("current_player", "enderchest", epos) + mv:to("current_player", "main", tpos) + if max_count then + mv:set_count(max_count) + end + mv:apply() + minetest.localplayer:set_wield_index(tpos) + return true + end + end + return false +end + +local function posround(n) + return math.floor(n + 0.5) +end + +local function fmt(c) + return tostring(posround(c.x))..","..tostring(posround(c.y))..","..tostring(posround(c.z)) +end + +local function map_pos(value) + if value.x then + return value + else + return {x = value[1], y = value[2], z = value[3]} + end +end + +function ws.invparse(location) + if type(location) == "string" then + if string.match(location, "^[-]?[0-9]+,[-]?[0-9]+,[-]?[0-9]+$") then + return "nodemeta:" .. location + else + return location + end + elseif type(location) == "table" then + return "nodemeta:" .. fmt(map_pos(location)) + end +end + +function ws.invpos(p) + return "nodemeta:"..p.x..","..p.y..","..p.z +end + + +-- TOOLS + + +local function check_tool(stack, node_groups, old_best_time) + local toolcaps = stack:get_tool_capabilities() + if not toolcaps then return end + local best_time = old_best_time + for group, groupdef in pairs(toolcaps.groupcaps) do + local level = node_groups[group] + if level then + local this_time = groupdef.times[level] + if this_time and this_time < best_time then + best_time = this_time + end + end + end + return best_time < old_best_time, best_time +end + +local function find_best_tool(nodename, switch) + local player = minetest.localplayer + local inventory = minetest.get_inventory("current_player") + local node_groups = minetest.get_node_def(nodename).groups + local new_index = player:get_wield_index() + local is_better, best_time = false, math.huge + + is_better, best_time = check_tool(player:get_wielded_item(), node_groups, best_time) + if inventory.hand then + is_better, best_time = check_tool(inventory.hand[1], node_groups, best_time) + end + + for index, stack in ipairs(inventory.main) do + is_better, best_time = check_tool(stack, node_groups, best_time) + if is_better then + new_index = index + end + end + + return new_index,best_time +end + +function ws.get_digtime(nodename) + local idx,tm=find_best_tool(nodename) + return tm +end + +function ws.select_best_tool(pos) + local nd=minetest.get_node_or_nil(pos) + local nodename='air' + if nd then nodename=nd.name end + local t=find_best_tool(nodename) + minetest.localplayer:set_wield_index(ws.to_hotbar(t,hotbar_slot)) + --minetest.localplayer:set_wield_index(find_best_tool(nodename)) +end + +--- COORDS +function ws.coord(x, y, z) + return vector.new(x,y,z) +end +function ws.ordercoord(c) + if c.x == nil then + return {x = c[1], y = c[2], z = c[3]} + else + return c + end +end + +-- x or {x,y,z} or {x=x,y=y,z=z} +function ws.optcoord(x, y, z) + if y and z then + return ws.coord(x, y, z) + else + return ws.ordercoord(x) + end +end +function ws.cadd(c1, c2) + return vector.add(c1,c2) + --return ws.coord(c1.x + c2.x, c1.y + c2.y, c1.z + c2.z) +end + +function ws.relcoord(x, y, z, rpos) + local pos = rpos or minetest.localplayer:get_pos() + pos.y=math.ceil(pos.y) + --math.floor(pos.y) + 0.5 + return ws.cadd(pos, ws.optcoord(x, y, z)) +end + +local function between(x, y, z) -- x is between y and z (inclusive) + return y <= x and x <= z +end + +function ws.getdir(yaw) -- + local rot = yaw or minetest.localplayer:get_yaw() % 360 + if between(rot, 315, 360) or between(rot, 0, 45) then + return "north" + elseif between(rot, 135, 225) then + return "south" + elseif between(rot, 225, 315) then + return "east" + elseif between(rot, 45, 135) then + return "west" + end +end + +function ws.getaxis() + local dir=ws.getdir() + if dir == "north" or dir == "south" then return "z" end + return "x" +end +function ws.setdir(dir) -- + if dir == "north" then + minetest.localplayer:set_yaw(0) + elseif dir == "south" then + minetest.localplayer:set_yaw(180) + elseif dir == "east" then + minetest.localplayer:set_yaw(270) + elseif dir == "west" then + minetest.localplayer:set_yaw(90) + end +end + +function ws.dircoord(f, y, r ,rpos, rdir) + local dir= ws.getdir(rdir) + local coord = ws.optcoord(f, y, r) + local f = coord.x + local y = coord.y + local r = coord.z + local lp= rpos or minetest.localplayer:get_pos() + if dir == "north" then + return ws.relcoord(r, y, f,rpos) + elseif dir == "south" then + return ws.relcoord(-r, y, -f,rpos) + elseif dir == "east" then + return ws.relcoord(f, y, -r,rpos) + elseif dir== "west" then + return ws.relcoord(-f, y, r,rpos) + end + return ws.relcoord(0, 0, 0,rpos) +end + +function ws.get_dimension(pos) + if pos.y > -65 then return "overworld" + elseif pos.y > -8000 then return "void" + elseif pos.y > -27000 then return "end" + elseif pos.y > -28930 then return "void" + elseif pos.y > -31000 then return "nether" + else return "void" + end +end + +function ws.aim(tpos) + local ppos=minetest.localplayer:get_pos() + local dir=vector.direction(ppos,tpos) + local yyaw=0; + local pitch=0; + if dir.x < 0 then + yyaw = math.atan2(-dir.x, dir.z) + (math.pi * 2) + else + yyaw = math.atan2(-dir.x, dir.z) + end + yyaw = ws.round2(math.deg(yyaw),2) + pitch = ws.round2(math.deg(math.asin(-dir.y) * 1),2); + minetest.localplayer:set_yaw(yyaw) + minetest.localplayer:set_pitch(pitch) +end + +function ws.gaim(tpos,v,g) + local v = v or 40 + local g = g or 9.81 + local ppos=minetest.localplayer:get_pos() + local dir=vector.direction(ppos,tpos) + local yyaw=0; + local pitch=0; + if dir.x < 0 then + yyaw = math.atan2(-dir.x, dir.z) + (math.pi * 2) + else + yyaw = math.atan2(-dir.x, dir.z) + end + yyaw = ws.round2(math.deg(yyaw),2) + local y = dir.y + dir.y = 0 + local x = vector.length(dir) + pitch=math.atan(math.pow(v, 2) / (g * x) + math.sqrt(math.pow(v, 4)/(math.pow(g, 2) * math.pow(x, 2)) - 2 * math.pow(v, 2) * y/(g * math.pow(x, 2)) - 1)) + --pitch = ws.round2(math.deg(math.asin(-dir.y) * 1),2); + minetest.localplayer:set_yaw(yyaw) + minetest.localplayer:set_pitch(math.deg(pitch)) +end + +function ws.buildable_to(pos) + local node=minetest.get_node_or_nil(pos) + if node then + return minetest.get_node_def(node.name).buildable_to + end +end + +function ws.tplace(p,n,stay) + if not p then return end + if n then ws.switch_to_item(n) end + local opos=ws.dircoord(0,0,0) + local tpos=vector.add(p,vector.new(0,1,0)) + minetest.localplayer:set_pos(tpos) + ws.place(p,{n}) + if not stay then + minetest.after(0.1,function() + minetest.localplayer:set_pos(opos) + end) + end +end + +minetest.register_chatcommand("tplace", { + description = "tp-place", + param = "Y", + func = function(param) + return ws.tplace(minetest.string_to_pos(param)) + end +}) + +function ws.ytp(param) + local y=tonumber(param) + local lp=ws.dircoord(0,0,0) + if lp.y < y + 50 then return false,"Can't TP up." end + if y < -30912 then return false,"Don't TP into the void lol." end + minetest.localplayer:set_pos(vector.new(lp.x,y,lp.z)) +end + +local function tablearg(arg) + local tb={} + if type(arg) == 'string' then + tb={arg} + elseif type(arg) == 'table' then + tb=arg + elseif type(arg) == 'function' then + tb=arg() + end + return tb +end + +function ws.isnode(pos,arg)--arg is either an itemstring, a table of itemstrings or a function returning an itemstring + local nodename=tablearg(arg) + local nd=minetest.get_node_or_nil(pos) + if nd and nodename and ws.in_list(nd.name,nodename) then + return true + end +end + +function ws.can_place_at(pos) + local node = minetest.get_node_or_nil(pos) + return (node and (node.name == "air" or node.name=="mcl_core:water_source" or node.name=="mcl_core:water_flowing" or node.name=="mcl_core:lava_source" or node.name=="mcl_core:lava_flowing" or minetest.get_node_def(node.name).buildable_to)) +end + +-- should check if wield is placeable +-- minetest.get_node(wielded:get_name()) ~= nil should probably work +-- otherwise it equips armor and eats food +function ws.can_place_wielded_at(pos) + local wield_empty = minetest.localplayer:get_wielded_item():is_empty() + return not wield_empty and ws.can_place_at(pos) +end + + +function ws.find_any_swap(items,hslot) + hslot=hslot or 8 + for i, v in ipairs(items) do + local n = minetest.find_item(v) + if n then + ws.switch_to_item(v,hslot) + return true + end + end + return false +end + + +-- swaps to any of the items and places if need be +-- returns true if placed and in inventory or already there, false otherwise + +local lastact=0 +local lastplc=0 +local lastdig=0 +local actint=10 +function ws.place(pos,items,hslot, place) + --if nodes_this_tick > 8 then return end + --nodes_this_tick = nodes_this_tick + 1 + --if not inside_constraints(pos) then return end + if not pos then return end + if not ws.can_place_at(pos) then return end + items=tablearg(items) + + place = place or minetest.place_node + + local node = minetest.get_node_or_nil(pos) + if not node then return end + -- already there + if ws.isnode(pos,items) then + return true + else + if ws.find_any_swap(items,hslot) then + place(pos) + return true + end + end +end + +function ws.place_if_able(pos) + if not pos then return end + if not inside_constraints(pos) then return end + if ws.can_place_wielded_at(pos) then + minetest.place_node(pos) + end +end + +function ws.is_diggable(pos) + if not pos then return false end + local nd=minetest.get_node_or_nil(pos) + if not nd then return false end + local n = minetest.get_node_def(nd.name) + if n and n.diggable then return true end + return false +end + +function ws.dig(pos,condition,autotool) + --if not inside_constraints(pos) then return end + if autotool == nil then autotool = true end + if condition and not condition(pos) then return false end + if not ws.is_diggable(pos) then return end + local nd=minetest.get_node_or_nil(pos) + if nd and minetest.get_node_def(nd.name).diggable then + if autotool then ws.select_best_tool(pos) end + minetest.dig_node(pos) + end + return true +end + +function ws.chunk_loaded() + local ign=minetest.find_nodes_near(ws.dircoord(0,0,0),10,{'ignore'},true) + if #ign == 0 then return true end + return false +end + +function ws.get_near(nodes,range) + range=range or 5 + local nds=minetest.find_nodes_near(ws.dircoord(0,0,0),rang,nodes,true) + if #nds > 0 then return nds end + return false +end + +function ws.is_laggy() + if tps_client and tps_client.ping and tps_client.ping > 1000 then return true end +end + + +function ws.donodes(poss,func,condition) + if ws.is_laggy() then return end + local dn_i=0 + for k,v in pairs(poss) do + if dn_i > 8 then return end + --local nd=minetest.get_node_or_nil(v) + if condition == nil or condition(v) then + func(v) + dn_i = dn_i + 1 + end + end +end + +function ws.dignodes(poss,condition) + local func=function(p) ws.dig(p) end + ws.donodes(poss,func,condition) +end + + +function ws.replace(pos,arg) + arg=tablearg(arg) + local nd=minetest.get_node_or_nil(pos) + if nd and not ws.in_list(nd.name,arg) and ws.buildable_to(pos) then + local tm=ws.get_digtime(nd.name) or 0 + ws.dig(pos) + minetest.after(tm + 0.1,function() + ws.place(pos,arg) + end) + return tm + else + return ws.place(pos,arg) + end +end + +function ws.playeron(p) + local pls=minetest.get_player_names() + for k,v in pairs(pls) do + if v == p then return true end + end + return false +end + + +function ws.between(x, y, z) -- x is between y and z (inclusive) + return y <= x and x <= z +end + + +local wall_pos1={x=-1255,y=6,z=792} +local wall_pos2={x=-1452,y=80,z=981} +local iwall_pos1={x=-1266,y=6,z=802} +local iwall_pos2={x=-1442,y=80,z=971} + +function ws.in_cube(tpos,wpos1,wpos2) + local xmax=wpos2.x + local xmin=wpos1.x + + local ymax=wpos2.y + local ymin=wpos1.y + + local zmax=wpos2.z + local zmin=wpos1.z + if wpos1.x > wpos2.x then + xmax=wpos1.x + xmin=wpos2.x + end + if wpos1.y > wpos2.y then + ymax=wpos1.y + ymin=wpos2.y + end + if wpos1.z > wpos2.z then + zmax=wpos1.z + zmin=wpos2.z + end + if ws.between(tpos.x,xmin,xmax) and ws.between(tpos.y,ymin,ymax) and ws.between(tpos.z,zmin,zmax) then + return true + end + return false +end + +function ws.in_wall(pos) + if ws.in_cube(pos,wall_pos1,wall_pos2) and not in_cube(pos,iwall_pos1,iwall_pos2) then + return true end + return false +end + +function ws.inside_wall(pos) + local p1=iwall_pos1 + local p2=iwall_pos2 + if ws.in_cube(pos,p1,p2) then return true end + return false +end + +function ws.find_closest_reachable_airpocket(pos) + local lp=ws.dircoord(0,0,0) + local nds=minetest.find_nodes_near(lp,5,{'air'}) + local odst=10 + local rt=lp + for k,v in ipairs(nds) do + local dst=vector.distance(pos,v) + if dst < odst then odst=dst rt=v end + end + if odst==10 then return false end + return vector.add(rt,vector.new(0,-1,0)) +end + + +-- DEBUG +local function printwieldedmeta() + ws.dcm(dump(minetest.localplayer:get_wielded_item():get_meta():to_table())) +end +minetest.register_cheat('ItemMeta','Test',printwieldedmeta) +-- CC0/Unlicense Emilia 2020 + +local seeds = { + "mcl_farming:wheat_seeds", + "mcl_farming:beetroot_seeds", + "mcl_farming:carrot_item", + "mcl_farming:potato_item" +} + +local nodeseeds = { + "mcl_farming:melon_seeds", + "mcl_farming:pumpkin_seeds" +} + +local tillable = { + "mcl_core:dirt", + "mcl_core:dirt_with_grass", + "mcl_farming:soil" +} + +local hoes = { + "mcl_farming:hoe_wood", + "mcl_farming:hoe_stone", + "mcl_farming:hoe_iron", + "mcl_farming:hoe_gold", + "mcl_farming:hoe_diamond" +} + +local water = { + "mcl_core:water_source", + "mcl_buckets:bucket_water", + "mcl_buckets:bucket_river_water" +} + +scaffold.register_template_scaffold("AutoFarm", "scaffold_farm", function(below) + local lp = vector.round(minetest.localplayer:get_pos()) + + -- farmland + if below.x % 5 ~= 0 or below.z % 5 ~= 0 then + if scaffold.place_if_needed(tillable, below) then + if scaffold.can_place_at(lp) then + if scaffold.find_any_swap(hoes) then + minetest.interact("place", below) + scaffold.place_if_needed(seeds, lp) + end + end + end + -- water + else + scaffold.place_if_needed(water, below) + end +end) + +scaffold.register_template_scaffold("AutoMelon", "scaffold_melon", function(below) + local lp = vector.round(minetest.localplayer:get_pos()) + + local x = below.x % 5 + local z = below.z % 5 + + -- water + if x == 0 and z == 0 then + scaffold.place_if_needed(water, below) + -- dirt + elseif z == 2 or z == 4 or ((x == 2 or x == 4) and z == 0) then + scaffold.place_if_needed(tillable, below) + -- farmland + elseif (x == 1 or z == 1) or (x == 3 or z == 3) then + if scaffold.place_if_needed(tillable, below) then + if scaffold.can_place_at(lp) then + if scaffold.find_any_swap(hoes) then + minetest.interact("place", below) + scaffold.place_if_needed(nodeseeds, lp) + end + end + end + end +end) diff --git a/scaffold/depends.txt b/scaffold/depends.txt new file mode 100644 index 0000000..a1135e0 --- /dev/null +++ b/scaffold/depends.txt @@ -0,0 +1 @@ +turtle diff --git a/scaffold/init.lua b/scaffold/init.lua new file mode 100644 index 0000000..403f25a --- /dev/null +++ b/scaffold/init.lua @@ -0,0 +1,1265 @@ + +ws = {} +ws.registered_globalhacks = {} +ws.displayed_wps={} + +ws.c = core + +ws.range=4 +ws.target=nil +ws.targetpos=nil + +local nextact = {} +local ghwason={} + +local nodes_this_tick=0 + +function ws.s(name,value) + if value == nil then + return ws.c.settings:get(name) + else + ws.c.settings:set(name,value) + return ws.c.settings:get(name) + end +end +function ws.sb(name,value) + if value == nil then + return ws.c.settings:get_bool(name) + else + ws.c.settings:set_bool(name,value) + return ws.c.settings:get_bool(name) + end +end + +function ws.dcm(msg) + return minetest.display_chat_message(msg) +end +function ws.set_bool_bulk(settings,value) + if type(settings) ~= 'table' then return false end + for k,v in pairs(settings) do + minetest.settings:set_bool(v,value) + end + return true +end + +function ws.shuffle(tbl) + for i = #tbl, 2, -1 do + local j = math.random(i) + tbl[i], tbl[j] = tbl[j], tbl[i] + end + return tbl +end + +function ws.in_list(val, list) + if type(list) ~= "table" then return false end + for i, v in pairs(list) do + if v == val then + return true + end + end + return false +end + +function ws.random_table_element(tbl) + local ks = {} + for k in pairs(tbl) do + table.insert(ks, k) + end + return tbl[ks[math.random(#ks)]] +end + +function ws.center() + --local lp=ws.dircoord(0,0,0) + --minetest.localplayer:set_pos(lp) +end + +function ws.globalhacktemplate(setting,func,funcstart,funcstop,daughters,delay) + funcstart = funcstart or function() end + funcstop = funcstop or function() end + delay = delay or 0.5 + return function() + if not minetest.localplayer then return end + if minetest.settings:get_bool(setting) then + if tps_client and tps_client.ping and tps_client.ping > 1000 then return end + nodes_this_tick = 0 + if nextact[setting] and nextact[setting] > os.clock() then return end + nextact[setting] = os.clock() + delay + if not ghwason[setting] then + if not funcstart() then + ws.set_bool_bulk(daughters,true) + ghwason[setting] = true + --ws.dcm(setting.. " activated") + ws.center() + minetest.settings:set('last-dir',ws.getdir()) + minetest.settings:set('last-y',ws.dircoord(0,0,0).y) + else minetest.settings:set_bool(setting,false) + end + else + func() + end + + elseif ghwason[setting] then + ghwason[setting] = false + ws.set_bool_bulk(daughters,false) + funcstop() + --ws.dcm(setting.. " deactivated") + end + end +end + +function ws.register_globalhack(func) + table.insert(ws.registered_globalhacks,func) +end + +function ws.register_globalhacktemplate(name,category,setting,func,funcstart,funcstop,daughters) + ws.register_globalhack(ws.globalhacktemplate(setting,func,funcstart,funcstop,daughters)) + minetest.register_cheat(name,category,setting) +end + +ws.rg=ws.register_globalhacktemplate + +function ws.step_globalhacks(dtime) + for i, v in ipairs(ws.registered_globalhacks) do + v(dtime) + end +end + +minetest.register_globalstep(function(dtime) ws.step_globalhacks(dtime) end) +minetest.settings:set_bool('continuous_forward',false) + +function ws.on_connect(func) + if not minetest.localplayer then minetest.after(0,function() ws.on_connect(func) end) return end + if func then func() end +end + +ws.on_connect(function() + local ldir =minetest.settings:get('last-dir') + if ldir then ws.setdir(ldir) end +end) + + +-- COORD MAGIC + +function ws.is_same_pos(pos1,pos2) + return vector.distance(vector.round(pos1),vector.round(pos2)) == 0 +end +function ws.get_reachable_positions(range,under) + under=under or false + range=range or 4 + local rt={} + local lp=vector.round(minetest.localplayer:get_pos()) + local ylim=range + if under then ylim=-1 end + for x = -range,range,1 do + for y = -range,ylim,1 do + for z = -range,range,1 do + table.insert(rt,vector.round(vector.add(lp,vector.new(x,y,z)))) + end + end + end + return rt +end + +function ws.do_area(radius,func,plane) + for k,v in pairs(ws.get_reachable_positions(range)) do + if not plane or v.y == minetest.localplayer:get_pos().y -1 then + func(v) + end + end +end + +function ws.get_hud_by_texture(texture) + local def + local i = -1 + repeat + i = i + 1 + def = minetest.localplayer:hud_get(i) + until not def or def.text == texture + if def then + return def + end + def.number=0 + return def +end + +function ws.find_player(name) + for k, v in ipairs(minetest.localplayer.get_nearby_objects(500)) do + if v:get_name() == name then + return v:get_pos(),v + end + end +end + +function ws.display_wp(pos,name) + local ix = #ws.displayed_wps + 1 + ws.displayed_wps[ix] = minetest.localplayer:hud_add({ + hud_elem_type = 'waypoint', + name = name, + text = name, + number = 0x00ff00, + world_pos = pos + }) + return ix +end + +function ws.clear_wp(ix) + table.remove(ws.displayed_wps,ix) +end + +function ws.clear_wps() + for k,v in ipairs(ws.displayed_wps) do + minetest.localplayer:hud_remove(v) + table.remove(ws.displayed_wps,k) + end +end + +function ws.register_chatcommand_alias(old, ...) + local def = assert(minetest.registered_chatcommands[old]) + def.name = nil + for i = 1, select('#', ...) do + minetest.register_chatcommand(select(i, ...), table.copy(def)) + end +end + +function ws.round2(num, numDecimalPlaces) + return tonumber(string.format("%." .. (numDecimalPlaces or 0) .. "f", num)) +end + +function ws.pos_to_string(pos) + if type(pos) == 'table' then + pos = minetest.pos_to_string(vector.round(pos)) + end + if type(pos) == 'string' then + return pos + end + return pos +end + +function ws.string_to_pos(pos) + if type(pos) == 'string' then + pos = minetest.string_to_pos(pos) + end + if type(pos) == 'table' then + return vector.round(pos) + end + return pos +end + + + +--ITEMS +function ws.find_item_in_table(items,rnd) + if type(items) == 'string' then + return minetest.find_item(items) + end + if type(items) ~= 'table' then return end + if rnd then items=ws.shuffle(items) end + for i, v in pairs(items) do + local n = minetest.find_item(v) + if n then + return n + end + end + return false +end + +function ws.find_empty(inv) + for i, v in ipairs(inv) do + if v:is_empty() then + return i + end + end + return false +end + +function ws.find_named(inv, name) + if not inv then return -1 end + if not name then return end + for i, v in ipairs(inv) do + if v:get_name():find(name) then + return i + end + end +end + +function ws.itemnameformat(description) + description = description:gsub(string.char(0x1b) .. "%(.@[^)]+%)", "") + description = description:match("([^\n]*)") + return description +end + +function ws.find_nametagged(list, name) + for i, v in ipairs(list) do + if ws.itemnameformat(v:get_description()) == name then + return i + end + end +end + +local hotbar_slot=8 +function ws.to_hotbar(it,hslot) + local tpos=nil + local plinv = minetest.get_inventory("current_player") + if hslot and hslot < 10 then + tpos=hslot + else + for i, v in ipairs(plinv.main) do + if i<10 and v:is_empty() then + tpos = i + break + end + end + end + if tpos == nil then tpos=hotbar_slot end + local mv = InventoryAction("move") + mv:from("current_player", "main", it) + mv:to("current_player", "main", tpos) + mv:apply() + return tpos +end + +function ws.switch_to_item(itname,hslot) + if not minetest.localplayer then return false end + local plinv = minetest.get_inventory("current_player") + for i, v in ipairs(plinv.main) do + if i<10 and v:get_name() == itname then + minetest.localplayer:set_wield_index(i) + return true + end + end + local pos = ws.find_named(plinv.main, itname) + if pos then + minetest.localplayer:set_wield_index(ws.to_hotbar(pos,hslot)) + return true + end + return false +end +function ws.in_inv(itname) + if not minetest.localplayer then return false end + local plinv = minetest.get_inventory("current_player") + local pos = ws.find_named(plinv.main, itname) + if pos then + return true + end +end + +function core.switch_to_item(item) return ws.switch_to_item(item) end + +function ws.switch_inv_or_echest(name,max_count,hslot) + if not minetest.localplayer then return false end + local plinv = minetest.get_inventory("current_player") + if ws.switch_to_item(name) then return true end + + local epos = ws.find_named(plinv.enderchest, name) + if epos then + local tpos + for i, v in ipairs(plinv.main) do + if i < 9 and v:is_empty() then + tpos = i + break + end + end + if not tpos then tpos=hotbar_slot end + + if tpos then + local mv = InventoryAction("move") + mv:from("current_player", "enderchest", epos) + mv:to("current_player", "main", tpos) + if max_count then + mv:set_count(max_count) + end + mv:apply() + minetest.localplayer:set_wield_index(tpos) + return true + end + end + return false +end + +local function posround(n) + return math.floor(n + 0.5) +end + +local function fmt(c) + return tostring(posround(c.x))..","..tostring(posround(c.y))..","..tostring(posround(c.z)) +end + +local function map_pos(value) + if value.x then + return value + else + return {x = value[1], y = value[2], z = value[3]} + end +end + +function ws.invparse(location) + if type(location) == "string" then + if string.match(location, "^[-]?[0-9]+,[-]?[0-9]+,[-]?[0-9]+$") then + return "nodemeta:" .. location + else + return location + end + elseif type(location) == "table" then + return "nodemeta:" .. fmt(map_pos(location)) + end +end + +function ws.invpos(p) + return "nodemeta:"..p.x..","..p.y..","..p.z +end + + +-- TOOLS + + +local function check_tool(stack, node_groups, old_best_time) + local toolcaps = stack:get_tool_capabilities() + if not toolcaps then return end + local best_time = old_best_time + for group, groupdef in pairs(toolcaps.groupcaps) do + local level = node_groups[group] + if level then + local this_time = groupdef.times[level] + if this_time and this_time < best_time then + best_time = this_time + end + end + end + return best_time < old_best_time, best_time +end + +local function find_best_tool(nodename, switch) + local player = minetest.localplayer + local inventory = minetest.get_inventory("current_player") + local node_groups = minetest.get_node_def(nodename).groups + local new_index = player:get_wield_index() + local is_better, best_time = false, math.huge + + is_better, best_time = check_tool(player:get_wielded_item(), node_groups, best_time) + if inventory.hand then + is_better, best_time = check_tool(inventory.hand[1], node_groups, best_time) + end + + for index, stack in ipairs(inventory.main) do + is_better, best_time = check_tool(stack, node_groups, best_time) + if is_better then + new_index = index + end + end + + return new_index,best_time +end + +function ws.get_digtime(nodename) + local idx,tm=find_best_tool(nodename) + return tm +end + +function ws.select_best_tool(pos) + local nd=minetest.get_node_or_nil(pos) + local nodename='air' + if nd then nodename=nd.name end + local t=find_best_tool(nodename) + minetest.localplayer:set_wield_index(ws.to_hotbar(t,hotbar_slot)) + --minetest.localplayer:set_wield_index(find_best_tool(nodename)) +end + +--- COORDS +function ws.coord(x, y, z) + return vector.new(x,y,z) +end +function ws.ordercoord(c) + if c.x == nil then + return {x = c[1], y = c[2], z = c[3]} + else + return c + end +end + +-- x or {x,y,z} or {x=x,y=y,z=z} +function ws.optcoord(x, y, z) + if y and z then + return ws.coord(x, y, z) + else + return ws.ordercoord(x) + end +end +function ws.cadd(c1, c2) + return vector.add(c1,c2) + --return ws.coord(c1.x + c2.x, c1.y + c2.y, c1.z + c2.z) +end + +function ws.relcoord(x, y, z, rpos) + local pos = rpos or minetest.localplayer:get_pos() + pos.y=math.ceil(pos.y) + --math.floor(pos.y) + 0.5 + return ws.cadd(pos, ws.optcoord(x, y, z)) +end + +local function between(x, y, z) -- x is between y and z (inclusive) + return y <= x and x <= z +end + +function ws.getdir(yaw) -- + local rot = yaw or minetest.localplayer:get_yaw() % 360 + if between(rot, 315, 360) or between(rot, 0, 45) then + return "north" + elseif between(rot, 135, 225) then + return "south" + elseif between(rot, 225, 315) then + return "east" + elseif between(rot, 45, 135) then + return "west" + end +end + +function ws.getaxis() + local dir=ws.getdir() + if dir == "north" or dir == "south" then return "z" end + return "x" +end +function ws.setdir(dir) -- + if dir == "north" then + minetest.localplayer:set_yaw(0) + elseif dir == "south" then + minetest.localplayer:set_yaw(180) + elseif dir == "east" then + minetest.localplayer:set_yaw(270) + elseif dir == "west" then + minetest.localplayer:set_yaw(90) + end +end + +function ws.dircoord(f, y, r ,rpos, rdir) + local dir= ws.getdir(rdir) + local coord = ws.optcoord(f, y, r) + local f = coord.x + local y = coord.y + local r = coord.z + local lp= rpos or minetest.localplayer:get_pos() + if dir == "north" then + return ws.relcoord(r, y, f,rpos) + elseif dir == "south" then + return ws.relcoord(-r, y, -f,rpos) + elseif dir == "east" then + return ws.relcoord(f, y, -r,rpos) + elseif dir== "west" then + return ws.relcoord(-f, y, r,rpos) + end + return ws.relcoord(0, 0, 0,rpos) +end + +function ws.get_dimension(pos) + if pos.y > -65 then return "overworld" + elseif pos.y > -8000 then return "void" + elseif pos.y > -27000 then return "end" + elseif pos.y > -28930 then return "void" + elseif pos.y > -31000 then return "nether" + else return "void" + end +end + +function ws.aim(tpos) + local ppos=minetest.localplayer:get_pos() + local dir=vector.direction(ppos,tpos) + local yyaw=0; + local pitch=0; + if dir.x < 0 then + yyaw = math.atan2(-dir.x, dir.z) + (math.pi * 2) + else + yyaw = math.atan2(-dir.x, dir.z) + end + yyaw = ws.round2(math.deg(yyaw),2) + pitch = ws.round2(math.deg(math.asin(-dir.y) * 1),2); + minetest.localplayer:set_yaw(yyaw) + minetest.localplayer:set_pitch(pitch) +end + +function ws.gaim(tpos,v,g) + local v = v or 40 + local g = g or 9.81 + local ppos=minetest.localplayer:get_pos() + local dir=vector.direction(ppos,tpos) + local yyaw=0; + local pitch=0; + if dir.x < 0 then + yyaw = math.atan2(-dir.x, dir.z) + (math.pi * 2) + else + yyaw = math.atan2(-dir.x, dir.z) + end + yyaw = ws.round2(math.deg(yyaw),2) + local y = dir.y + dir.y = 0 + local x = vector.length(dir) + pitch=math.atan(math.pow(v, 2) / (g * x) + math.sqrt(math.pow(v, 4)/(math.pow(g, 2) * math.pow(x, 2)) - 2 * math.pow(v, 2) * y/(g * math.pow(x, 2)) - 1)) + --pitch = ws.round2(math.deg(math.asin(-dir.y) * 1),2); + minetest.localplayer:set_yaw(yyaw) + minetest.localplayer:set_pitch(math.deg(pitch)) +end + +function ws.buildable_to(pos) + local node=minetest.get_node_or_nil(pos) + if node then + return minetest.get_node_def(node.name).buildable_to + end +end + +function ws.tplace(p,n,stay) + if not p then return end + if n then ws.switch_to_item(n) end + local opos=ws.dircoord(0,0,0) + local tpos=vector.add(p,vector.new(0,1,0)) + minetest.localplayer:set_pos(tpos) + ws.place(p,{n}) + if not stay then + minetest.after(0.1,function() + minetest.localplayer:set_pos(opos) + end) + end +end + +minetest.register_chatcommand("tplace", { + description = "tp-place", + param = "Y", + func = function(param) + return ws.tplace(minetest.string_to_pos(param)) + end +}) + +function ws.ytp(param) + local y=tonumber(param) + local lp=ws.dircoord(0,0,0) + if lp.y < y + 50 then return false,"Can't TP up." end + if y < -30912 then return false,"Don't TP into the void lol." end + minetest.localplayer:set_pos(vector.new(lp.x,y,lp.z)) +end + +local function tablearg(arg) + local tb={} + if type(arg) == 'string' then + tb={arg} + elseif type(arg) == 'table' then + tb=arg + elseif type(arg) == 'function' then + tb=arg() + end + return tb +end + +function ws.isnode(pos,arg)--arg is either an itemstring, a table of itemstrings or a function returning an itemstring + local nodename=tablearg(arg) + local nd=minetest.get_node_or_nil(pos) + if nd and nodename and ws.in_list(nd.name,nodename) then + return true + end +end + +function ws.can_place_at(pos) + local node = minetest.get_node_or_nil(pos) + return (node and (node.name == "air" or node.name=="mcl_core:water_source" or node.name=="mcl_core:water_flowing" or node.name=="mcl_core:lava_source" or node.name=="mcl_core:lava_flowing" or minetest.get_node_def(node.name).buildable_to)) +end + +-- should check if wield is placeable +-- minetest.get_node(wielded:get_name()) ~= nil should probably work +-- otherwise it equips armor and eats food +function ws.can_place_wielded_at(pos) + local wield_empty = minetest.localplayer:get_wielded_item():is_empty() + return not wield_empty and ws.can_place_at(pos) +end + + +function ws.find_any_swap(items,hslot) + hslot=hslot or 8 + for i, v in ipairs(items) do + local n = minetest.find_item(v) + if n then + ws.switch_to_item(v,hslot) + return true + end + end + return false +end + + +-- swaps to any of the items and places if need be +-- returns true if placed and in inventory or already there, false otherwise + +local lastact=0 +local lastplc=0 +local lastdig=0 +local actint=10 +function ws.place(pos,items,hslot, place) + --if nodes_this_tick > 8 then return end + --nodes_this_tick = nodes_this_tick + 1 + --if not inside_constraints(pos) then return end + if not pos then return end + if not ws.can_place_at(pos) then return end + items=tablearg(items) + + place = place or minetest.place_node + + local node = minetest.get_node_or_nil(pos) + if not node then return end + -- already there + if ws.isnode(pos,items) then + return true + else + if ws.find_any_swap(items,hslot) then + place(pos) + return true + end + end +end + +function ws.place_if_able(pos) + if not pos then return end + if not inside_constraints(pos) then return end + if ws.can_place_wielded_at(pos) then + minetest.place_node(pos) + end +end + +function ws.is_diggable(pos) + if not pos then return false end + local nd=minetest.get_node_or_nil(pos) + if not nd then return false end + local n = minetest.get_node_def(nd.name) + if n and n.diggable then return true end + return false +end + +function ws.dig(pos,condition,autotool) + --if not inside_constraints(pos) then return end + if autotool == nil then autotool = true end + if condition and not condition(pos) then return false end + if not ws.is_diggable(pos) then return end + local nd=minetest.get_node_or_nil(pos) + if nd and minetest.get_node_def(nd.name).diggable then + if autotool then ws.select_best_tool(pos) end + minetest.dig_node(pos) + end + return true +end + +function ws.chunk_loaded() + local ign=minetest.find_nodes_near(ws.dircoord(0,0,0),10,{'ignore'},true) + if #ign == 0 then return true end + return false +end + +function ws.get_near(nodes,range) + range=range or 5 + local nds=minetest.find_nodes_near(ws.dircoord(0,0,0),rang,nodes,true) + if #nds > 0 then return nds end + return false +end + +function ws.is_laggy() + if tps_client and tps_client.ping and tps_client.ping > 1000 then return true end +end + + +function ws.donodes(poss,func,condition) + if ws.is_laggy() then return end + local dn_i=0 + for k,v in pairs(poss) do + if dn_i > 8 then return end + --local nd=minetest.get_node_or_nil(v) + if condition == nil or condition(v) then + func(v) + dn_i = dn_i + 1 + end + end +end + +function ws.dignodes(poss,condition) + local func=function(p) ws.dig(p) end + ws.donodes(poss,func,condition) +end + + +function ws.replace(pos,arg) + arg=tablearg(arg) + local nd=minetest.get_node_or_nil(pos) + if nd and not ws.in_list(nd.name,arg) and ws.buildable_to(pos) then + local tm=ws.get_digtime(nd.name) or 0 + ws.dig(pos) + minetest.after(tm + 0.1,function() + ws.place(pos,arg) + end) + return tm + else + return ws.place(pos,arg) + end +end + +function ws.playeron(p) + local pls=minetest.get_player_names() + for k,v in pairs(pls) do + if v == p then return true end + end + return false +end + + +function ws.between(x, y, z) -- x is between y and z (inclusive) + return y <= x and x <= z +end + + +local wall_pos1={x=-1255,y=6,z=792} +local wall_pos2={x=-1452,y=80,z=981} +local iwall_pos1={x=-1266,y=6,z=802} +local iwall_pos2={x=-1442,y=80,z=971} + +function ws.in_cube(tpos,wpos1,wpos2) + local xmax=wpos2.x + local xmin=wpos1.x + + local ymax=wpos2.y + local ymin=wpos1.y + + local zmax=wpos2.z + local zmin=wpos1.z + if wpos1.x > wpos2.x then + xmax=wpos1.x + xmin=wpos2.x + end + if wpos1.y > wpos2.y then + ymax=wpos1.y + ymin=wpos2.y + end + if wpos1.z > wpos2.z then + zmax=wpos1.z + zmin=wpos2.z + end + if ws.between(tpos.x,xmin,xmax) and ws.between(tpos.y,ymin,ymax) and ws.between(tpos.z,zmin,zmax) then + return true + end + return false +end + +function ws.in_wall(pos) + if ws.in_cube(pos,wall_pos1,wall_pos2) and not in_cube(pos,iwall_pos1,iwall_pos2) then + return true end + return false +end + +function ws.inside_wall(pos) + local p1=iwall_pos1 + local p2=iwall_pos2 + if ws.in_cube(pos,p1,p2) then return true end + return false +end + +function ws.find_closest_reachable_airpocket(pos) + local lp=ws.dircoord(0,0,0) + local nds=minetest.find_nodes_near(lp,5,{'air'}) + local odst=10 + local rt=lp + for k,v in ipairs(nds) do + local dst=vector.distance(pos,v) + if dst < odst then odst=dst rt=v end + end + if odst==10 then return false end + return vector.add(rt,vector.new(0,-1,0)) +end + + +-- DEBUG +local function printwieldedmeta() + ws.dcm(dump(minetest.localplayer:get_wielded_item():get_meta():to_table())) +end +minetest.register_cheat('ItemMeta','Test',printwieldedmeta) +-- CC0/Unlicense Emilia & cora 2020 + +local category = "Scaffold" + +scaffold = {} +scaffold.lockdir = false +scaffold.locky = false +scaffold.constrain1 = false +scaffold.constrain2 = false +local hwps={} + +local multiscaff_width=5 +local multiscaff_depth=1 +local multiscaff_above=0 +local multiscaff_mod=1 + +local storage=minetest.get_mod_storage() + + +local nodes_per_tick = 8 + +local function setnpt() + nodes_per_tick = tonumber(minetest.settings:get("nodes_per_tick")) or 8 +end + +function scaffold.template(setting, func, offset, funcstop ) + offset = offset or {x = 0, y = -1, z = 0} + funcstop = funcstop or function() end + + return function() + if minetest.localplayer and minetest.settings:get_bool(setting) then + if scaffold.constrain1 and not inside_constraints(tgt) then return end + local tgt=vector.add(minetest.localplayer:get_pos(),offset) + func(tgt) + end + end +end + +function scaffold.register_template_scaffold(name, setting, func, offset, funcstop) + ws.rg(name,'Scaffold',setting,scaffold.template(setting, func, offset),funcstop ) +end + +local function between(x, y, z) return y <= x and x <= z end -- x is between y and z (inclusive) + +function scaffold.in_cube(tpos,wpos1,wpos2) + local xmax=wpos2.x + local xmin=wpos1.x + + local ymax=wpos2.y + local ymin=wpos1.y + + local zmax=wpos2.z + local zmin=wpos1.z + if wpos1.x > wpos2.x then + xmax=wpos1.x + xmin=wpos2.x + end + if wpos1.y > wpos2.y then + ymax=wpos1.y + ymin=wpos2.y + end + if wpos1.z > wpos2.z then + zmax=wpos1.z + zmin=wpos2.z + end + if between(tpos.x,xmin,xmax) and between(tpos.y,ymin,ymax) and between(tpos.z,zmin,zmax) then + return true + end + return false +end + +local function set_hwp(name,pos) + ws.display_wp(pos,name) +end + +function scaffold.set_pos1(pos) + if not pos then local pos=minetest.localplayer:get_pos() end + scaffold.constrain1=vector.round(pos) + local pstr=minetest.pos_to_string(scaffold.constrain1) + set_hwp('scaffold_pos1 '..pstr,scaffold.constrain1) + minetest.display_chat_message("scaffold pos1 set to "..pstr) +end +function scaffold.set_pos2(pos) + if not pos then pos=minetest.localplayer:get_pos() end + scaffold.constrain2=vector.round(pos) + local pstr=minetest.pos_to_string(scaffold.constrain2) + set_hwp('scaffold_pos2 '..pstr,scaffold.constrain2) + minetest.display_chat_message("scaffold pos2 set to "..pstr) +end + +function scaffold.reset() + scaffold.constrain1=false + scaffold.constrain2=false + for k,v in pairs(hwps) do + minetest.localplayer:hud_remove(v) + table.remove(hwps,k) + end +end + +local function inside_constraints(pos) + if (scaffold.constrain1 and scaffold.constrain2 and scaffold.in_cube(pos,scaffold.constrain1,scaffold.constrain2)) then return true + elseif not scaffold.constrain1 then return true + end + return false +end + +minetest.register_chatcommand("sc_pos1", { func = scaffold.set_pos1 }) +minetest.register_chatcommand("sc_pos2", { func = scaffold.set_pos2 }) +minetest.register_chatcommand("sc_reset", { func = scaffold.reset }) + + + + +function scaffold.can_place_at(pos) + local node = minetest.get_node_or_nil(pos) + return (node and (node.name == "air" or node.name=="mcl_core:water_source" or node.name=="mcl_core:water_flowing" or node.name=="mcl_core:lava_source" or node.name=="mcl_core:lava_flowing" or minetest.get_node_def(node.name).buildable_to)) +end + +-- should check if wield is placeable +-- minetest.get_node(wielded:get_name()) ~= nil should probably work +-- otherwise it equips armor and eats food +function scaffold.can_place_wielded_at(pos) + local wield_empty = minetest.localplayer:get_wielded_item():is_empty() + return not wield_empty and scaffold.can_place_at(pos) +end + + +function scaffold.find_any_swap(items) + local ts=8 + for i, v in ipairs(items) do + local n = minetest.find_item(v) + if n then + ws.switch_to_item(v) + return true + end + end + return false +end + +function scaffold.in_list(val, list) + if type(list) ~= "table" then return false end + for i, v in ipairs(list) do + if v == val then + return true + end + end + return false +end + +-- swaps to any of the items and places if need be +-- returns true if placed and in inventory or already there, false otherwise + +local lastact=0 +local lastplc=0 +local lastdig=0 +local actint=10 +function scaffold.place_if_needed(items, pos, place) + if not inside_constraints(pos) then return end + if not pos then return end + + place = place or minetest.place_node + + local node = minetest.get_node_or_nil(pos) + if not node then return end + -- already there + if node and scaffold.in_list(node.name, items) then + return true + else + local swapped = scaffold.find_any_swap(items) + + -- need to place + if swapped and scaffold.can_place_at(pos) then + --minetest.after("0.05",place,pos) + place(pos) + return true + -- can't place + else + return false + end + end +end + +function scaffold.place_if_able(pos) + if not pos then return end + if not inside_constraints(pos) then return end + if scaffold.can_place_wielded_at(pos) then + minetest.place_node(pos) + end +end + +local function is_diggable(pos) + if not pos then return false end + local nd=minetest.get_node_or_nil(pos) + if not nd then return false end + local n = minetest.get_node_def(nd.name) + if n and n.diggable then return true end + return false +end + +function scaffold.dig(pos) + if not inside_constraints(pos) then return end + if is_diggable(pos) then + local nd=minetest.get_node_or_nil(pos) + minetest.select_best_tool(nd.name) + if emicor then emicor.supertool() + end + --minetest.select_best_tool(nd.name) + minetest.dig_node(pos) + + end + return false +end + + +local mpath = minetest.get_modpath(minetest.get_current_modname()) +dofile(mpath .. "/sapscaffold.lua") +dofile(mpath .. "/slowscaffold.lua") +dofile(mpath .. "/autofarm.lua") +dofile(mpath .. "/railscaffold.lua") +dofile(mpath .. "/wallbot.lua") +dofile(mpath .. "/ow2bot.lua") +dofile(mpath .. "/atower.lua") +--dofile(mpath .. "/squarry.lua") +ws.rg('DigHead','Player','dighead',function() ws.dig(ws.dircoord(0,1,0)) end) + + + +local function checknode(pos) + local node = minetest.get_node_or_nil(pos) + if node then return true end + return false +end + +minetest.register_chatcommand('scaffw', { + func = function(param) multiscaff_width=tonumber(param) end +}) +minetest.register_chatcommand('scaffd', { + func = function(param) multiscaff_depth=tonumber(param) end +}) +minetest.register_chatcommand('scaffa', { + func = function(param) multiscaff_above=tonumber(param) end +}) +minetest.register_chatcommand('scaffm', { + func = function(param) multiscaff_mod=tonumber(param) end +}) + +local multiscaff_node=nil +ws.rg('MultiScaff','Scaffold','scaffold',function() + if not multiscaff_node then return end + local n=math.floor(multiscaff_width/2) + for i=-n,n do + for j=(multiscaff_depth * -1), -1 do + local p=ws.dircoord(0,j,i) + local nd=minetest.get_node_or_nil(p) + ws.place(p,{multiscaff_node}) + end + end +end,function() + multiscaff_node=minetest.localplayer:get_wielded_item():get_name() + ws.dcm("Multiscaff started. Width: "..multiscaff_width..', depth:'..multiscaff_depth..' Selected node: '..multiscaff_node) +end,function() + ws.dcm("Multiscaff stopped") +end) + +ws.rg('MScaffModulo','Scaffold','multiscaffm',function() + if not multiscaff_node then return end + ws.switch_to_item(multiscaff_node) + local n=math.floor(multiscaff_width/2) + for i=-n,n do + for j=(multiscaff_depth * -1), -1 do + local p=vector.round(ws.dircoord(0,j,i)) + if p.z % multiscaff_mod == 0 then + if p.x % multiscaff_mod ~=0 then + core.place_node(p) + end + else + if p.x % multiscaff_mod == 0 then + core.place_node(p) + end + end + end + end +end,function() + multiscaff_node=minetest.localplayer:get_wielded_item():get_name() + ws.dcm("ModuloScaff started. Width: "..multiscaff_width..', depth:'..multiscaff_depth..' Selected node: '..multiscaff_node) +end,function() + ws.dcm("Moduloscaff stopped") +end) + + + +scaffold.register_template_scaffold("WallScaffold", "scaffold_five_down", function(pos) + scaffold.place_if_able(ws.dircoord(0, -1, 0)) + scaffold.place_if_able(ws.dircoord(0, -2, 0)) + scaffold.place_if_able(ws.dircoord(0, -3, 0)) + scaffold.place_if_able(ws.dircoord(0, -4, 0)) + scaffold.place_if_able(ws.dircoord(0, -5, 0)) +end) + + +scaffold.register_template_scaffold("headTriScaff", "scaffold_three_wide_head", function(pos) + scaffold.place_if_able(ws.dircoord(0, 3, 0)) + scaffold.place_if_able(ws.dircoord(0, 3, 1)) + scaffold.place_if_able(ws.dircoord(0, 3, -1)) +end) + +scaffold.register_template_scaffold("RandomScaff", "scaffold_rnd", function(below) + local n = minetest.get_node_or_nil(below) + local nl=nlist.get('randomscaffold') + table.shuffle(nl) + if n and not scaffold.in_list(n.name, nl) then + scaffold.dig(below) + scaffold.place_if_needed(nl, below) + end +end) + + +ws.rg("HighwayZ","World","highwayz",function() + local positions = { + {x = 0, y = 0, z = z}, + {x = 1, y = 0, z = z}, + {x = 2, y = 1, z = z}, + {x = -2, y = 1, z = z}, + {x = -2, y = 0, z = z}, + {x = -1, y = 0, z = z}, + {x = 2, y = 0, z = z} + } + for i, p in pairs(positions) do + if i > nodes_per_tick then break end + minetest.place_node(p) + end + +end, setnpt) + +ws.rg("BlockWater","World","block_water",function() + local lp=ws.dircoord(0,0,0) + local positions = minetest.find_nodes_near(lp, 5, {"mcl_core:water_source", "mcl_core:water_flowing"}, true) + for i, p in pairs(positions) do + if i > nodes_per_tick then return end + minetest.place_node(p) + end +end,setnpt) + +ws.rg("BlockLava","World","block_lava",function() + local lp=ws.dircoord(0,0,0) + local positions = minetest.find_nodes_near(lp, 5, {"mcl_core:lava_source", "mcl_core:lava_flowing"}, true) + for i, p in pairs(positions) do + if i > nodes_per_tick then return end + minetest.place_node(p) + end +end,setnpt) + +ws.rg("BlockSources","World","block_sources",function() + local lp=ws.dircoord(0,0,0) + local positions = minetest.find_nodes_near(lp, 5, {"mcl_core:lava_source","mcl_nether:nether_lava_source","mcl_core:water_source"}, true) + for i, p in pairs(positions) do + if p.y<2 then + if p.x>500 and p.z>500 then return end + end + + if i > nodes_per_tick then return end + minetest.place_node(p) + end +end,setnpt) + +ws.rg("PlaceOnTop","World","place_on_top",function() + local lp=ws.dircoord(0,0,0) + local positions = minetest.find_nodes_near_under_air_except(lp, 5, item:get_name(), true) + for i, p in pairs(positions) do + if i > nodes_per_tick then break end + minetest.place_node(vector.add(p, {x = 0, y = 1, z = 0})) + end +end,setnpt) + +ws.rg("Nuke","World","nuke",function() + local pos=ws.dircoord(0,0,0) + local i = 0 + for x = pos.x - 4, pos.x + 4 do + for y = pos.y - 4, pos.y + 4 do + for z = pos.z - 4, pos.z + 4 do + local p = vector.new(x, y, z) + local node = minetest.get_node_or_nil(p) + local def = node and minetest.get_node_def(node.name) + if def and def.diggable then + if i > nodes_per_tick then return end + minetest.dig_node(p) + i = i + 1 + end + end + end + end +end,setnpt) diff --git a/scaffold/ow2bot.lua b/scaffold/ow2bot.lua new file mode 100644 index 0000000..d44c9ab --- /dev/null +++ b/scaffold/ow2bot.lua @@ -0,0 +1,891 @@ + +ws = {} +ws.registered_globalhacks = {} +ws.displayed_wps={} + +ws.c = core + +ws.range=4 +ws.target=nil +ws.targetpos=nil + +local nextact = {} +local ghwason={} + +local nodes_this_tick=0 + +function ws.s(name,value) + if value == nil then + return ws.c.settings:get(name) + else + ws.c.settings:set(name,value) + return ws.c.settings:get(name) + end +end +function ws.sb(name,value) + if value == nil then + return ws.c.settings:get_bool(name) + else + ws.c.settings:set_bool(name,value) + return ws.c.settings:get_bool(name) + end +end + +function ws.dcm(msg) + return minetest.display_chat_message(msg) +end +function ws.set_bool_bulk(settings,value) + if type(settings) ~= 'table' then return false end + for k,v in pairs(settings) do + minetest.settings:set_bool(v,value) + end + return true +end + +function ws.shuffle(tbl) + for i = #tbl, 2, -1 do + local j = math.random(i) + tbl[i], tbl[j] = tbl[j], tbl[i] + end + return tbl +end + +function ws.in_list(val, list) + if type(list) ~= "table" then return false end + for i, v in pairs(list) do + if v == val then + return true + end + end + return false +end + +function ws.random_table_element(tbl) + local ks = {} + for k in pairs(tbl) do + table.insert(ks, k) + end + return tbl[ks[math.random(#ks)]] +end + +function ws.center() + --local lp=ws.dircoord(0,0,0) + --minetest.localplayer:set_pos(lp) +end + +function ws.globalhacktemplate(setting,func,funcstart,funcstop,daughters,delay) + funcstart = funcstart or function() end + funcstop = funcstop or function() end + delay = delay or 0.5 + return function() + if not minetest.localplayer then return end + if minetest.settings:get_bool(setting) then + if tps_client and tps_client.ping and tps_client.ping > 1000 then return end + nodes_this_tick = 0 + if nextact[setting] and nextact[setting] > os.clock() then return end + nextact[setting] = os.clock() + delay + if not ghwason[setting] then + if not funcstart() then + ws.set_bool_bulk(daughters,true) + ghwason[setting] = true + --ws.dcm(setting.. " activated") + ws.center() + minetest.settings:set('last-dir',ws.getdir()) + minetest.settings:set('last-y',ws.dircoord(0,0,0).y) + else minetest.settings:set_bool(setting,false) + end + else + func() + end + + elseif ghwason[setting] then + ghwason[setting] = false + ws.set_bool_bulk(daughters,false) + funcstop() + --ws.dcm(setting.. " deactivated") + end + end +end + +function ws.register_globalhack(func) + table.insert(ws.registered_globalhacks,func) +end + +function ws.register_globalhacktemplate(name,category,setting,func,funcstart,funcstop,daughters) + ws.register_globalhack(ws.globalhacktemplate(setting,func,funcstart,funcstop,daughters)) + minetest.register_cheat(name,category,setting) +end + +ws.rg=ws.register_globalhacktemplate + +function ws.step_globalhacks(dtime) + for i, v in ipairs(ws.registered_globalhacks) do + v(dtime) + end +end + +minetest.register_globalstep(function(dtime) ws.step_globalhacks(dtime) end) +minetest.settings:set_bool('continuous_forward',false) + +function ws.on_connect(func) + if not minetest.localplayer then minetest.after(0,function() ws.on_connect(func) end) return end + if func then func() end +end + +ws.on_connect(function() + local ldir =minetest.settings:get('last-dir') + if ldir then ws.setdir(ldir) end +end) + + +-- COORD MAGIC + +function ws.is_same_pos(pos1,pos2) + return vector.distance(vector.round(pos1),vector.round(pos2)) == 0 +end +function ws.get_reachable_positions(range,under) + under=under or false + range=range or 4 + local rt={} + local lp=vector.round(minetest.localplayer:get_pos()) + local ylim=range + if under then ylim=-1 end + for x = -range,range,1 do + for y = -range,ylim,1 do + for z = -range,range,1 do + table.insert(rt,vector.round(vector.add(lp,vector.new(x,y,z)))) + end + end + end + return rt +end + +function ws.do_area(radius,func,plane) + for k,v in pairs(ws.get_reachable_positions(range)) do + if not plane or v.y == minetest.localplayer:get_pos().y -1 then + func(v) + end + end +end + +function ws.get_hud_by_texture(texture) + local def + local i = -1 + repeat + i = i + 1 + def = minetest.localplayer:hud_get(i) + until not def or def.text == texture + if def then + return def + end + def.number=0 + return def +end + +function ws.find_player(name) + for k, v in ipairs(minetest.localplayer.get_nearby_objects(500)) do + if v:get_name() == name then + return v:get_pos(),v + end + end +end + +function ws.display_wp(pos,name) + local ix = #ws.displayed_wps + 1 + ws.displayed_wps[ix] = minetest.localplayer:hud_add({ + hud_elem_type = 'waypoint', + name = name, + text = name, + number = 0x00ff00, + world_pos = pos + }) + return ix +end + +function ws.clear_wp(ix) + table.remove(ws.displayed_wps,ix) +end + +function ws.clear_wps() + for k,v in ipairs(ws.displayed_wps) do + minetest.localplayer:hud_remove(v) + table.remove(ws.displayed_wps,k) + end +end + +function ws.register_chatcommand_alias(old, ...) + local def = assert(minetest.registered_chatcommands[old]) + def.name = nil + for i = 1, select('#', ...) do + minetest.register_chatcommand(select(i, ...), table.copy(def)) + end +end + +function ws.round2(num, numDecimalPlaces) + return tonumber(string.format("%." .. (numDecimalPlaces or 0) .. "f", num)) +end + +function ws.pos_to_string(pos) + if type(pos) == 'table' then + pos = minetest.pos_to_string(vector.round(pos)) + end + if type(pos) == 'string' then + return pos + end + return pos +end + +function ws.string_to_pos(pos) + if type(pos) == 'string' then + pos = minetest.string_to_pos(pos) + end + if type(pos) == 'table' then + return vector.round(pos) + end + return pos +end + + + +--ITEMS +function ws.find_item_in_table(items,rnd) + if type(items) == 'string' then + return minetest.find_item(items) + end + if type(items) ~= 'table' then return end + if rnd then items=ws.shuffle(items) end + for i, v in pairs(items) do + local n = minetest.find_item(v) + if n then + return n + end + end + return false +end + +function ws.find_empty(inv) + for i, v in ipairs(inv) do + if v:is_empty() then + return i + end + end + return false +end + +function ws.find_named(inv, name) + if not inv then return -1 end + if not name then return end + for i, v in ipairs(inv) do + if v:get_name():find(name) then + return i + end + end +end + +function ws.itemnameformat(description) + description = description:gsub(string.char(0x1b) .. "%(.@[^)]+%)", "") + description = description:match("([^\n]*)") + return description +end + +function ws.find_nametagged(list, name) + for i, v in ipairs(list) do + if ws.itemnameformat(v:get_description()) == name then + return i + end + end +end + +local hotbar_slot=8 +function ws.to_hotbar(it,hslot) + local tpos=nil + local plinv = minetest.get_inventory("current_player") + if hslot and hslot < 10 then + tpos=hslot + else + for i, v in ipairs(plinv.main) do + if i<10 and v:is_empty() then + tpos = i + break + end + end + end + if tpos == nil then tpos=hotbar_slot end + local mv = InventoryAction("move") + mv:from("current_player", "main", it) + mv:to("current_player", "main", tpos) + mv:apply() + return tpos +end + +function ws.switch_to_item(itname,hslot) + if not minetest.localplayer then return false end + local plinv = minetest.get_inventory("current_player") + for i, v in ipairs(plinv.main) do + if i<10 and v:get_name() == itname then + minetest.localplayer:set_wield_index(i) + return true + end + end + local pos = ws.find_named(plinv.main, itname) + if pos then + minetest.localplayer:set_wield_index(ws.to_hotbar(pos,hslot)) + return true + end + return false +end +function ws.in_inv(itname) + if not minetest.localplayer then return false end + local plinv = minetest.get_inventory("current_player") + local pos = ws.find_named(plinv.main, itname) + if pos then + return true + end +end + +function core.switch_to_item(item) return ws.switch_to_item(item) end + +function ws.switch_inv_or_echest(name,max_count,hslot) + if not minetest.localplayer then return false end + local plinv = minetest.get_inventory("current_player") + if ws.switch_to_item(name) then return true end + + local epos = ws.find_named(plinv.enderchest, name) + if epos then + local tpos + for i, v in ipairs(plinv.main) do + if i < 9 and v:is_empty() then + tpos = i + break + end + end + if not tpos then tpos=hotbar_slot end + + if tpos then + local mv = InventoryAction("move") + mv:from("current_player", "enderchest", epos) + mv:to("current_player", "main", tpos) + if max_count then + mv:set_count(max_count) + end + mv:apply() + minetest.localplayer:set_wield_index(tpos) + return true + end + end + return false +end + +local function posround(n) + return math.floor(n + 0.5) +end + +local function fmt(c) + return tostring(posround(c.x))..","..tostring(posround(c.y))..","..tostring(posround(c.z)) +end + +local function map_pos(value) + if value.x then + return value + else + return {x = value[1], y = value[2], z = value[3]} + end +end + +function ws.invparse(location) + if type(location) == "string" then + if string.match(location, "^[-]?[0-9]+,[-]?[0-9]+,[-]?[0-9]+$") then + return "nodemeta:" .. location + else + return location + end + elseif type(location) == "table" then + return "nodemeta:" .. fmt(map_pos(location)) + end +end + +function ws.invpos(p) + return "nodemeta:"..p.x..","..p.y..","..p.z +end + + +-- TOOLS + + +local function check_tool(stack, node_groups, old_best_time) + local toolcaps = stack:get_tool_capabilities() + if not toolcaps then return end + local best_time = old_best_time + for group, groupdef in pairs(toolcaps.groupcaps) do + local level = node_groups[group] + if level then + local this_time = groupdef.times[level] + if this_time and this_time < best_time then + best_time = this_time + end + end + end + return best_time < old_best_time, best_time +end + +local function find_best_tool(nodename, switch) + local player = minetest.localplayer + local inventory = minetest.get_inventory("current_player") + local node_groups = minetest.get_node_def(nodename).groups + local new_index = player:get_wield_index() + local is_better, best_time = false, math.huge + + is_better, best_time = check_tool(player:get_wielded_item(), node_groups, best_time) + if inventory.hand then + is_better, best_time = check_tool(inventory.hand[1], node_groups, best_time) + end + + for index, stack in ipairs(inventory.main) do + is_better, best_time = check_tool(stack, node_groups, best_time) + if is_better then + new_index = index + end + end + + return new_index,best_time +end + +function ws.get_digtime(nodename) + local idx,tm=find_best_tool(nodename) + return tm +end + +function ws.select_best_tool(pos) + local nd=minetest.get_node_or_nil(pos) + local nodename='air' + if nd then nodename=nd.name end + local t=find_best_tool(nodename) + minetest.localplayer:set_wield_index(ws.to_hotbar(t,hotbar_slot)) + --minetest.localplayer:set_wield_index(find_best_tool(nodename)) +end + +--- COORDS +function ws.coord(x, y, z) + return vector.new(x,y,z) +end +function ws.ordercoord(c) + if c.x == nil then + return {x = c[1], y = c[2], z = c[3]} + else + return c + end +end + +-- x or {x,y,z} or {x=x,y=y,z=z} +function ws.optcoord(x, y, z) + if y and z then + return ws.coord(x, y, z) + else + return ws.ordercoord(x) + end +end +function ws.cadd(c1, c2) + return vector.add(c1,c2) + --return ws.coord(c1.x + c2.x, c1.y + c2.y, c1.z + c2.z) +end + +function ws.relcoord(x, y, z, rpos) + local pos = rpos or minetest.localplayer:get_pos() + pos.y=math.ceil(pos.y) + --math.floor(pos.y) + 0.5 + return ws.cadd(pos, ws.optcoord(x, y, z)) +end + +local function between(x, y, z) -- x is between y and z (inclusive) + return y <= x and x <= z +end + +function ws.getdir(yaw) -- + local rot = yaw or minetest.localplayer:get_yaw() % 360 + if between(rot, 315, 360) or between(rot, 0, 45) then + return "north" + elseif between(rot, 135, 225) then + return "south" + elseif between(rot, 225, 315) then + return "east" + elseif between(rot, 45, 135) then + return "west" + end +end + +function ws.getaxis() + local dir=ws.getdir() + if dir == "north" or dir == "south" then return "z" end + return "x" +end +function ws.setdir(dir) -- + if dir == "north" then + minetest.localplayer:set_yaw(0) + elseif dir == "south" then + minetest.localplayer:set_yaw(180) + elseif dir == "east" then + minetest.localplayer:set_yaw(270) + elseif dir == "west" then + minetest.localplayer:set_yaw(90) + end +end + +function ws.dircoord(f, y, r ,rpos, rdir) + local dir= ws.getdir(rdir) + local coord = ws.optcoord(f, y, r) + local f = coord.x + local y = coord.y + local r = coord.z + local lp= rpos or minetest.localplayer:get_pos() + if dir == "north" then + return ws.relcoord(r, y, f,rpos) + elseif dir == "south" then + return ws.relcoord(-r, y, -f,rpos) + elseif dir == "east" then + return ws.relcoord(f, y, -r,rpos) + elseif dir== "west" then + return ws.relcoord(-f, y, r,rpos) + end + return ws.relcoord(0, 0, 0,rpos) +end + +function ws.get_dimension(pos) + if pos.y > -65 then return "overworld" + elseif pos.y > -8000 then return "void" + elseif pos.y > -27000 then return "end" + elseif pos.y > -28930 then return "void" + elseif pos.y > -31000 then return "nether" + else return "void" + end +end + +function ws.aim(tpos) + local ppos=minetest.localplayer:get_pos() + local dir=vector.direction(ppos,tpos) + local yyaw=0; + local pitch=0; + if dir.x < 0 then + yyaw = math.atan2(-dir.x, dir.z) + (math.pi * 2) + else + yyaw = math.atan2(-dir.x, dir.z) + end + yyaw = ws.round2(math.deg(yyaw),2) + pitch = ws.round2(math.deg(math.asin(-dir.y) * 1),2); + minetest.localplayer:set_yaw(yyaw) + minetest.localplayer:set_pitch(pitch) +end + +function ws.gaim(tpos,v,g) + local v = v or 40 + local g = g or 9.81 + local ppos=minetest.localplayer:get_pos() + local dir=vector.direction(ppos,tpos) + local yyaw=0; + local pitch=0; + if dir.x < 0 then + yyaw = math.atan2(-dir.x, dir.z) + (math.pi * 2) + else + yyaw = math.atan2(-dir.x, dir.z) + end + yyaw = ws.round2(math.deg(yyaw),2) + local y = dir.y + dir.y = 0 + local x = vector.length(dir) + pitch=math.atan(math.pow(v, 2) / (g * x) + math.sqrt(math.pow(v, 4)/(math.pow(g, 2) * math.pow(x, 2)) - 2 * math.pow(v, 2) * y/(g * math.pow(x, 2)) - 1)) + --pitch = ws.round2(math.deg(math.asin(-dir.y) * 1),2); + minetest.localplayer:set_yaw(yyaw) + minetest.localplayer:set_pitch(math.deg(pitch)) +end + +function ws.buildable_to(pos) + local node=minetest.get_node_or_nil(pos) + if node then + return minetest.get_node_def(node.name).buildable_to + end +end + +function ws.tplace(p,n,stay) + if not p then return end + if n then ws.switch_to_item(n) end + local opos=ws.dircoord(0,0,0) + local tpos=vector.add(p,vector.new(0,1,0)) + minetest.localplayer:set_pos(tpos) + ws.place(p,{n}) + if not stay then + minetest.after(0.1,function() + minetest.localplayer:set_pos(opos) + end) + end +end + +minetest.register_chatcommand("tplace", { + description = "tp-place", + param = "Y", + func = function(param) + return ws.tplace(minetest.string_to_pos(param)) + end +}) + +function ws.ytp(param) + local y=tonumber(param) + local lp=ws.dircoord(0,0,0) + if lp.y < y + 50 then return false,"Can't TP up." end + if y < -30912 then return false,"Don't TP into the void lol." end + minetest.localplayer:set_pos(vector.new(lp.x,y,lp.z)) +end + +local function tablearg(arg) + local tb={} + if type(arg) == 'string' then + tb={arg} + elseif type(arg) == 'table' then + tb=arg + elseif type(arg) == 'function' then + tb=arg() + end + return tb +end + +function ws.isnode(pos,arg)--arg is either an itemstring, a table of itemstrings or a function returning an itemstring + local nodename=tablearg(arg) + local nd=minetest.get_node_or_nil(pos) + if nd and nodename and ws.in_list(nd.name,nodename) then + return true + end +end + +function ws.can_place_at(pos) + local node = minetest.get_node_or_nil(pos) + return (node and (node.name == "air" or node.name=="mcl_core:water_source" or node.name=="mcl_core:water_flowing" or node.name=="mcl_core:lava_source" or node.name=="mcl_core:lava_flowing" or minetest.get_node_def(node.name).buildable_to)) +end + +-- should check if wield is placeable +-- minetest.get_node(wielded:get_name()) ~= nil should probably work +-- otherwise it equips armor and eats food +function ws.can_place_wielded_at(pos) + local wield_empty = minetest.localplayer:get_wielded_item():is_empty() + return not wield_empty and ws.can_place_at(pos) +end + + +function ws.find_any_swap(items,hslot) + hslot=hslot or 8 + for i, v in ipairs(items) do + local n = minetest.find_item(v) + if n then + ws.switch_to_item(v,hslot) + return true + end + end + return false +end + + +-- swaps to any of the items and places if need be +-- returns true if placed and in inventory or already there, false otherwise + +local lastact=0 +local lastplc=0 +local lastdig=0 +local actint=10 +function ws.place(pos,items,hslot, place) + --if nodes_this_tick > 8 then return end + --nodes_this_tick = nodes_this_tick + 1 + --if not inside_constraints(pos) then return end + if not pos then return end + if not ws.can_place_at(pos) then return end + items=tablearg(items) + + place = place or minetest.place_node + + local node = minetest.get_node_or_nil(pos) + if not node then return end + -- already there + if ws.isnode(pos,items) then + return true + else + if ws.find_any_swap(items,hslot) then + place(pos) + return true + end + end +end + +function ws.place_if_able(pos) + if not pos then return end + if not inside_constraints(pos) then return end + if ws.can_place_wielded_at(pos) then + minetest.place_node(pos) + end +end + +function ws.is_diggable(pos) + if not pos then return false end + local nd=minetest.get_node_or_nil(pos) + if not nd then return false end + local n = minetest.get_node_def(nd.name) + if n and n.diggable then return true end + return false +end + +function ws.dig(pos,condition,autotool) + --if not inside_constraints(pos) then return end + if autotool == nil then autotool = true end + if condition and not condition(pos) then return false end + if not ws.is_diggable(pos) then return end + local nd=minetest.get_node_or_nil(pos) + if nd and minetest.get_node_def(nd.name).diggable then + if autotool then ws.select_best_tool(pos) end + minetest.dig_node(pos) + end + return true +end + +function ws.chunk_loaded() + local ign=minetest.find_nodes_near(ws.dircoord(0,0,0),10,{'ignore'},true) + if #ign == 0 then return true end + return false +end + +function ws.get_near(nodes,range) + range=range or 5 + local nds=minetest.find_nodes_near(ws.dircoord(0,0,0),rang,nodes,true) + if #nds > 0 then return nds end + return false +end + +function ws.is_laggy() + if tps_client and tps_client.ping and tps_client.ping > 1000 then return true end +end + + +function ws.donodes(poss,func,condition) + if ws.is_laggy() then return end + local dn_i=0 + for k,v in pairs(poss) do + if dn_i > 8 then return end + --local nd=minetest.get_node_or_nil(v) + if condition == nil or condition(v) then + func(v) + dn_i = dn_i + 1 + end + end +end + +function ws.dignodes(poss,condition) + local func=function(p) ws.dig(p) end + ws.donodes(poss,func,condition) +end + + +function ws.replace(pos,arg) + arg=tablearg(arg) + local nd=minetest.get_node_or_nil(pos) + if nd and not ws.in_list(nd.name,arg) and ws.buildable_to(pos) then + local tm=ws.get_digtime(nd.name) or 0 + ws.dig(pos) + minetest.after(tm + 0.1,function() + ws.place(pos,arg) + end) + return tm + else + return ws.place(pos,arg) + end +end + +function ws.playeron(p) + local pls=minetest.get_player_names() + for k,v in pairs(pls) do + if v == p then return true end + end + return false +end + + +function ws.between(x, y, z) -- x is between y and z (inclusive) + return y <= x and x <= z +end + + +local wall_pos1={x=-1255,y=6,z=792} +local wall_pos2={x=-1452,y=80,z=981} +local iwall_pos1={x=-1266,y=6,z=802} +local iwall_pos2={x=-1442,y=80,z=971} + +function ws.in_cube(tpos,wpos1,wpos2) + local xmax=wpos2.x + local xmin=wpos1.x + + local ymax=wpos2.y + local ymin=wpos1.y + + local zmax=wpos2.z + local zmin=wpos1.z + if wpos1.x > wpos2.x then + xmax=wpos1.x + xmin=wpos2.x + end + if wpos1.y > wpos2.y then + ymax=wpos1.y + ymin=wpos2.y + end + if wpos1.z > wpos2.z then + zmax=wpos1.z + zmin=wpos2.z + end + if ws.between(tpos.x,xmin,xmax) and ws.between(tpos.y,ymin,ymax) and ws.between(tpos.z,zmin,zmax) then + return true + end + return false +end + +function ws.in_wall(pos) + if ws.in_cube(pos,wall_pos1,wall_pos2) and not in_cube(pos,iwall_pos1,iwall_pos2) then + return true end + return false +end + +function ws.inside_wall(pos) + local p1=iwall_pos1 + local p2=iwall_pos2 + if ws.in_cube(pos,p1,p2) then return true end + return false +end + +function ws.find_closest_reachable_airpocket(pos) + local lp=ws.dircoord(0,0,0) + local nds=minetest.find_nodes_near(lp,5,{'air'}) + local odst=10 + local rt=lp + for k,v in ipairs(nds) do + local dst=vector.distance(pos,v) + if dst < odst then odst=dst rt=v end + end + if odst==10 then return false end + return vector.add(rt,vector.new(0,-1,0)) +end + + +-- DEBUG +local function printwieldedmeta() + ws.dcm(dump(minetest.localplayer:get_wielded_item():get_meta():to_table())) +end +minetest.register_cheat('ItemMeta','Test',printwieldedmeta) +local Y=1337 +local plnodes={'mcl_core:cobble','mcl_core:dirt','mcl_core:dirt_with_grass','mcl_core:obsidian'} +ws.rg("OW2Bot","Bots","ow2bot", function(pos) + local lp=minetest.localplayer:get_pos() + local r=3 + local pos1=vector.add(lp,{x=r,y=0,z=r}) + local pos2=vector.add(lp,{x=-r,y=0,z=-r}) + pos1.y=Y + pos2.y=Y + + ws.do_area(3,function(pos) + ws.place(pos,plnodes) + end,true) + +end,function() + + +end) diff --git a/scaffold/railscaffold.lua b/scaffold/railscaffold.lua new file mode 100644 index 0000000..c48acd2 --- /dev/null +++ b/scaffold/railscaffold.lua @@ -0,0 +1,1191 @@ + +ws = {} +ws.registered_globalhacks = {} +ws.displayed_wps={} + +ws.c = core + +ws.range=4 +ws.target=nil +ws.targetpos=nil + +local nextact = {} +local ghwason={} + +local nodes_this_tick=0 + +function ws.s(name,value) + if value == nil then + return ws.c.settings:get(name) + else + ws.c.settings:set(name,value) + return ws.c.settings:get(name) + end +end +function ws.sb(name,value) + if value == nil then + return ws.c.settings:get_bool(name) + else + ws.c.settings:set_bool(name,value) + return ws.c.settings:get_bool(name) + end +end + +function ws.dcm(msg) + return minetest.display_chat_message(msg) +end +function ws.set_bool_bulk(settings,value) + if type(settings) ~= 'table' then return false end + for k,v in pairs(settings) do + minetest.settings:set_bool(v,value) + end + return true +end + +function ws.shuffle(tbl) + for i = #tbl, 2, -1 do + local j = math.random(i) + tbl[i], tbl[j] = tbl[j], tbl[i] + end + return tbl +end + +function ws.in_list(val, list) + if type(list) ~= "table" then return false end + for i, v in pairs(list) do + if v == val then + return true + end + end + return false +end + +function ws.random_table_element(tbl) + local ks = {} + for k in pairs(tbl) do + table.insert(ks, k) + end + return tbl[ks[math.random(#ks)]] +end + +function ws.center() + --local lp=ws.dircoord(0,0,0) + --minetest.localplayer:set_pos(lp) +end + +function ws.globalhacktemplate(setting,func,funcstart,funcstop,daughters,delay) + funcstart = funcstart or function() end + funcstop = funcstop or function() end + delay = delay or 0.5 + return function() + if not minetest.localplayer then return end + if minetest.settings:get_bool(setting) then + if tps_client and tps_client.ping and tps_client.ping > 1000 then return end + nodes_this_tick = 0 + if nextact[setting] and nextact[setting] > os.clock() then return end + nextact[setting] = os.clock() + delay + if not ghwason[setting] then + if not funcstart() then + ws.set_bool_bulk(daughters,true) + ghwason[setting] = true + --ws.dcm(setting.. " activated") + ws.center() + minetest.settings:set('last-dir',ws.getdir()) + minetest.settings:set('last-y',ws.dircoord(0,0,0).y) + else minetest.settings:set_bool(setting,false) + end + else + func() + end + + elseif ghwason[setting] then + ghwason[setting] = false + ws.set_bool_bulk(daughters,false) + funcstop() + --ws.dcm(setting.. " deactivated") + end + end +end + +function ws.register_globalhack(func) + table.insert(ws.registered_globalhacks,func) +end + +function ws.register_globalhacktemplate(name,category,setting,func,funcstart,funcstop,daughters) + ws.register_globalhack(ws.globalhacktemplate(setting,func,funcstart,funcstop,daughters)) + minetest.register_cheat(name,category,setting) +end + +ws.rg=ws.register_globalhacktemplate + +function ws.step_globalhacks(dtime) + for i, v in ipairs(ws.registered_globalhacks) do + v(dtime) + end +end + +minetest.register_globalstep(function(dtime) ws.step_globalhacks(dtime) end) +minetest.settings:set_bool('continuous_forward',false) + +function ws.on_connect(func) + if not minetest.localplayer then minetest.after(0,function() ws.on_connect(func) end) return end + if func then func() end +end + +ws.on_connect(function() + local ldir =minetest.settings:get('last-dir') + if ldir then ws.setdir(ldir) end +end) + + +-- COORD MAGIC + +function ws.is_same_pos(pos1,pos2) + return vector.distance(vector.round(pos1),vector.round(pos2)) == 0 +end +function ws.get_reachable_positions(range,under) + under=under or false + range=range or 4 + local rt={} + local lp=vector.round(minetest.localplayer:get_pos()) + local ylim=range + if under then ylim=-1 end + for x = -range,range,1 do + for y = -range,ylim,1 do + for z = -range,range,1 do + table.insert(rt,vector.round(vector.add(lp,vector.new(x,y,z)))) + end + end + end + return rt +end + +function ws.do_area(radius,func,plane) + for k,v in pairs(ws.get_reachable_positions(range)) do + if not plane or v.y == minetest.localplayer:get_pos().y -1 then + func(v) + end + end +end + +function ws.get_hud_by_texture(texture) + local def + local i = -1 + repeat + i = i + 1 + def = minetest.localplayer:hud_get(i) + until not def or def.text == texture + if def then + return def + end + def.number=0 + return def +end + +function ws.find_player(name) + for k, v in ipairs(minetest.localplayer.get_nearby_objects(500)) do + if v:get_name() == name then + return v:get_pos(),v + end + end +end + +function ws.display_wp(pos,name) + local ix = #ws.displayed_wps + 1 + ws.displayed_wps[ix] = minetest.localplayer:hud_add({ + hud_elem_type = 'waypoint', + name = name, + text = name, + number = 0x00ff00, + world_pos = pos + }) + return ix +end + +function ws.clear_wp(ix) + table.remove(ws.displayed_wps,ix) +end + +function ws.clear_wps() + for k,v in ipairs(ws.displayed_wps) do + minetest.localplayer:hud_remove(v) + table.remove(ws.displayed_wps,k) + end +end + +function ws.register_chatcommand_alias(old, ...) + local def = assert(minetest.registered_chatcommands[old]) + def.name = nil + for i = 1, select('#', ...) do + minetest.register_chatcommand(select(i, ...), table.copy(def)) + end +end + +function ws.round2(num, numDecimalPlaces) + return tonumber(string.format("%." .. (numDecimalPlaces or 0) .. "f", num)) +end + +function ws.pos_to_string(pos) + if type(pos) == 'table' then + pos = minetest.pos_to_string(vector.round(pos)) + end + if type(pos) == 'string' then + return pos + end + return pos +end + +function ws.string_to_pos(pos) + if type(pos) == 'string' then + pos = minetest.string_to_pos(pos) + end + if type(pos) == 'table' then + return vector.round(pos) + end + return pos +end + + + +--ITEMS +function ws.find_item_in_table(items,rnd) + if type(items) == 'string' then + return minetest.find_item(items) + end + if type(items) ~= 'table' then return end + if rnd then items=ws.shuffle(items) end + for i, v in pairs(items) do + local n = minetest.find_item(v) + if n then + return n + end + end + return false +end + +function ws.find_empty(inv) + for i, v in ipairs(inv) do + if v:is_empty() then + return i + end + end + return false +end + +function ws.find_named(inv, name) + if not inv then return -1 end + if not name then return end + for i, v in ipairs(inv) do + if v:get_name():find(name) then + return i + end + end +end + +function ws.itemnameformat(description) + description = description:gsub(string.char(0x1b) .. "%(.@[^)]+%)", "") + description = description:match("([^\n]*)") + return description +end + +function ws.find_nametagged(list, name) + for i, v in ipairs(list) do + if ws.itemnameformat(v:get_description()) == name then + return i + end + end +end + +local hotbar_slot=8 +function ws.to_hotbar(it,hslot) + local tpos=nil + local plinv = minetest.get_inventory("current_player") + if hslot and hslot < 10 then + tpos=hslot + else + for i, v in ipairs(plinv.main) do + if i<10 and v:is_empty() then + tpos = i + break + end + end + end + if tpos == nil then tpos=hotbar_slot end + local mv = InventoryAction("move") + mv:from("current_player", "main", it) + mv:to("current_player", "main", tpos) + mv:apply() + return tpos +end + +function ws.switch_to_item(itname,hslot) + if not minetest.localplayer then return false end + local plinv = minetest.get_inventory("current_player") + for i, v in ipairs(plinv.main) do + if i<10 and v:get_name() == itname then + minetest.localplayer:set_wield_index(i) + return true + end + end + local pos = ws.find_named(plinv.main, itname) + if pos then + minetest.localplayer:set_wield_index(ws.to_hotbar(pos,hslot)) + return true + end + return false +end +function ws.in_inv(itname) + if not minetest.localplayer then return false end + local plinv = minetest.get_inventory("current_player") + local pos = ws.find_named(plinv.main, itname) + if pos then + return true + end +end + +function core.switch_to_item(item) return ws.switch_to_item(item) end + +function ws.switch_inv_or_echest(name,max_count,hslot) + if not minetest.localplayer then return false end + local plinv = minetest.get_inventory("current_player") + if ws.switch_to_item(name) then return true end + + local epos = ws.find_named(plinv.enderchest, name) + if epos then + local tpos + for i, v in ipairs(plinv.main) do + if i < 9 and v:is_empty() then + tpos = i + break + end + end + if not tpos then tpos=hotbar_slot end + + if tpos then + local mv = InventoryAction("move") + mv:from("current_player", "enderchest", epos) + mv:to("current_player", "main", tpos) + if max_count then + mv:set_count(max_count) + end + mv:apply() + minetest.localplayer:set_wield_index(tpos) + return true + end + end + return false +end + +local function posround(n) + return math.floor(n + 0.5) +end + +local function fmt(c) + return tostring(posround(c.x))..","..tostring(posround(c.y))..","..tostring(posround(c.z)) +end + +local function map_pos(value) + if value.x then + return value + else + return {x = value[1], y = value[2], z = value[3]} + end +end + +function ws.invparse(location) + if type(location) == "string" then + if string.match(location, "^[-]?[0-9]+,[-]?[0-9]+,[-]?[0-9]+$") then + return "nodemeta:" .. location + else + return location + end + elseif type(location) == "table" then + return "nodemeta:" .. fmt(map_pos(location)) + end +end + +function ws.invpos(p) + return "nodemeta:"..p.x..","..p.y..","..p.z +end + + +-- TOOLS + + +local function check_tool(stack, node_groups, old_best_time) + local toolcaps = stack:get_tool_capabilities() + if not toolcaps then return end + local best_time = old_best_time + for group, groupdef in pairs(toolcaps.groupcaps) do + local level = node_groups[group] + if level then + local this_time = groupdef.times[level] + if this_time and this_time < best_time then + best_time = this_time + end + end + end + return best_time < old_best_time, best_time +end + +local function find_best_tool(nodename, switch) + local player = minetest.localplayer + local inventory = minetest.get_inventory("current_player") + local node_groups = minetest.get_node_def(nodename).groups + local new_index = player:get_wield_index() + local is_better, best_time = false, math.huge + + is_better, best_time = check_tool(player:get_wielded_item(), node_groups, best_time) + if inventory.hand then + is_better, best_time = check_tool(inventory.hand[1], node_groups, best_time) + end + + for index, stack in ipairs(inventory.main) do + is_better, best_time = check_tool(stack, node_groups, best_time) + if is_better then + new_index = index + end + end + + return new_index,best_time +end + +function ws.get_digtime(nodename) + local idx,tm=find_best_tool(nodename) + return tm +end + +function ws.select_best_tool(pos) + local nd=minetest.get_node_or_nil(pos) + local nodename='air' + if nd then nodename=nd.name end + local t=find_best_tool(nodename) + minetest.localplayer:set_wield_index(ws.to_hotbar(t,hotbar_slot)) + --minetest.localplayer:set_wield_index(find_best_tool(nodename)) +end + +--- COORDS +function ws.coord(x, y, z) + return vector.new(x,y,z) +end +function ws.ordercoord(c) + if c.x == nil then + return {x = c[1], y = c[2], z = c[3]} + else + return c + end +end + +-- x or {x,y,z} or {x=x,y=y,z=z} +function ws.optcoord(x, y, z) + if y and z then + return ws.coord(x, y, z) + else + return ws.ordercoord(x) + end +end +function ws.cadd(c1, c2) + return vector.add(c1,c2) + --return ws.coord(c1.x + c2.x, c1.y + c2.y, c1.z + c2.z) +end + +function ws.relcoord(x, y, z, rpos) + local pos = rpos or minetest.localplayer:get_pos() + pos.y=math.ceil(pos.y) + --math.floor(pos.y) + 0.5 + return ws.cadd(pos, ws.optcoord(x, y, z)) +end + +local function between(x, y, z) -- x is between y and z (inclusive) + return y <= x and x <= z +end + +function ws.getdir(yaw) -- + local rot = yaw or minetest.localplayer:get_yaw() % 360 + if between(rot, 315, 360) or between(rot, 0, 45) then + return "north" + elseif between(rot, 135, 225) then + return "south" + elseif between(rot, 225, 315) then + return "east" + elseif between(rot, 45, 135) then + return "west" + end +end + +function ws.getaxis() + local dir=ws.getdir() + if dir == "north" or dir == "south" then return "z" end + return "x" +end +function ws.setdir(dir) -- + if dir == "north" then + minetest.localplayer:set_yaw(0) + elseif dir == "south" then + minetest.localplayer:set_yaw(180) + elseif dir == "east" then + minetest.localplayer:set_yaw(270) + elseif dir == "west" then + minetest.localplayer:set_yaw(90) + end +end + +function ws.dircoord(f, y, r ,rpos, rdir) + local dir= ws.getdir(rdir) + local coord = ws.optcoord(f, y, r) + local f = coord.x + local y = coord.y + local r = coord.z + local lp= rpos or minetest.localplayer:get_pos() + if dir == "north" then + return ws.relcoord(r, y, f,rpos) + elseif dir == "south" then + return ws.relcoord(-r, y, -f,rpos) + elseif dir == "east" then + return ws.relcoord(f, y, -r,rpos) + elseif dir== "west" then + return ws.relcoord(-f, y, r,rpos) + end + return ws.relcoord(0, 0, 0,rpos) +end + +function ws.get_dimension(pos) + if pos.y > -65 then return "overworld" + elseif pos.y > -8000 then return "void" + elseif pos.y > -27000 then return "end" + elseif pos.y > -28930 then return "void" + elseif pos.y > -31000 then return "nether" + else return "void" + end +end + +function ws.aim(tpos) + local ppos=minetest.localplayer:get_pos() + local dir=vector.direction(ppos,tpos) + local yyaw=0; + local pitch=0; + if dir.x < 0 then + yyaw = math.atan2(-dir.x, dir.z) + (math.pi * 2) + else + yyaw = math.atan2(-dir.x, dir.z) + end + yyaw = ws.round2(math.deg(yyaw),2) + pitch = ws.round2(math.deg(math.asin(-dir.y) * 1),2); + minetest.localplayer:set_yaw(yyaw) + minetest.localplayer:set_pitch(pitch) +end + +function ws.gaim(tpos,v,g) + local v = v or 40 + local g = g or 9.81 + local ppos=minetest.localplayer:get_pos() + local dir=vector.direction(ppos,tpos) + local yyaw=0; + local pitch=0; + if dir.x < 0 then + yyaw = math.atan2(-dir.x, dir.z) + (math.pi * 2) + else + yyaw = math.atan2(-dir.x, dir.z) + end + yyaw = ws.round2(math.deg(yyaw),2) + local y = dir.y + dir.y = 0 + local x = vector.length(dir) + pitch=math.atan(math.pow(v, 2) / (g * x) + math.sqrt(math.pow(v, 4)/(math.pow(g, 2) * math.pow(x, 2)) - 2 * math.pow(v, 2) * y/(g * math.pow(x, 2)) - 1)) + --pitch = ws.round2(math.deg(math.asin(-dir.y) * 1),2); + minetest.localplayer:set_yaw(yyaw) + minetest.localplayer:set_pitch(math.deg(pitch)) +end + +function ws.buildable_to(pos) + local node=minetest.get_node_or_nil(pos) + if node then + return minetest.get_node_def(node.name).buildable_to + end +end + +function ws.tplace(p,n,stay) + if not p then return end + if n then ws.switch_to_item(n) end + local opos=ws.dircoord(0,0,0) + local tpos=vector.add(p,vector.new(0,1,0)) + minetest.localplayer:set_pos(tpos) + ws.place(p,{n}) + if not stay then + minetest.after(0.1,function() + minetest.localplayer:set_pos(opos) + end) + end +end + +minetest.register_chatcommand("tplace", { + description = "tp-place", + param = "Y", + func = function(param) + return ws.tplace(minetest.string_to_pos(param)) + end +}) + +function ws.ytp(param) + local y=tonumber(param) + local lp=ws.dircoord(0,0,0) + if lp.y < y + 50 then return false,"Can't TP up." end + if y < -30912 then return false,"Don't TP into the void lol." end + minetest.localplayer:set_pos(vector.new(lp.x,y,lp.z)) +end + +local function tablearg(arg) + local tb={} + if type(arg) == 'string' then + tb={arg} + elseif type(arg) == 'table' then + tb=arg + elseif type(arg) == 'function' then + tb=arg() + end + return tb +end + +function ws.isnode(pos,arg)--arg is either an itemstring, a table of itemstrings or a function returning an itemstring + local nodename=tablearg(arg) + local nd=minetest.get_node_or_nil(pos) + if nd and nodename and ws.in_list(nd.name,nodename) then + return true + end +end + +function ws.can_place_at(pos) + local node = minetest.get_node_or_nil(pos) + return (node and (node.name == "air" or node.name=="mcl_core:water_source" or node.name=="mcl_core:water_flowing" or node.name=="mcl_core:lava_source" or node.name=="mcl_core:lava_flowing" or minetest.get_node_def(node.name).buildable_to)) +end + +-- should check if wield is placeable +-- minetest.get_node(wielded:get_name()) ~= nil should probably work +-- otherwise it equips armor and eats food +function ws.can_place_wielded_at(pos) + local wield_empty = minetest.localplayer:get_wielded_item():is_empty() + return not wield_empty and ws.can_place_at(pos) +end + + +function ws.find_any_swap(items,hslot) + hslot=hslot or 8 + for i, v in ipairs(items) do + local n = minetest.find_item(v) + if n then + ws.switch_to_item(v,hslot) + return true + end + end + return false +end + + +-- swaps to any of the items and places if need be +-- returns true if placed and in inventory or already there, false otherwise + +local lastact=0 +local lastplc=0 +local lastdig=0 +local actint=10 +function ws.place(pos,items,hslot, place) + --if nodes_this_tick > 8 then return end + --nodes_this_tick = nodes_this_tick + 1 + --if not inside_constraints(pos) then return end + if not pos then return end + if not ws.can_place_at(pos) then return end + items=tablearg(items) + + place = place or minetest.place_node + + local node = minetest.get_node_or_nil(pos) + if not node then return end + -- already there + if ws.isnode(pos,items) then + return true + else + if ws.find_any_swap(items,hslot) then + place(pos) + return true + end + end +end + +function ws.place_if_able(pos) + if not pos then return end + if not inside_constraints(pos) then return end + if ws.can_place_wielded_at(pos) then + minetest.place_node(pos) + end +end + +function ws.is_diggable(pos) + if not pos then return false end + local nd=minetest.get_node_or_nil(pos) + if not nd then return false end + local n = minetest.get_node_def(nd.name) + if n and n.diggable then return true end + return false +end + +function ws.dig(pos,condition,autotool) + --if not inside_constraints(pos) then return end + if autotool == nil then autotool = true end + if condition and not condition(pos) then return false end + if not ws.is_diggable(pos) then return end + local nd=minetest.get_node_or_nil(pos) + if nd and minetest.get_node_def(nd.name).diggable then + if autotool then ws.select_best_tool(pos) end + minetest.dig_node(pos) + end + return true +end + +function ws.chunk_loaded() + local ign=minetest.find_nodes_near(ws.dircoord(0,0,0),10,{'ignore'},true) + if #ign == 0 then return true end + return false +end + +function ws.get_near(nodes,range) + range=range or 5 + local nds=minetest.find_nodes_near(ws.dircoord(0,0,0),rang,nodes,true) + if #nds > 0 then return nds end + return false +end + +function ws.is_laggy() + if tps_client and tps_client.ping and tps_client.ping > 1000 then return true end +end + + +function ws.donodes(poss,func,condition) + if ws.is_laggy() then return end + local dn_i=0 + for k,v in pairs(poss) do + if dn_i > 8 then return end + --local nd=minetest.get_node_or_nil(v) + if condition == nil or condition(v) then + func(v) + dn_i = dn_i + 1 + end + end +end + +function ws.dignodes(poss,condition) + local func=function(p) ws.dig(p) end + ws.donodes(poss,func,condition) +end + + +function ws.replace(pos,arg) + arg=tablearg(arg) + local nd=minetest.get_node_or_nil(pos) + if nd and not ws.in_list(nd.name,arg) and ws.buildable_to(pos) then + local tm=ws.get_digtime(nd.name) or 0 + ws.dig(pos) + minetest.after(tm + 0.1,function() + ws.place(pos,arg) + end) + return tm + else + return ws.place(pos,arg) + end +end + +function ws.playeron(p) + local pls=minetest.get_player_names() + for k,v in pairs(pls) do + if v == p then return true end + end + return false +end + + +function ws.between(x, y, z) -- x is between y and z (inclusive) + return y <= x and x <= z +end + + +local wall_pos1={x=-1255,y=6,z=792} +local wall_pos2={x=-1452,y=80,z=981} +local iwall_pos1={x=-1266,y=6,z=802} +local iwall_pos2={x=-1442,y=80,z=971} + +function ws.in_cube(tpos,wpos1,wpos2) + local xmax=wpos2.x + local xmin=wpos1.x + + local ymax=wpos2.y + local ymin=wpos1.y + + local zmax=wpos2.z + local zmin=wpos1.z + if wpos1.x > wpos2.x then + xmax=wpos1.x + xmin=wpos2.x + end + if wpos1.y > wpos2.y then + ymax=wpos1.y + ymin=wpos2.y + end + if wpos1.z > wpos2.z then + zmax=wpos1.z + zmin=wpos2.z + end + if ws.between(tpos.x,xmin,xmax) and ws.between(tpos.y,ymin,ymax) and ws.between(tpos.z,zmin,zmax) then + return true + end + return false +end + +function ws.in_wall(pos) + if ws.in_cube(pos,wall_pos1,wall_pos2) and not in_cube(pos,iwall_pos1,iwall_pos2) then + return true end + return false +end + +function ws.inside_wall(pos) + local p1=iwall_pos1 + local p2=iwall_pos2 + if ws.in_cube(pos,p1,p2) then return true end + return false +end + +function ws.find_closest_reachable_airpocket(pos) + local lp=ws.dircoord(0,0,0) + local nds=minetest.find_nodes_near(lp,5,{'air'}) + local odst=10 + local rt=lp + for k,v in ipairs(nds) do + local dst=vector.distance(pos,v) + if dst < odst then odst=dst rt=v end + end + if odst==10 then return false end + return vector.add(rt,vector.new(0,-1,0)) +end + + +-- DEBUG +local function printwieldedmeta() + ws.dcm(dump(minetest.localplayer:get_wielded_item():get_meta():to_table())) +end +minetest.register_cheat('ItemMeta','Test',printwieldedmeta) +-- CC0/Unlicense Emilia/cora 2020 + +-- south:5,1.5 +--west:-x,1.5,-5 +--east:-x,1.5,5 +-- north 5,1.5(3096:2.5,25025:1.5),z +local direction = "" +local ground = { + "mesecons_torch:redstoneblock" +} + +local rails = { + "mcl_minecarts:golden_rail", + "mcl_minecarts:rail" +} + +local tunnelmaterial = { + 'mcl_core:glass_light_blue', + 'mcl_core:glass', + 'mcl_core:cobble', + 'mcl_core:stone', + 'mcl_nether:netherrack', + 'mcl_core:dirt', + 'mcl_core:andesite', + 'mcl_core:diorite', + 'mcl_core:granite', + "mesecons_torch:redstoneblock" +} + +local lightblock = "mcl_ocean:sea_lantern" +--local lightblock = "mcl_nether:glowstone" + +local function is_rail(pos) + pos=vector.round(pos) + if pos.y ~= 1 then return false end + if pos.z > 5 then + if pos.x == -5 then return "north" end + elseif pos.z < -5 then + if pos.x == 5 then return "south" end + end + if pos.x > 5 then + if pos.z == 5 then return "east" end + elseif pos.x < -5 then + if pos.z == -5 then return "west" end + end + return false +end + +local function get_railnode(pos) + if is_rail(pos) then + return "mcl_minecarts:golden_rail" + end + if is_rail(vector.add(pos,{x=0,y=-1,x=0})) then + return "mesecons_torch:redstoneblock" + end + return false +end + +local function is_lantern(pos) + local dir=ws.getdir() + pos=vector.round(pos) + if dir == "north" or dir == "south" then + if pos.z % 8 == 0 then + return true + end + else + if pos.x % 8 == 0 then + return true + end + end + return false +end + + + + +local function checknode(pos) + local lp = ws.dircoord(0,0,0) + local node = minetest.get_node_or_nil(pos) + if pos.y == lp.y then + if node and not node.name:find("_rail") then return true end + elseif node and node.name ~="mesecons_torch:redstoneblock" then return true + end + return false +end + +local function dignodes(poss) + for k,v in pairs(poss) do + if checknode(v) then ws.dig(v) end + end +end + +local function findliquids(pos,range) + range = range or 1 + if not pos then return end + local liquids={'mcl_core:lava_source','mcl_core:water_source','mcl_core:lava_flowing','mcl_core:water_flowing','mcl_nether:nether_lava_source','mcl_nether:nether_lava_flowing'} + local bn=minetest.find_nodes_near(pos, range, liquids, true) + if #bn < 0 then return bn end + return false +end + +local function blockliquids(pos) + if not pos then return end + local lp=ws.dircoord(0,0,0) + local liquids={'mcl_core:lava_source','mcl_core:water_source','mcl_core:lava_flowing','mcl_core:water_flowing','mcl_nether:nether_lava_source','mcl_nether:nether_lava_flowing'} + local bn=minetest.find_nodes_near(pos, 1, liquids, true) + local rt=false + if not bn then return rt end + for kk,vv in pairs(bn) do + if vv.y > lp.y - 1 or vv.y < -40 then + rt=true + scaffold.place_if_needed(tunnelmaterial,vv) + for i=-4,5,1 do + local tpos=vector.new(pos.x,lp.y,pos.z) + scaffold.place_if_needed(tunnelmaterial,ws.dircoord(i,2,0,tpos)) + scaffold.place_if_needed(tunnelmaterial,ws.dircoord(i,0,1,tpos)) + scaffold.place_if_needed(tunnelmaterial,ws.dircoord(i,1,1,tpos)) + scaffold.place_if_needed(tunnelmaterial,ws.dircoord(i,0,-1,tpos)) + scaffold.place_if_needed(tunnelmaterial,ws.dircoord(i,1,-1,tpos)) + end + end + end + return rt +end + +local function digob(sc) + local obpos={ + ws.dircoord(0,1,2,sc), + ws.dircoord(0,1,-2,sc), + ws.dircoord(0,1,1,sc), + ws.dircoord(0,1,-1,sc), + ws.dircoord(0,0,1,sc), + ws.dircoord(0,0,-1,sc) + } + ws.dignodes(obpos,function(pos) + local nd=minetest.get_node_or_nil(pos) + if nd and (nd.name == "mcl_core:obsidian" or nd.name == "mcl_minecarts:golden_rail_on" or nd.name == "mcl_minecarts:golden_rail" )then return true end + return false + end) +end + +local function invcheck(item) + if mintetest.switch_to_item(item) then return true end + refill.refill_at(ws.dircoord(1,1,0),'railkit') +end + +local function invcheck(item) + if mintetest.switch_to_item(item) then return true end + refill.refill_at(ws.dircoord(1,1,0),'railkit') +end +local function rnd(n) + return math.ceil(n) +end + +local function fmt(c) + return tostring(rnd(c.x))..","..tostring(rnd(c.y))..","..tostring(rnd(c.z)) +end +local function map_pos(value) + if value.x then + return value + else + return {x = value[1], y = value[2], z = value[3]} + end +end + +local function invparse(location) + if type(location) == "string" then + if string.match(location, "^[-]?[0-9]+,[-]?[0-9]+,[-]?[0-9]+$") then + return "nodemeta:" .. location + else + return location + end + elseif type(location) == "table" then + return "nodemeta:" .. fmt(map_pos(location)) + end +end + +local function take_railkit(pos) + local plinv = minetest.get_inventory(invparse(pos)) + local epos=ws.find_named(plinv,'railkit') + local mv = InventoryAction("move") + mv:from(invparse(pos), "main", epos) + mv:to("current_player", "main", 8) + mv:apply() + minetest.localplayer:set_wield_index(8) + return true + +end + +local restashing=false +function scaffold.restash() + if restashing then return end + restashing=true + ws.dig(ws.dircoord(1,0,1)) + ws.dig(ws.dircoord(1,1,1)) + ws.dig(ws.dircoord(2,0,1)) + ws.dig(ws.dircoord(2,1,1)) + + ws.place(ws.dircoord(1,0,1),{'mcl_chests:chest_small','mcl_chests:chest'}) + ws.place(ws.dircoord(1,1,1),{'railroad'}) + take_railkit(ws.dircoord(1,1,1)) + minetest.after("0.5",function() + ws.place(ws.dircoord(2,0,1),{'railkit'}) + ws.dig(ws.dircoord(1,1,1)) + end) + minetest.after("1.0",function() + autodupe.invtake(ws.dircoord(2,0,1)) + restashing=false + end) +end + + +local function slowdown(s) + minetest.localplayer:set_velocity(vector.new(0,0,0)) + minetest.settings:set('movement_speed_fast',math.abs(s)) +end +local fullspeed=100 +local function speedup() + minetest.settings:set('movement_speed_fast',fullspeed) +end + + +ws.rg("RailBot","Bots", "railbot", function() + local oldi=500 + for i=-50,50,1 do + local lpos=ws.dircoord(i,2,0) + local lpn=minetest.get_node_or_nil(ws.dircoord(i,0,0)) + local bln=minetest.get_node_or_nil(ws.dircoord(i,-1,0)) + local ltpn=minetest.get_node_or_nil(lpos) + if not bln or not lpn or not ltpn then + speedup() + elseif ( is_lantern(lpos) and ltpn.name ~= lightblock ) then + if (oldi > i) then + slowdown(8) + oldi=i + end + elseif bln.name=="mesecons_torch:redstoneblock" and lpn.name == "mcl_minecarts:golden_rail_on" then + speedup() + else + if (oldi > i) then + slowdown(8) + oldi=i + end + end + end + + local goon=false + for i=-4,4,1 do + local lpos=ws.dircoord(i,2,0) + local lpn=minetest.get_node_or_nil(ws.dircoord(i,0,0)) + local bln=minetest.get_node_or_nil(ws.dircoord(i,-1,0)) + local lpos=ws.dircoord(i,2,0) + + if not ( bln and bln.name=="mesecons_torch:redstoneblock" and lpn and lpn.name == "mcl_minecarts:golden_rail_on" ) then + goon=false + else + goon=true + end + + digob(ws.dircoord(i,0,0)) + + blockliquids(ws.dircoord(i,1,0)) + blockliquids(ws.dircoord(i,0,0)) + ws.dig(ws.dircoord(i,1,0)) + if checknode(ws.dircoord(i,0,0)) then ws.dig(ws.dircoord(i,0,0)) end + if checknode(ws.dircoord(i,-1,0)) then ws.dig(ws.dircoord(i,-1,0)) end + ws.place(ws.dircoord(i,-1,0),ground,7) + ws.place(ws.dircoord(i,0,0),rails,6) + + local lpos=ws.dircoord(i,2,0) + if is_lantern(lpos) then + local ln=minetest.get_node_or_nil(lpos) + if not ln or ln.name ~= lightblock then + goon=false + ws.dig(lpos) + ws.place(lpos,{lightblock},5) + end + end + end + + if (goon) then + local dir=ws.getdir() + local lp=minetest.localplayer:get_pos() + local rlp=vector.round(lp) + minetest.localplayer:set_pos(vector.new(rlp.x,lp.y,rlp.z)) + minetest.settings:set_bool('continuous_forward',true) + else + slowdown(5) + minetest.localplayer:set_velocity(vector.new(0,0,0)) + minetest.settings:set_bool('continuous_forward',false) + end + + +end, +function()--startfunc + minetest.settings:set('movement_speed_fast',500) + minetest.settings:set_bool('continuous_forward',false) +end,function() --stopfunc + minetest.localplayer:set_velocity(vector.new(0,0,0)) + minetest.settings:set('movement_speed_fast',20) + minetest.settings:set_bool('continuous_forward',false) +end,{'afly_snap','autorefill'}) --'scaffold_ltbm' + + + +scaffold.register_template_scaffold("LanternTBM", "scaffold_ltbm", function() + local dir=ws.getdir() + local lp=vector.round(ws.dircoord(0,0,0)) + local pl=is_lantern(lp) + if pl then + local lpos=ws.dircoord(0,2,0) + local nd=minetest.get_node_or_nil(lpos) + if nd and nd.name ~= lightblock then + ws.dig(lpos) + ws.place(lpos,lightblock,5) + end + end +end) diff --git a/scaffold/sapscaffold.lua b/scaffold/sapscaffold.lua new file mode 100644 index 0000000..dc82f60 --- /dev/null +++ b/scaffold/sapscaffold.lua @@ -0,0 +1,898 @@ + +ws = {} +ws.registered_globalhacks = {} +ws.displayed_wps={} + +ws.c = core + +ws.range=4 +ws.target=nil +ws.targetpos=nil + +local nextact = {} +local ghwason={} + +local nodes_this_tick=0 + +function ws.s(name,value) + if value == nil then + return ws.c.settings:get(name) + else + ws.c.settings:set(name,value) + return ws.c.settings:get(name) + end +end +function ws.sb(name,value) + if value == nil then + return ws.c.settings:get_bool(name) + else + ws.c.settings:set_bool(name,value) + return ws.c.settings:get_bool(name) + end +end + +function ws.dcm(msg) + return minetest.display_chat_message(msg) +end +function ws.set_bool_bulk(settings,value) + if type(settings) ~= 'table' then return false end + for k,v in pairs(settings) do + minetest.settings:set_bool(v,value) + end + return true +end + +function ws.shuffle(tbl) + for i = #tbl, 2, -1 do + local j = math.random(i) + tbl[i], tbl[j] = tbl[j], tbl[i] + end + return tbl +end + +function ws.in_list(val, list) + if type(list) ~= "table" then return false end + for i, v in pairs(list) do + if v == val then + return true + end + end + return false +end + +function ws.random_table_element(tbl) + local ks = {} + for k in pairs(tbl) do + table.insert(ks, k) + end + return tbl[ks[math.random(#ks)]] +end + +function ws.center() + --local lp=ws.dircoord(0,0,0) + --minetest.localplayer:set_pos(lp) +end + +function ws.globalhacktemplate(setting,func,funcstart,funcstop,daughters,delay) + funcstart = funcstart or function() end + funcstop = funcstop or function() end + delay = delay or 0.5 + return function() + if not minetest.localplayer then return end + if minetest.settings:get_bool(setting) then + if tps_client and tps_client.ping and tps_client.ping > 1000 then return end + nodes_this_tick = 0 + if nextact[setting] and nextact[setting] > os.clock() then return end + nextact[setting] = os.clock() + delay + if not ghwason[setting] then + if not funcstart() then + ws.set_bool_bulk(daughters,true) + ghwason[setting] = true + --ws.dcm(setting.. " activated") + ws.center() + minetest.settings:set('last-dir',ws.getdir()) + minetest.settings:set('last-y',ws.dircoord(0,0,0).y) + else minetest.settings:set_bool(setting,false) + end + else + func() + end + + elseif ghwason[setting] then + ghwason[setting] = false + ws.set_bool_bulk(daughters,false) + funcstop() + --ws.dcm(setting.. " deactivated") + end + end +end + +function ws.register_globalhack(func) + table.insert(ws.registered_globalhacks,func) +end + +function ws.register_globalhacktemplate(name,category,setting,func,funcstart,funcstop,daughters) + ws.register_globalhack(ws.globalhacktemplate(setting,func,funcstart,funcstop,daughters)) + minetest.register_cheat(name,category,setting) +end + +ws.rg=ws.register_globalhacktemplate + +function ws.step_globalhacks(dtime) + for i, v in ipairs(ws.registered_globalhacks) do + v(dtime) + end +end + +minetest.register_globalstep(function(dtime) ws.step_globalhacks(dtime) end) +minetest.settings:set_bool('continuous_forward',false) + +function ws.on_connect(func) + if not minetest.localplayer then minetest.after(0,function() ws.on_connect(func) end) return end + if func then func() end +end + +ws.on_connect(function() + local ldir =minetest.settings:get('last-dir') + if ldir then ws.setdir(ldir) end +end) + + +-- COORD MAGIC + +function ws.is_same_pos(pos1,pos2) + return vector.distance(vector.round(pos1),vector.round(pos2)) == 0 +end +function ws.get_reachable_positions(range,under) + under=under or false + range=range or 4 + local rt={} + local lp=vector.round(minetest.localplayer:get_pos()) + local ylim=range + if under then ylim=-1 end + for x = -range,range,1 do + for y = -range,ylim,1 do + for z = -range,range,1 do + table.insert(rt,vector.round(vector.add(lp,vector.new(x,y,z)))) + end + end + end + return rt +end + +function ws.do_area(radius,func,plane) + for k,v in pairs(ws.get_reachable_positions(range)) do + if not plane or v.y == minetest.localplayer:get_pos().y -1 then + func(v) + end + end +end + +function ws.get_hud_by_texture(texture) + local def + local i = -1 + repeat + i = i + 1 + def = minetest.localplayer:hud_get(i) + until not def or def.text == texture + if def then + return def + end + def.number=0 + return def +end + +function ws.find_player(name) + for k, v in ipairs(minetest.localplayer.get_nearby_objects(500)) do + if v:get_name() == name then + return v:get_pos(),v + end + end +end + +function ws.display_wp(pos,name) + local ix = #ws.displayed_wps + 1 + ws.displayed_wps[ix] = minetest.localplayer:hud_add({ + hud_elem_type = 'waypoint', + name = name, + text = name, + number = 0x00ff00, + world_pos = pos + }) + return ix +end + +function ws.clear_wp(ix) + table.remove(ws.displayed_wps,ix) +end + +function ws.clear_wps() + for k,v in ipairs(ws.displayed_wps) do + minetest.localplayer:hud_remove(v) + table.remove(ws.displayed_wps,k) + end +end + +function ws.register_chatcommand_alias(old, ...) + local def = assert(minetest.registered_chatcommands[old]) + def.name = nil + for i = 1, select('#', ...) do + minetest.register_chatcommand(select(i, ...), table.copy(def)) + end +end + +function ws.round2(num, numDecimalPlaces) + return tonumber(string.format("%." .. (numDecimalPlaces or 0) .. "f", num)) +end + +function ws.pos_to_string(pos) + if type(pos) == 'table' then + pos = minetest.pos_to_string(vector.round(pos)) + end + if type(pos) == 'string' then + return pos + end + return pos +end + +function ws.string_to_pos(pos) + if type(pos) == 'string' then + pos = minetest.string_to_pos(pos) + end + if type(pos) == 'table' then + return vector.round(pos) + end + return pos +end + + + +--ITEMS +function ws.find_item_in_table(items,rnd) + if type(items) == 'string' then + return minetest.find_item(items) + end + if type(items) ~= 'table' then return end + if rnd then items=ws.shuffle(items) end + for i, v in pairs(items) do + local n = minetest.find_item(v) + if n then + return n + end + end + return false +end + +function ws.find_empty(inv) + for i, v in ipairs(inv) do + if v:is_empty() then + return i + end + end + return false +end + +function ws.find_named(inv, name) + if not inv then return -1 end + if not name then return end + for i, v in ipairs(inv) do + if v:get_name():find(name) then + return i + end + end +end + +function ws.itemnameformat(description) + description = description:gsub(string.char(0x1b) .. "%(.@[^)]+%)", "") + description = description:match("([^\n]*)") + return description +end + +function ws.find_nametagged(list, name) + for i, v in ipairs(list) do + if ws.itemnameformat(v:get_description()) == name then + return i + end + end +end + +local hotbar_slot=8 +function ws.to_hotbar(it,hslot) + local tpos=nil + local plinv = minetest.get_inventory("current_player") + if hslot and hslot < 10 then + tpos=hslot + else + for i, v in ipairs(plinv.main) do + if i<10 and v:is_empty() then + tpos = i + break + end + end + end + if tpos == nil then tpos=hotbar_slot end + local mv = InventoryAction("move") + mv:from("current_player", "main", it) + mv:to("current_player", "main", tpos) + mv:apply() + return tpos +end + +function ws.switch_to_item(itname,hslot) + if not minetest.localplayer then return false end + local plinv = minetest.get_inventory("current_player") + for i, v in ipairs(plinv.main) do + if i<10 and v:get_name() == itname then + minetest.localplayer:set_wield_index(i) + return true + end + end + local pos = ws.find_named(plinv.main, itname) + if pos then + minetest.localplayer:set_wield_index(ws.to_hotbar(pos,hslot)) + return true + end + return false +end +function ws.in_inv(itname) + if not minetest.localplayer then return false end + local plinv = minetest.get_inventory("current_player") + local pos = ws.find_named(plinv.main, itname) + if pos then + return true + end +end + +function core.switch_to_item(item) return ws.switch_to_item(item) end + +function ws.switch_inv_or_echest(name,max_count,hslot) + if not minetest.localplayer then return false end + local plinv = minetest.get_inventory("current_player") + if ws.switch_to_item(name) then return true end + + local epos = ws.find_named(plinv.enderchest, name) + if epos then + local tpos + for i, v in ipairs(plinv.main) do + if i < 9 and v:is_empty() then + tpos = i + break + end + end + if not tpos then tpos=hotbar_slot end + + if tpos then + local mv = InventoryAction("move") + mv:from("current_player", "enderchest", epos) + mv:to("current_player", "main", tpos) + if max_count then + mv:set_count(max_count) + end + mv:apply() + minetest.localplayer:set_wield_index(tpos) + return true + end + end + return false +end + +local function posround(n) + return math.floor(n + 0.5) +end + +local function fmt(c) + return tostring(posround(c.x))..","..tostring(posround(c.y))..","..tostring(posround(c.z)) +end + +local function map_pos(value) + if value.x then + return value + else + return {x = value[1], y = value[2], z = value[3]} + end +end + +function ws.invparse(location) + if type(location) == "string" then + if string.match(location, "^[-]?[0-9]+,[-]?[0-9]+,[-]?[0-9]+$") then + return "nodemeta:" .. location + else + return location + end + elseif type(location) == "table" then + return "nodemeta:" .. fmt(map_pos(location)) + end +end + +function ws.invpos(p) + return "nodemeta:"..p.x..","..p.y..","..p.z +end + + +-- TOOLS + + +local function check_tool(stack, node_groups, old_best_time) + local toolcaps = stack:get_tool_capabilities() + if not toolcaps then return end + local best_time = old_best_time + for group, groupdef in pairs(toolcaps.groupcaps) do + local level = node_groups[group] + if level then + local this_time = groupdef.times[level] + if this_time and this_time < best_time then + best_time = this_time + end + end + end + return best_time < old_best_time, best_time +end + +local function find_best_tool(nodename, switch) + local player = minetest.localplayer + local inventory = minetest.get_inventory("current_player") + local node_groups = minetest.get_node_def(nodename).groups + local new_index = player:get_wield_index() + local is_better, best_time = false, math.huge + + is_better, best_time = check_tool(player:get_wielded_item(), node_groups, best_time) + if inventory.hand then + is_better, best_time = check_tool(inventory.hand[1], node_groups, best_time) + end + + for index, stack in ipairs(inventory.main) do + is_better, best_time = check_tool(stack, node_groups, best_time) + if is_better then + new_index = index + end + end + + return new_index,best_time +end + +function ws.get_digtime(nodename) + local idx,tm=find_best_tool(nodename) + return tm +end + +function ws.select_best_tool(pos) + local nd=minetest.get_node_or_nil(pos) + local nodename='air' + if nd then nodename=nd.name end + local t=find_best_tool(nodename) + minetest.localplayer:set_wield_index(ws.to_hotbar(t,hotbar_slot)) + --minetest.localplayer:set_wield_index(find_best_tool(nodename)) +end + +--- COORDS +function ws.coord(x, y, z) + return vector.new(x,y,z) +end +function ws.ordercoord(c) + if c.x == nil then + return {x = c[1], y = c[2], z = c[3]} + else + return c + end +end + +-- x or {x,y,z} or {x=x,y=y,z=z} +function ws.optcoord(x, y, z) + if y and z then + return ws.coord(x, y, z) + else + return ws.ordercoord(x) + end +end +function ws.cadd(c1, c2) + return vector.add(c1,c2) + --return ws.coord(c1.x + c2.x, c1.y + c2.y, c1.z + c2.z) +end + +function ws.relcoord(x, y, z, rpos) + local pos = rpos or minetest.localplayer:get_pos() + pos.y=math.ceil(pos.y) + --math.floor(pos.y) + 0.5 + return ws.cadd(pos, ws.optcoord(x, y, z)) +end + +local function between(x, y, z) -- x is between y and z (inclusive) + return y <= x and x <= z +end + +function ws.getdir(yaw) -- + local rot = yaw or minetest.localplayer:get_yaw() % 360 + if between(rot, 315, 360) or between(rot, 0, 45) then + return "north" + elseif between(rot, 135, 225) then + return "south" + elseif between(rot, 225, 315) then + return "east" + elseif between(rot, 45, 135) then + return "west" + end +end + +function ws.getaxis() + local dir=ws.getdir() + if dir == "north" or dir == "south" then return "z" end + return "x" +end +function ws.setdir(dir) -- + if dir == "north" then + minetest.localplayer:set_yaw(0) + elseif dir == "south" then + minetest.localplayer:set_yaw(180) + elseif dir == "east" then + minetest.localplayer:set_yaw(270) + elseif dir == "west" then + minetest.localplayer:set_yaw(90) + end +end + +function ws.dircoord(f, y, r ,rpos, rdir) + local dir= ws.getdir(rdir) + local coord = ws.optcoord(f, y, r) + local f = coord.x + local y = coord.y + local r = coord.z + local lp= rpos or minetest.localplayer:get_pos() + if dir == "north" then + return ws.relcoord(r, y, f,rpos) + elseif dir == "south" then + return ws.relcoord(-r, y, -f,rpos) + elseif dir == "east" then + return ws.relcoord(f, y, -r,rpos) + elseif dir== "west" then + return ws.relcoord(-f, y, r,rpos) + end + return ws.relcoord(0, 0, 0,rpos) +end + +function ws.get_dimension(pos) + if pos.y > -65 then return "overworld" + elseif pos.y > -8000 then return "void" + elseif pos.y > -27000 then return "end" + elseif pos.y > -28930 then return "void" + elseif pos.y > -31000 then return "nether" + else return "void" + end +end + +function ws.aim(tpos) + local ppos=minetest.localplayer:get_pos() + local dir=vector.direction(ppos,tpos) + local yyaw=0; + local pitch=0; + if dir.x < 0 then + yyaw = math.atan2(-dir.x, dir.z) + (math.pi * 2) + else + yyaw = math.atan2(-dir.x, dir.z) + end + yyaw = ws.round2(math.deg(yyaw),2) + pitch = ws.round2(math.deg(math.asin(-dir.y) * 1),2); + minetest.localplayer:set_yaw(yyaw) + minetest.localplayer:set_pitch(pitch) +end + +function ws.gaim(tpos,v,g) + local v = v or 40 + local g = g or 9.81 + local ppos=minetest.localplayer:get_pos() + local dir=vector.direction(ppos,tpos) + local yyaw=0; + local pitch=0; + if dir.x < 0 then + yyaw = math.atan2(-dir.x, dir.z) + (math.pi * 2) + else + yyaw = math.atan2(-dir.x, dir.z) + end + yyaw = ws.round2(math.deg(yyaw),2) + local y = dir.y + dir.y = 0 + local x = vector.length(dir) + pitch=math.atan(math.pow(v, 2) / (g * x) + math.sqrt(math.pow(v, 4)/(math.pow(g, 2) * math.pow(x, 2)) - 2 * math.pow(v, 2) * y/(g * math.pow(x, 2)) - 1)) + --pitch = ws.round2(math.deg(math.asin(-dir.y) * 1),2); + minetest.localplayer:set_yaw(yyaw) + minetest.localplayer:set_pitch(math.deg(pitch)) +end + +function ws.buildable_to(pos) + local node=minetest.get_node_or_nil(pos) + if node then + return minetest.get_node_def(node.name).buildable_to + end +end + +function ws.tplace(p,n,stay) + if not p then return end + if n then ws.switch_to_item(n) end + local opos=ws.dircoord(0,0,0) + local tpos=vector.add(p,vector.new(0,1,0)) + minetest.localplayer:set_pos(tpos) + ws.place(p,{n}) + if not stay then + minetest.after(0.1,function() + minetest.localplayer:set_pos(opos) + end) + end +end + +minetest.register_chatcommand("tplace", { + description = "tp-place", + param = "Y", + func = function(param) + return ws.tplace(minetest.string_to_pos(param)) + end +}) + +function ws.ytp(param) + local y=tonumber(param) + local lp=ws.dircoord(0,0,0) + if lp.y < y + 50 then return false,"Can't TP up." end + if y < -30912 then return false,"Don't TP into the void lol." end + minetest.localplayer:set_pos(vector.new(lp.x,y,lp.z)) +end + +local function tablearg(arg) + local tb={} + if type(arg) == 'string' then + tb={arg} + elseif type(arg) == 'table' then + tb=arg + elseif type(arg) == 'function' then + tb=arg() + end + return tb +end + +function ws.isnode(pos,arg)--arg is either an itemstring, a table of itemstrings or a function returning an itemstring + local nodename=tablearg(arg) + local nd=minetest.get_node_or_nil(pos) + if nd and nodename and ws.in_list(nd.name,nodename) then + return true + end +end + +function ws.can_place_at(pos) + local node = minetest.get_node_or_nil(pos) + return (node and (node.name == "air" or node.name=="mcl_core:water_source" or node.name=="mcl_core:water_flowing" or node.name=="mcl_core:lava_source" or node.name=="mcl_core:lava_flowing" or minetest.get_node_def(node.name).buildable_to)) +end + +-- should check if wield is placeable +-- minetest.get_node(wielded:get_name()) ~= nil should probably work +-- otherwise it equips armor and eats food +function ws.can_place_wielded_at(pos) + local wield_empty = minetest.localplayer:get_wielded_item():is_empty() + return not wield_empty and ws.can_place_at(pos) +end + + +function ws.find_any_swap(items,hslot) + hslot=hslot or 8 + for i, v in ipairs(items) do + local n = minetest.find_item(v) + if n then + ws.switch_to_item(v,hslot) + return true + end + end + return false +end + + +-- swaps to any of the items and places if need be +-- returns true if placed and in inventory or already there, false otherwise + +local lastact=0 +local lastplc=0 +local lastdig=0 +local actint=10 +function ws.place(pos,items,hslot, place) + --if nodes_this_tick > 8 then return end + --nodes_this_tick = nodes_this_tick + 1 + --if not inside_constraints(pos) then return end + if not pos then return end + if not ws.can_place_at(pos) then return end + items=tablearg(items) + + place = place or minetest.place_node + + local node = minetest.get_node_or_nil(pos) + if not node then return end + -- already there + if ws.isnode(pos,items) then + return true + else + if ws.find_any_swap(items,hslot) then + place(pos) + return true + end + end +end + +function ws.place_if_able(pos) + if not pos then return end + if not inside_constraints(pos) then return end + if ws.can_place_wielded_at(pos) then + minetest.place_node(pos) + end +end + +function ws.is_diggable(pos) + if not pos then return false end + local nd=minetest.get_node_or_nil(pos) + if not nd then return false end + local n = minetest.get_node_def(nd.name) + if n and n.diggable then return true end + return false +end + +function ws.dig(pos,condition,autotool) + --if not inside_constraints(pos) then return end + if autotool == nil then autotool = true end + if condition and not condition(pos) then return false end + if not ws.is_diggable(pos) then return end + local nd=minetest.get_node_or_nil(pos) + if nd and minetest.get_node_def(nd.name).diggable then + if autotool then ws.select_best_tool(pos) end + minetest.dig_node(pos) + end + return true +end + +function ws.chunk_loaded() + local ign=minetest.find_nodes_near(ws.dircoord(0,0,0),10,{'ignore'},true) + if #ign == 0 then return true end + return false +end + +function ws.get_near(nodes,range) + range=range or 5 + local nds=minetest.find_nodes_near(ws.dircoord(0,0,0),rang,nodes,true) + if #nds > 0 then return nds end + return false +end + +function ws.is_laggy() + if tps_client and tps_client.ping and tps_client.ping > 1000 then return true end +end + + +function ws.donodes(poss,func,condition) + if ws.is_laggy() then return end + local dn_i=0 + for k,v in pairs(poss) do + if dn_i > 8 then return end + --local nd=minetest.get_node_or_nil(v) + if condition == nil or condition(v) then + func(v) + dn_i = dn_i + 1 + end + end +end + +function ws.dignodes(poss,condition) + local func=function(p) ws.dig(p) end + ws.donodes(poss,func,condition) +end + + +function ws.replace(pos,arg) + arg=tablearg(arg) + local nd=minetest.get_node_or_nil(pos) + if nd and not ws.in_list(nd.name,arg) and ws.buildable_to(pos) then + local tm=ws.get_digtime(nd.name) or 0 + ws.dig(pos) + minetest.after(tm + 0.1,function() + ws.place(pos,arg) + end) + return tm + else + return ws.place(pos,arg) + end +end + +function ws.playeron(p) + local pls=minetest.get_player_names() + for k,v in pairs(pls) do + if v == p then return true end + end + return false +end + + +function ws.between(x, y, z) -- x is between y and z (inclusive) + return y <= x and x <= z +end + + +local wall_pos1={x=-1255,y=6,z=792} +local wall_pos2={x=-1452,y=80,z=981} +local iwall_pos1={x=-1266,y=6,z=802} +local iwall_pos2={x=-1442,y=80,z=971} + +function ws.in_cube(tpos,wpos1,wpos2) + local xmax=wpos2.x + local xmin=wpos1.x + + local ymax=wpos2.y + local ymin=wpos1.y + + local zmax=wpos2.z + local zmin=wpos1.z + if wpos1.x > wpos2.x then + xmax=wpos1.x + xmin=wpos2.x + end + if wpos1.y > wpos2.y then + ymax=wpos1.y + ymin=wpos2.y + end + if wpos1.z > wpos2.z then + zmax=wpos1.z + zmin=wpos2.z + end + if ws.between(tpos.x,xmin,xmax) and ws.between(tpos.y,ymin,ymax) and ws.between(tpos.z,zmin,zmax) then + return true + end + return false +end + +function ws.in_wall(pos) + if ws.in_cube(pos,wall_pos1,wall_pos2) and not in_cube(pos,iwall_pos1,iwall_pos2) then + return true end + return false +end + +function ws.inside_wall(pos) + local p1=iwall_pos1 + local p2=iwall_pos2 + if ws.in_cube(pos,p1,p2) then return true end + return false +end + +function ws.find_closest_reachable_airpocket(pos) + local lp=ws.dircoord(0,0,0) + local nds=minetest.find_nodes_near(lp,5,{'air'}) + local odst=10 + local rt=lp + for k,v in ipairs(nds) do + local dst=vector.distance(pos,v) + if dst < odst then odst=dst rt=v end + end + if odst==10 then return false end + return vector.add(rt,vector.new(0,-1,0)) +end + + +-- DEBUG +local function printwieldedmeta() + ws.dcm(dump(minetest.localplayer:get_wielded_item():get_meta():to_table())) +end +minetest.register_cheat('ItemMeta','Test',printwieldedmeta) +-- CC0/Unlicense Emilia 2020 + +local dirt = { + "mcl_core:dirt", + "mcl_core:dirt_with_grass", + "mcl_core:dirt_with_grass_snow", + "mcl_core:podzol" +} + +local saplings = { + "mcl_core:sapling", + "mcl_core:darksapling", + "mcl_core:junglesapling", + "mcl_core:sprucesapling", + "mcl_core:birchsapling", + "mcl_core:acaciasapling" +} + +scaffold.register_template_scaffold("SapScaffold", "scaffold_saplings", function(below) + local lp = vector.round(minetest.localplayer:get_pos()) + + if scaffold.place_if_needed(dirt, below) then + scaffold.place_if_needed(saplings, lp) + end +end) diff --git a/scaffold/settingtypes.txt b/scaffold/settingtypes.txt new file mode 100644 index 0000000..1d3f62e --- /dev/null +++ b/scaffold/settingtypes.txt @@ -0,0 +1 @@ +slow_blocks_per_second (Blocks placed per second) int 8 diff --git a/scaffold/slowscaffold.lua b/scaffold/slowscaffold.lua new file mode 100644 index 0000000..17cbc3c --- /dev/null +++ b/scaffold/slowscaffold.lua @@ -0,0 +1,925 @@ + +ws = {} +ws.registered_globalhacks = {} +ws.displayed_wps={} + +ws.c = core + +ws.range=4 +ws.target=nil +ws.targetpos=nil + +local nextact = {} +local ghwason={} + +local nodes_this_tick=0 + +function ws.s(name,value) + if value == nil then + return ws.c.settings:get(name) + else + ws.c.settings:set(name,value) + return ws.c.settings:get(name) + end +end +function ws.sb(name,value) + if value == nil then + return ws.c.settings:get_bool(name) + else + ws.c.settings:set_bool(name,value) + return ws.c.settings:get_bool(name) + end +end + +function ws.dcm(msg) + return minetest.display_chat_message(msg) +end +function ws.set_bool_bulk(settings,value) + if type(settings) ~= 'table' then return false end + for k,v in pairs(settings) do + minetest.settings:set_bool(v,value) + end + return true +end + +function ws.shuffle(tbl) + for i = #tbl, 2, -1 do + local j = math.random(i) + tbl[i], tbl[j] = tbl[j], tbl[i] + end + return tbl +end + +function ws.in_list(val, list) + if type(list) ~= "table" then return false end + for i, v in pairs(list) do + if v == val then + return true + end + end + return false +end + +function ws.random_table_element(tbl) + local ks = {} + for k in pairs(tbl) do + table.insert(ks, k) + end + return tbl[ks[math.random(#ks)]] +end + +function ws.center() + --local lp=ws.dircoord(0,0,0) + --minetest.localplayer:set_pos(lp) +end + +function ws.globalhacktemplate(setting,func,funcstart,funcstop,daughters,delay) + funcstart = funcstart or function() end + funcstop = funcstop or function() end + delay = delay or 0.5 + return function() + if not minetest.localplayer then return end + if minetest.settings:get_bool(setting) then + if tps_client and tps_client.ping and tps_client.ping > 1000 then return end + nodes_this_tick = 0 + if nextact[setting] and nextact[setting] > os.clock() then return end + nextact[setting] = os.clock() + delay + if not ghwason[setting] then + if not funcstart() then + ws.set_bool_bulk(daughters,true) + ghwason[setting] = true + --ws.dcm(setting.. " activated") + ws.center() + minetest.settings:set('last-dir',ws.getdir()) + minetest.settings:set('last-y',ws.dircoord(0,0,0).y) + else minetest.settings:set_bool(setting,false) + end + else + func() + end + + elseif ghwason[setting] then + ghwason[setting] = false + ws.set_bool_bulk(daughters,false) + funcstop() + --ws.dcm(setting.. " deactivated") + end + end +end + +function ws.register_globalhack(func) + table.insert(ws.registered_globalhacks,func) +end + +function ws.register_globalhacktemplate(name,category,setting,func,funcstart,funcstop,daughters) + ws.register_globalhack(ws.globalhacktemplate(setting,func,funcstart,funcstop,daughters)) + minetest.register_cheat(name,category,setting) +end + +ws.rg=ws.register_globalhacktemplate + +function ws.step_globalhacks(dtime) + for i, v in ipairs(ws.registered_globalhacks) do + v(dtime) + end +end + +minetest.register_globalstep(function(dtime) ws.step_globalhacks(dtime) end) +minetest.settings:set_bool('continuous_forward',false) + +function ws.on_connect(func) + if not minetest.localplayer then minetest.after(0,function() ws.on_connect(func) end) return end + if func then func() end +end + +ws.on_connect(function() + local ldir =minetest.settings:get('last-dir') + if ldir then ws.setdir(ldir) end +end) + + +-- COORD MAGIC + +function ws.is_same_pos(pos1,pos2) + return vector.distance(vector.round(pos1),vector.round(pos2)) == 0 +end +function ws.get_reachable_positions(range,under) + under=under or false + range=range or 4 + local rt={} + local lp=vector.round(minetest.localplayer:get_pos()) + local ylim=range + if under then ylim=-1 end + for x = -range,range,1 do + for y = -range,ylim,1 do + for z = -range,range,1 do + table.insert(rt,vector.round(vector.add(lp,vector.new(x,y,z)))) + end + end + end + return rt +end + +function ws.do_area(radius,func,plane) + for k,v in pairs(ws.get_reachable_positions(range)) do + if not plane or v.y == minetest.localplayer:get_pos().y -1 then + func(v) + end + end +end + +function ws.get_hud_by_texture(texture) + local def + local i = -1 + repeat + i = i + 1 + def = minetest.localplayer:hud_get(i) + until not def or def.text == texture + if def then + return def + end + def.number=0 + return def +end + +function ws.find_player(name) + for k, v in ipairs(minetest.localplayer.get_nearby_objects(500)) do + if v:get_name() == name then + return v:get_pos(),v + end + end +end + +function ws.display_wp(pos,name) + local ix = #ws.displayed_wps + 1 + ws.displayed_wps[ix] = minetest.localplayer:hud_add({ + hud_elem_type = 'waypoint', + name = name, + text = name, + number = 0x00ff00, + world_pos = pos + }) + return ix +end + +function ws.clear_wp(ix) + table.remove(ws.displayed_wps,ix) +end + +function ws.clear_wps() + for k,v in ipairs(ws.displayed_wps) do + minetest.localplayer:hud_remove(v) + table.remove(ws.displayed_wps,k) + end +end + +function ws.register_chatcommand_alias(old, ...) + local def = assert(minetest.registered_chatcommands[old]) + def.name = nil + for i = 1, select('#', ...) do + minetest.register_chatcommand(select(i, ...), table.copy(def)) + end +end + +function ws.round2(num, numDecimalPlaces) + return tonumber(string.format("%." .. (numDecimalPlaces or 0) .. "f", num)) +end + +function ws.pos_to_string(pos) + if type(pos) == 'table' then + pos = minetest.pos_to_string(vector.round(pos)) + end + if type(pos) == 'string' then + return pos + end + return pos +end + +function ws.string_to_pos(pos) + if type(pos) == 'string' then + pos = minetest.string_to_pos(pos) + end + if type(pos) == 'table' then + return vector.round(pos) + end + return pos +end + + + +--ITEMS +function ws.find_item_in_table(items,rnd) + if type(items) == 'string' then + return minetest.find_item(items) + end + if type(items) ~= 'table' then return end + if rnd then items=ws.shuffle(items) end + for i, v in pairs(items) do + local n = minetest.find_item(v) + if n then + return n + end + end + return false +end + +function ws.find_empty(inv) + for i, v in ipairs(inv) do + if v:is_empty() then + return i + end + end + return false +end + +function ws.find_named(inv, name) + if not inv then return -1 end + if not name then return end + for i, v in ipairs(inv) do + if v:get_name():find(name) then + return i + end + end +end + +function ws.itemnameformat(description) + description = description:gsub(string.char(0x1b) .. "%(.@[^)]+%)", "") + description = description:match("([^\n]*)") + return description +end + +function ws.find_nametagged(list, name) + for i, v in ipairs(list) do + if ws.itemnameformat(v:get_description()) == name then + return i + end + end +end + +local hotbar_slot=8 +function ws.to_hotbar(it,hslot) + local tpos=nil + local plinv = minetest.get_inventory("current_player") + if hslot and hslot < 10 then + tpos=hslot + else + for i, v in ipairs(plinv.main) do + if i<10 and v:is_empty() then + tpos = i + break + end + end + end + if tpos == nil then tpos=hotbar_slot end + local mv = InventoryAction("move") + mv:from("current_player", "main", it) + mv:to("current_player", "main", tpos) + mv:apply() + return tpos +end + +function ws.switch_to_item(itname,hslot) + if not minetest.localplayer then return false end + local plinv = minetest.get_inventory("current_player") + for i, v in ipairs(plinv.main) do + if i<10 and v:get_name() == itname then + minetest.localplayer:set_wield_index(i) + return true + end + end + local pos = ws.find_named(plinv.main, itname) + if pos then + minetest.localplayer:set_wield_index(ws.to_hotbar(pos,hslot)) + return true + end + return false +end +function ws.in_inv(itname) + if not minetest.localplayer then return false end + local plinv = minetest.get_inventory("current_player") + local pos = ws.find_named(plinv.main, itname) + if pos then + return true + end +end + +function core.switch_to_item(item) return ws.switch_to_item(item) end + +function ws.switch_inv_or_echest(name,max_count,hslot) + if not minetest.localplayer then return false end + local plinv = minetest.get_inventory("current_player") + if ws.switch_to_item(name) then return true end + + local epos = ws.find_named(plinv.enderchest, name) + if epos then + local tpos + for i, v in ipairs(plinv.main) do + if i < 9 and v:is_empty() then + tpos = i + break + end + end + if not tpos then tpos=hotbar_slot end + + if tpos then + local mv = InventoryAction("move") + mv:from("current_player", "enderchest", epos) + mv:to("current_player", "main", tpos) + if max_count then + mv:set_count(max_count) + end + mv:apply() + minetest.localplayer:set_wield_index(tpos) + return true + end + end + return false +end + +local function posround(n) + return math.floor(n + 0.5) +end + +local function fmt(c) + return tostring(posround(c.x))..","..tostring(posround(c.y))..","..tostring(posround(c.z)) +end + +local function map_pos(value) + if value.x then + return value + else + return {x = value[1], y = value[2], z = value[3]} + end +end + +function ws.invparse(location) + if type(location) == "string" then + if string.match(location, "^[-]?[0-9]+,[-]?[0-9]+,[-]?[0-9]+$") then + return "nodemeta:" .. location + else + return location + end + elseif type(location) == "table" then + return "nodemeta:" .. fmt(map_pos(location)) + end +end + +function ws.invpos(p) + return "nodemeta:"..p.x..","..p.y..","..p.z +end + + +-- TOOLS + + +local function check_tool(stack, node_groups, old_best_time) + local toolcaps = stack:get_tool_capabilities() + if not toolcaps then return end + local best_time = old_best_time + for group, groupdef in pairs(toolcaps.groupcaps) do + local level = node_groups[group] + if level then + local this_time = groupdef.times[level] + if this_time and this_time < best_time then + best_time = this_time + end + end + end + return best_time < old_best_time, best_time +end + +local function find_best_tool(nodename, switch) + local player = minetest.localplayer + local inventory = minetest.get_inventory("current_player") + local node_groups = minetest.get_node_def(nodename).groups + local new_index = player:get_wield_index() + local is_better, best_time = false, math.huge + + is_better, best_time = check_tool(player:get_wielded_item(), node_groups, best_time) + if inventory.hand then + is_better, best_time = check_tool(inventory.hand[1], node_groups, best_time) + end + + for index, stack in ipairs(inventory.main) do + is_better, best_time = check_tool(stack, node_groups, best_time) + if is_better then + new_index = index + end + end + + return new_index,best_time +end + +function ws.get_digtime(nodename) + local idx,tm=find_best_tool(nodename) + return tm +end + +function ws.select_best_tool(pos) + local nd=minetest.get_node_or_nil(pos) + local nodename='air' + if nd then nodename=nd.name end + local t=find_best_tool(nodename) + minetest.localplayer:set_wield_index(ws.to_hotbar(t,hotbar_slot)) + --minetest.localplayer:set_wield_index(find_best_tool(nodename)) +end + +--- COORDS +function ws.coord(x, y, z) + return vector.new(x,y,z) +end +function ws.ordercoord(c) + if c.x == nil then + return {x = c[1], y = c[2], z = c[3]} + else + return c + end +end + +-- x or {x,y,z} or {x=x,y=y,z=z} +function ws.optcoord(x, y, z) + if y and z then + return ws.coord(x, y, z) + else + return ws.ordercoord(x) + end +end +function ws.cadd(c1, c2) + return vector.add(c1,c2) + --return ws.coord(c1.x + c2.x, c1.y + c2.y, c1.z + c2.z) +end + +function ws.relcoord(x, y, z, rpos) + local pos = rpos or minetest.localplayer:get_pos() + pos.y=math.ceil(pos.y) + --math.floor(pos.y) + 0.5 + return ws.cadd(pos, ws.optcoord(x, y, z)) +end + +local function between(x, y, z) -- x is between y and z (inclusive) + return y <= x and x <= z +end + +function ws.getdir(yaw) -- + local rot = yaw or minetest.localplayer:get_yaw() % 360 + if between(rot, 315, 360) or between(rot, 0, 45) then + return "north" + elseif between(rot, 135, 225) then + return "south" + elseif between(rot, 225, 315) then + return "east" + elseif between(rot, 45, 135) then + return "west" + end +end + +function ws.getaxis() + local dir=ws.getdir() + if dir == "north" or dir == "south" then return "z" end + return "x" +end +function ws.setdir(dir) -- + if dir == "north" then + minetest.localplayer:set_yaw(0) + elseif dir == "south" then + minetest.localplayer:set_yaw(180) + elseif dir == "east" then + minetest.localplayer:set_yaw(270) + elseif dir == "west" then + minetest.localplayer:set_yaw(90) + end +end + +function ws.dircoord(f, y, r ,rpos, rdir) + local dir= ws.getdir(rdir) + local coord = ws.optcoord(f, y, r) + local f = coord.x + local y = coord.y + local r = coord.z + local lp= rpos or minetest.localplayer:get_pos() + if dir == "north" then + return ws.relcoord(r, y, f,rpos) + elseif dir == "south" then + return ws.relcoord(-r, y, -f,rpos) + elseif dir == "east" then + return ws.relcoord(f, y, -r,rpos) + elseif dir== "west" then + return ws.relcoord(-f, y, r,rpos) + end + return ws.relcoord(0, 0, 0,rpos) +end + +function ws.get_dimension(pos) + if pos.y > -65 then return "overworld" + elseif pos.y > -8000 then return "void" + elseif pos.y > -27000 then return "end" + elseif pos.y > -28930 then return "void" + elseif pos.y > -31000 then return "nether" + else return "void" + end +end + +function ws.aim(tpos) + local ppos=minetest.localplayer:get_pos() + local dir=vector.direction(ppos,tpos) + local yyaw=0; + local pitch=0; + if dir.x < 0 then + yyaw = math.atan2(-dir.x, dir.z) + (math.pi * 2) + else + yyaw = math.atan2(-dir.x, dir.z) + end + yyaw = ws.round2(math.deg(yyaw),2) + pitch = ws.round2(math.deg(math.asin(-dir.y) * 1),2); + minetest.localplayer:set_yaw(yyaw) + minetest.localplayer:set_pitch(pitch) +end + +function ws.gaim(tpos,v,g) + local v = v or 40 + local g = g or 9.81 + local ppos=minetest.localplayer:get_pos() + local dir=vector.direction(ppos,tpos) + local yyaw=0; + local pitch=0; + if dir.x < 0 then + yyaw = math.atan2(-dir.x, dir.z) + (math.pi * 2) + else + yyaw = math.atan2(-dir.x, dir.z) + end + yyaw = ws.round2(math.deg(yyaw),2) + local y = dir.y + dir.y = 0 + local x = vector.length(dir) + pitch=math.atan(math.pow(v, 2) / (g * x) + math.sqrt(math.pow(v, 4)/(math.pow(g, 2) * math.pow(x, 2)) - 2 * math.pow(v, 2) * y/(g * math.pow(x, 2)) - 1)) + --pitch = ws.round2(math.deg(math.asin(-dir.y) * 1),2); + minetest.localplayer:set_yaw(yyaw) + minetest.localplayer:set_pitch(math.deg(pitch)) +end + +function ws.buildable_to(pos) + local node=minetest.get_node_or_nil(pos) + if node then + return minetest.get_node_def(node.name).buildable_to + end +end + +function ws.tplace(p,n,stay) + if not p then return end + if n then ws.switch_to_item(n) end + local opos=ws.dircoord(0,0,0) + local tpos=vector.add(p,vector.new(0,1,0)) + minetest.localplayer:set_pos(tpos) + ws.place(p,{n}) + if not stay then + minetest.after(0.1,function() + minetest.localplayer:set_pos(opos) + end) + end +end + +minetest.register_chatcommand("tplace", { + description = "tp-place", + param = "Y", + func = function(param) + return ws.tplace(minetest.string_to_pos(param)) + end +}) + +function ws.ytp(param) + local y=tonumber(param) + local lp=ws.dircoord(0,0,0) + if lp.y < y + 50 then return false,"Can't TP up." end + if y < -30912 then return false,"Don't TP into the void lol." end + minetest.localplayer:set_pos(vector.new(lp.x,y,lp.z)) +end + +local function tablearg(arg) + local tb={} + if type(arg) == 'string' then + tb={arg} + elseif type(arg) == 'table' then + tb=arg + elseif type(arg) == 'function' then + tb=arg() + end + return tb +end + +function ws.isnode(pos,arg)--arg is either an itemstring, a table of itemstrings or a function returning an itemstring + local nodename=tablearg(arg) + local nd=minetest.get_node_or_nil(pos) + if nd and nodename and ws.in_list(nd.name,nodename) then + return true + end +end + +function ws.can_place_at(pos) + local node = minetest.get_node_or_nil(pos) + return (node and (node.name == "air" or node.name=="mcl_core:water_source" or node.name=="mcl_core:water_flowing" or node.name=="mcl_core:lava_source" or node.name=="mcl_core:lava_flowing" or minetest.get_node_def(node.name).buildable_to)) +end + +-- should check if wield is placeable +-- minetest.get_node(wielded:get_name()) ~= nil should probably work +-- otherwise it equips armor and eats food +function ws.can_place_wielded_at(pos) + local wield_empty = minetest.localplayer:get_wielded_item():is_empty() + return not wield_empty and ws.can_place_at(pos) +end + + +function ws.find_any_swap(items,hslot) + hslot=hslot or 8 + for i, v in ipairs(items) do + local n = minetest.find_item(v) + if n then + ws.switch_to_item(v,hslot) + return true + end + end + return false +end + + +-- swaps to any of the items and places if need be +-- returns true if placed and in inventory or already there, false otherwise + +local lastact=0 +local lastplc=0 +local lastdig=0 +local actint=10 +function ws.place(pos,items,hslot, place) + --if nodes_this_tick > 8 then return end + --nodes_this_tick = nodes_this_tick + 1 + --if not inside_constraints(pos) then return end + if not pos then return end + if not ws.can_place_at(pos) then return end + items=tablearg(items) + + place = place or minetest.place_node + + local node = minetest.get_node_or_nil(pos) + if not node then return end + -- already there + if ws.isnode(pos,items) then + return true + else + if ws.find_any_swap(items,hslot) then + place(pos) + return true + end + end +end + +function ws.place_if_able(pos) + if not pos then return end + if not inside_constraints(pos) then return end + if ws.can_place_wielded_at(pos) then + minetest.place_node(pos) + end +end + +function ws.is_diggable(pos) + if not pos then return false end + local nd=minetest.get_node_or_nil(pos) + if not nd then return false end + local n = minetest.get_node_def(nd.name) + if n and n.diggable then return true end + return false +end + +function ws.dig(pos,condition,autotool) + --if not inside_constraints(pos) then return end + if autotool == nil then autotool = true end + if condition and not condition(pos) then return false end + if not ws.is_diggable(pos) then return end + local nd=minetest.get_node_or_nil(pos) + if nd and minetest.get_node_def(nd.name).diggable then + if autotool then ws.select_best_tool(pos) end + minetest.dig_node(pos) + end + return true +end + +function ws.chunk_loaded() + local ign=minetest.find_nodes_near(ws.dircoord(0,0,0),10,{'ignore'},true) + if #ign == 0 then return true end + return false +end + +function ws.get_near(nodes,range) + range=range or 5 + local nds=minetest.find_nodes_near(ws.dircoord(0,0,0),rang,nodes,true) + if #nds > 0 then return nds end + return false +end + +function ws.is_laggy() + if tps_client and tps_client.ping and tps_client.ping > 1000 then return true end +end + + +function ws.donodes(poss,func,condition) + if ws.is_laggy() then return end + local dn_i=0 + for k,v in pairs(poss) do + if dn_i > 8 then return end + --local nd=minetest.get_node_or_nil(v) + if condition == nil or condition(v) then + func(v) + dn_i = dn_i + 1 + end + end +end + +function ws.dignodes(poss,condition) + local func=function(p) ws.dig(p) end + ws.donodes(poss,func,condition) +end + + +function ws.replace(pos,arg) + arg=tablearg(arg) + local nd=minetest.get_node_or_nil(pos) + if nd and not ws.in_list(nd.name,arg) and ws.buildable_to(pos) then + local tm=ws.get_digtime(nd.name) or 0 + ws.dig(pos) + minetest.after(tm + 0.1,function() + ws.place(pos,arg) + end) + return tm + else + return ws.place(pos,arg) + end +end + +function ws.playeron(p) + local pls=minetest.get_player_names() + for k,v in pairs(pls) do + if v == p then return true end + end + return false +end + + +function ws.between(x, y, z) -- x is between y and z (inclusive) + return y <= x and x <= z +end + + +local wall_pos1={x=-1255,y=6,z=792} +local wall_pos2={x=-1452,y=80,z=981} +local iwall_pos1={x=-1266,y=6,z=802} +local iwall_pos2={x=-1442,y=80,z=971} + +function ws.in_cube(tpos,wpos1,wpos2) + local xmax=wpos2.x + local xmin=wpos1.x + + local ymax=wpos2.y + local ymin=wpos1.y + + local zmax=wpos2.z + local zmin=wpos1.z + if wpos1.x > wpos2.x then + xmax=wpos1.x + xmin=wpos2.x + end + if wpos1.y > wpos2.y then + ymax=wpos1.y + ymin=wpos2.y + end + if wpos1.z > wpos2.z then + zmax=wpos1.z + zmin=wpos2.z + end + if ws.between(tpos.x,xmin,xmax) and ws.between(tpos.y,ymin,ymax) and ws.between(tpos.z,zmin,zmax) then + return true + end + return false +end + +function ws.in_wall(pos) + if ws.in_cube(pos,wall_pos1,wall_pos2) and not in_cube(pos,iwall_pos1,iwall_pos2) then + return true end + return false +end + +function ws.inside_wall(pos) + local p1=iwall_pos1 + local p2=iwall_pos2 + if ws.in_cube(pos,p1,p2) then return true end + return false +end + +function ws.find_closest_reachable_airpocket(pos) + local lp=ws.dircoord(0,0,0) + local nds=minetest.find_nodes_near(lp,5,{'air'}) + local odst=10 + local rt=lp + for k,v in ipairs(nds) do + local dst=vector.distance(pos,v) + if dst < odst then odst=dst rt=v end + end + if odst==10 then return false end + return vector.add(rt,vector.new(0,-1,0)) +end + + +-- DEBUG +local function printwieldedmeta() + ws.dcm(dump(minetest.localplayer:get_wielded_item():get_meta():to_table())) +end +minetest.register_cheat('ItemMeta','Test',printwieldedmeta) +-- CC0/Unlicense Emilia 2020 + +if minetest.settings:get("slow_blocks_per_second") == nil then + minetest.settings:set("slow_blocks_per_second", 8) +end + +-- Could remove the queue and have nowplace() check if it can place at the position + +local lastt = 0 + +local posqueue = {} + +local function posq_pos(pos) + local plen = #posqueue + for i = 0, #posqueue - 1 do + if vector.equals(pos, posqueue[plen - i]) then + return plen - i + end + end +end + +local function nowplace(pos) + local p = posq_pos(pos) + if p then + table.remove(posqueue, p) + end + + minetest.place_node(pos) +end + +local function place(pos) + if not posq_pos(pos) then + local now = os.clock() + + if lastt < now then + lastt = now + end + + local interval = 1 / minetest.settings:get("slow_blocks_per_second") + lastt = lastt + interval + + minetest.after(lastt - now, nowplace, pos) + + posqueue[#posqueue + 1] = pos + end +end + +scaffold.register_template_scaffold("SlowScaffold", "scaffold_slow", function(pos) + if scaffold.can_place_wielded_at(pos) then + place(pos) + end +end) diff --git a/scaffold/squarry.lua b/scaffold/squarry.lua new file mode 100644 index 0000000..b3aaba1 --- /dev/null +++ b/scaffold/squarry.lua @@ -0,0 +1,1028 @@ + +ws = {} +ws.registered_globalhacks = {} +ws.displayed_wps={} + +ws.c = core + +ws.range=4 +ws.target=nil +ws.targetpos=nil + +local nextact = {} +local ghwason={} + +local nodes_this_tick=0 + +function ws.s(name,value) + if value == nil then + return ws.c.settings:get(name) + else + ws.c.settings:set(name,value) + return ws.c.settings:get(name) + end +end +function ws.sb(name,value) + if value == nil then + return ws.c.settings:get_bool(name) + else + ws.c.settings:set_bool(name,value) + return ws.c.settings:get_bool(name) + end +end + +function ws.dcm(msg) + return minetest.display_chat_message(msg) +end +function ws.set_bool_bulk(settings,value) + if type(settings) ~= 'table' then return false end + for k,v in pairs(settings) do + minetest.settings:set_bool(v,value) + end + return true +end + +function ws.shuffle(tbl) + for i = #tbl, 2, -1 do + local j = math.random(i) + tbl[i], tbl[j] = tbl[j], tbl[i] + end + return tbl +end + +function ws.in_list(val, list) + if type(list) ~= "table" then return false end + for i, v in pairs(list) do + if v == val then + return true + end + end + return false +end + +function ws.random_table_element(tbl) + local ks = {} + for k in pairs(tbl) do + table.insert(ks, k) + end + return tbl[ks[math.random(#ks)]] +end + +function ws.center() + --local lp=ws.dircoord(0,0,0) + --minetest.localplayer:set_pos(lp) +end + +function ws.globalhacktemplate(setting,func,funcstart,funcstop,daughters,delay) + funcstart = funcstart or function() end + funcstop = funcstop or function() end + delay = delay or 0.5 + return function() + if not minetest.localplayer then return end + if minetest.settings:get_bool(setting) then + if tps_client and tps_client.ping and tps_client.ping > 1000 then return end + nodes_this_tick = 0 + if nextact[setting] and nextact[setting] > os.clock() then return end + nextact[setting] = os.clock() + delay + if not ghwason[setting] then + if not funcstart() then + ws.set_bool_bulk(daughters,true) + ghwason[setting] = true + --ws.dcm(setting.. " activated") + ws.center() + minetest.settings:set('last-dir',ws.getdir()) + minetest.settings:set('last-y',ws.dircoord(0,0,0).y) + else minetest.settings:set_bool(setting,false) + end + else + func() + end + + elseif ghwason[setting] then + ghwason[setting] = false + ws.set_bool_bulk(daughters,false) + funcstop() + --ws.dcm(setting.. " deactivated") + end + end +end + +function ws.register_globalhack(func) + table.insert(ws.registered_globalhacks,func) +end + +function ws.register_globalhacktemplate(name,category,setting,func,funcstart,funcstop,daughters) + ws.register_globalhack(ws.globalhacktemplate(setting,func,funcstart,funcstop,daughters)) + minetest.register_cheat(name,category,setting) +end + +ws.rg=ws.register_globalhacktemplate + +function ws.step_globalhacks(dtime) + for i, v in ipairs(ws.registered_globalhacks) do + v(dtime) + end +end + +minetest.register_globalstep(function(dtime) ws.step_globalhacks(dtime) end) +minetest.settings:set_bool('continuous_forward',false) + +function ws.on_connect(func) + if not minetest.localplayer then minetest.after(0,function() ws.on_connect(func) end) return end + if func then func() end +end + +ws.on_connect(function() + local ldir =minetest.settings:get('last-dir') + if ldir then ws.setdir(ldir) end +end) + + +-- COORD MAGIC + +function ws.is_same_pos(pos1,pos2) + return vector.distance(vector.round(pos1),vector.round(pos2)) == 0 +end +function ws.get_reachable_positions(range,under) + under=under or false + range=range or 4 + local rt={} + local lp=vector.round(minetest.localplayer:get_pos()) + local ylim=range + if under then ylim=-1 end + for x = -range,range,1 do + for y = -range,ylim,1 do + for z = -range,range,1 do + table.insert(rt,vector.round(vector.add(lp,vector.new(x,y,z)))) + end + end + end + return rt +end + +function ws.do_area(radius,func,plane) + for k,v in pairs(ws.get_reachable_positions(range)) do + if not plane or v.y == minetest.localplayer:get_pos().y -1 then + func(v) + end + end +end + +function ws.get_hud_by_texture(texture) + local def + local i = -1 + repeat + i = i + 1 + def = minetest.localplayer:hud_get(i) + until not def or def.text == texture + if def then + return def + end + def.number=0 + return def +end + +function ws.find_player(name) + for k, v in ipairs(minetest.localplayer.get_nearby_objects(500)) do + if v:get_name() == name then + return v:get_pos(),v + end + end +end + +function ws.display_wp(pos,name) + local ix = #ws.displayed_wps + 1 + ws.displayed_wps[ix] = minetest.localplayer:hud_add({ + hud_elem_type = 'waypoint', + name = name, + text = name, + number = 0x00ff00, + world_pos = pos + }) + return ix +end + +function ws.clear_wp(ix) + table.remove(ws.displayed_wps,ix) +end + +function ws.clear_wps() + for k,v in ipairs(ws.displayed_wps) do + minetest.localplayer:hud_remove(v) + table.remove(ws.displayed_wps,k) + end +end + +function ws.register_chatcommand_alias(old, ...) + local def = assert(minetest.registered_chatcommands[old]) + def.name = nil + for i = 1, select('#', ...) do + minetest.register_chatcommand(select(i, ...), table.copy(def)) + end +end + +function ws.round2(num, numDecimalPlaces) + return tonumber(string.format("%." .. (numDecimalPlaces or 0) .. "f", num)) +end + +function ws.pos_to_string(pos) + if type(pos) == 'table' then + pos = minetest.pos_to_string(vector.round(pos)) + end + if type(pos) == 'string' then + return pos + end + return pos +end + +function ws.string_to_pos(pos) + if type(pos) == 'string' then + pos = minetest.string_to_pos(pos) + end + if type(pos) == 'table' then + return vector.round(pos) + end + return pos +end + + + +--ITEMS +function ws.find_item_in_table(items,rnd) + if type(items) == 'string' then + return minetest.find_item(items) + end + if type(items) ~= 'table' then return end + if rnd then items=ws.shuffle(items) end + for i, v in pairs(items) do + local n = minetest.find_item(v) + if n then + return n + end + end + return false +end + +function ws.find_empty(inv) + for i, v in ipairs(inv) do + if v:is_empty() then + return i + end + end + return false +end + +function ws.find_named(inv, name) + if not inv then return -1 end + if not name then return end + for i, v in ipairs(inv) do + if v:get_name():find(name) then + return i + end + end +end + +function ws.itemnameformat(description) + description = description:gsub(string.char(0x1b) .. "%(.@[^)]+%)", "") + description = description:match("([^\n]*)") + return description +end + +function ws.find_nametagged(list, name) + for i, v in ipairs(list) do + if ws.itemnameformat(v:get_description()) == name then + return i + end + end +end + +local hotbar_slot=8 +function ws.to_hotbar(it,hslot) + local tpos=nil + local plinv = minetest.get_inventory("current_player") + if hslot and hslot < 10 then + tpos=hslot + else + for i, v in ipairs(plinv.main) do + if i<10 and v:is_empty() then + tpos = i + break + end + end + end + if tpos == nil then tpos=hotbar_slot end + local mv = InventoryAction("move") + mv:from("current_player", "main", it) + mv:to("current_player", "main", tpos) + mv:apply() + return tpos +end + +function ws.switch_to_item(itname,hslot) + if not minetest.localplayer then return false end + local plinv = minetest.get_inventory("current_player") + for i, v in ipairs(plinv.main) do + if i<10 and v:get_name() == itname then + minetest.localplayer:set_wield_index(i) + return true + end + end + local pos = ws.find_named(plinv.main, itname) + if pos then + minetest.localplayer:set_wield_index(ws.to_hotbar(pos,hslot)) + return true + end + return false +end +function ws.in_inv(itname) + if not minetest.localplayer then return false end + local plinv = minetest.get_inventory("current_player") + local pos = ws.find_named(plinv.main, itname) + if pos then + return true + end +end + +function core.switch_to_item(item) return ws.switch_to_item(item) end + +function ws.switch_inv_or_echest(name,max_count,hslot) + if not minetest.localplayer then return false end + local plinv = minetest.get_inventory("current_player") + if ws.switch_to_item(name) then return true end + + local epos = ws.find_named(plinv.enderchest, name) + if epos then + local tpos + for i, v in ipairs(plinv.main) do + if i < 9 and v:is_empty() then + tpos = i + break + end + end + if not tpos then tpos=hotbar_slot end + + if tpos then + local mv = InventoryAction("move") + mv:from("current_player", "enderchest", epos) + mv:to("current_player", "main", tpos) + if max_count then + mv:set_count(max_count) + end + mv:apply() + minetest.localplayer:set_wield_index(tpos) + return true + end + end + return false +end + +local function posround(n) + return math.floor(n + 0.5) +end + +local function fmt(c) + return tostring(posround(c.x))..","..tostring(posround(c.y))..","..tostring(posround(c.z)) +end + +local function map_pos(value) + if value.x then + return value + else + return {x = value[1], y = value[2], z = value[3]} + end +end + +function ws.invparse(location) + if type(location) == "string" then + if string.match(location, "^[-]?[0-9]+,[-]?[0-9]+,[-]?[0-9]+$") then + return "nodemeta:" .. location + else + return location + end + elseif type(location) == "table" then + return "nodemeta:" .. fmt(map_pos(location)) + end +end + +function ws.invpos(p) + return "nodemeta:"..p.x..","..p.y..","..p.z +end + + +-- TOOLS + + +local function check_tool(stack, node_groups, old_best_time) + local toolcaps = stack:get_tool_capabilities() + if not toolcaps then return end + local best_time = old_best_time + for group, groupdef in pairs(toolcaps.groupcaps) do + local level = node_groups[group] + if level then + local this_time = groupdef.times[level] + if this_time and this_time < best_time then + best_time = this_time + end + end + end + return best_time < old_best_time, best_time +end + +local function find_best_tool(nodename, switch) + local player = minetest.localplayer + local inventory = minetest.get_inventory("current_player") + local node_groups = minetest.get_node_def(nodename).groups + local new_index = player:get_wield_index() + local is_better, best_time = false, math.huge + + is_better, best_time = check_tool(player:get_wielded_item(), node_groups, best_time) + if inventory.hand then + is_better, best_time = check_tool(inventory.hand[1], node_groups, best_time) + end + + for index, stack in ipairs(inventory.main) do + is_better, best_time = check_tool(stack, node_groups, best_time) + if is_better then + new_index = index + end + end + + return new_index,best_time +end + +function ws.get_digtime(nodename) + local idx,tm=find_best_tool(nodename) + return tm +end + +function ws.select_best_tool(pos) + local nd=minetest.get_node_or_nil(pos) + local nodename='air' + if nd then nodename=nd.name end + local t=find_best_tool(nodename) + minetest.localplayer:set_wield_index(ws.to_hotbar(t,hotbar_slot)) + --minetest.localplayer:set_wield_index(find_best_tool(nodename)) +end + +--- COORDS +function ws.coord(x, y, z) + return vector.new(x,y,z) +end +function ws.ordercoord(c) + if c.x == nil then + return {x = c[1], y = c[2], z = c[3]} + else + return c + end +end + +-- x or {x,y,z} or {x=x,y=y,z=z} +function ws.optcoord(x, y, z) + if y and z then + return ws.coord(x, y, z) + else + return ws.ordercoord(x) + end +end +function ws.cadd(c1, c2) + return vector.add(c1,c2) + --return ws.coord(c1.x + c2.x, c1.y + c2.y, c1.z + c2.z) +end + +function ws.relcoord(x, y, z, rpos) + local pos = rpos or minetest.localplayer:get_pos() + pos.y=math.ceil(pos.y) + --math.floor(pos.y) + 0.5 + return ws.cadd(pos, ws.optcoord(x, y, z)) +end + +local function between(x, y, z) -- x is between y and z (inclusive) + return y <= x and x <= z +end + +function ws.getdir(yaw) -- + local rot = yaw or minetest.localplayer:get_yaw() % 360 + if between(rot, 315, 360) or between(rot, 0, 45) then + return "north" + elseif between(rot, 135, 225) then + return "south" + elseif between(rot, 225, 315) then + return "east" + elseif between(rot, 45, 135) then + return "west" + end +end + +function ws.getaxis() + local dir=ws.getdir() + if dir == "north" or dir == "south" then return "z" end + return "x" +end +function ws.setdir(dir) -- + if dir == "north" then + minetest.localplayer:set_yaw(0) + elseif dir == "south" then + minetest.localplayer:set_yaw(180) + elseif dir == "east" then + minetest.localplayer:set_yaw(270) + elseif dir == "west" then + minetest.localplayer:set_yaw(90) + end +end + +function ws.dircoord(f, y, r ,rpos, rdir) + local dir= ws.getdir(rdir) + local coord = ws.optcoord(f, y, r) + local f = coord.x + local y = coord.y + local r = coord.z + local lp= rpos or minetest.localplayer:get_pos() + if dir == "north" then + return ws.relcoord(r, y, f,rpos) + elseif dir == "south" then + return ws.relcoord(-r, y, -f,rpos) + elseif dir == "east" then + return ws.relcoord(f, y, -r,rpos) + elseif dir== "west" then + return ws.relcoord(-f, y, r,rpos) + end + return ws.relcoord(0, 0, 0,rpos) +end + +function ws.get_dimension(pos) + if pos.y > -65 then return "overworld" + elseif pos.y > -8000 then return "void" + elseif pos.y > -27000 then return "end" + elseif pos.y > -28930 then return "void" + elseif pos.y > -31000 then return "nether" + else return "void" + end +end + +function ws.aim(tpos) + local ppos=minetest.localplayer:get_pos() + local dir=vector.direction(ppos,tpos) + local yyaw=0; + local pitch=0; + if dir.x < 0 then + yyaw = math.atan2(-dir.x, dir.z) + (math.pi * 2) + else + yyaw = math.atan2(-dir.x, dir.z) + end + yyaw = ws.round2(math.deg(yyaw),2) + pitch = ws.round2(math.deg(math.asin(-dir.y) * 1),2); + minetest.localplayer:set_yaw(yyaw) + minetest.localplayer:set_pitch(pitch) +end + +function ws.gaim(tpos,v,g) + local v = v or 40 + local g = g or 9.81 + local ppos=minetest.localplayer:get_pos() + local dir=vector.direction(ppos,tpos) + local yyaw=0; + local pitch=0; + if dir.x < 0 then + yyaw = math.atan2(-dir.x, dir.z) + (math.pi * 2) + else + yyaw = math.atan2(-dir.x, dir.z) + end + yyaw = ws.round2(math.deg(yyaw),2) + local y = dir.y + dir.y = 0 + local x = vector.length(dir) + pitch=math.atan(math.pow(v, 2) / (g * x) + math.sqrt(math.pow(v, 4)/(math.pow(g, 2) * math.pow(x, 2)) - 2 * math.pow(v, 2) * y/(g * math.pow(x, 2)) - 1)) + --pitch = ws.round2(math.deg(math.asin(-dir.y) * 1),2); + minetest.localplayer:set_yaw(yyaw) + minetest.localplayer:set_pitch(math.deg(pitch)) +end + +function ws.buildable_to(pos) + local node=minetest.get_node_or_nil(pos) + if node then + return minetest.get_node_def(node.name).buildable_to + end +end + +function ws.tplace(p,n,stay) + if not p then return end + if n then ws.switch_to_item(n) end + local opos=ws.dircoord(0,0,0) + local tpos=vector.add(p,vector.new(0,1,0)) + minetest.localplayer:set_pos(tpos) + ws.place(p,{n}) + if not stay then + minetest.after(0.1,function() + minetest.localplayer:set_pos(opos) + end) + end +end + +minetest.register_chatcommand("tplace", { + description = "tp-place", + param = "Y", + func = function(param) + return ws.tplace(minetest.string_to_pos(param)) + end +}) + +function ws.ytp(param) + local y=tonumber(param) + local lp=ws.dircoord(0,0,0) + if lp.y < y + 50 then return false,"Can't TP up." end + if y < -30912 then return false,"Don't TP into the void lol." end + minetest.localplayer:set_pos(vector.new(lp.x,y,lp.z)) +end + +local function tablearg(arg) + local tb={} + if type(arg) == 'string' then + tb={arg} + elseif type(arg) == 'table' then + tb=arg + elseif type(arg) == 'function' then + tb=arg() + end + return tb +end + +function ws.isnode(pos,arg)--arg is either an itemstring, a table of itemstrings or a function returning an itemstring + local nodename=tablearg(arg) + local nd=minetest.get_node_or_nil(pos) + if nd and nodename and ws.in_list(nd.name,nodename) then + return true + end +end + +function ws.can_place_at(pos) + local node = minetest.get_node_or_nil(pos) + return (node and (node.name == "air" or node.name=="mcl_core:water_source" or node.name=="mcl_core:water_flowing" or node.name=="mcl_core:lava_source" or node.name=="mcl_core:lava_flowing" or minetest.get_node_def(node.name).buildable_to)) +end + +-- should check if wield is placeable +-- minetest.get_node(wielded:get_name()) ~= nil should probably work +-- otherwise it equips armor and eats food +function ws.can_place_wielded_at(pos) + local wield_empty = minetest.localplayer:get_wielded_item():is_empty() + return not wield_empty and ws.can_place_at(pos) +end + + +function ws.find_any_swap(items,hslot) + hslot=hslot or 8 + for i, v in ipairs(items) do + local n = minetest.find_item(v) + if n then + ws.switch_to_item(v,hslot) + return true + end + end + return false +end + + +-- swaps to any of the items and places if need be +-- returns true if placed and in inventory or already there, false otherwise + +local lastact=0 +local lastplc=0 +local lastdig=0 +local actint=10 +function ws.place(pos,items,hslot, place) + --if nodes_this_tick > 8 then return end + --nodes_this_tick = nodes_this_tick + 1 + --if not inside_constraints(pos) then return end + if not pos then return end + if not ws.can_place_at(pos) then return end + items=tablearg(items) + + place = place or minetest.place_node + + local node = minetest.get_node_or_nil(pos) + if not node then return end + -- already there + if ws.isnode(pos,items) then + return true + else + if ws.find_any_swap(items,hslot) then + place(pos) + return true + end + end +end + +function ws.place_if_able(pos) + if not pos then return end + if not inside_constraints(pos) then return end + if ws.can_place_wielded_at(pos) then + minetest.place_node(pos) + end +end + +function ws.is_diggable(pos) + if not pos then return false end + local nd=minetest.get_node_or_nil(pos) + if not nd then return false end + local n = minetest.get_node_def(nd.name) + if n and n.diggable then return true end + return false +end + +function ws.dig(pos,condition,autotool) + --if not inside_constraints(pos) then return end + if autotool == nil then autotool = true end + if condition and not condition(pos) then return false end + if not ws.is_diggable(pos) then return end + local nd=minetest.get_node_or_nil(pos) + if nd and minetest.get_node_def(nd.name).diggable then + if autotool then ws.select_best_tool(pos) end + minetest.dig_node(pos) + end + return true +end + +function ws.chunk_loaded() + local ign=minetest.find_nodes_near(ws.dircoord(0,0,0),10,{'ignore'},true) + if #ign == 0 then return true end + return false +end + +function ws.get_near(nodes,range) + range=range or 5 + local nds=minetest.find_nodes_near(ws.dircoord(0,0,0),rang,nodes,true) + if #nds > 0 then return nds end + return false +end + +function ws.is_laggy() + if tps_client and tps_client.ping and tps_client.ping > 1000 then return true end +end + + +function ws.donodes(poss,func,condition) + if ws.is_laggy() then return end + local dn_i=0 + for k,v in pairs(poss) do + if dn_i > 8 then return end + --local nd=minetest.get_node_or_nil(v) + if condition == nil or condition(v) then + func(v) + dn_i = dn_i + 1 + end + end +end + +function ws.dignodes(poss,condition) + local func=function(p) ws.dig(p) end + ws.donodes(poss,func,condition) +end + + +function ws.replace(pos,arg) + arg=tablearg(arg) + local nd=minetest.get_node_or_nil(pos) + if nd and not ws.in_list(nd.name,arg) and ws.buildable_to(pos) then + local tm=ws.get_digtime(nd.name) or 0 + ws.dig(pos) + minetest.after(tm + 0.1,function() + ws.place(pos,arg) + end) + return tm + else + return ws.place(pos,arg) + end +end + +function ws.playeron(p) + local pls=minetest.get_player_names() + for k,v in pairs(pls) do + if v == p then return true end + end + return false +end + + +function ws.between(x, y, z) -- x is between y and z (inclusive) + return y <= x and x <= z +end + + +local wall_pos1={x=-1255,y=6,z=792} +local wall_pos2={x=-1452,y=80,z=981} +local iwall_pos1={x=-1266,y=6,z=802} +local iwall_pos2={x=-1442,y=80,z=971} + +function ws.in_cube(tpos,wpos1,wpos2) + local xmax=wpos2.x + local xmin=wpos1.x + + local ymax=wpos2.y + local ymin=wpos1.y + + local zmax=wpos2.z + local zmin=wpos1.z + if wpos1.x > wpos2.x then + xmax=wpos1.x + xmin=wpos2.x + end + if wpos1.y > wpos2.y then + ymax=wpos1.y + ymin=wpos2.y + end + if wpos1.z > wpos2.z then + zmax=wpos1.z + zmin=wpos2.z + end + if ws.between(tpos.x,xmin,xmax) and ws.between(tpos.y,ymin,ymax) and ws.between(tpos.z,zmin,zmax) then + return true + end + return false +end + +function ws.in_wall(pos) + if ws.in_cube(pos,wall_pos1,wall_pos2) and not in_cube(pos,iwall_pos1,iwall_pos2) then + return true end + return false +end + +function ws.inside_wall(pos) + local p1=iwall_pos1 + local p2=iwall_pos2 + if ws.in_cube(pos,p1,p2) then return true end + return false +end + +function ws.find_closest_reachable_airpocket(pos) + local lp=ws.dircoord(0,0,0) + local nds=minetest.find_nodes_near(lp,5,{'air'}) + local odst=10 + local rt=lp + for k,v in ipairs(nds) do + local dst=vector.distance(pos,v) + if dst < odst then odst=dst rt=v end + end + if odst==10 then return false end + return vector.add(rt,vector.new(0,-1,0)) +end + + +-- DEBUG +local function printwieldedmeta() + ws.dcm(dump(minetest.localplayer:get_wielded_item():get_meta():to_table())) +end +minetest.register_cheat('ItemMeta','Test',printwieldedmeta) +local sq_pos1={x=-30800,y=1,z=-30800} +local sq_pos2={x=-30880,y=80,z=-30880} +local digging=false +local flying=false +local target=vector.new(0,0,0) + + +local function between(x, y, z) return y <= x and x <= z end -- x is between y and z (inclusive) + +local function in_cube(tpos,wpos1,wpos2) + local xmax=wpos2.x + local xmin=wpos1.x + + local ymax=wpos2.y + local ymin=wpos1.y + + local zmax=wpos2.z + local zmin=wpos1.z + if wpos1.x > wpos2.x then + xmax=wpos1.x + xmin=wpos2.x + end + if wpos1.y > wpos2.y then + ymax=wpos1.y + ymin=wpos2.y + end + if wpos1.z > wpos2.z then + zmax=wpos1.z + zmin=wpos2.z + end + if between(tpos.x,xmin,xmax) and between(tpos.y,ymin,ymax) and between(tpos.z,zmin,zmax) then + return true + end + return false +end + +local function get_nodes_in_range(range,air) + local lp=minetest.localplayer:get_pos() + local p1=vector.add(lp,{x=range,y=range,z=range}) + local p2=vector.add(lp,{x=-range,y=-range,z=-range}) + local nn=nlist.get_mclnodes() + if air then table.insert(nn,'air') end + local nds,cnt=minetest.find_nodes_in_area(p1,p2,nn,true) + local rt={} + for k,v in pairs(nds) do for kk,vv in pairs(v) do + local nd=minetest.get_node_or_nil(vv) + if nd then table.insert(rt,vv) end + end end + return rt +end + +local function get_randompos(wpos1,wpos2) + local xmax=wpos2.x + local xmin=wpos1.x + + local ymax=wpos2.y + local ymin=wpos1.y + + local zmax=wpos2.z + local zmin=wpos1.z + if wpos1.x > wpos2.x then + xmax=wpos1.x + xmin=wpos2.x + end + if wpos1.y > wpos2.y then + ymax=wpos1.y + ymin=wpos2.y + end + if wpos1.z > wpos2.z then + zmax=wpos1.z + zmin=wpos2.z + end + return vector.new(math.random(xmin,xmax),math.random(ymin,ymax),math.random(zmin,zmax)) +end + +local nextdig=0 +local function flythere(pos) + flying=true + minetest.settings:set_bool('noclip',false) + minetest.settings:set_bool('scaffold_quarrytool',true) + minetest.settings:set_bool("pitch_move",true) + minetest.settings:set_bool("free_move",true) + minetest.settings:set_bool("continuous_forward",true) + autofly.aim(pos) + core.set_keypress("special1", true) +end + +local function stopflight() + flying=false + digging=true + minetest.settings:set_bool("continuous_forward",false) + minetest.settings:set_bool('scaffold_walltool',false) + minetest.settings:set_bool("noclip",false) + minetest.settings:set_bool("pitch_move",false) + core.set_keypress("special1", false) +end + +local function do_nodes_in_range(action) + local nds={} + if action == 'dig' then nds=get_nodes_in_range(6) + else nds=get_nodes_in_range(6,true) end + if #nds > 0 then diggin=true else diggin=false end + for k,v in pairs(nds) do + if v then + --minetest.switch_to_item("mcl_books:book_written") + if action == 'dig' then + minetest.select_best_tool(minetest.get_node_or_nil(v).name) + minetest.dig_node(v) + else + local headpos=vector.add(minetest.localplayer:get_pos(),{x=0,y=0,z=0}) + if vector.distance(headpos,v) == 0 then return end + scaffold.place_if_able(v) + end + end + end + +end +--randomseed(os.clock()) +scaffold.register_template_scaffold("QuarryTool", "scaffold_quarrytool", function(pos) + + do_nodes_in_range('dig') +end) +scaffold.register_template_scaffold("PlaceRange", "scaffold_placer", function(pos) + do_nodes_in_range() + local headpos=vector.add(minetest.localplayer:get_pos(),vector.new(0,1,0)) + local headnod=minetest.get_node_or_nil(headpos) + if headnod.name ~= 'air' then scaffold.dig(headpos) end +end) +local qbot_wason=false + +ws.register_globalhacktemplate("QuarryBot", "Bots", "scaffold_quarrybot", function(pos) + local lp=minetest.localplayer:get_pos() + if not digging and not flying then + local nds=get_nodes_in_range(50) + if #nds == 0 then + target=get_randompos(sq_pos1,sq_pos2) + else + target=nds[math.random(#nds)] + end + flythere(target) + elseif vector.distance(lp,target) < 5 then + stopflight() + end +end,function() + scaffold.set_pos1(sq_pos1) + scaffold.set_pos2(sq_pos2) + minetest.settings:set_bool('scaffold_constrain',true) + minetest.settings:set_bool('scaffold_quarrytool',true) +end,function() + qbot_wason=false + flying=false + digging=false + minetest.settings:set_bool('scaffold_quarrytool',false) + stopflight() +end) diff --git a/scaffold/wallbot.lua b/scaffold/wallbot.lua new file mode 100644 index 0000000..33383a5 --- /dev/null +++ b/scaffold/wallbot.lua @@ -0,0 +1,1135 @@ + +ws = {} +ws.registered_globalhacks = {} +ws.displayed_wps={} + +ws.c = core + +ws.range=4 +ws.target=nil +ws.targetpos=nil + +local nextact = {} +local ghwason={} + +local nodes_this_tick=0 + +function ws.s(name,value) + if value == nil then + return ws.c.settings:get(name) + else + ws.c.settings:set(name,value) + return ws.c.settings:get(name) + end +end +function ws.sb(name,value) + if value == nil then + return ws.c.settings:get_bool(name) + else + ws.c.settings:set_bool(name,value) + return ws.c.settings:get_bool(name) + end +end + +function ws.dcm(msg) + return minetest.display_chat_message(msg) +end +function ws.set_bool_bulk(settings,value) + if type(settings) ~= 'table' then return false end + for k,v in pairs(settings) do + minetest.settings:set_bool(v,value) + end + return true +end + +function ws.shuffle(tbl) + for i = #tbl, 2, -1 do + local j = math.random(i) + tbl[i], tbl[j] = tbl[j], tbl[i] + end + return tbl +end + +function ws.in_list(val, list) + if type(list) ~= "table" then return false end + for i, v in pairs(list) do + if v == val then + return true + end + end + return false +end + +function ws.random_table_element(tbl) + local ks = {} + for k in pairs(tbl) do + table.insert(ks, k) + end + return tbl[ks[math.random(#ks)]] +end + +function ws.center() + --local lp=ws.dircoord(0,0,0) + --minetest.localplayer:set_pos(lp) +end + +function ws.globalhacktemplate(setting,func,funcstart,funcstop,daughters,delay) + funcstart = funcstart or function() end + funcstop = funcstop or function() end + delay = delay or 0.5 + return function() + if not minetest.localplayer then return end + if minetest.settings:get_bool(setting) then + if tps_client and tps_client.ping and tps_client.ping > 1000 then return end + nodes_this_tick = 0 + if nextact[setting] and nextact[setting] > os.clock() then return end + nextact[setting] = os.clock() + delay + if not ghwason[setting] then + if not funcstart() then + ws.set_bool_bulk(daughters,true) + ghwason[setting] = true + --ws.dcm(setting.. " activated") + ws.center() + minetest.settings:set('last-dir',ws.getdir()) + minetest.settings:set('last-y',ws.dircoord(0,0,0).y) + else minetest.settings:set_bool(setting,false) + end + else + func() + end + + elseif ghwason[setting] then + ghwason[setting] = false + ws.set_bool_bulk(daughters,false) + funcstop() + --ws.dcm(setting.. " deactivated") + end + end +end + +function ws.register_globalhack(func) + table.insert(ws.registered_globalhacks,func) +end + +function ws.register_globalhacktemplate(name,category,setting,func,funcstart,funcstop,daughters) + ws.register_globalhack(ws.globalhacktemplate(setting,func,funcstart,funcstop,daughters)) + minetest.register_cheat(name,category,setting) +end + +ws.rg=ws.register_globalhacktemplate + +function ws.step_globalhacks(dtime) + for i, v in ipairs(ws.registered_globalhacks) do + v(dtime) + end +end + +minetest.register_globalstep(function(dtime) ws.step_globalhacks(dtime) end) +minetest.settings:set_bool('continuous_forward',false) + +function ws.on_connect(func) + if not minetest.localplayer then minetest.after(0,function() ws.on_connect(func) end) return end + if func then func() end +end + +ws.on_connect(function() + local ldir =minetest.settings:get('last-dir') + if ldir then ws.setdir(ldir) end +end) + + +-- COORD MAGIC + +function ws.is_same_pos(pos1,pos2) + return vector.distance(vector.round(pos1),vector.round(pos2)) == 0 +end +function ws.get_reachable_positions(range,under) + under=under or false + range=range or 4 + local rt={} + local lp=vector.round(minetest.localplayer:get_pos()) + local ylim=range + if under then ylim=-1 end + for x = -range,range,1 do + for y = -range,ylim,1 do + for z = -range,range,1 do + table.insert(rt,vector.round(vector.add(lp,vector.new(x,y,z)))) + end + end + end + return rt +end + +function ws.do_area(radius,func,plane) + for k,v in pairs(ws.get_reachable_positions(range)) do + if not plane or v.y == minetest.localplayer:get_pos().y -1 then + func(v) + end + end +end + +function ws.get_hud_by_texture(texture) + local def + local i = -1 + repeat + i = i + 1 + def = minetest.localplayer:hud_get(i) + until not def or def.text == texture + if def then + return def + end + def.number=0 + return def +end + +function ws.find_player(name) + for k, v in ipairs(minetest.localplayer.get_nearby_objects(500)) do + if v:get_name() == name then + return v:get_pos(),v + end + end +end + +function ws.display_wp(pos,name) + local ix = #ws.displayed_wps + 1 + ws.displayed_wps[ix] = minetest.localplayer:hud_add({ + hud_elem_type = 'waypoint', + name = name, + text = name, + number = 0x00ff00, + world_pos = pos + }) + return ix +end + +function ws.clear_wp(ix) + table.remove(ws.displayed_wps,ix) +end + +function ws.clear_wps() + for k,v in ipairs(ws.displayed_wps) do + minetest.localplayer:hud_remove(v) + table.remove(ws.displayed_wps,k) + end +end + +function ws.register_chatcommand_alias(old, ...) + local def = assert(minetest.registered_chatcommands[old]) + def.name = nil + for i = 1, select('#', ...) do + minetest.register_chatcommand(select(i, ...), table.copy(def)) + end +end + +function ws.round2(num, numDecimalPlaces) + return tonumber(string.format("%." .. (numDecimalPlaces or 0) .. "f", num)) +end + +function ws.pos_to_string(pos) + if type(pos) == 'table' then + pos = minetest.pos_to_string(vector.round(pos)) + end + if type(pos) == 'string' then + return pos + end + return pos +end + +function ws.string_to_pos(pos) + if type(pos) == 'string' then + pos = minetest.string_to_pos(pos) + end + if type(pos) == 'table' then + return vector.round(pos) + end + return pos +end + + + +--ITEMS +function ws.find_item_in_table(items,rnd) + if type(items) == 'string' then + return minetest.find_item(items) + end + if type(items) ~= 'table' then return end + if rnd then items=ws.shuffle(items) end + for i, v in pairs(items) do + local n = minetest.find_item(v) + if n then + return n + end + end + return false +end + +function ws.find_empty(inv) + for i, v in ipairs(inv) do + if v:is_empty() then + return i + end + end + return false +end + +function ws.find_named(inv, name) + if not inv then return -1 end + if not name then return end + for i, v in ipairs(inv) do + if v:get_name():find(name) then + return i + end + end +end + +function ws.itemnameformat(description) + description = description:gsub(string.char(0x1b) .. "%(.@[^)]+%)", "") + description = description:match("([^\n]*)") + return description +end + +function ws.find_nametagged(list, name) + for i, v in ipairs(list) do + if ws.itemnameformat(v:get_description()) == name then + return i + end + end +end + +local hotbar_slot=8 +function ws.to_hotbar(it,hslot) + local tpos=nil + local plinv = minetest.get_inventory("current_player") + if hslot and hslot < 10 then + tpos=hslot + else + for i, v in ipairs(plinv.main) do + if i<10 and v:is_empty() then + tpos = i + break + end + end + end + if tpos == nil then tpos=hotbar_slot end + local mv = InventoryAction("move") + mv:from("current_player", "main", it) + mv:to("current_player", "main", tpos) + mv:apply() + return tpos +end + +function ws.switch_to_item(itname,hslot) + if not minetest.localplayer then return false end + local plinv = minetest.get_inventory("current_player") + for i, v in ipairs(plinv.main) do + if i<10 and v:get_name() == itname then + minetest.localplayer:set_wield_index(i) + return true + end + end + local pos = ws.find_named(plinv.main, itname) + if pos then + minetest.localplayer:set_wield_index(ws.to_hotbar(pos,hslot)) + return true + end + return false +end +function ws.in_inv(itname) + if not minetest.localplayer then return false end + local plinv = minetest.get_inventory("current_player") + local pos = ws.find_named(plinv.main, itname) + if pos then + return true + end +end + +function core.switch_to_item(item) return ws.switch_to_item(item) end + +function ws.switch_inv_or_echest(name,max_count,hslot) + if not minetest.localplayer then return false end + local plinv = minetest.get_inventory("current_player") + if ws.switch_to_item(name) then return true end + + local epos = ws.find_named(plinv.enderchest, name) + if epos then + local tpos + for i, v in ipairs(plinv.main) do + if i < 9 and v:is_empty() then + tpos = i + break + end + end + if not tpos then tpos=hotbar_slot end + + if tpos then + local mv = InventoryAction("move") + mv:from("current_player", "enderchest", epos) + mv:to("current_player", "main", tpos) + if max_count then + mv:set_count(max_count) + end + mv:apply() + minetest.localplayer:set_wield_index(tpos) + return true + end + end + return false +end + +local function posround(n) + return math.floor(n + 0.5) +end + +local function fmt(c) + return tostring(posround(c.x))..","..tostring(posround(c.y))..","..tostring(posround(c.z)) +end + +local function map_pos(value) + if value.x then + return value + else + return {x = value[1], y = value[2], z = value[3]} + end +end + +function ws.invparse(location) + if type(location) == "string" then + if string.match(location, "^[-]?[0-9]+,[-]?[0-9]+,[-]?[0-9]+$") then + return "nodemeta:" .. location + else + return location + end + elseif type(location) == "table" then + return "nodemeta:" .. fmt(map_pos(location)) + end +end + +function ws.invpos(p) + return "nodemeta:"..p.x..","..p.y..","..p.z +end + + +-- TOOLS + + +local function check_tool(stack, node_groups, old_best_time) + local toolcaps = stack:get_tool_capabilities() + if not toolcaps then return end + local best_time = old_best_time + for group, groupdef in pairs(toolcaps.groupcaps) do + local level = node_groups[group] + if level then + local this_time = groupdef.times[level] + if this_time and this_time < best_time then + best_time = this_time + end + end + end + return best_time < old_best_time, best_time +end + +local function find_best_tool(nodename, switch) + local player = minetest.localplayer + local inventory = minetest.get_inventory("current_player") + local node_groups = minetest.get_node_def(nodename).groups + local new_index = player:get_wield_index() + local is_better, best_time = false, math.huge + + is_better, best_time = check_tool(player:get_wielded_item(), node_groups, best_time) + if inventory.hand then + is_better, best_time = check_tool(inventory.hand[1], node_groups, best_time) + end + + for index, stack in ipairs(inventory.main) do + is_better, best_time = check_tool(stack, node_groups, best_time) + if is_better then + new_index = index + end + end + + return new_index,best_time +end + +function ws.get_digtime(nodename) + local idx,tm=find_best_tool(nodename) + return tm +end + +function ws.select_best_tool(pos) + local nd=minetest.get_node_or_nil(pos) + local nodename='air' + if nd then nodename=nd.name end + local t=find_best_tool(nodename) + minetest.localplayer:set_wield_index(ws.to_hotbar(t,hotbar_slot)) + --minetest.localplayer:set_wield_index(find_best_tool(nodename)) +end + +--- COORDS +function ws.coord(x, y, z) + return vector.new(x,y,z) +end +function ws.ordercoord(c) + if c.x == nil then + return {x = c[1], y = c[2], z = c[3]} + else + return c + end +end + +-- x or {x,y,z} or {x=x,y=y,z=z} +function ws.optcoord(x, y, z) + if y and z then + return ws.coord(x, y, z) + else + return ws.ordercoord(x) + end +end +function ws.cadd(c1, c2) + return vector.add(c1,c2) + --return ws.coord(c1.x + c2.x, c1.y + c2.y, c1.z + c2.z) +end + +function ws.relcoord(x, y, z, rpos) + local pos = rpos or minetest.localplayer:get_pos() + pos.y=math.ceil(pos.y) + --math.floor(pos.y) + 0.5 + return ws.cadd(pos, ws.optcoord(x, y, z)) +end + +local function between(x, y, z) -- x is between y and z (inclusive) + return y <= x and x <= z +end + +function ws.getdir(yaw) -- + local rot = yaw or minetest.localplayer:get_yaw() % 360 + if between(rot, 315, 360) or between(rot, 0, 45) then + return "north" + elseif between(rot, 135, 225) then + return "south" + elseif between(rot, 225, 315) then + return "east" + elseif between(rot, 45, 135) then + return "west" + end +end + +function ws.getaxis() + local dir=ws.getdir() + if dir == "north" or dir == "south" then return "z" end + return "x" +end +function ws.setdir(dir) -- + if dir == "north" then + minetest.localplayer:set_yaw(0) + elseif dir == "south" then + minetest.localplayer:set_yaw(180) + elseif dir == "east" then + minetest.localplayer:set_yaw(270) + elseif dir == "west" then + minetest.localplayer:set_yaw(90) + end +end + +function ws.dircoord(f, y, r ,rpos, rdir) + local dir= ws.getdir(rdir) + local coord = ws.optcoord(f, y, r) + local f = coord.x + local y = coord.y + local r = coord.z + local lp= rpos or minetest.localplayer:get_pos() + if dir == "north" then + return ws.relcoord(r, y, f,rpos) + elseif dir == "south" then + return ws.relcoord(-r, y, -f,rpos) + elseif dir == "east" then + return ws.relcoord(f, y, -r,rpos) + elseif dir== "west" then + return ws.relcoord(-f, y, r,rpos) + end + return ws.relcoord(0, 0, 0,rpos) +end + +function ws.get_dimension(pos) + if pos.y > -65 then return "overworld" + elseif pos.y > -8000 then return "void" + elseif pos.y > -27000 then return "end" + elseif pos.y > -28930 then return "void" + elseif pos.y > -31000 then return "nether" + else return "void" + end +end + +function ws.aim(tpos) + local ppos=minetest.localplayer:get_pos() + local dir=vector.direction(ppos,tpos) + local yyaw=0; + local pitch=0; + if dir.x < 0 then + yyaw = math.atan2(-dir.x, dir.z) + (math.pi * 2) + else + yyaw = math.atan2(-dir.x, dir.z) + end + yyaw = ws.round2(math.deg(yyaw),2) + pitch = ws.round2(math.deg(math.asin(-dir.y) * 1),2); + minetest.localplayer:set_yaw(yyaw) + minetest.localplayer:set_pitch(pitch) +end + +function ws.gaim(tpos,v,g) + local v = v or 40 + local g = g or 9.81 + local ppos=minetest.localplayer:get_pos() + local dir=vector.direction(ppos,tpos) + local yyaw=0; + local pitch=0; + if dir.x < 0 then + yyaw = math.atan2(-dir.x, dir.z) + (math.pi * 2) + else + yyaw = math.atan2(-dir.x, dir.z) + end + yyaw = ws.round2(math.deg(yyaw),2) + local y = dir.y + dir.y = 0 + local x = vector.length(dir) + pitch=math.atan(math.pow(v, 2) / (g * x) + math.sqrt(math.pow(v, 4)/(math.pow(g, 2) * math.pow(x, 2)) - 2 * math.pow(v, 2) * y/(g * math.pow(x, 2)) - 1)) + --pitch = ws.round2(math.deg(math.asin(-dir.y) * 1),2); + minetest.localplayer:set_yaw(yyaw) + minetest.localplayer:set_pitch(math.deg(pitch)) +end + +function ws.buildable_to(pos) + local node=minetest.get_node_or_nil(pos) + if node then + return minetest.get_node_def(node.name).buildable_to + end +end + +function ws.tplace(p,n,stay) + if not p then return end + if n then ws.switch_to_item(n) end + local opos=ws.dircoord(0,0,0) + local tpos=vector.add(p,vector.new(0,1,0)) + minetest.localplayer:set_pos(tpos) + ws.place(p,{n}) + if not stay then + minetest.after(0.1,function() + minetest.localplayer:set_pos(opos) + end) + end +end + +minetest.register_chatcommand("tplace", { + description = "tp-place", + param = "Y", + func = function(param) + return ws.tplace(minetest.string_to_pos(param)) + end +}) + +function ws.ytp(param) + local y=tonumber(param) + local lp=ws.dircoord(0,0,0) + if lp.y < y + 50 then return false,"Can't TP up." end + if y < -30912 then return false,"Don't TP into the void lol." end + minetest.localplayer:set_pos(vector.new(lp.x,y,lp.z)) +end + +local function tablearg(arg) + local tb={} + if type(arg) == 'string' then + tb={arg} + elseif type(arg) == 'table' then + tb=arg + elseif type(arg) == 'function' then + tb=arg() + end + return tb +end + +function ws.isnode(pos,arg)--arg is either an itemstring, a table of itemstrings or a function returning an itemstring + local nodename=tablearg(arg) + local nd=minetest.get_node_or_nil(pos) + if nd and nodename and ws.in_list(nd.name,nodename) then + return true + end +end + +function ws.can_place_at(pos) + local node = minetest.get_node_or_nil(pos) + return (node and (node.name == "air" or node.name=="mcl_core:water_source" or node.name=="mcl_core:water_flowing" or node.name=="mcl_core:lava_source" or node.name=="mcl_core:lava_flowing" or minetest.get_node_def(node.name).buildable_to)) +end + +-- should check if wield is placeable +-- minetest.get_node(wielded:get_name()) ~= nil should probably work +-- otherwise it equips armor and eats food +function ws.can_place_wielded_at(pos) + local wield_empty = minetest.localplayer:get_wielded_item():is_empty() + return not wield_empty and ws.can_place_at(pos) +end + + +function ws.find_any_swap(items,hslot) + hslot=hslot or 8 + for i, v in ipairs(items) do + local n = minetest.find_item(v) + if n then + ws.switch_to_item(v,hslot) + return true + end + end + return false +end + + +-- swaps to any of the items and places if need be +-- returns true if placed and in inventory or already there, false otherwise + +local lastact=0 +local lastplc=0 +local lastdig=0 +local actint=10 +function ws.place(pos,items,hslot, place) + --if nodes_this_tick > 8 then return end + --nodes_this_tick = nodes_this_tick + 1 + --if not inside_constraints(pos) then return end + if not pos then return end + if not ws.can_place_at(pos) then return end + items=tablearg(items) + + place = place or minetest.place_node + + local node = minetest.get_node_or_nil(pos) + if not node then return end + -- already there + if ws.isnode(pos,items) then + return true + else + if ws.find_any_swap(items,hslot) then + place(pos) + return true + end + end +end + +function ws.place_if_able(pos) + if not pos then return end + if not inside_constraints(pos) then return end + if ws.can_place_wielded_at(pos) then + minetest.place_node(pos) + end +end + +function ws.is_diggable(pos) + if not pos then return false end + local nd=minetest.get_node_or_nil(pos) + if not nd then return false end + local n = minetest.get_node_def(nd.name) + if n and n.diggable then return true end + return false +end + +function ws.dig(pos,condition,autotool) + --if not inside_constraints(pos) then return end + if autotool == nil then autotool = true end + if condition and not condition(pos) then return false end + if not ws.is_diggable(pos) then return end + local nd=minetest.get_node_or_nil(pos) + if nd and minetest.get_node_def(nd.name).diggable then + if autotool then ws.select_best_tool(pos) end + minetest.dig_node(pos) + end + return true +end + +function ws.chunk_loaded() + local ign=minetest.find_nodes_near(ws.dircoord(0,0,0),10,{'ignore'},true) + if #ign == 0 then return true end + return false +end + +function ws.get_near(nodes,range) + range=range or 5 + local nds=minetest.find_nodes_near(ws.dircoord(0,0,0),rang,nodes,true) + if #nds > 0 then return nds end + return false +end + +function ws.is_laggy() + if tps_client and tps_client.ping and tps_client.ping > 1000 then return true end +end + + +function ws.donodes(poss,func,condition) + if ws.is_laggy() then return end + local dn_i=0 + for k,v in pairs(poss) do + if dn_i > 8 then return end + --local nd=minetest.get_node_or_nil(v) + if condition == nil or condition(v) then + func(v) + dn_i = dn_i + 1 + end + end +end + +function ws.dignodes(poss,condition) + local func=function(p) ws.dig(p) end + ws.donodes(poss,func,condition) +end + + +function ws.replace(pos,arg) + arg=tablearg(arg) + local nd=minetest.get_node_or_nil(pos) + if nd and not ws.in_list(nd.name,arg) and ws.buildable_to(pos) then + local tm=ws.get_digtime(nd.name) or 0 + ws.dig(pos) + minetest.after(tm + 0.1,function() + ws.place(pos,arg) + end) + return tm + else + return ws.place(pos,arg) + end +end + +function ws.playeron(p) + local pls=minetest.get_player_names() + for k,v in pairs(pls) do + if v == p then return true end + end + return false +end + + +function ws.between(x, y, z) -- x is between y and z (inclusive) + return y <= x and x <= z +end + + +local wall_pos1={x=-1255,y=6,z=792} +local wall_pos2={x=-1452,y=80,z=981} +local iwall_pos1={x=-1266,y=6,z=802} +local iwall_pos2={x=-1442,y=80,z=971} + +function ws.in_cube(tpos,wpos1,wpos2) + local xmax=wpos2.x + local xmin=wpos1.x + + local ymax=wpos2.y + local ymin=wpos1.y + + local zmax=wpos2.z + local zmin=wpos1.z + if wpos1.x > wpos2.x then + xmax=wpos1.x + xmin=wpos2.x + end + if wpos1.y > wpos2.y then + ymax=wpos1.y + ymin=wpos2.y + end + if wpos1.z > wpos2.z then + zmax=wpos1.z + zmin=wpos2.z + end + if ws.between(tpos.x,xmin,xmax) and ws.between(tpos.y,ymin,ymax) and ws.between(tpos.z,zmin,zmax) then + return true + end + return false +end + +function ws.in_wall(pos) + if ws.in_cube(pos,wall_pos1,wall_pos2) and not in_cube(pos,iwall_pos1,iwall_pos2) then + return true end + return false +end + +function ws.inside_wall(pos) + local p1=iwall_pos1 + local p2=iwall_pos2 + if ws.in_cube(pos,p1,p2) then return true end + return false +end + +function ws.find_closest_reachable_airpocket(pos) + local lp=ws.dircoord(0,0,0) + local nds=minetest.find_nodes_near(lp,5,{'air'}) + local odst=10 + local rt=lp + for k,v in ipairs(nds) do + local dst=vector.distance(pos,v) + if dst < odst then odst=dst rt=v end + end + if odst==10 then return false end + return vector.add(rt,vector.new(0,-1,0)) +end + + +-- DEBUG +local function printwieldedmeta() + ws.dcm(dump(minetest.localplayer:get_wielded_item():get_meta():to_table())) +end +minetest.register_cheat('ItemMeta','Test',printwieldedmeta) +local bpos = { + {x=-1265,y=40,z=802}, + {x=-1265,y=40,z=972}, + {x=-1443,y=40,z=972}, + {x=-1443,y=40,z=802} +} + +local wbtarget = bpos[1] + + +local function between(x, y, z) -- x is between y and z (inclusive) + return y <= x and x <= z +end + +local function mkposvec(vec) + vec.x=vec.x + 30927 + vec.y=vec.y + 30927 + vec.z=vec.z + 30927 + return vec +end + +local function normvec(vec) + vec.x=vec.x - 30927 + vec.y=vec.y - 30927 + vec.z=vec.z - 30927 + return vec +end +local wall_pos1={x=-1254,y=-4,z=791} +local wall_pos2={x=-1454,y=80,z=983} +local iwall_pos1={x=-1264,y=-4,z=801} +local iwall_pos2={x=-1444,y=80,z=973} + +local function in_cube(tpos,wpos1,wpos2) + local xmax=wpos2.x + local xmin=wpos1.x + + local ymax=wpos2.y + local ymin=wpos1.y + + local zmax=wpos2.z + local zmin=wpos1.z + if wpos1.x > wpos2.x then + xmax=wpos1.x + xmin=wpos2.x + end + if wpos1.y > wpos2.y then + ymax=wpos1.y + ymin=wpos2.y + end + if wpos1.z > wpos2.z then + zmax=wpos1.z + zmin=wpos2.z + end + if between(tpos.x,xmin,xmax) and between(tpos.y,ymin,ymax) and between(tpos.z,zmin,zmax) then + return true + end + return false +end + +local function in_wall(pos) + if in_cube(pos,wall_pos1,wall_pos2) and not in_cube(pos,iwall_pos1,iwall_pos2) then + return true end + return false +end + +local function iwall_node(pos) + if pos.y>80 or pos.y < -2 then return false end + local dir=ws.getdir() + if dir == "north" then + if pos.z == 973 and pos.x < -1264 and pos.x > -1444 then + if pos.y % 2 == 0 then + if pos .x % 2 == 0 then + return "mcl_core:obsidian" + else + return "mcl_core:stonebrick" + end + else + if pos .x % 2 == 0 then + return "mcl_core:stonebrick" + else + return "mcl_core:obsidian" + end + end + + end + elseif dir == "east" then + if pos.x == -1264 and pos.z > 801 and pos.z < 973 then + if pos.y % 2 == 0 then + if pos .z % 2 == 0 then + return "mcl_core:stonebrick" + else + return "mcl_core:obsidian" + end + else + if pos .z % 2 == 0 then + return "mcl_core:obsidian" + else + return "mcl_core:stonebrick" + end + end + end + elseif dir == "south" then + if pos.z == 801 and pos.x < -1264 and pos.x > -1444 then + if pos.y % 2 == 0 then + if pos .x % 2 == 0 then + return "mcl_core:obsidian" + else + return "mcl_core:stonebrick" + end + else + if pos .x % 2 == 0 then + return "mcl_core:stonebrick" + else + return "mcl_core:obsidian" + end + end + end + elseif dir == "west" then + if pos.x == -1444 and pos.z > 801 and pos.z < 973 then + if pos.y % 2 == 0 then + if pos .z % 2 == 0 then + return "mcl_core:stonebrick" + else + return "mcl_core:obsidian" + end + else + if pos.z % 2 == 0 then + return "mcl_core:obsidian" + else + return "mcl_core:stonebrick" + end + end + end + end + return false +end + + +local lwltime=0 +scaffold.register_template_scaffold("WallTool", "scaffold_walltool", function(pos) + if os.clock() < lwltime then return end + lwltime=os.clock()+.5 + local lp=minetest.localplayer:get_pos() + local p1=vector.add(lp,{x=5,y=5,z=5}) + local p2=vector.add(lp,{x=-5,y=-5,z=-5}) + local nn=nlist.get_mclnodes() + local cobble='mcl_core:cobble' + table.insert(nn,'air') + --local nds,cnt=minetest.find_nodes_in_area(p1,p2,nn,true) + --local nds=minetest.find_nodes_near_except(lp,5,{cobble}) + local i=1 + local nds=minetest.find_nodes_near(lp,10,{'air'}) + for k,vv in pairs(nds) do + if i > 8 then return end + local iwn=iwall_node(vv) + local nd=minetest.get_node_or_nil(vv) + if vv and in_wall(vv) then + i = i + 1 + if nd and nd.name ~= 'air' then + scaffold.dig(vv) + else + ws.place(vv,{cobble}) + end + elseif vv and iwn then + i = i + 1 + if nd and nd.name ~= iwn and nd.name ~= 'air' then + ws.dig(vv) + else + ws.place(vv,iwn) + end + end + end +end) + +ws.rg('AWalltool','Bots','scaffold_awalltool',function() + --local nds=minetest.find_nodes_near_except(ws.dircoord(0,0,0),6,{'mcl_core:cobble'}) + local nds=minetest.find_nodes_near(ws.dircoord(0,0,0),7,{'air'}) + local rt=true + for k,v in ipairs(nds) do + if in_wall(v) then + rt=false + end + end + minetest.settings:set_bool('continuous_forward',rt) +end,function() end, function()end, {'scaffold_walltool','afly_snap'}) + +local function find_closest_safe(pos) + local odst=500 + local res=pos + local poss=minetest.find_nodes_near(pos,10,{'air'}) + for k,v in ipairs(poss) do + local dst=vector.distance(pos,v) + if not in_wall(v) and dst < odst then + odst=dst + res=vector.add(v,vector.new(0,-1,0)) + end + end + return res +end + +local function wallbot_find(range) + local lp=ws.dircoord(0,0,0) + local nds=minetest.find_nodes_near_except(lp,range,{'mcl_core:cobble'}) + local odst=500 + local tg=nil + res=nil + for k,v in ipairs(nds) do + if in_wall(v) then + local dst=vector.distance(lp,v) + if odst > dst then odst=dst res=v end + end + end + if res then find_closest_safe(res) + else return false end +end + +local function random_iwall() + math.randomseed(os.clock()) + local x=math.random(0,90) + local y=math.random(10,70) + local z=math.random(0,90) + local rpos={x=-1254 - x,y=y,z=791 + z} +end + + +local wallbot_state=0 +local wallbot_target=nil +ws.rg('WallBot','Bots','scaffold_wallbot',function() + local nds=nil + if not wallbot_target then wallbot_state=0 end + if wallbot_state == 0 then --searching + wallbot_target=wallbot_find(79) + if wallbot_target then + wallbot_state=2 + else + wallbot_target=random_iwall() + wallbot_state=1 + end + elseif wallbot_state == 1 then --flying - searching + if incremental_tp.tpactive then return end + if vector.distance(ws.dircoord(0,0,0),wallbot_target) < 10 then + minetest.after(5,function() wallbot_state=0 end) + return + end + incremental_tp.tp(wallbot_target,1,1) + elseif wallbot_state == 2 then --flying - target + if incremental_tp.tpactive then return end + if vector.distance(ws.dircoord(0,0,0),wallbot_target) < 10 then + wallbot_state=3 + return + end + incremental_tp.tp(wallbot_target,1,1) + elseif wallbot_state == 3 then --filling + if not wallbot_find(10) then + wallbot_state=0 + return + end + else + wallbot_state=0 + end + +end,function() wallbot_state=0 end,function() end,{'scaffold_walltool'}) diff --git a/supernotes/init.lua b/supernotes/init.lua new file mode 100644 index 0000000..00613fc --- /dev/null +++ b/supernotes/init.lua @@ -0,0 +1,177 @@ +-- CC0/Unlicense system32 2020 + +-- WIKI FORMATTING +-- [[link]] -> link to 'link' +-- [[link|Wow]] -> link to 'link' with the display value of 'Wow' +-- {{coords|X Y Z}} -> coordinates for X Y Z that adds an autofly waypoint at that coordinate when clicked + +--[[ +TODO/wishlist: + + maybe extract text between these and put them somewhere, replacing them with a temp marker which is replaced with the original text once formatting is done + +headers + +maybe make the page list a wiki page? + +page history +--]] + +local storage = minetest.get_mod_storage() + +local start_page = "Welcome to supernotes! Click Edit/Save to edit or save a page, Pages to list all pages, and type in that Page bar to go to a page." + +local sidebar_page = "Sidebar" + +local example_json = '{"sidebar": "' .. sidebar_page .. '", "start": "' .. start_page .. '"}' + +local pages = minetest.parse_json(storage:get("supernotes_pages") or example_json) + +local function save() + storage:set_string("supernotes_pages", minetest.write_json(pages)) +end + + +local formspec_base = [[ +size[10,9] + +field[0.3,0.5;6.5,1;page;Page/search;NOTE_TITLE] +button[6.6,0.2;1.5,1;go;Go] +button[8.2,0.2;1.5,1;pages;Pages] + +hypertext[0.3,1.7;2,7;sidebar;SIDEBAR_TEXT] +ARTICLE_AREA + +button_exit[0,8.2;2,1;quit;Quit] +button[7.9,8.2;2,1;edit;Edit/Save] + +field_close_on_enter[page;false] +]] + +local formspec_article = formspec_base:gsub("ARTICLE_AREA", "hypertext[2.5,1.7;7,7;article;ARTICLE_TEXT]") + +local formspec_edit = formspec_base:gsub("ARTICLE_AREA", "textarea[2.5,1.7;7,7;article;;ARTICLE_TEXT]") + +local formspec_pagelist = formspec_base:gsub("ARTICLE_AREA", "textlist[2.5,1.7;7,6.3;article;ARTICLES]") + +local function format_coords(text) + local num = "([-]?%d+.?%d*)" + local space = "%s+" + text = text:gsub("{{coords|" .. num .. space .. num .. space .. num .. "}}", "%1 %2 %3") + return text +end + +local function format_wikilinks(text) + local tmatch = "[^%[%|%]]" + text = text:gsub("%[%[(" .. tmatch .. "-)|(" .. tmatch .. "-)%]%]", "%2") + text = text:gsub("%[%[(" .. tmatch .. "-)%]%]", "%1") + return text +end + +local function wikiformat(text) + text = format_coords(text) + text = format_wikilinks(text) + return text +end + + +local function getkeys(t) + local out = {} + for k, v in pairs(t) do + out[#out + 1] = k + end + return out +end + +local function startswith(s1, s2) + return string.sub(s1, 1, string.len(s2)) == s2 +end + +local editing = false +local current_page = nil + +local function show_page(page, mode) + page = page or "" + current_page = page + + local fs = "" + if mode == nil then + fs = formspec_article + fs = fs:gsub("ARTICLE_TEXT", wikiformat(pages[page] or "Page empty. Click Edit to create.")) + elseif mode == "list" then + fs = formspec_pagelist + fs = fs:gsub("ARTICLES", table.concat(getkeys(pages), ",")) + elseif mode == "edit" then + editing = true + fs = formspec_edit + fs = fs:gsub("ARTICLE_TEXT", minetest.formspec_escape(pages[page] or "")) + end + + fs = fs:gsub("NOTE_TITLE", page) + fs = fs:gsub("SIDEBAR_TEXT", wikiformat(pages.sidebar or "")) + + minetest.show_formspec("supernotes", fs) +end + +local function linkfield(field) + if startswith(field or "", "action:") then + local action = field:match("action:(.*)") + local wikilink = action:match("link_(.*)") + local x, y, z = action:match("coords_([^_]+)_([^_]+)_([^_]+)") + + if wikilink then + show_page(wikilink) + return true + elseif x and y and z then + if autofly then + -- maybe use autofly.set_hud_wp instead? + autofly.set_waypoint(x .. "," .. y .. "," .. z, "Supernotes: " .. x .. "," .. y .. "," .. z) + autofly.display_formspec() + end + return true + end + end +end + + +minetest.register_on_formspec_input(function(formspec, fields) + if formspec == "supernotes" then + -- go to page + if fields.page ~= "" and (fields.go or fields.page ~= current_page) then + show_page(fields.page) + -- enter edit mode + elseif fields.edit and not editing then + show_page(fields.page, "edit") + -- exit edit mode + elseif fields.edit and editing then + editing = false + pages[fields.page] = fields.article + save() + show_page(fields.page) + -- list pages + elseif fields.pages then + show_page(fields.page, "list") + -- make sure to exit editing mode if exiting + elseif fields.quit then + editing = false + -- process links for article and sidebar + elseif linkfield(fields.article) then + elseif linkfield(fields.sidebar) then + -- list link clicking + elseif startswith(fields.article, "CHG") then + local target = fields.article:match("CHG:(.*)") + show_page(getkeys(pages)[tonumber(target)]) + end + end +end) + +minetest.register_on_shutdown(save) + +minetest.register_chatcommand("notes", { + params = "", + description = "Open Supernotes at the specified page or last opened page.", + func = function(params) + local page = string.split(params, " ")[1] + show_page(page or current_page or "start") + end +}) diff --git a/supernotes/mod.conf b/supernotes/mod.conf new file mode 100644 index 0000000..2945998 --- /dev/null +++ b/supernotes/mod.conf @@ -0,0 +1,2 @@ +name = supernotes +description = Adds a wiki-like local notetaking system. Integrates with cora's autofly. diff --git a/tchat/init.lua b/tchat/init.lua new file mode 100644 index 0000000..f42d418 --- /dev/null +++ b/tchat/init.lua @@ -0,0 +1,643 @@ +--- +-- coras teamchat .. indev v0.5 +-- +-- adds a team chat for you and a couple friends, also prevents accidental sending of coordinates +-- to say something in teamchat either activate teammode in the dragonfire menu or use .t message +-- +-- supports the Wisp encrypted whisper mod +-- +-- .t to say something in team chat (or regular chat if team mode is on) +-- .tadd to add a team member +-- .tdel to remove +-- .tlist to list team +-- +-- .coords to send a message containing coordinates +-- .mcoord to send a player your current coordinates + + +--[[ +Public methods + +tchat.contains_coords(message) - returns true if the message contains coordinates (2d or 3d) + +tchat.send(message) - send a message to teamchat, returns true if sent, nil if not +tchat.send_conditional(message, inverse?) - send a message to teamchat or regular chat, returns true if sent to teamchat, false if main chat, nil if not sent +tchat.send_coords(message) - send a message containing coordinates, true if sent, nil if not + +tchat.whisper_coords(player) - DM current coords to a player + +tchat.chat_clear() - clear chat widget +tchat.chat_set([]) - set chat widget +tchat.chat_append([] or message) - append to chat widget + +tchat.team_add_player(player) - add player to team list +tchat.team_remove_player(player) - remove player from team list +tchat.team_clear() - clear team list +tchat.team_set([]) - set team list + + +Public properties + +tchat.chat: last few chat messages +tchat.team: team list +tchat.team_online: online team list +tchat.players: currently online players + + +Settings + +bool tchat_view_chat - if the team chat is shown +bool tchat_view_team_list - if the team list is shown +bool tchat_view_player_list - if the player list is shown +bool tchat_team_mode - if team mode is on + +bool tchat_colorize_team - if true, team list will show all team members colored for who is online +bool tchat_use_wisp - if true, encrypt all messages using Wisp + +str tchat_prefix_message - prefix for teamchat messages +str tchat_prefix_receive - prefix for received messages +str tchat_prefix_self - prefix for self sent messages +str tchat_prefix_send - prefix for sent messages + +str tchat_blacklist - comma separated list of accounts that cannot send team chat messages (useful for secret alts) + +num tchat_chat_length - chat length (messages, not lines) +num tchat_chat_width - chat width (columns) +--]] + + +--- +-- settings + +local function init_settings(setting_table) + for k, v in pairs(setting_table) do + if minetest.settings:get(k) == nil then + if type(v) == "boolean" then + minetest.settings:set_bool(k, v) + else + minetest.settings:set(k, v) + end + end + end +end + +init_settings({ + tchat_view_chat = false, + tchat_view_team_list = true, + tchat_view_player_list = true, + tchat_team_mode = false, + + tchat_colorize_team = false, + + tchat_prefix_message = "TCHAT", + tchat_prefix_receive = "From", + tchat_prefix_self = "To Yourself", + tchat_prefix_send = "To", + + tchat_use_wisp = false, + + tchat_hide_sent = true, + tchat_blacklist = "", + + tchat_chat_length = 6, + tchat_chat_width = 80 +}) + + +--- +-- globals + +tchat = {} + +tchat.team = {} +tchat.team_online = {} +tchat.chat = {} +tchat.players = {} + +-- used for logs +local server_info = minetest.get_server_info() +local server_id = server_info.address .. ':' .. server_info.port + +local max_total_chat_length = 1024 + +local player_list_epoch = 0 + +local message_prefix = minetest.settings:get("tchat_prefix_message") +local message_receive = minetest.settings:get("tchat_prefix_receive") +local message_receive_self = minetest.settings:get("tchat_prefix_self") +local message_to = minetest.settings:get("tchat_prefix_send") + +local team_mode = minetest.settings:get_bool("tchat_team_mode") + +local use_wisp = minetest.settings:get_bool("tchat_use_wisp") + +local hide_sent = minetest.settings:get_bool("tchat_hide_sent") +local blacklist = string.split(minetest.settings:get("tchat_blacklist")) + +local chat_length = tonumber(minetest.settings:get("tchat_chat_length")) +local chat_width = tonumber(minetest.settings:get("tchat_chat_width")) + +local storage = minetest.get_mod_storage() + +if storage:get("tchat_team") == nil or storage:get("tchat_team") == "null" then + storage:set_string("tchat_team", "[]") +end + +tchat.team = minetest.parse_json(storage:get_string("tchat_team")) + +-- overrides contains_coords() the next time it runs +local message_confirmed_safe = false + +-- coordinate matching +local pattern = "[-]?%d[.%d]*" +local space = "[,%s]+" +local pattern_three = pattern .. space .. pattern .. space .. pattern +local pattern_two = pattern .. space .. pattern + +local chat_idx +local player_list_idx +local team_list_idx +local chat_str = "" + + +--- +-- private stuff + +local function apply(list, func, filter) + local out = {} + for k, v in ipairs(list) do + if filter(v) then + out[#out + 1] = func(v) + else + out[#out + 1] = v + end + end + return out +end + +local function uniq(list) + local last + local out = {} + for k, v in ipairs(list) do + if last ~= v then + out[#out + 1] = v + end + last = v + end + return out +end + +-- limit a list to the last size elements +local function limit_list(list, size) + local out = {} + for i = math.max(1, #list - size), #list do + out[#out + 1] = list[i] + end + return out +end + +local function in_list(list, value) + for k, v in ipairs(list) do + if v == value then + return true + end + end + return false +end + + +local function get_team_str() + if minetest.settings:get_bool("tchat_colorize_team") then + return table.concat(apply(tchat.team, + function(value) + return minetest.colorize("#00FFFF", value) + end, + function(value) + return in_list(tchat.team_online, value) + end), "\n") + else + return table.concat(tchat.team_online, "\n") + end +end + + +local function display_chat() + return minetest.localplayer:hud_add({ + hud_elem_type = 'text', + name = "Teamchat", + text = "Team Chat\n\n" .. chat_str, + number = 0xEEFFEE, + direction = 0, + position = {x=0.01, y=0.45}, + scale = {x=0.9, y=0.9}, + alignment = {x=1, y=1}, + offset = {x=0, y=0} + }) +end + +local function display_player_list() + return minetest.localplayer:hud_add({ + hud_elem_type = 'text', + name = "Online Players", + text = "Players\n\n" .. table.concat(tchat.players, "\n"), + number = 0xDDFFDD, + direction = 0, + position = {x=0.9, y=0.01}, + alignment = {x=1, y=1}, + offset = {x=0, y=0} + }) +end + +-- should prob have all team members with online ones colored +local function display_team_list() + return minetest.localplayer:hud_add({ + hud_elem_type = 'text', + name = "Team", + text = "Team\n\n" .. get_team_str(), + number = 0x00FF00, + direction = 0, + position = {x=0.8, y=0.01}, + alignment = {x=1, y=1}, + offset = {x=0, y=0} + }) +end + +local function auto_display(idx, setting, func) + if minetest.settings:get_bool(setting) then + if not idx then + return func() + end + else + if idx then + minetest.localplayer:hud_remove(idx) + return nil + end + end + return idx +end + +local function auto_update(idx, text) + if idx ~= nil then + minetest.localplayer:hud_change(idx, "text", text) + end +end + +local function update_team_online() + tchat.team_online = {} + for k, v in ipairs(tchat.players) do + if in_list(tchat.team, v) then + tchat.team_online[#tchat.team_online + 1] = v + end + end +end + +local function update_chat_str() + chat_str = "" + for k, v in ipairs(limit_list(tchat.chat, chat_length - 1)) do + chat_str = chat_str .. "\n" .. minetest.wrap_text(v, chat_width) + end + chat_str = table.concat(limit_list(string.split(chat_str, "\n"), chat_length - 1), "\n") + + -- update chat (do it here so external mods can add to the chat) + auto_update(chat_idx, "Team Chat\n\n" .. chat_str) +end + +local function team_add_self() + tchat.team_add_player(minetest.localplayer:get_name()) +end + + +--- +-- public interface + + +function tchat.contains_coords(message) + if (not message_confirmed_safe and (message:find(pattern_three) or message:find(pattern_two))) then + return true + end + return false +end + + +local function dm(player, message) + if wisp == nil or not use_wisp then + minetest.send_chat_message("/msg " .. player .." " .. message) + else + wisp.send(player, message, true) + end +end + +-- send +function tchat.send(message, force_coords, force_commands) + if (tchat.contains_coords(message) and not force_coords) or in_list(blacklist, minetest.localplayer:get_name()) then + return + end + + if message:sub(1,1) == "/" and not force_commands then + minetest.display_chat_message("A /command was scheduled to be sent to team chat but wasn't sent.") + return + end + + local me = minetest.localplayer:get_name() + + if not in_list(tchat.team, minetest.localplayer:get_name()) then + team_add_self() + end + + update_team_online() + + local prepend = "" + if use_wisp then + prepend = "E " + end + + tchat.chat_append(prepend .. me .. ": " .. message) + + for k, p in ipairs(tchat.team_online) do + if p ~= me then + dm(p, message_prefix .. " " .. message) + end + end + return true +end + +function tchat.send_conditional(message, inverse, force_coords) + if tchat.contains_coords(message) and not force_coords then + return + end + + team_mode = minetest.settings:get_bool("tchat_team_mode") + + local tm = team_mode + if inverse then + tm = not team_mode + end + + if tm then + tchat.send(message) + return true + else + minetest.send_chat_message(message) + return false + end +end + +function tchat.send_coords(message) + message_confirmed_safe = true + local ret = tchat.send_conditional(message) + message_confirmed_safe = false + return ret +end + + +function tchat.whisper_coords(player) + if player == "" then + return + end + local coords = minetest.pos_to_string(vector.round(minetest.localplayer:get_pos())) + minetest.run_server_chatcommand("w", param .. " " .. coords) +end + + +-- chat +local function autoclear_chat() + if #tchat.chat > max_total_chat_length then + tchat = limit_list(tchat.chat, max_chat_total_length) + end +end + +function tchat.chat_clear() + tchat.chat = {} + update_chat_str() +end + +function tchat.chat_set(message_list) + chat = message_list + autoclear_chat() + update_chat_str() +end + +function tchat.chat_append(message) + tchat.chat[#tchat.chat + 1] = message + autoclear_chat() + + minetest.log("action", "[tchat] " .. minetest.localplayer:get_name() .. "@" .. server_id .. " " .. message) + + update_chat_str() + + -- popup chat if its closed + minetest.settings:set_bool("tchat_view_chat", true) + chat_idx = auto_display(chat_idx, "tchat_view_chat", display_chat) +end + + +local function team_save() + storage:set_string("tchat_team" , minetest.write_json(tchat.team)) +end + +-- team +function tchat.team_add_player(player) + if not in_list(tchat.team, player) then + tchat.team[#tchat.team + 1] = player + update_team_online() + team_save() + end +end + +function tchat.team_remove_player(player) + local out = {} + for k, v in ipairs(tchat.team) do + if v ~= player then + out[#out + 1] = v + end + end + tchat.team = out + team_save() +end + +function tchat.team_clear() + tchat.team = {} + team_save() +end + +function tchat.team_set(player_list) + tchat.team = player_list + team_save() +end + + +--- +-- callbacks + +minetest.register_on_sending_chat_message(function(message) + if tchat.contains_coords(message) then + minetest.display_chat_message("Message contained coordinates, be careful.") + return true + end + + team_mode = minetest.settings:get_bool("tchat_team_mode") + + if not team_mode then + return + end + + tchat.send(message) + return true +end) + + +local function message_sent(message) + return message == "Message sent." +end + +local function clean_message(message) + -- dirty, strips out legitimate uses of the prefix + message = message:gsub(message_prefix, "") + message = message:gsub("^" .. message_receive, "") + message = message:gsub("^" .. message_receive_self, minetest.localplayer:get_name()) + + message = message:gsub(": ", ": ") + message = message:match("^%s*(.-)%s*$") + + return message +end + +-- greedily be the first in the receiving list (prob doesnt always work) +table.insert(minetest.registered_on_receiving_chat_message, 1, function(message) + if hide_sent and message_sent(message) then + return true + end + + -- bit dirty, doesnt check the prefix position + if not message:find(message_prefix) then + return + end + + local player = message:match(message_receive .. " (.+): " .. message_prefix) + + local from_self = message:sub(1, message_receive_self:len()) == message_receive_self + local received = message:sub(1, message_receive:len()) == message_receive + local sent = message:sub(1, message_to:len()) == message_to + + if sent and not from_self then + return true + end + + if not from_self and not in_list(tchat.team_online, player) then + return + end + + -- add to chat list + if from_self or received then + tchat.chat_append(clean_message(message)) + return true + end +end) + +if wisp ~= nil then + wisp.register_on_receive_split(function(player, message) + if message:find(message_prefix) then + tchat.chat_append("E " .. player .. ": " .. clean_message(message)) + return true + end + end) +end + +minetest.register_globalstep(function() + -- update data + if player_list_epoch < os.time() + 2 then + -- update players, remove duplicates + tchat.players = minetest.get_player_names() + table.sort(tchat.players) + tchat.players = uniq(tchat.players) + + update_team_online() + + -- update HUD + auto_update(player_list_idx, "Players\n\n" .. table.concat(tchat.players, "\n")) + auto_update(team_list_idx, "Team\n\n" .. get_team_str()) + + player_list_epoch = os.time() + end + + -- display (if we need to) + if minetest.localplayer then + chat_idx = auto_display(chat_idx, "tchat_view_chat", display_chat) + player_list_idx = auto_display(player_list_idx, "tchat_view_player_list", display_player_list) + team_list_idx = auto_display(team_list_idx, "tchat_view_team_list", display_team_list) + end +end) + + +--- +-- command/cheat interface + +minetest.register_chatcommand("t", { + params = "", + description = "Send a message to your team chat, or regular chat if team mode is on.", + func = function(message) + if tchat.contains_coords(message) then + minetest.display_chat_message("Message contained coordinates, be careful.") + return + end + tchat.send_conditional(message, true) + end +}) +minetest.register_chatcommand("tcoords", { + params = "", + description = "Send a message containing coordinates to teamchat.", + func = function(message) + tchat.send(message, true) + end +}) +minetest.register_chatcommand("tlist", { + description = "List your team.", + func = function(param) + minetest.display_chat_message(table.concat(tchat.team, ", ")) + end +}) +minetest.register_chatcommand("tadd", { + params = "", + description = "Add player to your team.", + func = tchat.team_add_player +}) +minetest.register_chatcommand("tdel", { + params = "", + description = "Remove player from your team.", + func = tchat.team_remove_player +}) +minetest.register_chatcommand("tclear", { + description = "Clear team list.", + func = tchat.team_clear +}) + +minetest.register_chatcommand("tchat_clear", { + description = "Clear team chat widget.", + func = tchat.chat_clear +}) + +minetest.register_chatcommand("coords", { + params = "", + description = "Send message containing coordinates.", + func = tchat.send_coords +}) +minetest.register_chatcommand("mcoord", { + params = "", + description = "Whisper current coordinates to player.", + func = tchat.whisper_coords +}) + + +-- this fallbacks to showing everything if the cheat menu is unavailable +-- use advanced settings instead :] +if (_G["minetest"]["register_cheat"] == nil) then + minetest.settings:set_bool('tchat_team_mode', true) + minetest.settings:set_bool('tchat_view_team_list', true) + minetest.settings:set_bool('tchat_view_player_list', true) + minetest.settings:set_bool('tchat_view_chat', true) +else + minetest.register_cheat("Teamchat Mode", "Chat", "tchat_team_mode") + minetest.register_cheat("Show Team List", "Chat", "tchat_view_team_list") + minetest.register_cheat("Show Player List", "Chat", "tchat_view_player_list") + minetest.register_cheat("Show Teamchat", "Chat", "tchat_view_chat") +end diff --git a/tchat/mod.conf b/tchat/mod.conf new file mode 100644 index 0000000..23f4ada --- /dev/null +++ b/tchat/mod.conf @@ -0,0 +1,3 @@ +name = tchat +author = cora, system32 +description = Adds a team chat and prevents accidental sending of coordinates. Supports Wisp for encrypting messages. diff --git a/tchat/settingtypes.txt b/tchat/settingtypes.txt new file mode 100644 index 0000000..eb4c093 --- /dev/null +++ b/tchat/settingtypes.txt @@ -0,0 +1,18 @@ +tchat_view_chat (Show team chat) bool false +tchat_view_team_list (Show team list) bool true +tchat_view_player_list (Show player list) bool true +tchat_team_mode (Team mode) bool false + +tchat_colorize_team (Show colorized team list) bool false +tchat_use_wisp (Use Wisp to encrypt outgoing messages) bool false + +tchat_prefix_message (Message Prefix) string TCHAT +tchat_prefix_receive (Received PM starting string) string From +tchat_prefix_self (Received PM starting string to yourself) string To Yourself +tchat_prefix_send (Outgoing PM starting string) string To + +tchat_blacklist (Names that can't use team chat) string +tchat_hide_sent (Hide "Message sent." server messages) bool true + +tchat_chat_length (Maximum team chat messages) int 6 +tchat_chat_width (Team chat width in columns) int 80 diff --git a/turtle/init.lua b/turtle/init.lua new file mode 100644 index 0000000..641a2bd --- /dev/null +++ b/turtle/init.lua @@ -0,0 +1,518 @@ +-- CC0/Unlicense Emilia 2020 + +turtle = {} + +local mod_prefix = minetest.get_modpath(minetest.get_current_modname()) +tlang = dofile(mod_prefix .. "/tlang.lua") + +function turtle.coord(x, y, z) + return {x = x, y = y, z = z} +end + +turtle.pos1 = turtle.coord(0, 0, 0) +turtle.pos2 = turtle.coord(0, 0, 0) + +local function format_coord(c) + return tostring(c.x) .. " " .. tostring(c.y) .. " " .. tostring(c.z) +end + +local function parse_coord(c) +end + +-- can include ~ + - along with num and , +local function parse_relative_coord(c) +end + +function turtle.ordercoord(c) + if c.x == nil then + return {x = c[1], y = c[2], z = c[3]} + else + return c + end +end + +-- x or {x,y,z} or {x=x,y=y,z=z} +function turtle.optcoord(x, y, z) + if y and z then + return turtle.coord(x, y, z) + else + return turtle.ordercoord(x) + end +end + +-- swap x and y if x > y +local function swapg(x, y) + if x > y then + return y, x + else + return x, y + end +end + +-- swaps coordinates around such that (matching ords of) c1 < c2 and the overall cuboid is the same shape +function turtle.rectify(c1, c2) + c1.x, c2.x = swapg(c1.x, c2.x) + c1.y, c2.y = swapg(c1.y, c2.y) + c1.z, c2.z = swapg(c1.z, c2.z) + return c1, c2 +end + +-- converts a coordinate to a system where 0,0 is the southwestern corner of the world +function turtle.zeroidx(c) + local side = 30912 + return turtle.coord(c.x + side, c.y + side, c.z + side) +end + +-- swaps coords and subtracts such that c1 == {0, 0, 0} and c2 is the distance from c1 +-- returns rectified c1/c2 and the relativized version +function turtle.relativize(c1, c2) + c1, c2 = turtle.rectify(c1, c2) + + local c1z = turtle.zeroidx(c1) + local c2z = turtle.zeroidx(c2) + + local rel = turtle.coord(c2z.x - c1z.x, c2z.y - c1z.y, c2z.z - c1z.z) + + return c1, rel +end + + +-- get the inventory index of the best tool to mine x, y, z +-- returns a wield index, which starts at 0 +function turtle.get_best_tool_index(x, y, z) + local node = minetest.get_node_or_nil(turtle.optcoord(x, y, z)) + if not node then + return + end + + local nodecaps = minetest.get_node_def(node.name).groups + + local idx = minetest.localplayer:get_wield_index() + local best = math.huge + + for i, v in ipairs(minetest.get_inventory("current_player").main) do + for gk, gv in pairs(v:get_tool_capabilities().groupcaps) do + local level = nodecaps[gk] + if level and gv.times[level] < best then + idx = i + best = gv.times[level] + end + end + end + + return idx +end + +-- switch to the fastest tool to mine x, y, z +function turtle.switch_best(x, y, z) + local prev = minetest.localplayer:get_wield_index() + + local index = turtle.get_best_tool_index(x, y, z) + + if prev ~= index then + minetest.localplayer:set_wield_index(index) + end +end + + +function turtle.mine(x, y, z) + turtle.switch_best(x, y, z) + minetest.dig_node(turtle.optcoord(x, y, z)) +end + +function turtle.place(x, y, z) + minetest.place_node(turtle.optcoord(x, y, z)) +end + +function turtle.cadd(c1, c2) + return turtle.coord(c1.x + c2.x, c1.y + c2.y, c1.z + c2.z) +end + +function turtle.relcoord(x, y, z) + local pos = minetest.localplayer:get_pos() + if pos.y > -5000 then pos.y=pos.y-1 end + return turtle.cadd(pos, turtle.optcoord(x, y, z)) +end + +local function between(x, y, z) -- x is between y and z (inclusive) + return y <= x and x <= z +end + +function turtle.getdir() -- + local rot = minetest.localplayer:get_yaw() % 360 + if between(rot, 315, 360) or between(rot, 0, 45) then + return "north" + elseif between(rot, 135, 225) then + return "south" + elseif between(rot, 225, 315) then + return "east" + elseif between(rot, 45, 135) then + return "west" + end +end +function turtle.setdir(dir) -- + if dir == "north" then + minetest.localplayer:set_yaw(0) + elseif dir == "south" then + minetest.localplayer:set_yaw(180) + elseif dir == "east" then + minetest.localplayer:set_yaw(270) + elseif dir == "west" then + minetest.localplayer:set_yaw(90) + end +end + +function turtle.dircoord(f, y, r) + local dir=turtle.getdir() + local coord = turtle.optcoord(f, y, r) + local f = coord.x + local y = coord.y + local r = coord.z + local lp=minetest.localplayer:get_pos() + if dir == "north" then + return turtle.relcoord(r, y, f) + elseif dir == "south" then + return turtle.relcoord(-r, y, -f) + elseif dir == "east" then + return turtle.relcoord(f, y, -r) + elseif dir== "west" then + return turtle.relcoord(-f, y, r) + end + + return turtle.relcoord(0, 0, 0) +end + +function turtle.move(x, y, z) + minetest.localplayer:set_pos(turtle.optcoord(x, y, z)) +end + +function turtle.advance(amount) + amount = amount or 1 + turtle.move(turtle.dircoord(amount, 0, 0)) +end + +function turtle.descend(amount) + amount = amount or 1 + turtle.move(turtle.relcoord(0, -amount, 0)) +end + +function turtle.rotate_abs(deg) + minetest.localplayer:set_yaw(deg) +end + +function turtle.rotate(deg) + local prev = minetest.localplayer:get_yaw() + minetest.localplayer:set_yaw((prev + deg) % 360) +end + +function turtle.rotate_right(deg) + deg = deg or 90 + turtle.rotate(-deg) +end + +function turtle.rotate_left(deg) + deg = deg or 90 + turtle.rotate(deg) +end + +function turtle.isblock(block, x, y, z) + local node = minetest.get_node_or_nil(turtle.optcoord(x, y, z)) + return node ~= nil and block == node.name +end + +function turtle.checkmine(x, y, z) + while true do + turtle.mine(x, y, z) + busysleep(0.125) + -- i hate lua + minetest.log(tostring(turtle.isblock("air", x, y, z))) + if turtle.isblock("air", x, y, z) then + break + end + end +end + +function turtle.tp(coords) + minetest.localplayer:set_pos(coords) +end + +function turtle.moveto(x, y, z) + turtle.tp(turtle.optcoord(x, y, z)) +end + +function turtle.linemine(distance, func) + for i = 1, distance do + turtle.checkmine(turtle.dircoord(1, 1, 0)) + turtle.checkmine(turtle.dircoord(1, 0, 0)) + turtle.advance() + + if func then + func() + end + end +end + + +local function left_or_right(left) + if left then + turtle.rotate_left() + else + turtle.rotate_right() + end +end + + +local function quarry_clear_liquids() + -- puts blocks in front, both sides, and two below where they are liquid + -- it does all this one step ahead so no spillage may occur +end + + +-- needs to check for liquids (would need to be done in linemine) +function turtle.quarry(cstart, cend) + -- get a nice cuboid + cstart, cend = turtle.rectify(cstart, cend) + local start, relend = turtle.relativize(cstart, cend) + + -- makes it start at the top rather than the bottom + cend.y, cstart.y = swapg(cend.y, cstart.y) + + -- go to the start + turtle.moveto(turtle.cadd(cstart, turtle.coord(0, 1, 0))) + turtle.rotate_abs(0) + + -- main loop (zig zag pattern) + for height = 0, math.floor(relend.y / 2) do + -- go down two blocks + turtle.mine(turtle.relcoord(0, -1, 0)) + turtle.mine(turtle.relcoord(0, -2, 0)) + turtle.descend(2) + + for width = 0, relend.x do + -- swaps left/right rotations each layer and zig zag + local leftiness = ((height + width + 1) % 2) == 0 + + -- actually mine + turtle.linemine(relend.z) -- maybe relend.z to make the end inclusive? + left_or_right(leftiness) + -- dont rotate at the end of the layer + if width ~= relend.x then + turtle.linemine(1) + left_or_right(leftiness) + end + end + + -- flip around to start again on the next layer + turtle.rotate(180) + end +end + + +minetest.register_chatcommand("quarry", { + func = function() + turtle.quarry({x = -60, y = 1, z = -60}, {x = -40, y = -5, z = -40}) + end +}) + +turtle.builtins = {} + +function turtle.builtins.mine(state) + +end + +function turtle.builtins.advance(state) + +end + +function turtle.builtins.descend(state) + +end + +function turtle.builtins.v3add(state) + +end + +function turtle.builtins.rotate(state) + +end + +function turtle.builtins.relativize(state) + +end + +function turtle.builtins.swapg(state) + +end + +function turtle.builtins.rectify(state) + +end + +local quarry_tlang = [[ +# turtle.builtins: mine advance v3add descend rotate relativize swapg rectify +# tlang operators: // + +################################ +# Mine ahead length nodes (including head and feet) +{ 0 `length args +#### + { + i length == {break} if + + [1 1 0] dircoord mine + [1 0 0] dircoord mine + 1 wait + advance + } `i forever +} `linemine = + + +################################ +# Mine the cuboid defined by start and end +{ 0 `start `end args +#### + rectify `start = `end = + start end relativize + + start end swapg_y `relstart = `relend = + + start [0 1 0] v3add moveto + 0 rotate_abs + + relend.y 2 // `yend = + + { + height yend > {break} if + + [0 -1 0] dircoord mine + [0 -2 0] dircoord mine + 2 descend + + { + width relend.x > {break} if + + height width + 1 + 2 % 0 == `leftiness = + + relend.z linemine + leftiness left_or_right + + width relend.x != { + 1 linemine + leftiness left_or_right + } if + } `width forever + + 180 rotate + } `height forever +} `quarry = +]] + + +turtle.states = {} +turtle.states_available = false + +function turtle.schedule(name, state) + if type(name) == "table" then + error("turtle.schedule: first parameter should be the task's name") + return + end + + turtle.states[#turtle.states + 1] = {name = name, state = state} + turtle.states_available = true +end + +function turtle.get_symbolic(name) + local dead = {} + + for i, v in ipairs(turtle.states) do + if i == name or v.name == name then + table.insert(dead, 1, i) + end + end + + return dead +end + +function turtle.kill_symbolic(name) + local dead = turtle.get_symbolic(name) + + for i, v in ipairs(dead) do + table.remove(turtle.states, v) + end +end + +function turtle.pause_symbolic(name) + local dead = turtle.get_symbolic(name) + + for i, v in ipairs(dead) do + turtle.states[v].state.paused = true + end +end + +function turtle.resume_symbolic(name) + local dead = turtle.get_symbolic(name) + + for i, v in ipairs(dead) do + turtle.states[v].state.paused = nil + end +end + +function turtle.run_states(dtime) + if turtle.states_available then + local dead = {} + + for i, v in ipairs(turtle.states) do + local ret = tlang.step(v.state) + if ret ~= true and ret ~= nil then + if type(ret) == "string" then + minetest.display_chat_message("Turtle/tlang ERROR in " .. v.name .. ": " .. ret) + end + table.insert(dead, 1, i) + end + end + + for i, v in ipairs(dead) do + table.remove(turtle.states, v) + end + + turtle.states_available = #turtle.states ~= 0 + end +end + +minetest.register_globalstep(turtle.run_states) + +minetest.register_chatcommand("tlang", { + description = "Run a tlang program.", + params = "", + func = function(params) + local state = tlang.get_state(params) + turtle.schedule("chat_script", state) + end +}) + +minetest.register_chatcommand("tl_list", { + description = "List running tlang states.", + func = function() + for i, v in ipairs(turtle.states) do + minetest.display_chat_message(tostring(i) .. " " .. v.name) + end + end +}) + +minetest.register_chatcommand("tl_kill", { + description = "Kill a tlang state.", + params = "", + func = turtle.kill_symbolic +}) + +minetest.register_chatcommand("tl_pause", { + description = "Pause a tlang state.", + params = "", + func = turtle.pause_symbolic +}) + +minetest.register_chatcommand("tl_resume", { + description = "Resume a tlang state.", + params = "", + func = turtle.resume_symbolic +}) diff --git a/turtle/mod.conf b/turtle/mod.conf new file mode 100644 index 0000000..243d0fd --- /dev/null +++ b/turtle/mod.conf @@ -0,0 +1,3 @@ +name = turtle +author = system32 +description = Adds a system to control the player like a graphics turtle. diff --git a/turtle/tlang.lua b/turtle/tlang.lua new file mode 100644 index 0000000..d3698db --- /dev/null +++ b/turtle/tlang.lua @@ -0,0 +1,263 @@ +-- CC0/Unlicense Emilia 2020 + +local tlang = {} + +local prefix = "" +if minetest ~= nil then + prefix = minetest.get_modpath(minetest.get_current_modname()) .. "/" +end + +local function merge_tables(l1, l2) + local out = {} + + for k, v in pairs(l1) do + out[k] = v + end + + for k, v in pairs(l2) do + out[k] = v + end + + return out +end + +local function load_api_file(file) + loadfile(prefix .. file)(tlang) +end + +load_api_file("tlang_lex.lua") +load_api_file("tlang_parse.lua") +load_api_file("tlang_vm.lua") + + +function tlang.combine_builtins(b1, b2) + return merge_tables(b1, b2) +end + +function tlang.construct_builtins(builtins) + return merge_tables(tlang.builtins, builtins) +end + +-- TODO +--[[ +lexer should include line/character number in symbols +error messages +maps should be able to have out of order number indexes (like [1 2 3 10:"Out of order"]) +map.key accessing syntax + parse as identifier, include . as identifier character, split on . and thats the indexing tree +--]] + +function tlang.run(state) + while true do + local more = tlang.step(state) + if more == true or more == nil then + -- continue along + elseif type(more) == "string" then + print(more) -- error + elseif more == false then + return -- done + else + print("Unknown error, tlang.step returned: " .. tostring(more)) + end + end +end + +function tlang.get_state(code) + local lexed = tlang.lex(code) + local parsed = tlang.parse(lexed) + + return { + locals = {{ + pc = {sg = 1, pos = {"__ast__"}, elem = 1}, + vars = { + __src__ = tlang.value_to_tlang(code), + __lex__ = tlang.value_to_tlang(lexed), + __ast__ = {type = "code", value = parsed} + } + }}, + stack = {}, + code_stack = {}, + builtins = tlang.builtins + } +end + +function tlang.exec(code) + local state = tlang.get_state(code) + tlang.run(state) +end + +function tlang.pretty_pc(pc) + return tostring(pc.sg) .. ";" .. table.concat(pc.pos, ".") .. ";" .. tostring(pc.elem) +end + +function tlang.format_table(t, depth, maxdepth) + depth = depth or 0 + maxdepth = maxdepth or -1 + + if depth == maxdepth then + return "{...}" + end + + local out = {} + out[1] = "{\n" + + for k, v in pairs(t) do + local idx = k + if type(k) == "string" then + idx = '"' .. k .. '"' + elseif type(k) == "table" then + idx = "{...}" + end + + out[#out + 1] = string.rep("\t", depth + 1) .. "[" .. idx .. "] = " + + if type(v) == "table" then + out[#out + 1] = tlang.format_table(v, depth + 1, maxdepth) + elseif type(v) == "string" then + out[#out + 1] = '"' .. v .. '"' + else + out[#out + 1] = tostring(v) + end + + out[#out + 1] = ",\n" + end + + out[#out + 1] = string.rep("\t", depth) .. "}" + return table.concat(out) +end + +function tlang.print_table(t, maxdepth) + print(tlang.format_table(t, nil, maxdepth)) +end + +local function test() + local complex = [[{dup *} `square = + -5.42 square + "Hello, world!" print + [1 2 3 str:"String"] + ]] + + local number = "-4.2123" + + local simple = "{dup *}" + + local map = "[this:2 that:3]" + + local square = [[{dup *} `square = + 5 square print]] + + local square_run = "5 {dup *} run print" + + local comment_test = "'asd' print # 'aft' print" + + local forever_test = [[ + 5 # iteration count + { + dup # duplicate iter count + print # print countdown + -- # decrement + dup 0 == # check if TOS is 0 + {break} if # break if TOS == 0 + } + forever # run loop + ]] + + local local_test = [[ + 'outside' `var = + { + var print # should be 'outside' + 'inside' `var = + var print # should be 'inside' + } run + var print # should be 'inside' + ]] + + local while_test = [[ + 5 `cur = + { + `cur -- + cur + } { + "four times" print + } while + ]] + + local repeat_test = [[ + { + "four times" print + } 4 repeat + { + i print + } 5 `i repeat + ]] + + local stack_test = "5 5 == print" + + local args_test = [[ + { 0 `first `second args + first print + second print + } `test = + 1 2 test + ]] + + local ifelse_test = [[ + { + { + 'if' print + } { + 'else' print + } if + } `ifprint = + + 1 ifprint + 0 ifprint + ]] + + local nest_run = [[ + { + { + 'innermost' print + } run + } run + 'work' print + ]] + + local mapid_test = "this.that.2.here .81..wao.88912" + + local paren_test = "('works' print) 'out' print" + + local mapdot_test = [[ + [1 a:5 b:[a:2 b:3] 3] `a = + 4 `a.a = + a.1 print + a.2 print + a.a print + a.b.b print + ]] + + local stackdot_test = [[ + [a:1 b:2] + .b print + 6 `.a = + .a print + ]] + + local funcfunc_test = [[ + {dup *} `square = + {dup square *} `cube = + 5 cube print + ]] + + local test = funcfunc_test + + --tlang.print_table(tlang.lex(test)) + --tlang.print_table(tlang.parse(tlang.lex(test))) + tlang.exec(test) +end + +if minetest == nil then + test() +end + +return tlang diff --git a/turtle/tlang_lex.lua b/turtle/tlang_lex.lua new file mode 100644 index 0000000..8cc4bd0 --- /dev/null +++ b/turtle/tlang_lex.lua @@ -0,0 +1,345 @@ +-- CC0/Unlicense Emilia 2020 + +local tlang = ... + +local function in_list(value, list) + for k, v in ipairs(list) do + if v == value then + return true + end + end + return false +end + + +-- lex state +--[[ +{ + code = "", + position = int +} +--]] + +-- lex types +--[[ +literal + number + quote + identifier + mapid -- TEMP + string +symbol +code_open +code_close +code_e_open +code_e_close +map_open +map_close +map_relation +--]] + + +-- yeah yeah regex im lazy in this time consuming way shush +local whitespace = {" ", "\t", "\n", "\r", "\v"} +local identifier_start = { + "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", + "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", + "_", "." +} +local identifier_internal = { + "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", + "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", + "_", + "0", "1", "2", "3", "4", "5", "6", "7", "8", "9" +} +local symbol_start = {"!", "-", "+", "=", "&", "*", "/", "^", "%", ">", "<", "?", "~"} +local symbol_values = { + "!", "-", "+", "=", "&", "*", "/", "^", "%", ">", "<", "?", "~" +} +local string_start = {"\"", "'"} +local number_start = {"-", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9"} +local number_values = {"0", "1", "2", "3", "4", "5", "6", "7", "8", "9"} +local escape_values = {n = "\n", r = "\r", v = "\v", t = "\t", ['"'] = '"', ["'"] = "'"} +local symbols = { + "!", "-", "+", "=", "&", "*", "/", "^", "%", ">", "<", "?", "~", + "&&", "||", "==", "!=", ">=", "<=", "--", "++" +} + +local function lex_peek(state) + local out = state.code:sub(state.position, state.position) + if out == "" then + return nil + end + return out +end + +local function lex_next(state) + local value = lex_peek(state) + state.position = state.position + 1 + return value +end + +local function lex_expect(state, chars) + if type(chars) == "string" then + chars = {chars} + end + + local n = lex_next(state) + if in_list(n, chars) then + return n + else + return nil -- ERROR! + end +end + +local function lex_whitespace(state) + while true do + local n = lex_peek(state) + if not in_list(n, whitespace) then + return + end + lex_next(state) + end +end + +local function lex_identifier_raw(state, top) + local identifier = {} + local n = 1 + + while true do + local cur = lex_peek(state) + if in_list(cur, identifier_internal) then + identifier[n] = lex_next(state) + n = n + 1 + elseif cur == "." then + lex_next(state) + local subs = lex_identifier_raw(state) + + if type(subs) == "string" then + subs = {subs} + end + + if n > 1 then + table.insert(subs, 1, table.concat(identifier)) + elseif top then -- TOS .key.key syntax + table.insert(subs, 1, '') + end + + return subs + else + break + end + end + + return {table.concat(identifier)} +end + +local function lex_identifier(state) + local id = lex_identifier_raw(state, true) + return {type = "literal", subtype = "identifier", value = id} +end + +-- `identifier +local function lex_quote(state) + lex_next(state) + local val = lex_identifier(state) + val.subtype = "quote" + return val +end + +local function lex_single_char(state, t, char) + lex_next(state) + return {type = t, value = char} +end + +local function lex_code_open(state) + return lex_single_char(state, "code_open", "{") +end + +local function lex_code_close(state) + return lex_single_char(state, "code_close", "}") +end + +local function lex_code_e_open(state) + return lex_single_char(state, "code_e_open", "(") +end + +local function lex_code_e_close(state) + return lex_single_char(state, "code_e_close", ")") +end + +local function lex_map_open(state) + return lex_single_char(state, "map_open", "[") +end + +local function lex_map_relation(state) + return lex_single_char(state, "map_relation", ":") +end + +local function lex_map_close(state) + return lex_single_char(state, "map_close", "]") +end + +local function lex_string_escape(state) + local n = lex_next(state) + return escape_values[n] +end + +local function lex_string(state) + local bchar = lex_next(state) + + local escaped = false + local string = {} + local stringi = 1 + + while true do + local n = lex_next(state) + + if n == bchar then + return {type = "literal", subtype = "string", value = table.concat(string)} + elseif n == "\\" then + n = lex_string_escape(state) + end + + if n == nil then + return nil -- ERROR + end + + string[stringi] = n + stringi = stringi + 1 + end +end + +local function lex_number(state) + local used_sep = false + local num = {} + local numi = 1 + + local n = lex_peek(state) + if in_list(n, number_start) then + num[numi] = lex_next(state) + numi = numi + 1 + + while true do + n = lex_peek(state) + + if n == "." and not used_sep then + used_sep = true + elseif not in_list(n, number_values) then + return {type = "literal", subtype = "number", value = table.concat(num)} + end + + num[numi] = lex_next(state) + numi = numi + 1 + end + end +end + +local function lex_symbol(state) + local sym = {} + local symi = 1 + + while true do + local n = lex_peek(state) + if not in_list(n, symbol_values) then + local symbol = table.concat(sym) + if in_list(symbol, symbols) then + return {type = "symbol", value = symbol} + else + return nil -- ERROR + end + elseif n == nil then + return nil -- ERROR + else + sym[symi] = lex_next(state) + symi = symi + 1 + end + end +end + +local function lex_number_or_symbol(state) + local nextpeek = state.code:sub(state.position + 1, state.position + 1) + if in_list(nextpeek, number_values) then + return lex_number(state) + else + return lex_symbol(state) + end +end + +local function lex_comment(state) + while true do + local n = lex_next(state) + if n == nil or n == "\n" then + return false + end + end +end + +local function lex_step(state) + local cur = lex_peek(state) + + if cur == nil then + return nil + end + + if in_list(cur, whitespace) then + lex_whitespace(state) + end + + cur = lex_peek(state) + + if cur == "`" then + return lex_quote(state) + elseif cur == "-" then -- special case for negative numbers and the minus + return lex_number_or_symbol(state) + elseif in_list(cur, symbol_start) then + return lex_symbol(state) + elseif cur == "{" then + return lex_code_open(state) + elseif cur == "}" then + return lex_code_close(state) + elseif cur == "(" then + return lex_code_e_open(state) + elseif cur == ")" then + return lex_code_e_close(state) + elseif cur == "[" then + return lex_map_open(state) + elseif cur == "]" then + return lex_map_close(state) + elseif cur == ":" then + return lex_map_relation(state) + elseif in_list(cur, identifier_start) then + return lex_identifier(state) + elseif in_list(cur, string_start) then + return lex_string(state) + elseif in_list(cur, number_start) then + return lex_number(state) + elseif cur == "#" then + return lex_comment(state) + end +end + +-- lex +function tlang.lex(code) + local state = {code = code, position = 1} + local lexed = {} + local lexi = 1 + + while true do + local n = lex_step(state) + + if n == nil then + if state.position <= #state.code then + return nil + else + return lexed + end + end + + -- comment lexer returns false + if n ~= false then + lexed[lexi] = n + lexi = lexi + 1 + end + end +end diff --git a/turtle/tlang_parse.lua b/turtle/tlang_parse.lua new file mode 100644 index 0000000..5c14f72 --- /dev/null +++ b/turtle/tlang_parse.lua @@ -0,0 +1,208 @@ +-- CC0/Unlicense Emilia 2020 + +-- parse types +--[[ +quote +identifier +code +map +string +number +symbol +--]] + +local tlang = ... + +local internal = {} + +local function sublist(list, istart, iend, inclusive) + local o = {} + local oi = 1 + + inclusive = inclusive or false + + for i, v in ipairs(list) do + iend = iend or 0 -- idk how but iend can become nil + + local uninc = i > istart and i < iend + local incl = i >= istart and i <= iend + + if (inclusive and incl) or (not inclusive and uninc) then + o[oi] = v + oi = oi + 1 + end + end + + return o +end + + +local function parse_peek(state) + return state.lexed[state.position] +end + +local function parse_next(state) + local n = parse_peek(state) + state.position = state.position + 1 + return n +end + +local function parse_identifier(state) + local lexid = parse_next(state).value + + for i, v in ipairs(lexid) do + if v:match("^[0-9]+$") then + lexid[i] = tonumber(v) + end + end + + return {type = "identifier", value = lexid} +end + +local function parse_map(state) + local map = {} + local mapi = 1 + + if parse_next(state).type ~= "map_open" then + return nil -- ERROR + end + + while true do + local n = parse_next(state) + local skip = false -- lua has no continue, 5.1 has no goto + + if n == nil then + return nil -- ERROR + end + + if n.type == "map_close" then + break + elseif n.type == "literal" and (n.subtype == "identifier" or n.subtype == "string") then + local key = n.value + local mr = parse_peek(state) + + if type(key) == "table" then + key = key[1] + end + + if mr.type == "map_relation" then + parse_next(state) + local nval = internal.parse_step(state) + + if nval == nil then + return nil -- ERROR + end + + map[key] = nval + skip = true + end + end + + if not skip then + local nval = tlang.parse({n}) + + if nval == nil then + return nil -- ERROR + end + + map[mapi] = nval[1] + mapi = mapi + 1 + end + end + + return {type = "map", value = map} +end + +local function parse_find_matching(state, open, close) + local level = 1 + + parse_next(state) -- skip beginning + + while level ~= 0 do + local n = parse_next(state) + if n == nil then + return nil -- ERROR + elseif n.type == open then + level = level + 1 + elseif n.type == close then + level = level - 1 + end + end + + return state.position - 1 +end + +local function parse_code(state, open, close) + local istart = state.position + local iend = parse_find_matching(state, open, close) + + return { + type = "code", + value = tlang.parse(sublist(state.lexed, istart, iend)) + } +end + +function internal.parse_step(state) + local n = parse_peek(state) + + if n == nil then + return nil + elseif n.type == "code_open" then + return parse_code(state, "code_open", "code_close") + elseif n.type == "code_e_open" then + return { + parse_code(state, "code_e_open", "code_e_close"), + {type = "identifier", value = "run"} + } + -- also return run + elseif n.type == "map_open" then + local istart = state.position + local iend = parse_find_matching(state, "map_open", "map_close") + return parse_map({lexed = sublist(state.lexed, istart, iend, true), position = 1}) + elseif n.type == "literal" then + if n.subtype == "number" then + parse_next(state) + return {type = "number", value = tonumber(n.value)} + elseif n.subtype == "string" then + parse_next(state) + return {type = "string", value = n.value} + elseif n.subtype == "identifier" then + return parse_identifier(state) + elseif n.subtype == "quote" then + parse_next(state) + return {type = "quote", value = n.value} + end + elseif n.type == "symbol" then + parse_next(state) + return {type = "symbol", value = n.value} + end +end + + +-- parse +function tlang.parse(lexed) + local state = {lexed = lexed, position = 1} + local tree = {} + local treei = 1 + + while true do + local n = internal.parse_step(state) + + if n == nil then + if state.position <= #state.lexed then + return nil + else + return tree + end + end + + if n.type == nil then -- () = {} run + tree[treei] = n[1] + tree[treei + 1] = n[2] + treei = treei + 2 + else + tree[treei] = n + treei = treei + 1 + end + end +end diff --git a/turtle/tlang_vm.lua b/turtle/tlang_vm.lua new file mode 100644 index 0000000..81c22ea --- /dev/null +++ b/turtle/tlang_vm.lua @@ -0,0 +1,741 @@ +-- CC0/Unlicense Emilia 2020 + +local tlang = ... + +local function in_list(value, list) + for k, v in ipairs(list) do + if v == value then + return true + end + end + return false +end + +local function in_keys(value, list) + return list[value] ~= nil +end + +-- state +--[[ + { + locals = {}, + stack = {}, + builtins = {}, + code_stack = {}, + wait_target = float, + paused = f/t, + nextpop = f/t + } +--]] + +-- program counter +--[[ + sg = 0/1, + pos = int/string, + elem = int +--]] + +function tlang.boolean_to_number(b) + if b then + return 1 + else + return 0 + end +end + +function tlang.number_to_boolean(n) + if n ~= 0 then + return true + else + return false + end +end + +-- convert a lua value into a tlang literal +function tlang.value_to_tlang(value) + local t = type(value) + if t == "string" then + return {type = "string", value = value} + elseif t == "number" then + return {type = "number", value = value} + elseif t == "boolean" then + return {type = "number", value = tlang.boolean_to_number(value)} + elseif t == "table" then + local map = {} + + for k, v in pairs(value) do + map[k] = tlang.value_to_tlang(v) + end + + return {type = "map", value = map} + end +end + +-- convert a tlang literal to a lua value +function tlang.tlang_to_value(tl) + if type(tl) ~= "table" then + return + end + + if tl.type == "map" then + local o = {} + + for k, v in pairs(tl.value) do + o[k] = tlang.tlang_to_value(v) + end + + return o + else + return tl.value + end +end + +local literals = { + "quote", + "code", + "map", + "string", + "number" +} + + +function tlang.call(state, target) + if target.sg == 0 then + state.code_stack[#state.code_stack + 1] = state.stack[target.pos] + table.remove(state.stack, target.pos) + target.pos = #state.code_stack + end + + state.locals[#state.locals + 1] = {vars = {}, pc = target} +end + +function tlang.call_tos(state) + tlang.call(state, {sg = 0, pos = #state.stack, elem = 1}) +end + +function tlang.call_var(state, name) + if type(name) ~= "table" then + name = {name} + end + + tlang.call(state, {sg = 1, pos = name, elem = 1}) +end + +function tlang.call_builtin(state, name) + local f = state.builtins[name] + f(state) +end + +function tlang.call_var_or_builtin(state, name) + if in_keys(name, state.builtins) then + tlang.call_builtin(state, name) + else + tlang.call_var(state, name) + end +end + +function tlang.push_values(state, vals) + for i, v in ipairs(vals) do + tlang.push(state, v) + end +end + +function tlang.lua_call_tos(state, ...) + tlang.push_values(state, {...}) + tlang.call_tos(state) +end + +function tlang.lua_call_var(state, name, ...) + tlang.push_values(state, {...}) + tlang.call_var(state, name) +end + +local function find_var_pos(state, name) + local slen = #state.locals + + for i = 1, slen do + local v = state.locals[slen + 1 - i] + if in_keys(name, v.vars) then + return slen + 1 - i + end + end +end + +function tlang.map_access_assign(state, index, start, assign) + local container + local curtab + + if start then + container = start + elseif index[1] == "" and #index > 1 then + curtab = state.stack[#state.stack].value + else + local pos = find_var_pos(state, index[1]) + -- assignments can go at the current scope + if assign then + pos = pos or #state.locals + elseif not pos then + return nil -- ERROR, variable undefined + end + + container = state.locals[pos].vars + end + + if not container and not curtab then + return + end + + if #index == 1 then + if assign then + container[index[1]] = assign + return + else + return container[index[1]] + end + end + + curtab = curtab or container[index[1]].value + + for idx = 2, #index - 1 do + curtab = curtab[index[idx]] + + if not curtab then + return nil + end + + curtab = curtab.value + end + + if assign then + curtab[index[#index]] = assign + else + return curtab[index[#index]] + end +end + +function tlang.near_access(state, index) + return tlang.map_access_assign(state, index) +end + +function tlang.near_assign(state, index, value) + tlang.map_access_assign(state, index, nil, value) +end + +function tlang.global_access(state, index) + tlang.map_access_assign(state, index, state.locals[1].vars) +end + +function tlang.global_assign(state, index, value) + tlang.map_access_assign(state, index, state.locals[1].vars, value) +end + +function tlang.local_access(state, index) + tlang.map_access_assign(state, index, state.locals[#state.locals].vars) +end + +function tlang.local_assign(state, index, value) + tlang.map_access_assign(state, index, state.locals[#state.locals].vars, value) +end + +function tlang.get_pc(state) + return state.locals[#state.locals].pc +end + +local function accesspc(state, pc) + local code + if pc.sg == 0 then -- stack + code = state.code_stack[pc.pos] + elseif pc.sg == 1 then -- global + code = tlang.near_access(state, pc.pos) + end + + if code then + return code.value[pc.elem] + end +end + +function tlang.increment_pc(state, pc) + local next_pc = {sg = pc.sg, pos = pc.pos, elem = pc.elem + 1} + + if accesspc(state, next_pc) then + return next_pc + end +end + +local function getnext(state) + if state.locals[#state.locals].nextpop then + local pc = tlang.get_pc(state) + + -- allows for finished states to be used in calls + if #state.locals == 1 then + return nil + end + + state.locals[#state.locals] = nil + + -- pop code stack + if pc.sg == 0 then + state.code_stack[pc.pos] = nil + end + + return getnext(state) + end + + local current + if not state.locals[#state.locals].nextpop then + state.current_pc = tlang.get_pc(state) + current = accesspc(state, state.current_pc) + end + + local incd = tlang.increment_pc(state, tlang.get_pc(state)) + if not incd then + state.locals[#state.locals].nextpop = true + else + state.locals[#state.locals].pc = incd + end + + return current +end + +-- doesn't support jumping out of scope yet +function tlang.set_next_pc(state, pc) + -- this probably causes issues when jumping outside scope + state.locals[#state.locals].nextpop = nil + + state.locals[#state.locals].pc = pc +end + +function tlang.peek_raw(state) + return state.stack[#state.stack] +end + +function tlang.pop_raw(state) + local tos = tlang.peek_raw(state) + state.stack[#state.stack] = nil + return tos +end + +function tlang.push_raw(state, value) + state.stack[#state.stack + 1] = value +end + +function tlang.peek(state) + return tlang.tlang_to_value(tlang.peek_raw(state)) +end + +function tlang.pop(state) + return tlang.tlang_to_value(tlang.pop_raw(state)) +end + +function tlang.push(state, value) + tlang.push_raw(state, tlang.value_to_tlang(value)) +end + +local function statepeek_type(state, t) + local tos = tlang.peek_raw(state) + + if tos.type == t then + return tos + else + return nil -- ERROR + end +end + +local function statepop_type(state, t) + local tos = tlang.peek_raw(state) + + if tos.type == t then + return tlang.pop_raw(state) + else + return nil -- ERROR + end +end + +local function statepop_num(state) + return statepop_type(state, "number") +end + +local function statepush_num(state, number) + tlang.push_raw(state, {type = "number", value = number}) +end + + + +tlang.builtins = {} + +function tlang.builtins.run(state) + tlang.call_tos(state) +end + +tlang.builtins["="] = function(state) + local name = statepop_type(state, "quote") + local value = tlang.pop_raw(state) + + tlang.near_assign(state, name.value, value) +end + +function tlang.unary(func) + return function(state) + local tos = tlang.pop_raw(state) + if tos.type == "number" then + statepush_num(state, func(tos.value)) + elseif tos.type == "quote" then + local n = tlang.near_access(state, tos.value) + tlang.near_assign(state, tos.value, {type = "number", value = func(n.value)}) + end + end +end + +function tlang.binary(func) + return function(state) + local tos = statepop_num(state) + local tos1 = statepop_num(state) + + statepush_num(state, func(tos1.value, tos.value)) + end +end + +tlang.builtins["--"] = tlang.unary(function(v) + return v - 1 +end) + +tlang.builtins["++"] = tlang.unary(function(v) + return v + 1 +end) + +tlang.builtins["!"] = tlang.unary(function(v) + return tlang.boolean_to_number(not tlang.number_to_boolean(v)) +end) + +tlang.builtins["+"] = tlang.binary(function(v1, v2) + return v1 + v2 +end) + +tlang.builtins["-"] = tlang.binary(function(v1, v2) + return v1 - v2 +end) + +tlang.builtins["*"] = tlang.binary(function(v1, v2) + return v1 * v2 +end) + +tlang.builtins["/"] = tlang.binary(function(v1, v2) + return v1 / v2 +end) + +tlang.builtins["%"] = tlang.binary(function(v1, v2) + return v1 % v2 +end) + +tlang.builtins["=="] = tlang.binary(function(v1, v2) + return tlang.boolean_to_number(v1 == v2) +end) + +tlang.builtins["!="] = tlang.binary(function(v1, v2) + return tlang.boolean_to_number(v1 ~= v2) +end) + +tlang.builtins[">="] = tlang.binary(function(v1, v2) + return tlang.boolean_to_number(v1 >= v2) +end) + +tlang.builtins["<="] = tlang.binary(function(v1, v2) + return tlang.boolean_to_number(v1 <= v2) +end) + +tlang.builtins[">"] = tlang.binary(function(v1, v2) + return tlang.boolean_to_number(v1 > v2) +end) + +tlang.builtins["<"] = tlang.binary(function(v1, v2) + return tlang.boolean_to_number(v1 < v2) +end) + +tlang.builtins["&&"] = tlang.binary(function(v1, v2) + return tlang.boolean_to_number( + tlang.number_to_boolean(v1) and tlang.number_to_boolean(v2)) +end) + +tlang.builtins["||"] = tlang.binary(function(v1, v2) + return tlang.boolean_to_number( + tlang.number_to_boolean(v1) or tlang.number_to_boolean(v2)) +end) + +tlang.builtins["if"] = function(state) + local tos = statepop_type(state, "code") + local tos1 = tlang.pop_raw(state) + + if tos1.type == "number" then + if tos1.value ~= 0 then + tlang.push_raw(state, tos) + tlang.call_tos(state) + end + elseif tos1.type == "code" then + local tos2 = statepop_num(state) + if tos2.value ~= 0 then + tlang.push_raw(state, tos1) + tlang.call_tos(state) + else + tlang.push_raw(state, tos) + tlang.call_tos(state) + end + end +end + +function tlang.builtins.print(state) + local value = tlang.pop_raw(state) + + if minetest then + local message = "[tlang] " .. tostring(value.value) + minetest.display_chat_message(message) + minetest.log("info", message) + else + print(value.value) + end +end + +function tlang.builtins.dup(state) + tlang.push_raw(state, tlang.peek_raw(state)) +end + +function tlang.builtins.popoff(state) + state.stack[#state.stack] = nil +end + +function tlang.builtins.wait(state) + local tos = statepop_type(state, "number") + state.wait_target = os.clock() + tos.value +end + +tlang.builtins["forever"] = function(state) + local slen = #state.locals + + if state.locals[slen].broke == true then + state.locals[slen].broke = nil + state.locals[slen].loop_code = nil + + return + end + + if state.locals[slen].loop_code == nil then + local tos = tlang.pop_raw(state) + + if tos.type == "code" then + state.locals[slen].loop_code = tos + elseif tos.type == "quote" then + state.locals[slen].loop_code = statepop_type(state, "code") + state.locals[slen].repeat_n = 0 + state.locals[slen].loop_var = tos.value + end + end + + if state.locals[slen].loop_var then + tlang.local_assign(state, + state.locals[slen].loop_var, + {type = "number", value = state.locals[slen].repeat_n}) + state.locals[slen].repeat_n = state.locals[slen].repeat_n + 1 + end + + tlang.push_raw(state, state.locals[slen].loop_code) + + tlang.set_next_pc(state, state.current_pc) + + tlang.call_tos(state) +end + +tlang.builtins["while"] = function(state) + local slen = #state.locals + + if state.locals[slen].broke == true then + state.locals[slen].broke = nil + state.locals[slen].loop_code = nil + state.locals[slen].test_code = nil + state.locals[slen].loop_stage = nil + + return + end + + if state.locals[slen].loop_code == nil then + local while_block = statepop_type(state, "code") + local test_block = statepop_type(state, "code") + + state.locals[slen].test_code = test_block + state.locals[slen].loop_code = while_block + state.locals[slen].loop_stage = 0 + end + + -- stage 0, run test + if state.locals[slen].loop_stage == 0 then + tlang.push_raw(state, state.locals[slen].test_code) + tlang.set_next_pc(state, state.current_pc) + tlang.call_tos(state) + + state.locals[slen].loop_stage = 1 + -- stage 1, run while + elseif state.locals[slen].loop_stage == 1 then + local tos = tlang.pop_raw(state) + if tos and tos.value ~= 0 then + tlang.push_raw(state, state.locals[slen].loop_code) + tlang.set_next_pc(state, state.current_pc) + tlang.call_tos(state) + else + tlang.set_next_pc(state, state.current_pc) + state.locals[slen].broke = true + end + + state.locals[slen].loop_stage = 0 + end +end + +tlang.builtins["repeat"] = function(state) + local slen = #state.locals + + if state.locals[slen].broke == true then + state.locals[slen].broke = nil + state.locals[slen].loop_code = nil + state.locals[slen].repeat_count = nil + state.locals[slen].repeat_n = nil + state.locals[slen].loop_var = nil + + return + end + + if state.locals[slen].loop_code == nil then + local num_var = tlang.pop_raw(state) + local count + local block + + if num_var.type == "quote" then + count = statepop_num(state) + state.locals[slen].loop_var = num_var.value + else + count = num_var + end + + block = statepop_type(state, "code") + + state.locals[slen].loop_code = block + state.locals[slen].repeat_count = count.value + state.locals[slen].repeat_n = 0 + end + + if state.locals[slen].repeat_n ~= state.locals[slen].repeat_count then + if state.locals[slen].loop_var then + tlang.local_assign(state, + state.locals[slen].loop_var, + {type = "number", value = state.locals[slen].repeat_n}) + end + + tlang.push_raw(state, state.locals[slen].loop_code) + + tlang.set_next_pc(state, state.current_pc) + + tlang.call_tos(state) + + state.locals[slen].repeat_n = state.locals[slen].repeat_n + 1 + else + tlang.set_next_pc(state, state.current_pc) + state.locals[slen].broke = true + end +end + +tlang.builtins["break"] = function(state) + local slen = #state.locals + local pos = 0 + local found = false + + -- find highest loop_code + -- slen - i to perform basically bitwise inverse + -- it allows it to count down the list effectively + for i = 1, slen do + if state.locals[slen + 1 - i].loop_code then + pos = slen + 1 - i + found = true + end + end + + if found then + -- pop the top layers + for i = pos + 1, #state.locals do + state.locals[i] = nil + end + + -- break in the lower layer + state.locals[#state.locals].broke = true + end +end + +tlang.builtins["return"] = function(state) + state.locals[#state.locals] = nil +end + +tlang.builtins["args"] = function(state) + local vars = {} + local vari = 1 + + while true do + local n = tlang.pop_raw(state) + if n.type == "quote" then + vars[vari] = n.value + vari = vari + 1 + elseif n.type == "number" and n.value == 0 then + break + else + return false + end + end + + for i, v in ipairs(vars) do + tlang.local_assign(state, v, tlang.pop_raw(state)) + end +end + + +-- returns: +-- true - more to do +-- nil - more to do but waiting +-- false - finished +-- string - error +function tlang.step(state) + if state.paused or (state.wait_target and os.clock() < state.wait_target) then + return nil + end + + local cur = getnext(state) + + if cur == nil then + if state.locals[1].nextpop then + state.finished = true + return false + else + return "Error: code exited early" + end + else + state.finished = false + end + + if in_list(cur.type, literals) then + state.stack[#state.stack + 1] = cur + elseif cur.type == "identifier" or cur.type == "symbol" then + local strname = cur.value + if type(cur.value) == "table" then + strname = cur.value[1] + end + + if in_keys(strname, state.builtins) then + local f = state.builtins[strname] + f(state) + else + local var = tlang.near_access(state, cur.value) + if var == nil then + return "Undefined identifier: " .. table.concat(cur.value, ".") + elseif var.type == "code" then + tlang.call_var(state, cur.value) + else + state.stack[#state.stack + 1] = var + end + end + end + + return true +end diff --git a/undying/init.lua b/undying/init.lua new file mode 100644 index 0000000..dbf0074 --- /dev/null +++ b/undying/init.lua @@ -0,0 +1,54 @@ +-- +-- undying + + +local sh=false + +local function findbones() + return minetest.find_node_near(minetest.localplayer:get_pos(), 6, {"bones:bones"},true) +end + +local function digbones() + local bn=findbones() + if not bn then return false end + minetest.dig_node(bn) + if findbones() then minetest.after("0.1",digbones) end +end + +minetest.register_on_death(function() + if not minetest.settings:get_bool("undying") then return end + sh=false + minetest.after("0.1",function() minetest.send_chat_message("/home") end) + minetest.after("0.2",function() + digbones() + for k, v in ipairs(minetest.localplayer.get_nearby_objects(10)) do + if (v:is_player() and v:get_name() ~= minetest.localplayer:get_name()) then + local pos = v:get_pos() + pos.y = pos.y - 1 + autofly.aim(pos) + end + end + end) +end) + +minetest.register_on_damage_taken(function(hp) + if not sh and minetest.settings:get_bool("undying") then + local hhp=minetest.localplayer:get_hp() + --if (hhp==0 ) then return end + if (hhp < 2 ) then + sh=true + minetest.settings:set_bool("autorespawn",true) + minetest.send_chat_message("/sethome") end + end +end +) +minetest.register_on_receiving_chat_message(function(msg) + if (msg:find('Teleported to home!') or msg:find('Home set!')) then return true end +end) + +-- REG cheats on DF +if (_G["minetest"]["register_cheat"] ~= nil) then + minetest.register_cheat("Undying", "Combat", "undying") +else + minetest.settings:set_bool('undying',true) +end diff --git a/undying/mod.conf b/undying/mod.conf new file mode 100644 index 0000000..7543d10 --- /dev/null +++ b/undying/mod.conf @@ -0,0 +1,3 @@ +name = undying +author = cora +description = revert death on servers with /sethome / bones diff --git a/undying/settingtypes.txt b/undying/settingtypes.txt new file mode 100644 index 0000000..00e2940 --- /dev/null +++ b/undying/settingtypes.txt @@ -0,0 +1 @@ +undying (Undying) bool false diff --git a/waterbot/init.lua b/waterbot/init.lua new file mode 100644 index 0000000..a3e1da1 --- /dev/null +++ b/waterbot/init.lua @@ -0,0 +1,131 @@ +-- CC0/Unlicense Emilia 2020 +waterbot = {} + +-- TODO: FreeRefills tries to pick up too much at once +-- quint time :] + +-- Lua doesnt have enums and tables look gross +-- should still be a table tho +local WATER_USABLE = 0 -- water source +local WATER_STABLE = 1 -- water source used for refreshing other sources +local WATER_USED = 2 -- water source that can be bucketed +local AIR = 3 -- something that water can flow into and renew +local SOLID = 4 -- something that water cannot flow into and renew + +local function get_offset(pos, radius) + return vector.round({ + x = pos.x - radius - 1, + y = pos.y - radius - 1, + z = pos.z - radius - 1 + }) +end + +-- returns {{{n n n} {n n n} ...} {...} ...} +local function get_intarr(pos, radius) + local offset = get_offset(pos, radius) + local out = {} + local diameter = radius * 2 + 1 + + for z = 1, diameter do + table.insert(out, {}) + for y = 1, diameter do + table.insert(out[#out], {}) + for x = 1, diameter do + local npos = {x = x, y = y, z = z} + local node = minetest.get_node_or_nil(vector.add(offset, npos)) + local v = SOLID + if node then + if node.name == "mcl_core:water_source" then + v = WATER_USABLE + elseif node.name == "air" then + v = AIR + end + end + table.insert(out[#out][#out[#out]], v) + end + end + end + + return out +end + +local function coord_valid(coord, width, height) + return ((coord[1] > 0) and (coord[2] > 0)) and ((coord[1] <= width) and (coord[2] <= height)) +end + +-- returns modified list and safe sources +-- table is [z][y][x] accessed +-- safe sources is a coordinate list +-- this is like cellular automata but the state is mogrified in place +local function mogrify_stable(t, offset) + local safe = {} + + -- indented like this because this is necessary and full indent would be ugly + for zi, zv in ipairs(t) do + for yi, yv in ipairs(zv) do + for xi, xv in ipairs(yv) do + if xv == WATER_USABLE then + local nhood = { + {xi - 1, zi}, + {xi, zi - 1}, + {xi + 1, zi}, + {xi, zi + 1} + } + + local last + local applied = false + + for i, v in ipairs(nhood) do + if not applied and coord_valid(v, #yv, #t) then + local check = t[v[2]][yi][v[1]] + if check == WATER_USABLE or check == WATER_STABLE then + if not last then + last = v + else + t[ v[2]][yi][ v[1]] = WATER_STABLE + t[last[2]][yi][last[1]] = WATER_STABLE + yv[xi] = WATER_USED + table.insert(safe, + vector.add(offset, + {x = xi, y = yi, z = zi})) + applied = true + end + end + end + end + end + end + end + end + + return t, safe +end + +function waterbot.find_renewable_water_near(pos, radius) + local int = get_intarr(pos, radius) + local offset = get_offset(pos, radius) + local mint, safe = mogrify_stable(int, offset) + return safe +end + + +local epoch = os.clock() + +minetest.register_globalstep(function() + if minetest.settings:get_bool("waterbot_refill") and os.clock() >= epoch + 2 then + local pos = minetest.localplayer:get_pos() + local sources = waterbot.find_renewable_water_near(pos, 6) + + for i, v in ipairs(sources) do + if minetest.switch_to_item("mcl_buckets:bucket_empty") then + minetest.interact("place", v) + else + break + end + end + + epoch = os.clock() + end +end) + +minetest.register_cheat("FreeRefills", "Inventory", "waterbot_refill") diff --git a/worldstorage/README.md b/worldstorage/README.md new file mode 100644 index 0000000..eb14f75 --- /dev/null +++ b/worldstorage/README.md @@ -0,0 +1,11 @@ +The mod can only be used if `worldstorage.get_current_worldname()` doesn't return `nil`. +- `worldstorage.get_current_worldname()` +- `worldstorage.get_int(key)` +- `worldstorage.set_int(key, value)` +- `worldstorage.get_float(key)` +- `worldstorage.set_float(key, value)` +- `worldstorage.get_string(key)` +- `worldstorage.set_string(key, value)` +- `worldstorage.to_table()` +- `worldstorage.from_table(values)` +- `worldstorage.register_on_activate(func())` \ No newline at end of file diff --git a/worldstorage/init.lua b/worldstorage/init.lua new file mode 100644 index 0000000..d1d6b64 --- /dev/null +++ b/worldstorage/init.lua @@ -0,0 +1,107 @@ + +worldstorage = {} + +local modstorage = core.get_mod_storage() + +local activate_functions = {} +function worldstorage.register_on_activate(f) + activate_functions[#activate_functions+1] = f +end + +local worldname +function worldstorage.get_current_worldname() + return worldname +end +local function set_worldname(name) + local first_time = worldname == nil + worldname = name + for i = 1, #activate_functions do + activate_functions[i](first_time) + end + return "Worldname set to "..name.."." +end + +--minetest.register_on_connect(function() +minetest.show_formspec("worldstorage_worldname", + "field[worldname;Enter the current worldname:;]") +--end) + +minetest.register_on_formspec_input(function(formname, fields) + if formname ~= "worldstorage_worldname" then + return + end + if not fields.worldname then + minetest.display_chat_message("You can still set the worldname with ".. + "the chatcommand.") + return true + end + local msg = set_worldname(fields.worldname) + minetest.display_chat_message(msg) + return true +end) + +minetest.register_chatcommand("worldname", { + params = "set / get", + description = "", + func = function(param) + if param == "get" then + if not worldname then + return false, "No worldname set." + end + return true, worldname + elseif param:sub(1, 4) == "set " then + param = param:sub(5) + end + return true, set_worldname(param) + end, +}) + +worldstorage.register_on_activate(function(first_time) + if not first_time then + return + end + + local prefix = worldname.."/" + + function worldstorage.get_int(key) + return modstorage:get_int(prefix..key) + end + + function worldstorage.set_int(key, value) + modstorage:set_int(prefix..key, value) + end + + function worldstorage.get_float(key) + return modstorage:get_float(prefix..key) + end + + function worldstorage.set_float(key, value) + modstorage:set_float(prefix..key, value) + end + + function worldstorage.get_string(key) + return modstorage:get_string(prefix..key) + end + + function worldstorage.set_string(key, value) + modstorage:set_string(prefix..key, value) + end + + function worldstorage.to_table() + local t = modstorage:to_table() + for k,_ in pairs(t) do + if k:sub(1, #prefix) ~= prefix then + t[k] = nil + end + end + return t + end + + function worldstorage.from_table(values) + local t = modstorage:to_table() + for k, v in pairs(values) do + t[prefix..k] = v + end + modstorage:from_table(t) + end +end) diff --git a/worldstorage/mod.conf b/worldstorage/mod.conf new file mode 100644 index 0000000..191b8ee --- /dev/null +++ b/worldstorage/mod.conf @@ -0,0 +1 @@ +name = worldstorage \ No newline at end of file