Add files via upload

main
BotBoyM1 2022-11-03 12:16:04 +03:00 committed by GitHub
parent 85cdc084be
commit 479395f067
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
67 changed files with 17136 additions and 0 deletions

24
fish_bot/LICENSE Normal file
View File

@ -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 <https://unlicense.org>

2
fish_bot/README.md Normal file
View File

@ -0,0 +1,2 @@
# fishbot
Basic mineclone fishing bot for dragonfire. Put a fishing rod in the hotbar, aim at water and activate FishBot.

45
fish_bot/init.lua Normal file
View File

@ -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')

0
frenemies/init.lua Normal file
View File

0
frenemies/mod.conf Normal file
View File

162
goddessmode/init.lua Normal file
View File

@ -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

3
goddessmode/mod.conf Normal file
View File

@ -0,0 +1,3 @@
name = goddessmode
author = cora
description = defensive combat hax

View File

@ -0,0 +1 @@
goddess (Goddess Mode) bool true

0
hignore/init.lua Normal file
View File

0
hignore/mod.conf Normal file
View File

0
hignore/settingtypes.txt Normal file
View File

0
hpchange/init.lua Normal file
View File

0
hpchange/mod.conf Normal file
View File

127
incrementaltp/init.lua Normal file
View File

@ -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 = "<destination>",
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 = "<destination>",
func = function(params)
local pos = minetest.string_to_pos(params)
incremental_tp.tp(pos, 0.5, 0.4)
end
})
-- chunk_rand

0
invrefill/init.lua Normal file
View File

0
invutil/LICENSE Normal file
View File

0
invutil/README Normal file
View File

0
invutil/init.lua Normal file
View File

0
invutil/mod.conf Normal file
View File

0
invutil/settingtypes.txt Normal file
View File

80
itemcount/init.lua Normal file
View File

@ -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)

2
itemcount/mod.conf Normal file
View File

@ -0,0 +1,2 @@
name = itemcount
description = Display currently held item's total count.

192
kamikaze/init.lua Normal file
View File

@ -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')

146
muse/init.lua Normal file
View File

@ -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 = "<track>",
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
})

981
nlist/forms.lua Normal file
View File

@ -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)

2109
nlist/init.lua Normal file

File diff suppressed because it is too large Load Diff

2
notebook/README.md Normal file
View File

@ -0,0 +1,2 @@
The chatcommand `notebook` is added.
It opens a formspec in that you can write anything.

1
notebook/depends.txt Normal file
View File

@ -0,0 +1 @@
worldstorage

49
notebook/init.lua Normal file
View File

@ -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

1
notebook/mod.conf Normal file
View File

@ -0,0 +1 @@
name = notebook

70
optimize/init.lua Normal file
View File

@ -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")

View File

@ -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

288
quint/init.lua Normal file
View File

@ -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)

399
quotebot/init.lua Normal file
View File

@ -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 <ID>
.qb_direct player <ID>
.qb_chance num/player <num>
.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 = "<player>",
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 = "<player>",
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 = "<id> <quote>",
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 = "<ID>",
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 = "<ID>",
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 = "<player> <ID>",
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 = "<num/player> <num>",
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 = "<file>",
description = "Export quote setup to a file.",
func = function(params)
quotebot.export(params)
end
})
minetest.register_chatcommand("qb_import", {
params = "<file>",
description = "Import quote setup from a file.",
func = function(params)
quotebot.import(params)
end
})
end

2
quotebot/mod.conf Normal file
View File

@ -0,0 +1,2 @@
name = quotebot
description = Randomly say quotes when users send messages.

45
randomscreenshot/init.lua Normal file
View File

@ -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

View File

@ -0,0 +1,3 @@
name = randomscreenshot
author = cora
description = take screenshots at random intervals

View File

@ -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

881
scaffold/atower.lua Normal file
View File

@ -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'})

950
scaffold/autofarm.lua Normal file
View File

@ -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)

1
scaffold/depends.txt Normal file
View File

@ -0,0 +1 @@
turtle

1265
scaffold/init.lua Normal file

File diff suppressed because it is too large Load Diff

891
scaffold/ow2bot.lua Normal file
View File

@ -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)

1191
scaffold/railscaffold.lua Normal file

File diff suppressed because it is too large Load Diff

898
scaffold/sapscaffold.lua Normal file
View File

@ -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)

View File

@ -0,0 +1 @@
slow_blocks_per_second (Blocks placed per second) int 8

925
scaffold/slowscaffold.lua Normal file
View File

@ -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)

1028
scaffold/squarry.lua Normal file

File diff suppressed because it is too large Load Diff

1135
scaffold/wallbot.lua Normal file

File diff suppressed because it is too large Load Diff

177
supernotes/init.lua Normal file
View File

@ -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:
<nowiki></nowiki> 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 .. "}}", "<action name=coords_%1_%2_%3>%1 %2 %3</action>")
return text
end
local function format_wikilinks(text)
local tmatch = "[^%[%|%]]"
text = text:gsub("%[%[(" .. tmatch .. "-)|(" .. tmatch .. "-)%]%]", "<action name=link_%1>%2</action>")
text = text:gsub("%[%[(" .. tmatch .. "-)%]%]", "<action name=link_%1>%1</action>")
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 = "<page/empty>",
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
})

2
supernotes/mod.conf Normal file
View File

@ -0,0 +1,2 @@
name = supernotes
description = Adds a wiki-like local notetaking system. Integrates with cora's autofly.

643
tchat/init.lua Normal file
View File

@ -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 = "<message>",
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 = "<message>",
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 = "<player>",
description = "Add player to your team.",
func = tchat.team_add_player
})
minetest.register_chatcommand("tdel", {
params = "<player>",
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 = "<message>",
description = "Send message containing coordinates.",
func = tchat.send_coords
})
minetest.register_chatcommand("mcoord", {
params = "<player>",
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

3
tchat/mod.conf Normal file
View File

@ -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.

18
tchat/settingtypes.txt Normal file
View File

@ -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

518
turtle/init.lua Normal file
View File

@ -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 = "<code>",
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 = "<task>",
func = turtle.kill_symbolic
})
minetest.register_chatcommand("tl_pause", {
description = "Pause a tlang state.",
params = "<task>",
func = turtle.pause_symbolic
})
minetest.register_chatcommand("tl_resume", {
description = "Resume a tlang state.",
params = "<task>",
func = turtle.resume_symbolic
})

3
turtle/mod.conf Normal file
View File

@ -0,0 +1,3 @@
name = turtle
author = system32
description = Adds a system to control the player like a graphics turtle.

263
turtle/tlang.lua Normal file
View File

@ -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

345
turtle/tlang_lex.lua Normal file
View File

@ -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

208
turtle/tlang_parse.lua Normal file
View File

@ -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

741
turtle/tlang_vm.lua Normal file
View File

@ -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

54
undying/init.lua Normal file
View File

@ -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

3
undying/mod.conf Normal file
View File

@ -0,0 +1,3 @@
name = undying
author = cora
description = revert death on servers with /sethome / bones

1
undying/settingtypes.txt Normal file
View File

@ -0,0 +1 @@
undying (Undying) bool false

131
waterbot/init.lua Normal file
View File

@ -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")

11
worldstorage/README.md Normal file
View File

@ -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())`

107
worldstorage/init.lua Normal file
View File

@ -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 <name> / 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)

1
worldstorage/mod.conf Normal file
View File

@ -0,0 +1 @@
name = worldstorage