· 4 years ago · Mar 14, 2021, 08:42 AM
1--[[
2HPWebcamAble presents...
3ControlMe
4
5--=== Description ===--
6This program lets you control a turtle from a pocket computer!
7
8This version of the program is for the POCKET COMPUTER
9Get the Turtle version here: pastebin.com/zjn3E5LS
10
11
12--=== Installation ===--
13Pastebin Code: mTGccYbM
14
15To download a file from pastebin, run this command in a computer:
16pastebin get <code> <file name>
17
18
19--=== Update History ===--
20The pastebin will always have the most recent version
21
22|1.0|
23-Release
24]]
25
26--=== Variables ===--
27local args = {...}
28local protocalName = "controlme"
29local turtleID
30local w,h = term.getSize()
31local tools = {}
32local knownTools = {
33 ["modem"] = {text = "Modem", upAndDown = false},
34 ["minecraft:diamond_pickaxe"] = {text = "Mine", upAndDown = true, action = "turtle.dig"},
35 ["minecraft:diamond_sword"] = {text = "Attack", upAndDown = true, action = "turtle.attack"},
36 ["minecraft:diamond_hoe"] = {text = "Hoe", upAndDown = true, action = "turtle.dig"},
37 ["minecraft:diamond_axe"] = {text = "Chop", upAndDown = true, action = "turtle.dig"},
38 ["minecraft:diamond_shovel"] = {text = "Dig", upAndDown = true, action = "turtle.dig"}
39}
40local turtleImage = [[ffffffffff
41f77777777f
42f77ffff77f
43f77777777f
44f88888888f
45f88888888f
46ffffffffff
47]]
48local inventoryImage = [[77777777777777777
4978887888788878887
5078887888788878887
5177777777777777777
5278887888788878887
5378887888788878887
5477777777777777777
5578887888788878887
5678887888788878887
5777777777777777777
5878887888788878887
5978887888788878887
6077777777777777777
61]]
62
63
64--=== Functions ===--
65local function color(text,back)
66 local temp = text and term.setTextColor(text) or nil
67 temp = back and term.setBackgroundColor(back) or nil
68end
69
70local function printC(text,y)
71 if type(text) ~= "string" or type(y) ~= "number" then error("expected string,number, got "..type(text)..","..type(y),2) end
72 local lenght = #text
73 local start = math.floor((w-lenght)/2)+1
74 term.setCursorPos(start,y)
75 term.write(text)
76 return start,start+lenght
77end
78
79-- These functions are from my Screen API
80-- http://pastebin.com/4j4mJsWw
81local default = {
82 object = {
83 text = nil,
84 name = "default",
85 minX = 1,minY = 1,
86 maxX = 7,maxY = 3,
87 states = {
88 on = {text = colors.white, back = colors.lime},
89 off = {text = colors.white, back = colors.red}
90 },
91 clickable = false,
92 visible = true,
93 state = "off",
94 action = nil
95 },
96 clickArea = {
97 name = "default",
98 minX = 1,minY = 1,
99 maxX = 5,maxY = 3,
100 clickable = true,
101 action = nil
102 }
103}
104
105local function fillTable(toFill,fillWith) --Used by the API
106 if toFill == nil then toFill = {} end
107 for a,b in pairs(fillWith) do
108 if type(b) == "table" then
109 toFill[a] = fillTable(toFill[a],b)
110 else
111 toFill[a] = toFill[a]==nil and b or toFill[a]
112 end
113 end
114 return toFill
115end
116
117local function countIndexes(tbl) --Also used by API
118 local total = 0
119 for a,b in pairs(tbl) do total = total+1 end
120 return total
121end
122
123local function assert(check,err,lvl)
124 lvl = lvl or 2
125 if not check then error(err,lvl+1) end
126 return check
127end
128
129function new(nBackground)
130 local api = {}
131 local objects = {}
132 local clickAreas = {}
133 local background = nBackground
134
135 function api.checkPos(x,y)
136 x,y = tonumber(x),tonumber(y)
137 assert(x and y,"expected number,number")
138 for name,data in pairs(clickAreas) do
139 if x >= data.minX and x <= data.maxX and y >= data.minY and y <= data.maxY and data.clickable then
140 if type(data.action)=="function" then return true,true,data.action()
141 else return true,false,"click_area",name end
142 end
143 end
144 for name,data in pairs(objects) do
145 if data.clickable and data.visible and x >= data.minX and x <= data.maxX and y >= data.minY and y <= data.maxY then
146 if type(data.action)=="function" then return true,true,data.action()
147 else return true,false,"object",name end
148 end
149 end
150 return false
151 end
152
153 function api.handleEvents(bUseRaw)
154 local pull = bUseRaw and os.pullEventRaw or os.pullEvent
155 local event = {pull()}
156 if event[1] == "mouse_click" then
157 local wasElement,hadFunction,elementType,name = api.checkPos(event[3],event[4])
158 if wasElement then
159 if not hadFunction then
160 return elementType,name,event[2]
161 else return nil
162 end
163 end
164 end
165 return unpack(event)
166 end
167
168 function api.setDefaultObject(newDefaultObject)
169 assert(type(newDefaultObject)=="table","expected table, got "..type(newDefaultObject))
170 default.object = fillTable(newDefaultObject,default.object)
171 end
172
173 function api.setDefaultClickArea(newDefaultClickArea)
174 assert(type(newDefaultClickArea)=="table","expected table, got "..type(newDefaultClickArea))
175 default.clickArea = fillTable(newDefaultClickArea,default.clickArea)
176 end
177
178 function api.draw()
179 if background then term.setBackgroundColor(background) term.clear() end
180 for name,data in pairs(objects) do
181 api.drawObject(name)
182 end
183 end
184
185 function api.addClickArea(clickAreaInfo)
186 assert(type(clickAreaInfo)=="table","expected table, got "..type(clickAreaInfo))
187 fillTable(clickAreaInfo,default.clickArea)
188 assert(clickAreas[clickAreaInfo.name]==nil,"an object with the name '"..clickAreaInfo.name.."' already exists")
189 clickAreas[clickAreaInfo.name] = clickAreaInfo
190 end
191
192 function api.toggleClickArea(name)
193 assert(clickAreas[name]~=nil,"Click Area '"..name.."' doesn't exist")
194 clickAreas[name].clickable = not clickAreas[name].clickable
195 return clickAreas[name].clickable
196 end
197
198 function api.getClickArea(name)
199 assert(clickAreas[name]~=nil,"Click Area '"..name.."' doesn't exist")
200 return clickAreas[name]
201 end
202
203 function api.addObject(objectInfo)
204 assert(type(objectInfo)=="table","expected table, got "..type(objectInfo))
205 objectInfo = fillTable(objectInfo,default.object)
206 assert(objects[objectInfo.name]==nil,"an object with the name '"..objectInfo.name.."' already exists")
207 objects[objectInfo.name] = objectInfo
208 end
209
210 function api.drawObject(name)
211 assert(objects[name]~=nil,"Object '"..name.."' doesn't exsist")
212 local objData = objects[name]
213 if objData.visible == false then return end
214 assert(objData.states~=nil,"Object '"..name.."' has no states!")
215 assert(objData.states[objData.state],"Object '"..name.."' doesn't have state '"..objData.state.."'")
216 term.setBackgroundColor(objData.states[objData.state].back)
217 term.setTextColor(objData.states[objData.state].text)
218 for i = 0, objData.maxY-objData.minY do
219 term.setCursorPos(objData.minX,objData.minY+i)
220 term.write(string.rep(" ",objData.maxX-objData.minX+1))
221 end
222 if objData.text then
223 local xPos = objData.minX+math.floor(((objData.maxX-objData.minX+1)-#objData.text)/2)
224 local yPos = objData.minY+(objData.maxY-objData.minY)/2
225 term.setCursorPos(xPos,yPos) term.write(objData.text)
226 end
227 end
228
229 function api.toggleObjectState(name) -- Only works if an object has two states
230 assert(objects[name]~=nil,"Object '"..name.."' doesn't exist")
231 assert(countIndexes(objects[name].states)==2,"Object '"..name.."' can't be toggled, it doesn't have two states")
232 curState = objects[name].state
233 for a,b in pairs(objects[name].states) do
234 if a ~= curState then
235 objects[name].state = a
236 end
237 end
238 return objects[name].state
239 end
240
241 function api.setObjectState(name,state)
242 assert(objects[name] ~= nil,"Object '"..name.."' doesn't exist")
243 assert(objects[name].states[state],"Object '"..name.."' doesn't have state '"..state.."'")
244 objects[name].state = state
245 end
246
247 function api.getObject(name)
248 assert(objects[name] ~= nil,"Object '"..name.."' doesn't exist")
249 return objects[name]
250 end
251
252 function api.toggleObjectVisible(name)
253 assert(objects[name] ~= nil,"Object '"..name.."' doesn't exist")
254 objects[name].visible = not objects[name].visible
255 return objects[name].visible
256 end
257
258 function api.toggleObjectClickable(name)
259 assert(objects[name] ~= nil,"Object '"..name.."' doesn't exist")
260 objects[name].clickable = not objects[name].clickable
261 return objects[name].clickable
262 end
263
264 return api
265end
266-- End Screen API
267
268local function checkMessage(event,skipTableCheck,skipIDCheck)
269 return event[4] == protocalName and (skipTableCheck or type(event[3]) == "table") and (skipIDCheck or event[2] == turtleID)
270end
271
272local function message(message)
273 rednet.send(turtleID,message,protocalName)
274end
275
276local function broadcast(message)
277 rednet.broadcast(message,protocalName)
278end
279
280local function execute(func,waitTime)
281 message({action="execute",func=func})
282 local timer = waitTime and os.startTimer(waitTime) or "done"
283 local completed = false
284 while true do
285 local event = {os.pullEvent()}
286 if event[1] == "rednet_message" and checkMessage(event) and event[3].action == "completed" then
287 if timer == "done" then return event[3].result
288 else completed = true end
289 elseif event[1] == "timer" and timer == event[2] then
290 if completed then break
291 else timer = "done" end
292 end
293 end
294end
295
296local function getModem()
297 for a,b in pairs(rs.getSides()) do
298 if peripheral.getType(b) == "modem" and peripheral.call(b,"isWireless") then
299 return b
300 end
301 end
302end
303
304local function waitForMessage(skipTableCheck,skipIDCheck)
305 while true do
306 local event = {os.pullEvent("rednet_message")}
307 if checkMessage(event,skipTableCheck,skipIDCheck) then
308 return event[3]
309 end
310 end
311end
312
313-- Create the screen
314local mainScreen = new()
315
316local function action(buttonName,func,waitTime)
317 mainScreen.getObject(buttonName).state = "active" mainScreen.drawObject(buttonName)
318 execute(func,waitTime or 0.5)
319 mainScreen.getObject(buttonName).state = "on" mainScreen.drawObject(buttonName)
320end
321
322local function forward()
323 action("forward","turtle.forward()")
324end
325
326local function turnLeft()
327 action("turnLeft","turtle.turnLeft()")
328end
329
330local function turnRight()
331 action("turnRight","turtle.turnRight()")
332end
333
334local function back()
335 action("back","turtle.back()")
336end
337
338local function up()
339 action("up","turtle.up()")
340end
341
342local function down()
343 action("down","turtle.down()")
344end
345
346local drawMainScreen
347
348local function options()
349 -- Silly animition that I love so please don't question it
350 for i = 0, 12 do
351 color(colors.white,colors.blue) term.clear()
352 paintutils.drawImage(turtleImage,9,7-(i/2))
353 color(colors.white,colors.green) term.setCursorPos(1,h-i) term.clearLine() printC("Options",h-i)
354 color(colors.white,colors.brown)
355 for a = 0, i-1 do
356 term.setCursorPos(1,h-a) term.clearLine()
357 end
358 sleep(0.1)
359 end
360
361 color(colors.black,colors.lightGray)
362 printC(" ",10)
363 printC(" Disconnect ",11)
364 printC(" and Exit ",12)
365 printC(" ",13)
366 printC(" ",15)
367 printC(" Back ",16)
368 printC(" ",17)
369
370 while true do
371 local event = {os.pullEvent("mouse_click")}
372 if event[4] == 10 or event[4] == 11 or event[4] == 12 or event[4] == 1 then
373 error()
374 elseif event[4] == 16 or event[4] == 15 or event[4] == 17 then
375 break
376 end
377 end
378
379 for i = 12, 0, -1 do
380 color(colors.white,colors.blue) term.clear()
381 paintutils.drawImage(turtleImage,9,7-(i/2))
382 color(colors.white,colors.green) term.setCursorPos(1,h-i) term.clearLine() printC("Options",h-i)
383 color(colors.white,colors.brown)
384 for a = 0, i-1 do
385 term.setCursorPos(1,h-a) term.clearLine()
386 end
387 sleep(0.1)
388 end
389 drawMainScreen()
390end
391
392-- Add buttons
393--mainScreen.addObject({name="inventory", minX=10, maxY=12, state="default", maxX=17, clickable=false, visible=true, states={default={text=1,back=256,},}, minY=11, text="View Inv", })
394mainScreen.addObject({name="turnRight", minX=20, maxY=18, state="on", maxX=25, clickable=true, visible=true, states={off={text=1,back=256,},on={text=32768,back=1,},active={text=1,back=32,},}, minY=16, text="Turn R", action=turnRight})
395mainScreen.addObject({name="actionLeft", minX=20, maxY=11, state="on", maxX=26, clickable=false, visible=true, states={off={text=1,back=256,},on={text=32768,back=1,},active={text=1,back=32,},}, text="No Tool", minY=9, })
396mainScreen.addObject({name="turnLeft", minX=2, maxY=18, state="on", maxX=7, clickable=true, visible=true, states={off={text=1,back=256,},on={text=32768,back=1,},active={text=1,back=32,},}, minY=16, text="Turn L", action=turnLeft})
397mainScreen.addObject({name="actionRightDown", minX=3, maxY=13, state="on", maxX=7, clickable=false, visible=true, states={off={text=1,back=256,},on={text=32768,back=1,},active={text=1,back=32,},}, minY=13, text="v", })
398mainScreen.addObject({name="actionRight", minX=1, maxY=11, state="on", maxX=7, clickable=false, visible=true, states={off={text=1,back=256,},on={text=32768,back=1,},active={text=1,back=32,},}, minY=9, text="No Tool", })
399mainScreen.addObject({name="back", minX=9, maxY=17, state="on", maxX=18, clickable=true, visible=true, states={off={text=1,back=256,},on={text=32768,back=1,},active={text=1,back=32,},}, minY=15, text="Backward", action=back})
400mainScreen.addObject({minY=20, minX=1, state="default", maxY=20, maxX=26, clickable=true, visible=true, states={default={text=1,back=8192,},}, name="options", text="Options", action = options})
401mainScreen.addObject({name="actionRightUp", minX=3, maxY=7, state="on", maxX=7, clickable=true, visible=true, states={off={text=1,back=256,},on={text=32768,back=1,},active={text=1,back=32,},}, minY=7, text="^", })
402mainScreen.addObject({name="actionLeftDown", minX=20, maxY=13, state="on", maxX=24, clickable=false, visible=true, states={off={text=1,back=256,},on={text=32768,back=1,},active={text=1,back=32,},}, text="v", minY=13, })
403mainScreen.addObject({name="down", minX=20, maxY=4, state="on", maxX=25, clickable=true, visible=true, states={off={text=1,back=256,},on={text=32768,back=1,},active={text=1,back=32,},}, text="Down", minY=2, action=down})
404mainScreen.addObject({name="up", minX=2, maxY=4, state="on", maxX=7, clickable=true, visible=true, states={off={text=1,back=256,},on={text=32768,back=1,},active={text=1,back=32,},}, text="Up", minY=2, action=up})
405mainScreen.addObject({name="actionLeftUp", minX=20, maxY=7, state="on", maxX=24, clickable=false, visible=true, states={off={text=1,back=256,},on={text=32768,back=1,},active={text=1,back=32,},}, text="^", minY=7, })
406mainScreen.addObject({name="forward", minX=9, maxY=5, state="on", maxX=18, clickable=true, visible=true, states={off={text=1,back=256,},on={text=32768,back=1,},active={text=1,back=32,},}, minY=3, text="Forward", action=forward})
407-- Function to draw the screen
408drawMainScreen = function()
409 color(nil,colors.blue) term.clear()
410 paintutils.drawImage(turtleImage,9,7)
411 mainScreen.draw()
412end
413
414local function getFreeSlot()
415 for i = 1, 16 do
416 local result = execute("turtle.getItemCount("..i..")")
417 if result == 0 then
418 return i
419 end
420 end
421 return false
422end
423
424local function showToolName(name,side)
425 local obj = mainScreen.getObject("action"..side)
426 local up = mainScreen.getObject("action"..side.."Up")
427 local down = mainScreen.getObject("action"..side.."Down")
428 if knownTools[name] then
429 obj.text = knownTools[name].text
430 obj.state = "on"
431 obj.clickable = true
432 if knownTools[name].action then
433 obj.action = function() action("action"..side,knownTools[ tools.left ].action.."()",0.2) end
434 end
435 mainScreen.drawObject("action"..side)
436 if knownTools[name].upAndDown then
437 up.state = "on"
438 up.clickable = true
439 up.action = function() action("action"..side.."Up",knownTools[name].action.."Up()",0.2) end
440 down.state = "on"
441 down.clickable = true
442 down.action = function() action("action"..side.."Down",knownTools[name].action.."Down()",0.2) end
443 mainScreen.drawObject("action"..side.."Up")
444 mainScreen.drawObject("action"..side.."Down")
445 else
446 up.state = "off"
447 up.clickable = false
448 down.state = "off"
449 down.clickable = false
450 mainScreen.drawObject("action"..side.."Up")
451 mainScreen.drawObject("action"..side.."Down")
452 end
453 else
454 obj.text = name=="none" and "No Tool" or "?"
455 obj.state ="off"
456 obj.clickable = false
457 mainScreen.drawObject("action"..side)
458 up.state = "off"
459 up.clickable = false
460 down.state = "off"
461 down.clickable = false
462 mainScreen.drawObject("action"..side.."Up")
463 mainScreen.drawObject("action"..side.."Down")
464 end
465end
466
467local function getLeftTool()
468 message({action="getLeftTool"})
469 while true do
470 local msg = waitForMessage()
471 if msg.action == "getLeftTool" then
472 tools.left = msg.result
473 return msg.result
474 end
475 end
476end
477
478local function getRightTool()
479 message({action="getRightTool"})
480 while true do
481 local msg = waitForMessage()
482 if msg.action == "getRightTool" then
483 tools.right = msg.result
484 return msg.result
485 end
486 end
487end
488
489local function detectTools()
490 showToolName(getRightTool(),"Right")
491 showToolName(getLeftTool(),"Left")
492end
493
494--=== Program ===--
495if not pocket then
496 printError("This program requires a pocket computer!")
497 return
498elseif args[1] == "help" then
499 print("ControlMe")
500 print("By HPWebcamAble")
501 print("")
502 print("Use this program to remotly control your turtle!")
503 print("Start this program on your pockect computer and the corresponding program on a turtle and control away!")
504end
505
506do
507 local modemSide = getModem()
508 if modemSide then
509 rednet.open(modemSide)
510 else
511 printError("This program requires a wireless modem")
512 return
513 end
514end
515
516local continue = true
517while continue do
518 color(colors.white,colors.black) term.clear()
519 printC("Enter your Turtle's ID:",5)
520 term.setCursorPos(math.floor(w/2)-2,6)
521 local input = tonumber(read())
522
523 if input then
524 color(colors.white,colors.black) term.clear()
525 printC("Connecting to",5)
526 printC("turtle ID "..input,6)
527 printC("(*)",8)
528 local state = 1
529
530 turtleID = input
531 message({action="connect",id=input})
532 local timer = os.startTimer(1)
533 while true do
534 local event = {os.pullEventRaw()}
535 if event[1] == "terminate" then
536 term.clear() term.setCursorPos(1,1) print("Program terminated")
537 return
538 elseif event[1] == "rednet_message" and checkMessage(event,true) then
539 if event[3] == "connected" then
540 continue = false
541 break
542 end
543 elseif event[1] == "timer" and event[2] == timer then
544 state = (state == 4 and 1 or state+1)
545 term.clearLine() printC(string.rep("(",state).."*"..string.rep(")",state),8)
546 message({action="connect",id=input})
547 timer = os.startTimer(1)
548 end
549 end
550 end
551end
552
553local function heartbeat()
554
555 message("ping")
556 local timer = os.startTimer(2)
557 while true do
558 local event = {os.pullEvent()}
559 if event[1] == "rednet_message" then
560 if checkMessage(event,true) and event[3] == "pong" then
561 sleep(3)
562 message("ping")
563 timer = os.startTimer(2)
564 end
565 elseif event[1] == "timer" and event[2] == timer then
566 color(colors.white,colors.black) term.setCursorPos(1,1) term.clear() print("Lost contact with turtle!")
567 error("FORCEQUIT")
568 end
569 end
570
571end
572
573local function main()
574
575 drawMainScreen()
576 detectTools()
577
578 while true do
579 local event = {mainScreen.handleEvents()}
580 if event[1] == "rednet_message" and checkMessage(event) then
581 local msg = event[3]
582 if msg.action == "ping" then
583 message("pong")
584 end
585 elseif event[1] == "key" then
586 if event[2] == keys.up or event[2] == keys.w then
587 forward()
588 elseif event[2] == keys.down or event[2] == keys.s then
589 back()
590 elseif event[2] == keys.right or event[2] == keys.d then
591 turnRight()
592 elseif event[2] == keys.left or event[2] == keys.a then
593 turnLeft()
594 elseif event[2] == keys.space then
595 up()
596 elseif event[2] == keys.leftShift then
597 down()
598 elseif event[2] == keys.q then
599
600 end
601 elseif event[1] == "object" then
602
603 end
604 end
605
606end
607
608local function run()
609 parallel.waitForAny(main,heartbeat)
610end
611
612local state,err = pcall(run)
613
614if err then
615 if not err:find("Terminated") and not err:find("FORCEQUIT") then
616 color(colors.white,colors.black)
617 term.clear()
618 printC("An error occured:",1)
619 term.setCursorPos(1,3)
620 print(err)
621 elseif not err:find("FORCEQUIT") then
622 color(colors.white,colors.black)
623 term.clear()
624 term.setCursorPos(1,1)
625 end
626else
627 color(colors.white,colors.black)
628 term.clear()
629 term.setCursorPos(1,1)
630end