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