· 5 years ago · Apr 17, 2020, 01:36 PM
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 get_wheat()
288 message({action="get_wheat"})
289end
290
291local function get_carrot()
292 message({action="get_carrot"})
293end
294
295local function execute(func,waitTime)
296 message({action="execute",func=func})
297 local timer = waitTime and os.startTimer(waitTime) or "done"
298 local completed = false
299 while true do
300 local event = {os.pullEvent()}
301 if event[1] == "rednet_message" and checkMessage(event) and event[3].action == "completed" then
302 if timer == "done" then return event[3].result
303 else completed = true end
304 elseif event[1] == "timer" and timer == event[2] then
305 if completed then break
306 else timer = "done" end
307 end
308 end
309end
310
311local function getModem()
312 for a,b in pairs(rs.getSides()) do
313 if peripheral.getType(b) == "modem" and peripheral.call(b,"isWireless") then
314 return b
315 end
316 end
317end
318
319local function waitForMessage(skipTableCheck,skipIDCheck)
320 while true do
321 local event = {os.pullEvent("rednet_message")}
322 if checkMessage(event,skipTableCheck,skipIDCheck) then
323 return event[3]
324 end
325 end
326end
327
328-- Create the screen
329local mainScreen = new()
330
331local function action(buttonName,func,waitTime)
332 mainScreen.getObject(buttonName).state = "active" mainScreen.drawObject(buttonName)
333 execute(func,waitTime or 0.5)
334 mainScreen.getObject(buttonName).state = "on" mainScreen.drawObject(buttonName)
335end
336
337local function forward()
338 action("forward","turtle.forward()")
339end
340
341local function turnLeft()
342 action("turnLeft","turtle.turnLeft()")
343end
344
345local function turnRight()
346 action("turnRight","turtle.turnRight()")
347end
348
349local function back()
350 action("back","turtle.back()")
351end
352
353local function up()
354 get_carrot()
355end
356
357local function down()
358 get_wheat()
359end
360
361local drawMainScreen
362
363local function options()
364 -- Silly animition that I love so please don't question it
365 for i = 0, 12 do
366 color(colors.white,colors.blue) term.clear()
367 paintutils.drawImage(turtleImage,9,7-(i/2))
368 color(colors.white,colors.green) term.setCursorPos(1,h-i) term.clearLine() printC("Options",h-i)
369 color(colors.white,colors.brown)
370 for a = 0, i-1 do
371 term.setCursorPos(1,h-a) term.clearLine()
372 end
373 sleep(0.1)
374 end
375
376 color(colors.black,colors.lightGray)
377 printC(" ",10)
378 printC(" Disconnect ",11)
379 printC(" and Exit ",12)
380 printC(" ",13)
381 printC(" ",15)
382 printC(" Back ",16)
383 printC(" ",17)
384
385 while true do
386 local event = {os.pullEvent("mouse_click")}
387 if event[4] == 10 or event[4] == 11 or event[4] == 12 or event[4] == 1 then
388 error()
389 elseif event[4] == 16 or event[4] == 15 or event[4] == 17 then
390 break
391 end
392 end
393
394 for i = 12, 0, -1 do
395 color(colors.white,colors.blue) term.clear()
396 paintutils.drawImage(turtleImage,9,7-(i/2))
397 color(colors.white,colors.green) term.setCursorPos(1,h-i) term.clearLine() printC("Options",h-i)
398 color(colors.white,colors.brown)
399 for a = 0, i-1 do
400 term.setCursorPos(1,h-a) term.clearLine()
401 end
402 sleep(0.1)
403 end
404 drawMainScreen()
405end
406
407-- Add buttons
408--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", })
409mainScreen.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})
410mainScreen.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, })
411mainScreen.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})
412mainScreen.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", })
413mainScreen.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", })
414mainScreen.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})
415mainScreen.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})
416mainScreen.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="^", })
417mainScreen.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, })
418mainScreen.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="Wheat", minY=2, action=down})
419mainScreen.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="Carrot", minY=2, action=up})
420mainScreen.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, })
421mainScreen.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})
422-- Function to draw the screen
423drawMainScreen = function()
424 color(nil,colors.blue) term.clear()
425 paintutils.drawImage(turtleImage,9,7)
426 mainScreen.draw()
427end
428
429local function getFreeSlot()
430 for i = 1, 16 do
431 local result = execute("turtle.getItemCount("..i..")")
432 if result == 0 then
433 return i
434 end
435 end
436 return false
437end
438
439local function showToolName(name,side)
440 local obj = mainScreen.getObject("action"..side)
441 local up = mainScreen.getObject("action"..side.."Up")
442 local down = mainScreen.getObject("action"..side.."Down")
443 if knownTools[name] then
444 obj.text = knownTools[name].text
445 obj.state = "on"
446 obj.clickable = true
447 if knownTools[name].action then
448 obj.action = function() action("action"..side,knownTools[ tools.left ].action.."()",0.2) end
449 end
450 mainScreen.drawObject("action"..side)
451 if knownTools[name].upAndDown then
452 up.state = "on"
453 up.clickable = true
454 up.action = function() action("action"..side.."Up",knownTools[name].action.."Up()",0.2) end
455 down.state = "on"
456 down.clickable = true
457 down.action = function() action("action"..side.."Down",knownTools[name].action.."Down()",0.2) end
458 mainScreen.drawObject("action"..side.."Up")
459 mainScreen.drawObject("action"..side.."Down")
460 else
461 up.state = "off"
462 up.clickable = false
463 down.state = "off"
464 down.clickable = false
465 mainScreen.drawObject("action"..side.."Up")
466 mainScreen.drawObject("action"..side.."Down")
467 end
468 else
469 obj.text = name=="none" and "No Tool" or "?"
470 obj.state ="off"
471 obj.clickable = false
472 mainScreen.drawObject("action"..side)
473 up.state = "off"
474 up.clickable = false
475 down.state = "off"
476 down.clickable = false
477 mainScreen.drawObject("action"..side.."Up")
478 mainScreen.drawObject("action"..side.."Down")
479 end
480end
481
482local function getLeftTool()
483 message({action="getLeftTool"})
484 while true do
485 local msg = waitForMessage()
486 if msg.action == "getLeftTool" then
487 tools.left = msg.result
488 return msg.result
489 end
490 end
491end
492
493local function getRightTool()
494 message({action="getRightTool"})
495 while true do
496 local msg = waitForMessage()
497 if msg.action == "getRightTool" then
498 tools.right = msg.result
499 return msg.result
500 end
501 end
502end
503
504local function detectTools()
505 showToolName(getRightTool(),"Right")
506 showToolName(getLeftTool(),"Left")
507end
508
509--=== Program ===--
510if not pocket then
511 printError("This program requires a pocket computer!")
512 return
513elseif args[1] == "help" then
514 print("ControlMe")
515 print("By HPWebcamAble")
516 print("")
517 print("Use this program to remotly control your turtle!")
518 print("Start this program on your pockect computer and the corresponding program on a turtle and control away!")
519end
520
521do
522 local modemSide = getModem()
523 if modemSide then
524 rednet.open(modemSide)
525 else
526 printError("This program requires a wireless modem")
527 return
528 end
529end
530
531local continue = true
532while continue do
533 color(colors.white,colors.black) term.clear()
534 printC("Enter your Turtle's ID:",5)
535 term.setCursorPos(math.floor(w/2)-2,6)
536 local input = tonumber(read())
537
538 if input then
539 color(colors.white,colors.black) term.clear()
540 printC("Connecting to",5)
541 printC("turtle ID "..input,6)
542 printC("(*)",8)
543 local state = 1
544
545 turtleID = input
546 message({action="connect",id=input})
547 local timer = os.startTimer(1)
548 while true do
549 local event = {os.pullEventRaw()}
550 if event[1] == "terminate" then
551 term.clear() term.setCursorPos(1,1) print("Program terminated")
552 return
553 elseif event[1] == "rednet_message" and checkMessage(event,true) then
554 if event[3] == "connected" then
555 continue = false
556 break
557 end
558 elseif event[1] == "timer" and event[2] == timer then
559 state = (state == 4 and 1 or state+1)
560 term.clearLine() printC(string.rep("(",state).."*"..string.rep(")",state),8)
561 message({action="connect",id=input})
562 timer = os.startTimer(1)
563 end
564 end
565 end
566end
567
568local function heartbeat()
569
570 message("ping")
571 local timer = os.startTimer(2)
572 while true do
573 local event = {os.pullEvent()}
574 if event[1] == "rednet_message" then
575 if checkMessage(event,true) and event[3] == "pong" then
576 sleep(3)
577 message("ping")
578 timer = os.startTimer(2)
579 end
580 elseif event[1] == "timer" and event[2] == timer then
581 color(colors.white,colors.black) term.setCursorPos(1,1) term.clear() print("Lost contact with turtle!")
582 error("FORCEQUIT")
583 end
584 end
585
586end
587
588local function main()
589
590 drawMainScreen()
591 detectTools()
592
593 while true do
594 local event = {mainScreen.handleEvents()}
595 if event[1] == "rednet_message" and checkMessage(event) then
596 local msg = event[3]
597 if msg.action == "ping" then
598 message("pong")
599 end
600 elseif event[1] == "key" then
601 if event[2] == keys.up or event[2] == keys.w then
602 forward()
603 elseif event[2] == keys.down or event[2] == keys.s then
604 back()
605 elseif event[2] == keys.right or event[2] == keys.d then
606 turnRight()
607 elseif event[2] == keys.left or event[2] == keys.a then
608 turnLeft()
609 elseif event[2] == keys.space then
610 up()
611 elseif event[2] == keys.leftShift then
612 down()
613 elseif event[2] == keys.q then
614
615 end
616 elseif event[1] == "object" then
617
618 end
619 end
620
621end
622
623local function run()
624 parallel.waitForAny(main,heartbeat)
625end
626
627local state,err = pcall(run)
628
629if err then
630 if not err:find("Terminated") and not err:find("FORCEQUIT") then
631 color(colors.white,colors.black)
632 term.clear()
633 printC("An error occured:",1)
634 term.setCursorPos(1,3)
635 print(err)
636 elseif not err:find("FORCEQUIT") then
637 color(colors.white,colors.black)
638 term.clear()
639 term.setCursorPos(1,1)
640 end
641else
642 color(colors.white,colors.black)
643 term.clear()
644 term.setCursorPos(1,1)
645end