Add more
This commit is contained in:
parent
386e76c80c
commit
2a2aa6a793
5 changed files with 1400 additions and 0 deletions
98
fly2.lua
Normal file
98
fly2.lua
Normal file
|
|
@ -0,0 +1,98 @@
|
|||
--- This script allows the player to fly, as if they were in creative mode. Be warned, this isn't perfect, and lag may
|
||||
--- result in your death.
|
||||
|
||||
--- Firstly we want to ensure that we have a neural interface and wrap it.
|
||||
local modules = peripheral.find("neuralInterface")
|
||||
if not modules then
|
||||
error("Must have a neural interface", 0)
|
||||
end
|
||||
|
||||
--- - We require a sensor and introspection module in order to gather information about the player
|
||||
--- - The sensor is used to determine where the ground is relative to the player, meaning we can slow the player
|
||||
--- before they hit the floor.
|
||||
--- - The kinetic augment is (obviously) used to launch the player.
|
||||
if not modules.hasModule("plethora:sensor") then error("Must have a sensor", 0) end
|
||||
if not modules.hasModule("plethora:scanner") then error("Must have a scanner", 0) end
|
||||
if not modules.hasModule("plethora:introspection") then error("Must have an introspection module", 0) end
|
||||
if not modules.hasModule("plethora:kinetic", 0) then error("Must have a kinetic agument", 0) end
|
||||
|
||||
--- We run several loop at once, to ensure that various components do not delay each other.
|
||||
local meta = {}
|
||||
local hover = false
|
||||
parallel.waitForAny(
|
||||
--- This loop just pulls user input. It handles a couple of function keys, as well as
|
||||
--- setting the "hover" field to true/false.
|
||||
---
|
||||
--- We recommend running [with the keyboard in your neural interface](../items/keyboard.html#using-with-the-neural-interface),
|
||||
--- as this allows you to navigate without having the interface open.
|
||||
function()
|
||||
while true do
|
||||
local event, key = os.pullEvent()
|
||||
if event == "key" and key == keys.o then
|
||||
-- The O key launches you high into the air.
|
||||
modules.launch(0, -90, 3)
|
||||
elseif event == "key" and key == keys.p then
|
||||
-- The P key launches you a little into the air.
|
||||
modules.launch(0, -90, 1)
|
||||
elseif event == "key" and key == keys.l then
|
||||
-- The l key launches you in whatever direction you are looking.
|
||||
modules.launch(meta.yaw, meta.pitch, 3)
|
||||
elseif event == "key" and key == keys.k then
|
||||
-- Holding the K key enables "hover" mode. We disable it when it is released.
|
||||
if not hover then
|
||||
hover = true
|
||||
os.queueEvent("hover")
|
||||
end
|
||||
elseif event == "key_up" and key == keys.k then
|
||||
hover = false
|
||||
end
|
||||
end
|
||||
end,
|
||||
--- Continuously update the metadata. We do this in a separate loop to ensure this doesn't delay
|
||||
--- other functions
|
||||
function()
|
||||
while true do
|
||||
meta = modules.getMetaOwner()
|
||||
end
|
||||
end,
|
||||
--- If we are hovering then attempt to catapult us back into air, with sufficient velocity to
|
||||
--- just counteract gravity.
|
||||
function()
|
||||
while true do
|
||||
if hover then
|
||||
-- We calculate the required motion we need to take
|
||||
local mY = meta.motionY
|
||||
mY = (mY - 0.138) / 0.8
|
||||
|
||||
-- If it is sufficiently large then we fire ourselves in that direction.
|
||||
if mY > 0.5 or mY < 0 then
|
||||
local sign = 1
|
||||
if mY < 0 then sign = -1 end
|
||||
modules.launch(0, 90 * sign, math.min(4, math.abs(mY)))
|
||||
else
|
||||
sleep(0)
|
||||
end
|
||||
else
|
||||
os.pullEvent("hover")
|
||||
end
|
||||
end
|
||||
end,
|
||||
--- If we can detect a block below us, and we're falling sufficiently fast, then attempt to slow our fall. This
|
||||
---needs to react as fast as possible, so we can't call many peripheral methods here.
|
||||
function()
|
||||
while true do
|
||||
local blocks = modules.scan()
|
||||
for y = 0, -8, -1 do
|
||||
-- Scan from the current block downwards
|
||||
local block = blocks[1 + (8 + (8 + y)*17 + 8*17^2)]
|
||||
if block.name ~= "minecraft:air" then
|
||||
if meta.motionY < -0.3 then
|
||||
-- If we're moving slowly, then launch ourselves up
|
||||
modules.launch(0, -90, math.min(4, meta.motionY / -0.5))
|
||||
end
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
)
|
||||
189
minit.lua
Normal file
189
minit.lua
Normal file
|
|
@ -0,0 +1,189 @@
|
|||
-- Minit Beta 1.0.0
|
||||
-- Copyright (C) 2023 AlexDevs
|
||||
-- This software is licensed under the MIT license.
|
||||
|
||||
settings.define("minit.cycleSleep", {
|
||||
description = "Sleep time between cycles",
|
||||
type = "number",
|
||||
default = 0.1,
|
||||
})
|
||||
|
||||
settings.define("minit.cycleTimeout", {
|
||||
description = "Cycles timeout",
|
||||
type = "number",
|
||||
default = 1,
|
||||
})
|
||||
|
||||
settings.define("minit.modulesPath", {
|
||||
description = "Path to modules",
|
||||
type = "string",
|
||||
default = "modules",
|
||||
})
|
||||
|
||||
local modulesPath = settings.get("minit.modulesPath")
|
||||
local neuralInterface
|
||||
|
||||
local expect = require("cc.expect").expect
|
||||
|
||||
local logPrefix = "%s %s:"
|
||||
local function getTime()
|
||||
return os.date("%H:%M:%S")
|
||||
end
|
||||
local function log(label, ...)
|
||||
local time = getTime()
|
||||
print(string.format(logPrefix, time, label), ...)
|
||||
end
|
||||
|
||||
local function logError(label, ...)
|
||||
local time = getTime()
|
||||
printError(string.format(logPrefix, time, label), ...)
|
||||
end
|
||||
|
||||
local modules = {}
|
||||
local function loadModules()
|
||||
log("Minit", "Loading modules from /" .. modulesPath)
|
||||
local files = fs.list(modulesPath)
|
||||
for i = 1, #files do
|
||||
local ok, par = pcall(require, fs.combine(modulesPath, files[i])
|
||||
:gsub("/", ".")
|
||||
:gsub("%.lua$", ""))
|
||||
if ok then
|
||||
par.name = par.name or files[i]:gsub("%.lua$", "")
|
||||
table.insert(modules, par)
|
||||
log("Minit", "Loaded module " .. par.name)
|
||||
else
|
||||
logError("Minit", "Could not load module " .. files[i] .. ": " .. par)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function getCallbacks(name, ...)
|
||||
local args = table.pack(...)
|
||||
local callbacks = {}
|
||||
for i = 1, #modules do
|
||||
local module = modules[i]
|
||||
if type(module[name]) == "function" then
|
||||
table.insert(callbacks, function()
|
||||
module[name](table.unpack(args))
|
||||
end)
|
||||
end
|
||||
end
|
||||
return table.unpack(callbacks)
|
||||
end
|
||||
|
||||
local tasks = {}
|
||||
local function addTask(task)
|
||||
expect(1, task, "function")
|
||||
table.insert(tasks, coroutine.create(task))
|
||||
end
|
||||
|
||||
local function initModules()
|
||||
for i = 1, #modules do
|
||||
local module = modules[i]
|
||||
if type(module.init) == "function" then
|
||||
module.init({
|
||||
addTask = addTask,
|
||||
log = function(...)
|
||||
log(module.name, ...)
|
||||
end,
|
||||
logError = function(...)
|
||||
logError(module.name, ...)
|
||||
end,
|
||||
})
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function setupNeuralInterface()
|
||||
neuralInterface = peripheral.wrap("back")
|
||||
|
||||
if not neuralInterface then
|
||||
error("No neural interface found")
|
||||
end
|
||||
|
||||
-- Wait for owner to be alive
|
||||
local function getOwner()
|
||||
local meta
|
||||
parallel.waitForAny(function()
|
||||
meta = neuralInterface.getMetaOwner()
|
||||
end, function()
|
||||
sleep(1)
|
||||
end)
|
||||
|
||||
return meta
|
||||
end
|
||||
|
||||
local meta = getOwner()
|
||||
if not meta or not meta.isAlive then
|
||||
log("Minit", "Waiting for respawn...")
|
||||
end
|
||||
while not meta or not meta.isAlive do
|
||||
sleep(0.2)
|
||||
meta = getOwner()
|
||||
end
|
||||
|
||||
parallel.waitForAll(getCallbacks("setup", neuralInterface))
|
||||
end
|
||||
|
||||
local function cycleUpdate()
|
||||
local metaOwner = neuralInterface.getMetaOwner()
|
||||
|
||||
parallel.waitForAll(getCallbacks("update", metaOwner))
|
||||
|
||||
sleep(settings.get("minit.cycleSleep"))
|
||||
end
|
||||
|
||||
local function tasksHandler()
|
||||
local ev = { n = 0 }
|
||||
local filters = {}
|
||||
|
||||
while true do
|
||||
for i, thread in pairs(tasks) do
|
||||
if coroutine.status(thread) == "dead" then
|
||||
tasks[i] = nil
|
||||
filters[i] = nil
|
||||
else
|
||||
if filters[i] == nil or filters[i] == ev[1] or ev[1] == "terminate" then
|
||||
local ok, param = coroutine.resume(thread, table.unpack(ev, 1, ev.n))
|
||||
if not ok then
|
||||
logError("Task", param)
|
||||
else
|
||||
filters[i] = param
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
ev = table.pack(coroutine.yield())
|
||||
end
|
||||
end
|
||||
|
||||
local function main()
|
||||
setupNeuralInterface()
|
||||
while true do
|
||||
parallel.waitForAny(
|
||||
cycleUpdate,
|
||||
function()
|
||||
sleep(settings.get("minit.cycleTimeout"))
|
||||
end
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
loadModules()
|
||||
initModules()
|
||||
|
||||
parallel.waitForAny(
|
||||
function()
|
||||
while true do
|
||||
local ok, err = pcall(main)
|
||||
if not ok then
|
||||
if err == "Terminated" then
|
||||
break
|
||||
end
|
||||
logError("Minit", err)
|
||||
sleep(1)
|
||||
end
|
||||
end
|
||||
end,
|
||||
tasksHandler
|
||||
)
|
||||
721
nictl
Normal file
721
nictl
Normal file
|
|
@ -0,0 +1,721 @@
|
|||
local module = {
|
||||
name = "ni-ctl",
|
||||
}
|
||||
|
||||
local username = settings.get "username" or "gollark"
|
||||
local ni
|
||||
local speaker = peripheral.find "speaker"
|
||||
local modem = peripheral.find "modem"
|
||||
local offload_laser = settings.get "offload_laser"
|
||||
local w, h
|
||||
|
||||
if _G.thing_group then
|
||||
pcall(_G.thing_group.remove)
|
||||
end
|
||||
if _G.canvas3d_group then
|
||||
pcall(_G.canvas3d_group.clear)
|
||||
pcall(_G.canvas3d_group.remove)
|
||||
end
|
||||
local group
|
||||
local group_3d
|
||||
local function initialize_group_thing()
|
||||
if group then pcall(group.remove) end
|
||||
if group_3d then pcall(group_3d.remove) end
|
||||
group = ni.canvas().addGroup({ w - 70, 10 })
|
||||
ni.canvas3d().clear()
|
||||
group_3d = ni.canvas3d().create()
|
||||
_G.thing_group = group
|
||||
_G.canvas3d_group = group_3d
|
||||
end
|
||||
|
||||
|
||||
local targets = {}
|
||||
|
||||
local use_spudnet = offload_laser
|
||||
|
||||
local spudnet_send, spudnet_background
|
||||
if use_spudnet then
|
||||
print "SPUDNET interface loading."
|
||||
spudnet_send, spudnet_background = require "ni-ctl_spudnet_interface" ()
|
||||
end
|
||||
|
||||
local function offload_protocol(...)
|
||||
spudnet_send { "exec", { ... } }
|
||||
end
|
||||
|
||||
local function is_target(name)
|
||||
for target, type in pairs(targets) do
|
||||
if name:lower():match(target) then return type end
|
||||
end
|
||||
end
|
||||
|
||||
local function vector_sqlength(self)
|
||||
return self.x * self.x + self.y * self.y + self.z * self.z
|
||||
end
|
||||
|
||||
local function project(line_start, line_dir, point)
|
||||
local t = (point - line_start):dot(line_dir) / vector_sqlength(line_dir)
|
||||
return line_start + line_dir * t, t
|
||||
end
|
||||
|
||||
local function calc_yaw_pitch(v)
|
||||
local x, y, z = v.x, v.y, v.z
|
||||
local pitch = -math.atan2(y, math.sqrt(x * x + z * z))
|
||||
local yaw = math.atan2(-x, z)
|
||||
return math.deg(yaw), math.deg(pitch)
|
||||
end
|
||||
|
||||
local settings_cfg = {
|
||||
brake = { type = "bool", default = true, shortcode = "b" },
|
||||
counter = { type = "bool", default = false, shortcode = "c" }, -- counterattack
|
||||
highlight = { type = "bool", default = false, shortcode = "h" },
|
||||
dodge = { type = "bool", default = true, shortcode = "d" },
|
||||
power = { type = "number", default = 5, max = 5, min = 0.5 }, -- laser power
|
||||
flight = { type = "string", default = "std", shortcode = "f", alias = { fly = true } },
|
||||
drill = { type = "bool", default = false, shortcode = "D", persist = false },
|
||||
show_acceleration = { type = "bool", default = false },
|
||||
pitch_controls = { type = "bool", default = false },
|
||||
ext_highlight = { type = "bool", default = false }
|
||||
}
|
||||
local SAVEFILE = "ni-ctl-settings"
|
||||
if fs.exists(SAVEFILE) then
|
||||
local f = fs.open(SAVEFILE, "r")
|
||||
for key, value in pairs(textutils.unserialise(f.readAll())) do
|
||||
settings_cfg[key].value = value
|
||||
end
|
||||
f.close()
|
||||
end
|
||||
local gsettings = {}
|
||||
setmetatable(gsettings, {
|
||||
__index = function(_, key)
|
||||
local cfg = settings_cfg[key]
|
||||
if cfg.value == nil then return cfg.default else return cfg.value end
|
||||
end,
|
||||
__newindex = function(_, key, value)
|
||||
print("set", key, "to", value)
|
||||
settings_cfg[key].value = value
|
||||
if settings_cfg[key].persist ~= false then
|
||||
local kv = {}
|
||||
for key, cfg in pairs(settings_cfg) do
|
||||
kv[key] = cfg.value
|
||||
end
|
||||
local f = fs.open(SAVEFILE, "w")
|
||||
f.write(textutils.serialise(kv))
|
||||
f.close()
|
||||
end
|
||||
os.queueEvent "settings_change"
|
||||
end
|
||||
})
|
||||
|
||||
local work_queue = {}
|
||||
|
||||
local addressed_lasers = {}
|
||||
local function bool_to_yn(b)
|
||||
if b == true then
|
||||
return "y"
|
||||
elseif b == false then
|
||||
return "n"
|
||||
else
|
||||
return "?"
|
||||
end
|
||||
end
|
||||
|
||||
local status_lines = {}
|
||||
local notices = {}
|
||||
local function push_notice(t)
|
||||
table.insert(notices, { t, os.epoch "utc" })
|
||||
end
|
||||
|
||||
local function lase(entity)
|
||||
local target_location = entity.s
|
||||
for i = 1, 5 do
|
||||
target_location = entity.s + entity.v * (target_location:length() / 1.5)
|
||||
end
|
||||
local y, p = calc_yaw_pitch(target_location)
|
||||
if offload_laser then offload_protocol("fire", y, p, gsettings.power) else ni.fire(y, p, gsettings.power) end
|
||||
end
|
||||
|
||||
local user_meta
|
||||
local fast_mode_reqs = {}
|
||||
|
||||
local colortheme = {
|
||||
status = 0xFFFFFFFF,
|
||||
notice = 0xFF8800FF,
|
||||
follow = 0xFF00FFFF,
|
||||
watch = 0xFFFF00FF,
|
||||
laser = 0xFF0000FF,
|
||||
entity = 0x00FFFFFF,
|
||||
select = 0x00FF00FF
|
||||
}
|
||||
|
||||
local function schedule(fn, time, uniquename)
|
||||
if uniquename then
|
||||
work_queue[uniquename] = { os.clock() + time, fn }
|
||||
else
|
||||
table.insert(work_queue, { os.clock() + time, fn })
|
||||
end
|
||||
end
|
||||
|
||||
local function direction_vector(yaw, pitch)
|
||||
return vector.new(
|
||||
-math.sin(math.rad(yaw)) * math.cos(math.rad(pitch)),
|
||||
-math.sin(math.rad(pitch)),
|
||||
math.cos(math.rad(yaw)) * math.cos(math.rad(pitch))
|
||||
)
|
||||
end
|
||||
|
||||
--[[
|
||||
local function ni.launch(yaw, pitch, power)
|
||||
ni.ni.launch(yaw, pitch, power)
|
||||
if user_meta then
|
||||
local impulse = vector.new(
|
||||
-math.sin(math.rad(yaw)) * math.cos(math.rad(pitch)),
|
||||
math.cos(math.rad(yaw)) * math.cos(math.rad(pitch)),
|
||||
-math.sin(math.rad(pitch))
|
||||
)
|
||||
if user_meta.isElytraFlying then
|
||||
impulse = 0.4 * impulse
|
||||
end
|
||||
user_meta.velocity = user_meta.velocity + (impulse * power)
|
||||
end
|
||||
end]]
|
||||
|
||||
local gravity_motion_offset = 0.07840001
|
||||
|
||||
--[[
|
||||
local inav_position = nil
|
||||
local inav_delta = nil
|
||||
local scaler = 20
|
||||
|
||||
local function navigation()
|
||||
while true do
|
||||
local real_pos = vector.new(gps.locate())
|
||||
if inav_position then
|
||||
print(inav_position - real_pos)
|
||||
local real_delta = (inav_position - real_pos):length()
|
||||
local delta_size = inav_delta:length()
|
||||
print("calculated delta was", delta_size / real_delta, "of real")
|
||||
end
|
||||
inav_position = real_pos
|
||||
inav_delta = vector.new(0, 0, 0)
|
||||
sleep(3)
|
||||
end
|
||||
end
|
||||
]]
|
||||
|
||||
local function scan_entities()
|
||||
local last_velocity
|
||||
local last_time
|
||||
|
||||
local function update_motion_vars(new_meta)
|
||||
local time = os.epoch "utc"
|
||||
if user_meta then
|
||||
-- walking hack
|
||||
if not (user_meta.isFlying or user_meta.isElytraFlying) then
|
||||
if user_meta.isInWater then
|
||||
new_meta.motionY = new_meta.motionY + 0.02
|
||||
else
|
||||
new_meta.motionY = new_meta.motionY + gravity_motion_offset
|
||||
end
|
||||
end
|
||||
user_meta.velocity = vector.new(new_meta.motionX, new_meta.motionY, new_meta.motionZ)
|
||||
user_meta.motionX = new_meta.motionX
|
||||
user_meta.motionY = new_meta.motionY
|
||||
user_meta.motionZ = new_meta.motionZ
|
||||
user_meta.pitch = new_meta.pitch
|
||||
user_meta.yaw = new_meta.yaw
|
||||
if last_time and last_velocity then
|
||||
local timestep = (time - last_time) / 1000
|
||||
user_meta.acceleration = (user_meta.velocity - last_velocity) / timestep
|
||||
end
|
||||
last_velocity = user_meta.velocity
|
||||
end
|
||||
last_time = time
|
||||
end
|
||||
|
||||
while true do
|
||||
fast_mode_reqs.laser = false
|
||||
fast_mode_reqs.acting = false
|
||||
local entities = ni.sense()
|
||||
for _, entity in pairs(entities) do
|
||||
if entity.displayName == username then
|
||||
update_motion_vars(entity)
|
||||
end
|
||||
end
|
||||
local maybe_players = {}
|
||||
local things = {}
|
||||
local lasers = {}
|
||||
local ok, user_meta_temp
|
||||
if ni.getMetaOwner then
|
||||
ok, user_meta_temp = pcall(ni.getMetaOwner, username)
|
||||
else
|
||||
ok, user_meta_temp = pcall(ni.getMetaByName, username)
|
||||
end
|
||||
if not ok or not user_meta_temp then
|
||||
speaker.playSound("entity.enderdragon.death")
|
||||
user_meta = nil
|
||||
for name, cfg in pairs(settings_cfg) do
|
||||
if cfg.persist == false then
|
||||
cfg.value = nil
|
||||
end
|
||||
end
|
||||
work_queue = {}
|
||||
ni = peripheral.wrap "back"
|
||||
ni.canvas().clear()
|
||||
error("Failed to fetch user metadata (assuming death): " .. tostring(user_meta_temp))
|
||||
end
|
||||
user_meta = user_meta_temp
|
||||
update_motion_vars(user_meta)
|
||||
if user_meta.acceleration and gsettings.show_acceleration then
|
||||
status_lines.acceleration = ("Acc: %.2f/%.2f"):format(user_meta.acceleration:length(),
|
||||
user_meta.acceleration.y)
|
||||
end
|
||||
|
||||
status_lines.vel = ("Vel: %.2f/%.2f"):format(user_meta.velocity:length(), user_meta.motionY)
|
||||
|
||||
fast_mode_reqs.lasers = false
|
||||
|
||||
for _, entity in pairs(entities) do
|
||||
entity.s = vector.new(entity.x, entity.y, entity.z)
|
||||
entity.v = vector.new(entity.motionX, entity.motionY, entity.motionZ)
|
||||
if entity.displayName ~= username then
|
||||
things[entity.displayName] = (things[entity.displayName] or 0) + 1
|
||||
end
|
||||
if entity.displayName ~= username and entity.displayName == entity.name and (math.floor(entity.yaw) ~= entity.yaw and math.floor(entity.pitch) ~= entity.pitch) then -- player, quite possibly
|
||||
entity.v = entity.v + vector.new(0, gravity_motion_offset, 0)
|
||||
table.insert(maybe_players, entity)
|
||||
end
|
||||
if entity.name == "plethora:laser" then
|
||||
fast_mode_reqs.lasers = true
|
||||
end
|
||||
if entity.name == "plethora:laser" and not addressed_lasers[entity.id] then
|
||||
local closest_approach, param = project(entity.s, entity.v - user_meta.velocity, vector.new(0, 0, 0))
|
||||
if param > 0 and vector_sqlength(closest_approach) < 5 then
|
||||
push_notice "Laser detected"
|
||||
fast_mode_reqs.laser = true
|
||||
local time_to_impact = (entity.s:length() / (entity.v - user_meta.velocity):length()) / 20
|
||||
print("got inbound laser", time_to_impact, vector_sqlength(closest_approach), param)
|
||||
addressed_lasers[entity.id] = true
|
||||
if gsettings.dodge then
|
||||
schedule(function()
|
||||
push_notice "Executing dodging"
|
||||
local dir2d = vector.new(entity.motionX - user_meta.motionX, 0,
|
||||
entity.motionZ - user_meta.motionZ)
|
||||
local perpendicular_dir2d = vector.new(1, 0, -dir2d.x / dir2d.z)
|
||||
-- NaN contingency measures
|
||||
if perpendicular_dir2d.x ~= perpendicular_dir2d.x or perpendicular_dir2d.z ~= perpendicular_dir2d.z then
|
||||
perpendicular_dir2d = vector.new(-dir2d.z / dir2d.x, 0, 1)
|
||||
end
|
||||
if perpendicular_dir2d.x ~= perpendicular_dir2d.x or perpendicular_dir2d.z ~= perpendicular_dir2d.z then
|
||||
local theta = math.random() * math.pi * 2
|
||||
perpendicular_dir2d = vector.new(math.cos(theta), 0, math.sin(theta))
|
||||
end
|
||||
local y, p = calc_yaw_pitch(perpendicular_dir2d)
|
||||
if math.random(1, 2) == 1 then p = -p end
|
||||
ni.launch(y, p, 3)
|
||||
end, math.max(0, time_to_impact / 2 - 0.1))
|
||||
end
|
||||
schedule(function() addressed_lasers[entity.id] = false end, 15)
|
||||
table.insert(lasers, entity)
|
||||
end
|
||||
end
|
||||
end
|
||||
for _, laser in pairs(lasers) do
|
||||
for _, player in pairs(maybe_players) do
|
||||
local closest_approach, param = project(laser.s, laser.v, player.s)
|
||||
print(player.displayName, closest_approach, param)
|
||||
if param < 0 and vector_sqlength(closest_approach - player.s) < 8 and gsettings.counter then
|
||||
print("execute counterattack", player.displayName)
|
||||
push_notice(("Counterattack %s"):format(player.displayName))
|
||||
targets[player.displayName:lower()] = "laser"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local flags = {}
|
||||
for key, cfg in pairs(settings_cfg) do
|
||||
if cfg.shortcode and cfg.type == "bool" then
|
||||
if gsettings[key] then
|
||||
table.insert(flags, cfg.shortcode)
|
||||
end
|
||||
end
|
||||
end
|
||||
table.sort(flags)
|
||||
status_lines.flags = "Flags: " .. table.concat(flags)
|
||||
|
||||
local i = 0
|
||||
local ok, err = pcall(group.clear)
|
||||
if not ok then
|
||||
initialize_group_thing()
|
||||
end
|
||||
group_3d.clear()
|
||||
group_3d.recenter()
|
||||
local time = os.epoch "utc"
|
||||
for _, text in pairs(status_lines) do
|
||||
group.addText({ 0, i * 7 }, text, colortheme.status, 0.6)
|
||||
i = i + 1
|
||||
end
|
||||
for ix, info in pairs(notices) do
|
||||
if time >= (info[2] + 2000) then notices[ix] = nil end
|
||||
group.addText({ 0, i * 7 }, info[1], colortheme.notice, 0.6)
|
||||
i = i + 1
|
||||
end
|
||||
for thing, count in pairs(things) do
|
||||
local text = thing
|
||||
if count ~= 1 then text = text .. " " .. count end
|
||||
group.addText({ 0, i * 7 }, text, colortheme[is_target(thing) or "entity"], 0.6)
|
||||
i = i + 1
|
||||
end
|
||||
|
||||
for _, entity in pairs(entities) do
|
||||
local action = is_target(entity.displayName)
|
||||
if action then
|
||||
if action == "laser" then
|
||||
schedule(function() lase(entity) end, 0, entity.id)
|
||||
elseif action == "watch" then
|
||||
schedule(function() ni.look(calc_yaw_pitch(entity.s)) end, 0, entity.id)
|
||||
elseif action == "follow" then
|
||||
schedule(function()
|
||||
local y, p = calc_yaw_pitch(entity.s)
|
||||
ni.launch(y, p, math.min(entity.s:length() / 24, 2))
|
||||
end, 0, entity.id)
|
||||
end
|
||||
fast_mode_reqs.acting = true
|
||||
end
|
||||
if gsettings.highlight and things[entity.displayName] and things[entity.displayName] < 20 then
|
||||
local color = colortheme[action or "entity"]
|
||||
local object = group_3d.addBox(entity.x - 0.25, entity.y - 0.25, entity.z - 0.25)
|
||||
object.setColor(color)
|
||||
object.setAlpha(128)
|
||||
object.setDepthTested(false)
|
||||
object.setSize(0.5, 0.5, 0.5)
|
||||
if gsettings.ext_highlight then
|
||||
local frame = group_3d.addFrame({ entity.x - 0.25, entity.y + 0.25, entity.z - 0.25 })
|
||||
frame.setDepthTested(false)
|
||||
frame.addText({ 0, 0 }, entity.displayName, nil, 3)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local fast_mode = false
|
||||
for _, m in pairs(fast_mode_reqs) do
|
||||
fast_mode = fast_mode or m
|
||||
end
|
||||
|
||||
--status_lines.fast_mode = "Fast scan: " .. bool_to_yn(fast_mode)
|
||||
|
||||
if fast_mode then sleep() else sleep(0.2) end
|
||||
end
|
||||
end
|
||||
|
||||
local function queue_handler()
|
||||
while true do
|
||||
local init = os.clock()
|
||||
for index, arg in pairs(work_queue) do
|
||||
if arg[1] <= os.clock() then
|
||||
arg[2]()
|
||||
work_queue[index] = nil
|
||||
end
|
||||
end
|
||||
if os.clock() == init then sleep() end
|
||||
end
|
||||
end
|
||||
|
||||
local function estimate_tps()
|
||||
while true do
|
||||
local game_time_start = os.epoch "utc"
|
||||
sleep(5)
|
||||
local game_time_end = os.epoch "utc"
|
||||
local utc_elapsed_seconds = (game_time_end - game_time_start) / 5000
|
||||
status_lines.tps = ("TPS: %.0f"):format(20 / utc_elapsed_seconds)
|
||||
end
|
||||
end
|
||||
|
||||
local flight_shortcodes = {
|
||||
o = "off",
|
||||
b = "brake",
|
||||
h = "hpower",
|
||||
l = "lpower",
|
||||
s = "std",
|
||||
a = "align",
|
||||
v = "hover"
|
||||
}
|
||||
|
||||
local flight_powers = {
|
||||
std = 1,
|
||||
lpower = 0.5,
|
||||
hpower = 4,
|
||||
align = 1,
|
||||
hover = 1
|
||||
}
|
||||
|
||||
local flight_target = nil
|
||||
|
||||
local function xz_plane(v)
|
||||
return vector.new(v.x, 0, v.z)
|
||||
end
|
||||
|
||||
-- As far as I can tell, a speed of more than 10 in the X/Z plane causes a reset of your velocity by the server and thus horrible rubberbanding.
|
||||
local function maxvel_compensatory_launch(yaw, pitch, power)
|
||||
local effective_power = (user_meta and user_meta.isElytraFlying) and (power * 0.4) or power
|
||||
local impulse = direction_vector(yaw, pitch) * effective_power
|
||||
local power_over_velocity_limit = math.max(xz_plane(user_meta.velocity + impulse):length() - 10, 0)
|
||||
if user_meta and user_meta.isElytraFlying then
|
||||
power = power - power_over_velocity_limit / 0.4
|
||||
else
|
||||
power = power - power_over_velocity_limit
|
||||
end
|
||||
power = math.min(math.max(power, 0), 4)
|
||||
if power > 0 then
|
||||
ni.launch(yaw, pitch, power)
|
||||
end
|
||||
end
|
||||
|
||||
local function run_flight()
|
||||
while true do
|
||||
while not user_meta do sleep() end
|
||||
if flight_shortcodes[gsettings.flight] then gsettings.flight = flight_shortcodes[gsettings.flight] end
|
||||
local disp = gsettings.flight
|
||||
local sleep_time = 0.1
|
||||
if ((user_meta.acceleration and (
|
||||
(user_meta.motionY + math.max(user_meta.acceleration.y, 0) < -0.3)
|
||||
or (user_meta.acceleration.y < -5)))
|
||||
or (not user_meta.acceleration and user_meta.motionY < -0.3))
|
||||
and gsettings.brake then
|
||||
--[[if user_meta.motionY < 0 then
|
||||
local stop_commands = {}
|
||||
local remaining = -user_meta.motionY + 0.1
|
||||
while remaining > 0 do
|
||||
local pow = math.min(4, remaining)
|
||||
table.insert(stop_commands, function() ni.launch(0, 270, pow) end)
|
||||
remaining = remaining - pow
|
||||
end
|
||||
local go_commands = {}
|
||||
local remaining = -user_meta.motionY
|
||||
while remaining > 0 do
|
||||
local pow = math.min(4, remaining)
|
||||
table.insert(go_commands, function() ni.launch(0, 90, pow) end)
|
||||
remaining = remaining - pow
|
||||
end
|
||||
parallel.waitForAll(table.unpack(stop_commands))
|
||||
parallel.waitForAll(table.unpack(go_commands))
|
||||
else]]
|
||||
ni.launch(0, 270, math.max(0.4, math.min(4, -user_meta.motionY / 3)))
|
||||
sleep_time = nil
|
||||
--ni.launch(0, 270, 0.4)
|
||||
--end
|
||||
fast_mode_reqs.flying = true
|
||||
disp = disp .. " F"
|
||||
end
|
||||
fast_mode_reqs.flying = false
|
||||
if gsettings.flight == "std" or gsettings.flight == "hpower" or gsettings.flight == "lpower" or gsettings.flight == "align" or gsettings.flight == "hover" then
|
||||
if user_meta.isElytraFlying or user_meta.isSneaking then
|
||||
fast_mode_reqs.flying = true
|
||||
end
|
||||
if user_meta.isElytraFlying ~= user_meta.isSneaking then
|
||||
local power = flight_powers[gsettings.flight]
|
||||
if user_meta.isInWater then
|
||||
power = power * 2
|
||||
end
|
||||
local yaw, pitch = user_meta.yaw, user_meta.pitch
|
||||
if pitch == 90 and gsettings.pitch_controls then
|
||||
local y, p = calc_yaw_pitch(-user_meta.velocity)
|
||||
ni.launch(y, p, math.min(user_meta.velocity:length(), 4))
|
||||
sleep_time = nil
|
||||
else
|
||||
maxvel_compensatory_launch(yaw, (gsettings.flight ~= "align" and pitch) or 10, power)
|
||||
sleep_time = nil
|
||||
end
|
||||
end
|
||||
elseif gsettings.flight == "brake" then
|
||||
local y, p = calc_yaw_pitch(-user_meta.velocity)
|
||||
ni.launch(y, p, math.min(user_meta.velocity:length(), 1))
|
||||
fast_mode_reqs.flying = true
|
||||
end
|
||||
if gsettings.flight == "hover" then
|
||||
fast_mode_reqs.flying = true
|
||||
local mY = user_meta.motionY
|
||||
sleep_time = 0
|
||||
mY = (mY - 0.138) / 0.8
|
||||
|
||||
-- If it is sufficiently large then we fire ourselves in that direction.
|
||||
if mY > 0.5 or mY < 0 then
|
||||
local sign = 1
|
||||
if mY < 0 then sign = -1 end
|
||||
ni.launch(0, 90 * sign, math.min(4, math.abs(mY)))
|
||||
sleep_time = nil
|
||||
end
|
||||
end
|
||||
status_lines.flight = "Flight: " .. disp
|
||||
if sleep_time ~= nil then sleep(sleep_time) end
|
||||
end
|
||||
end
|
||||
|
||||
local function within_epsilon(a, b)
|
||||
return math.abs(a - b) < 1
|
||||
end
|
||||
|
||||
-- TODO: unified navigation framework
|
||||
local function fly_to_target()
|
||||
local last_s, last_t
|
||||
while true do
|
||||
while not user_meta do sleep() end
|
||||
if flight_target then
|
||||
local x, y, z = gps.locate()
|
||||
if not y then
|
||||
push_notice "GPS error"
|
||||
else
|
||||
if y < 256 then
|
||||
ni.launch(0, 270, 4)
|
||||
end
|
||||
local position = vector.new(x, 0, z)
|
||||
local curr_t = os.clock()
|
||||
local displacement = flight_target - position
|
||||
status_lines.flight_target = ("%d from %d %d"):format(displacement:length(), flight_target.x,
|
||||
flight_target.z)
|
||||
local real_displacement = displacement
|
||||
if last_t then
|
||||
local delta_t = curr_t - last_t
|
||||
local delta_s = displacement - last_s
|
||||
local deriv = delta_s * (1 / delta_t)
|
||||
displacement = displacement + deriv
|
||||
end
|
||||
local pow = math.max(math.min(4, displacement:length() / 40), 0)
|
||||
local yaw, pitch = calc_yaw_pitch(displacement)
|
||||
maxvel_compensatory_launch(yaw, pitch, pow)
|
||||
--sleep(0)
|
||||
last_t = curr_t
|
||||
last_s = real_displacement
|
||||
if within_epsilon(position.x, flight_target.x) and within_epsilon(position.z, flight_target.z) then flight_target = nil end
|
||||
end
|
||||
else
|
||||
status_lines.flight_target = nil
|
||||
end
|
||||
sleep(0.1)
|
||||
end
|
||||
end
|
||||
|
||||
local function handle_commands()
|
||||
while true do
|
||||
local _, user, command, args = os.pullEvent "command"
|
||||
if user == username then
|
||||
if command == "lase" then
|
||||
if args[1] then
|
||||
targets[table.concat(args, " "):lower()] = "laser"
|
||||
end
|
||||
elseif command == "ctg" then
|
||||
args[1] = args[1] or ".*"
|
||||
local arg = table.concat(args, " ")
|
||||
for k, v in pairs(targets) do
|
||||
if k:lower():match(arg) then
|
||||
chatbox.tell(user, k .. ": " .. v)
|
||||
targets[k:lower()] = nil
|
||||
end
|
||||
end
|
||||
elseif command == "watch" then
|
||||
if args[1] then
|
||||
targets[table.concat(args, " "):lower()] = "watch"
|
||||
end
|
||||
elseif command == "select" then
|
||||
if args[1] then
|
||||
targets[table.concat(args, " "):lower()] = "select"
|
||||
end
|
||||
elseif command == "follow" then
|
||||
if args[1] then
|
||||
targets[table.concat(args, " "):lower()] = "follow"
|
||||
end
|
||||
elseif command == "notice_test" then
|
||||
push_notice(table.concat(args, " "))
|
||||
elseif command == "flyto" then
|
||||
if args[1] == "cancel" or args[1] == nil then
|
||||
flight_target = nil
|
||||
else
|
||||
local x, z = tonumber(args[1]), tonumber(args[2])
|
||||
if type(x) ~= "number" or type(z) ~= "number" then
|
||||
chatbox.tell(user, "Usage: \\flyto x z")
|
||||
else
|
||||
flight_target = vector.new(x, 0, z)
|
||||
end
|
||||
end
|
||||
elseif command == "update" then
|
||||
local h, e = http.get "https://osmarks.net/stuff/ni-ctl.lua"
|
||||
assert(h, "HTTP: " .. (e or ""))
|
||||
local data = h.readAll()
|
||||
h.close()
|
||||
local file = fs.open(shell.getRunningProgram(), "w")
|
||||
file.write(data)
|
||||
file.close()
|
||||
chatbox.tell(user, "Update updated.")
|
||||
else
|
||||
for key, cfg in pairs(settings_cfg) do
|
||||
if key == command or cfg.shortcode == command or (cfg.alias and cfg.alias[command]) then
|
||||
if cfg.type == "bool" then
|
||||
if args[1] and (args[1]:match "y" or args[1]:match "t" or args[1]:match "on") then
|
||||
gsettings[key] = true
|
||||
elseif args[1] and (args[1]:match "f" or args[1]:match "^n") then
|
||||
gsettings[key] = false
|
||||
else
|
||||
gsettings[key] = not gsettings[key]
|
||||
end
|
||||
chatbox.tell(user, ("%s: %s"):format(key, tostring(gsettings[key])))
|
||||
elseif cfg.type == "number" then
|
||||
local value = tonumber(args[1])
|
||||
if not value then chatbox.tell(user, "Not a number") end
|
||||
if cfg.max and value > cfg.max then chatbox.tell(user, ("Max is %d"):format(cfg.max)) end
|
||||
if cfg.min and value < cfg.min then chatbox.tell(user, ("Max is %d"):format(cfg.min)) end
|
||||
gsettings[key] = value
|
||||
else
|
||||
gsettings[key] = args[1]
|
||||
end
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function drill()
|
||||
while true do
|
||||
if gsettings.drill then
|
||||
repeat sleep() until user_meta
|
||||
if offload_laser then
|
||||
offload_protocol("fire", user_meta.yaw, user_meta.pitch, gsettings.power)
|
||||
else
|
||||
schedule(
|
||||
function()
|
||||
repeat sleep() until user_meta
|
||||
ni.fire(user_meta.yaw, user_meta.pitch, gsettings.power)
|
||||
end, 0, "drill")
|
||||
end
|
||||
sleep(0.1)
|
||||
else
|
||||
os.pullEvent "settings_change"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function module.init(init)
|
||||
init.log("This module is WIP!")
|
||||
init.addTask(function()
|
||||
while true do
|
||||
local cmds = { queue_handler, scan_entities, run_flight, handle_commands, estimate_tps, fly_to_target, drill }
|
||||
if spudnet_background then
|
||||
table.insert(cmds, spudnet_background)
|
||||
end
|
||||
local ok, err = pcall(parallel.waitForAny, unpack(cmds))
|
||||
if err == "Terminated" then break end
|
||||
printError(err)
|
||||
sleep(0.2)
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
function module.setup(neural)
|
||||
ni = neural
|
||||
w, h = ni.canvas().getSize()
|
||||
|
||||
initialize_group_thing()
|
||||
end
|
||||
|
||||
return module
|
||||
110
pcm-norma-client.lua
Normal file
110
pcm-norma-client.lua
Normal file
|
|
@ -0,0 +1,110 @@
|
|||
local connectionURL = "wss://0a71-2a03-ec00-b14a-55a-39f7-fdb9-1340-2e18.ngrok-free.app"
|
||||
|
||||
local function splitByChunk(text, chunkSize)
|
||||
local s = {}
|
||||
for i=1, #text, chunkSize do
|
||||
s[#s+1] = text:sub(i,i+chunkSize - 1)
|
||||
end
|
||||
return s
|
||||
end
|
||||
|
||||
function string.starts(String,Start)
|
||||
return string.sub(String,1,string.len(Start))==Start
|
||||
end
|
||||
|
||||
local dfpwm = require("cc.audio.dfpwm")
|
||||
local ws, err = http.websocket(connectionURL)
|
||||
local speakers = {peripheral.find("speaker")}
|
||||
local monitor = peripheral.find("monitor")
|
||||
|
||||
if monitor then
|
||||
monitor.clear()
|
||||
monitor.setCursorPos(1, 1)
|
||||
monitor.write("yourfriend's music")
|
||||
monitor.setCursorPos(1, 3)
|
||||
monitor.write("currently playing:")
|
||||
monitor.setCursorPos(1, 4)
|
||||
|
||||
monitor.write("nothing")
|
||||
end
|
||||
|
||||
function restart()
|
||||
print("connection with ", connectionURL, " closed!")
|
||||
print("restarting in 1s")
|
||||
os.sleep(1)
|
||||
ws, err = http.websocket(connectionURL)
|
||||
if not ws then
|
||||
printError(err)
|
||||
restart()
|
||||
end
|
||||
end
|
||||
|
||||
if not ws then
|
||||
restart()
|
||||
printError(err)
|
||||
end
|
||||
|
||||
|
||||
parallel.waitForAll(
|
||||
function()
|
||||
while true do
|
||||
local _, url = os.pullEvent("websocket_closed")
|
||||
if url == connectionURL then
|
||||
restart()
|
||||
end
|
||||
end
|
||||
end,
|
||||
function()
|
||||
while true do
|
||||
local _, url, resp, isBinary = os.pullEvent("websocket_message")
|
||||
if url == connectionURL then
|
||||
if not string.starts(resp, "{") then
|
||||
local bytes = {("b"):rep(#resp):unpack(resp)}
|
||||
bytes[#bytes]=nil
|
||||
|
||||
local functions = {}
|
||||
|
||||
for k,v in pairs(speakers) do
|
||||
table.insert(functions, function ()
|
||||
while not v.playAudio(bytes, 3) do
|
||||
os.pullEvent("speaker_audio_empty")
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
parallel.waitForAll(table.unpack(functions))
|
||||
else
|
||||
local json = textutils.unserialiseJSON(resp)
|
||||
if json.type == "update" then
|
||||
local file = io.open(shell.getRunningProgram(), "w")
|
||||
if not file then return end
|
||||
file:write(json.file)
|
||||
end
|
||||
if json.type == "playing" then
|
||||
if monitor then
|
||||
local width, height = monitor.getSize()
|
||||
for k=4,height do
|
||||
monitor.setCursorPos(1, k)
|
||||
monitor.write(string.rep(" ", width))
|
||||
end
|
||||
|
||||
for k, v in pairs(splitByChunk(json.data, width)) do
|
||||
monitor.setCursorPos(1, k+3)
|
||||
monitor.write(v)
|
||||
end
|
||||
else
|
||||
print("Playing: ")
|
||||
print(json.data)
|
||||
end
|
||||
end
|
||||
if json.type == "reboot" then
|
||||
os.reboot()
|
||||
end
|
||||
if json.type == "shutdown" then
|
||||
os.shutdown()
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
)
|
||||
282
shrekFly.lua
Normal file
282
shrekFly.lua
Normal file
|
|
@ -0,0 +1,282 @@
|
|||
-- shrekflight
|
||||
-- Releasd under MIT by ShreksHellraiser
|
||||
|
||||
-- This is a script that provides basic creative-like flight and automatic flight to coordinates using plethora.
|
||||
-- Requires a keyboard, entity sensor, introspection module, and kinetic module
|
||||
|
||||
-- To fly, simply double tap space, hold space to fly up, hold shift to fly down.
|
||||
-- You can modify your horizontal and vertical speeds in this with the commands
|
||||
-- ^vspeed and ^hspeed
|
||||
|
||||
-- This also includes a ^goto command, this accepts a coordinate and will automatically fly you to that location.
|
||||
-- This is not *100%* reliable, but as long as you *first* set your position ~200 blocks above the ground and then
|
||||
-- set your target to where you want you should be fine.
|
||||
-- supports ~ style relative coordinates for your current gps location, and . for your set goto position.
|
||||
-- ^toggle will disable/reenable goto, entering fly mode by double tapping will too.
|
||||
|
||||
local modules = peripheral.find("neuralInterface")
|
||||
if not modules then
|
||||
error("Must have a neural interface", 0)
|
||||
end
|
||||
|
||||
if not modules.hasModule("plethora:sensor") then error("Must have a sensor", 0) end
|
||||
if not modules.hasModule("plethora:introspection") then error("Must have an introspection module", 0) end
|
||||
if not modules.hasModule("plethora:kinetic", 0) then error("Must have a kinetic agument", 0) end
|
||||
-- if not modules.hasModule("plethora:glasses") then error("Must have overlay glasses", 0) end
|
||||
|
||||
local function start_pid(k_p, k_i, k_d)
|
||||
local e, de, ie = 0, 0, 0
|
||||
---Process a frame of the PID
|
||||
---@param dt number frametime
|
||||
---@param sp number desired setpoint
|
||||
---@param pv number process variable
|
||||
return function(dt, sp, pv)
|
||||
local ne = sp - pv
|
||||
de = (ne - e) / dt -- instantaneous derivative
|
||||
ie = ie + (ne * dt) -- summed integral
|
||||
e = ne -- error
|
||||
return (k_p * e) + (k_i * ie) + (k_d * de)
|
||||
end
|
||||
end
|
||||
|
||||
local target_coords = { 0, 200, 0 }
|
||||
local goto_enable = false
|
||||
local hover_enable = false
|
||||
|
||||
local function control()
|
||||
-- assert(chatbox.isConnected(), "Chatbox isn't connected!")
|
||||
while true do
|
||||
local event, user, command, args, data = os.pullEvent("command")
|
||||
if data.ownerOnly then
|
||||
if command == "goto" then
|
||||
local x, y, z = gps.locate()
|
||||
for i = 1, 3 do
|
||||
if args[i] then
|
||||
if args[i]:sub(1, 1) == "~" and x and y and z then
|
||||
-- current coords
|
||||
local offset = tonumber(args[i]:sub(2, -1)) or 0
|
||||
print(offset)
|
||||
target_coords[i] = ((i == 1 and x) or (i == 2 and y) or z) + offset
|
||||
elseif args[i]:sub(1, 1) == "." then
|
||||
-- change it
|
||||
local offset = tonumber(args[i]:sub(2, -1)) or 0
|
||||
target_coords[i] = target_coords[i] + offset
|
||||
elseif tonumber(args[i]) then
|
||||
print(i, args[i])
|
||||
target_coords[i] = tonumber(args[i])
|
||||
end
|
||||
end
|
||||
end
|
||||
print(("target %u %u %u"):format(target_coords[1], target_coords[2], target_coords[3]))
|
||||
goto_enable = true
|
||||
hover_enable = false
|
||||
elseif command == "toggle" then
|
||||
hover_enable = hover_enable and goto_enable
|
||||
goto_enable = not goto_enable
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function calc_xz_angle(x, z)
|
||||
local xz_angle = math.atan(math.abs(x / z)) * 180 / math.pi
|
||||
if z == 0 then
|
||||
xz_angle = 90
|
||||
end
|
||||
|
||||
if x > 0 then
|
||||
xz_angle = -xz_angle -- -90 @ +x
|
||||
if z < 0 then
|
||||
-- -180 @ -z
|
||||
xz_angle = -180 - xz_angle
|
||||
end
|
||||
elseif z < 0 then
|
||||
-- +180 @ -z
|
||||
xz_angle = -180 - xz_angle
|
||||
end
|
||||
return xz_angle
|
||||
end
|
||||
|
||||
local function calc_yxz_angle(y, xz)
|
||||
local yxz_angle = -math.atan(math.abs(y / xz)) * 180 / math.pi
|
||||
if xz == 0 then
|
||||
yxz_angle = -90
|
||||
end
|
||||
|
||||
-- hard coded boost to emphasize upward vertical movement
|
||||
-- if y > 2 and yxz_angle < 10 then
|
||||
-- yxz_angle = 20
|
||||
-- end
|
||||
if y < 0 then
|
||||
yxz_angle = -yxz_angle
|
||||
end
|
||||
return yxz_angle
|
||||
end
|
||||
|
||||
local function p_pid()
|
||||
local y_pid = start_pid(0.05, 0.005, 0.06)
|
||||
local x_pid = start_pid(0.1, 0, 0)
|
||||
local z_pid = start_pid(0.1, 0, 0)
|
||||
local t0 = os.epoch("utc")
|
||||
sleep()
|
||||
while true do
|
||||
sleep(0.2)
|
||||
local x, y, z = gps.locate()
|
||||
if goto_enable and x and y and z and x == x and y == y and z == z then
|
||||
local t = os.epoch("utc")
|
||||
local dt = (t - t0) / 1000
|
||||
local y_impulse = y_pid(dt, target_coords[2], y)
|
||||
local x_impulse = x_pid(dt, target_coords[1], x)
|
||||
local z_impulse = z_pid(dt, target_coords[3], z)
|
||||
local x_vec = vector.new(x_impulse, 0, 0)
|
||||
local y_vec = vector.new(0, y_impulse, 0)
|
||||
local z_vec = vector.new(0, 0, z_impulse)
|
||||
local result = ((x_vec + y_vec + z_vec) / 3)
|
||||
|
||||
local xz_hyp = math.sqrt(result.x ^ 2 + result.z ^ 2)
|
||||
local xz_angle = calc_xz_angle(result.x, result.z)
|
||||
local yxz_angle = calc_yxz_angle(result.y, xz_hyp)
|
||||
|
||||
modules.launch(xz_angle, yxz_angle, math.min(4, math.abs(result:length())))
|
||||
t0 = t
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local target_vel = { 0, 0, 0 }
|
||||
local y_impulse_scale = 0.5
|
||||
local k_p = 4
|
||||
local owner = modules.getMetaOwner()
|
||||
local function v_pid()
|
||||
local x_pid = start_pid(0.1, 0, 0)
|
||||
local z_pid = start_pid(0.1, 0, 0)
|
||||
local t0 = os.epoch("utc")
|
||||
sleep()
|
||||
while true do
|
||||
local t = os.epoch("utc")
|
||||
while hover_enable and not goto_enable do
|
||||
t = os.epoch("utc")
|
||||
sleep(0)
|
||||
owner = modules.getMetaOwner()
|
||||
if not owner.isAirborne then
|
||||
hover_enable = false
|
||||
end
|
||||
local dt = (t - t0) / 1000
|
||||
local ticks = dt / 0.05
|
||||
local x, y, z = owner.motionX, owner.motionY, owner.motionZ
|
||||
|
||||
local y_vel_err = (0.08 * ticks) + target_vel[2] - y
|
||||
-- IMPULSE STRENGTH LINEARLY CORROLATES TO VELOCITY
|
||||
local y_impulse = k_p * y_vel_err / y_impulse_scale
|
||||
|
||||
local x_impulse = x_pid(dt, target_vel[1], x)
|
||||
local z_impulse = z_pid(dt, target_vel[3], z)
|
||||
|
||||
local x_vec = vector.new(x_impulse, 0, 0)
|
||||
local y_vec = vector.new(0, y_impulse, 0)
|
||||
local z_vec = vector.new(0, 0, z_impulse)
|
||||
local result = ((x_vec + y_vec + z_vec) / 3)
|
||||
local xz_hyp = math.sqrt(result.x ^ 2 + result.z ^ 2)
|
||||
local xz_angle = calc_xz_angle(result.x, result.z)
|
||||
local yxz_angle = calc_yxz_angle(result.y, xz_hyp)
|
||||
local power = math.min(4, math.abs(result:length()))
|
||||
|
||||
modules.launch(xz_angle, yxz_angle, power)
|
||||
t0 = t
|
||||
end
|
||||
os.pullEvent("velocity_control")
|
||||
end
|
||||
end
|
||||
|
||||
local function wrap_angle(t)
|
||||
if t > 180 then
|
||||
t = -360 + t
|
||||
elseif t < -180 then
|
||||
t = 360 + t
|
||||
end
|
||||
return t
|
||||
end
|
||||
|
||||
local function rad(deg)
|
||||
return deg / 180 * math.pi
|
||||
end
|
||||
|
||||
local function deg(rad)
|
||||
return rad * 180 / math.pi
|
||||
end
|
||||
|
||||
local v_speed = 0.5
|
||||
local sprinting_speed = 6
|
||||
|
||||
local held_keys = {}
|
||||
local function get_v_vectors()
|
||||
local theta = owner.yaw
|
||||
local result = vector.new(0, 0, 0)
|
||||
local forward_power = ((held_keys[keys.w] and 1) or (held_keys[keys.s] and -1) or 0)
|
||||
local sideways_power = ((held_keys[keys.d] and -1) or (held_keys[keys.a] and 1) or 0)
|
||||
local h_power = 3
|
||||
if owner.isSprinting then
|
||||
h_power = sprinting_speed
|
||||
end
|
||||
result.x = result.x + h_power * math.cos(rad(theta)) * sideways_power
|
||||
result.z = result.z + h_power * math.sin(rad(theta)) * sideways_power
|
||||
result.x = result.x + h_power * math.cos(rad(wrap_angle(theta + 90))) * forward_power
|
||||
result.z = result.z + h_power * math.sin(rad(wrap_angle(theta + 90))) * forward_power
|
||||
return result.x, result.z
|
||||
end
|
||||
|
||||
local function v_control()
|
||||
local space_presses = 0
|
||||
local space_press_timer
|
||||
local t0 = os.epoch("utc")
|
||||
while true do
|
||||
local event, key, command, args, data = os.pullEvent()
|
||||
local t = os.epoch("utc")
|
||||
local dt = (t - t0) / 1000
|
||||
if event == "key" then
|
||||
if key == keys.space or key == keys.h then
|
||||
if not held_keys[keys.space] then
|
||||
space_presses = space_presses + 1
|
||||
end
|
||||
if space_presses > 1 or key == keys.h then
|
||||
-- double tapped
|
||||
hover_enable = not hover_enable
|
||||
goto_enable = false
|
||||
print("double tap", hover_enable)
|
||||
os.queueEvent("velocity_control")
|
||||
space_presses = 0
|
||||
else
|
||||
target_vel[2] = v_speed
|
||||
end
|
||||
else
|
||||
space_presses = 0
|
||||
end
|
||||
if key == keys.leftShift then
|
||||
target_vel[2] = -v_speed
|
||||
end
|
||||
held_keys[key] = true
|
||||
elseif event == "key_up" then
|
||||
held_keys[key] = false
|
||||
if key == keys.space or key == keys.leftShift then
|
||||
if space_press_timer then
|
||||
os.cancelTimer(space_press_timer)
|
||||
end
|
||||
space_press_timer = os.startTimer(0.5)
|
||||
target_vel[2] = 0
|
||||
end
|
||||
elseif event == "timer" and key == space_press_timer then
|
||||
space_presses = 0
|
||||
elseif event == "command" and data.ownerOnly then
|
||||
local speed = tonumber(args[1])
|
||||
if command == "hspeed" and speed then
|
||||
sprinting_speed = speed --[[@as number]]
|
||||
elseif command == "vspeed" and speed then
|
||||
v_speed = speed
|
||||
end
|
||||
end
|
||||
t0 = t
|
||||
target_vel[1], target_vel[3] = get_v_vectors()
|
||||
end
|
||||
end
|
||||
|
||||
parallel.waitForAny(v_pid, v_control, p_pid, control)
|
||||
Loading…
Add table
Add a link
Reference in a new issue