hyperlane/goto.lua
by hugeblank
1
term.clear()
2
term.setCursorPos(1,1)
3
local args = table.pack(...)
4
5
local mods = {}
6
local ms = peripheral.getMethods("back")
7
for i = 1, #ms do
8
mods[ms[i]] = true
9
end
10
assert(mods.getMetaOwner, "Entity Sensor & Introspection Module Required")
11
assert(mods.launch, "Kinetic Augment Required.")
12
assert(mods.getBlockMeta, "Automatic mode unavalible.")
13
14
local meta, mode, launch, yaw
15
local power = 0.5
16
local x, z = 0, 0
17
18
local laneData
19
20
do
21
local file = http.get("https://p.sc3.io/api/v1/pastes/Q2EcezEjth/raw")
22
laneData = textutils.unserializeJSON(file.readAll())
23
file.close()
24
25
for i = 1, #laneData.lanes-1 do
26
local lanea = laneData.lanes[i]
27
if not lanea.isects then
28
lanea.isects = {}
29
end
30
for j = i+1, #laneData.lanes do
31
local laneb = laneData.lanes[j]
32
if not laneb.isects then
33
laneb.isects = {}
34
end
35
if lanea.axis ~= laneb.axis and (lanea.pos >= laneb.start and lanea.pos <= laneb["end"]) then
36
lanea.isects[#lanea.isects+1] = { on = laneb, pos = lanea.pos }
37
laneb.isects[#laneb.isects+1] = { on = lanea, pos = laneb.pos }
38
end
39
end
40
end
41
for _, stop in ipairs(laneData.stops) do
42
local found = false
43
for _, lane in ipairs(laneData.lanes) do
44
if lane.name == stop.on then
45
-- if not lane.stops then
46
-- lane.stops = {}
47
-- end
48
-- lane.stops[#lane.stops+1] = stop
49
stop.on = lane
50
found = true
51
break
52
end
53
end
54
assert(found, stop.name..", '"..tostring(stop.on).."' is not a valid lane")
55
end
56
end
57
58
local function getxz(node)
59
if node.on.axis == "z" then
60
return node.on.pos, node.pos
61
else
62
return node.pos, node.on.pos
63
end
64
end
65
66
local function squaredDistance(x1, z1, x2, z2)
67
return math.pow(x2-x1, 2) + math.pow(z2-z1, 2)
68
end
69
70
local function aStar(start, goal, h)
71
local openSet, cameFrom = {start}, {}
72
local gScore = {
73
[start] = 0
74
}
75
local fScore = {
76
[start] = h(start)
77
}
78
79
while #openSet > 0 do
80
local current, score, r
81
for i, node in ipairs(openSet) do
82
if not current or score > fScore[node] then
83
current, score, r = node, fScore[node], i
84
end
85
end
86
if current.on == goal.on then
87
local total_path = {current, goal}
88
while cameFrom[current] do
89
current = cameFrom[current]
90
table.insert(total_path, 1, current)
91
end
92
return total_path
93
end
94
table.remove(openSet, r)
95
local x1, z1 = getxz(current)
96
for i, neighbor in ipairs(current.on.isects) do
97
local x2, z2 = getxz(neighbor)
98
local tent_gScore = gScore[current] + squaredDistance(x1, z1, x2, z2)
99
if not gScore[neighbor] or tent_gScore < gScore[neighbor] then
100
cameFrom[neighbor] = current
101
gScore[neighbor] = tent_gScore
102
fScore[neighbor] = tent_gScore + h(neighbor)
103
local add = true
104
for _, node in ipairs(openSet) do
105
if node == neighbor then
106
add = false
107
break
108
end
109
end
110
if add then
111
openSet[#openSet+1] = neighbor
112
end
113
end
114
end
115
end
116
error("Could not find path to "..goal.name)
117
end
118
119
local function route(to_stop, tx, tz)
120
local start = {
121
name = "Start"
122
}
123
for _, lane in ipairs(laneData.lanes) do
124
if lane.pos == tz and lane.axis == "x" then
125
start.on = lane
126
start.pos = tx
127
elseif lane.pos == tx and lane.axis == "z" then
128
start.on = lane
129
start.pos = tz
130
end
131
if start.on then
132
break
133
end
134
end
135
assert(start.on, "Not on lane? Something broke somewhere.")
136
local sx, sz = getxz(to_stop)
137
local route = aStar(start, to_stop, function(node)
138
local nx, nz = getxz(node)
139
return squaredDistance(nx, nz, sx, sz)
140
end)
141
return route
142
end
143
144
local onIce, routes = false
145
146
local function handleMetaOwner()
147
while true do
148
meta = peripheral.call("back", "getMetaOwner")
149
x, z = x + meta.motionX, z + meta.motionZ
150
end
151
end
152
153
local function handleRouting()
154
local dest = table.concat(args):lower()
155
for _, stop in ipairs(laneData.stops) do
156
if dest == stop.name:lower() then
157
dest = stop
158
break
159
end
160
end
161
assert(type(dest) ~= "string", "Could not find stop named"..table.concat(args))
162
while true do
163
if peripheral.call("back", "getBlockMeta", 0, -2, 0).name:match("slab") and peripheral.call("back", "getBlockMeta", 0, -3, 0).name == "minecraft:packed_ice" then
164
if not onIce then
165
routes = route(dest, math.floor(x), math.floor(z))
166
-- term.setCursorPos(1,2)
167
-- term.clearLine()
168
end
169
onIce = true
170
else
171
onIce = false
172
-- term.setCursorPos(1,1)
173
-- term.clearLine()
174
-- term.setCursorPos(1,2)
175
-- term.write("Not on a hyperlane. Get on lane to resume routing.")
176
print("Not on a hyperlane. Get on lane to resume routing.")
177
end
178
sleep(0.5)
179
end
180
end
181
182
local approach = 80 -- Start approach from 80 blocks away
183
local function reverse_lerp(goal, pos, vel)
184
local distance = math.max(pos, goal) - math.min(pos, goal)
185
if distance < approach then
186
if math.abs(vel) > 0.5 then
187
return 0, false
188
end
189
return math.max(math.pow(distance/approach, 2), distance <= 2 and 0.006 or 0.01), distance <= 0.1
190
else
191
return 1
192
end
193
end
194
195
local function handleMovement()
196
while true do
197
if routes and onIce then
198
-- term.setCursorPos(1, 1)
199
-- term.write("Routing from current position")
200
print("Routing from current position")
201
local xAxis = table.remove(routes, 1).on.axis == 'x'
202
while #routes > 0 and onIce do
203
local route = table.remove(routes, 1)
204
-- term.setCursorPos(1, 2)
205
-- term.clearLine()
206
-- term.write("Heading to "..(route.name or route.on.name))
207
local gx, gz = getxz(route)
208
gx, gz = gx + 0.5, gz + 0.5
209
local pm = 1
210
local done, get
211
if xAxis then -- inverse of route point axis
212
get = function()
213
local y = 90
214
if gx-x > 0 then
215
y = y - 180
216
end
217
return gx, x, meta.motionX, y
218
end
219
else
220
get = function()
221
local y = 180
222
if gz-z > 0 then
223
y = y - 180
224
end
225
return gz, z, meta.motionZ , y
226
end
227
end
228
print("Heading to "..(route.name or route.on.name))
229
parallel.waitForAny(function()
230
while true do
231
local g, p, v, y = get()
232
pm, done = reverse_lerp(g, p, v)
233
peripheral.call("back", "launch", y, 0, power*pm)
234
end
235
end, function()
236
local prev = false
237
while true do
238
if prev and done then
239
return
240
end
241
prev = done
242
sleep(1)
243
end
244
end)
245
xAxis = not xAxis
246
end
247
-- term.setCursorPos(1, 1)
248
-- term.clearLine()
249
-- term.write("Arrived at destination")
250
print("Arrived at destination")
251
-- term.setCursorPos(1, 2)
252
-- term.clearLine()
253
-- term.setCursorPos(1, 2)
254
sleep(1)
255
return
256
else
257
sleep(1)
258
end
259
end
260
end
261
262
local function handleGPS()
263
while true do
264
local tx, _, tz = gps.locate()
265
if tx then
266
x, z = tx, tz
267
end
268
sleep(1)
269
end
270
end
271
272
local ok, err = pcall(function()
273
parallel.waitForAny(handleMetaOwner, handleRouting, handleMovement, handleGPS)
274
end)
275
if not ok then
276
print(err)
277
os.pullEvent("key")
278
end