View difference between Paste ID: MiWCax4N and mTGccYbM
SHOW: | | - or go back to the newest paste.
1
--[[
2
HPWebcamAble presents...
3
ControlMe
4
5
--=== Description ===--
6
This program lets you control a turtle from a pocket computer!
7
8
This version of the program is for the POCKET COMPUTER
9
Get the Turtle version here: pastebin.com/zjn3E5LS
10
11
12
--=== Installation ===--
13
Pastebin Code: mTGccYbM
14
15
To download a file from pastebin, run this command in a computer:
16
pastebin get <code> <file name>
17
18
19
--=== Update History ===--
20
The pastebin will always have the most recent version
21
22
|1.0|
23
-Release
24
]]
25
26
--=== Variables ===--
27
local args = {...}
28
local protocalName = "controlme"
29
local turtleID
30
local w,h = term.getSize()
31
local tools = {}
32
local 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
}
40
local turtleImage = [[ffffffffff
41
f77777777f
42
f77ffff77f
43
f77777777f
44
f88888888f
45
f88888888f
46
ffffffffff
47
]]
48
local inventoryImage = [[77777777777777777
49
78887888788878887
50
78887888788878887
51
77777777777777777
52
78887888788878887
53
78887888788878887
54
77777777777777777
55
78887888788878887
56
78887888788878887
57
77777777777777777
58
78887888788878887
59
78887888788878887
60
77777777777777777
61
]]
62
63
-- Load Images
64
do
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")
69
end
70
71
--=== Functions ===--
72
local function color(text,back)
73
  local temp = text and term.setTextColor(text) or nil
74
  temp = back and term.setBackgroundColor(back) or nil
75
end
76
77
local 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
84
end
85
86
-- These functions are from my Screen API
87
-- http://pastebin.com/4j4mJsWw
88
local 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
112
local 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
122
end
123
124
local 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
128
end
129
130
local function assert(check,err,lvl)
131
  lvl = lvl or 2
132
  if not check then error(err,lvl+1) end
133
  return check
134
end
135
136
function 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
272
end
273
-- End Screen API
274
275
local function checkMessage(event,skipTableCheck,skipIDCheck)
276
  return event[4] == protocalName and (skipTableCheck or type(event[3]) == "table") and (skipIDCheck or event[2] == turtleID)
277
end
278
279
local function message(message)
280
  rednet.send(turtleID,message,protocalName)
281
end
282
283
local function broadcast(message)
284
  rednet.broadcast(message,protocalName)
285
end
286
287
local 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
301
end
302
303
local 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
309
end
310
311
local 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
318
end
319
320
-- Create the screen
321
local mainScreen = new()
322
323
local 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)
327
end
328
329
local function forward()
330
  action("forward","turtle.forward()")
331
end
332
333
local function turnLeft()
334
  action("turnLeft","turtle.turnLeft()")
335
end
336
337
local function turnRight()
338
  action("turnRight","turtle.turnRight()")
339
end
340
341
local function back()
342
  action("back","turtle.back()")
343
end
344
345
local function up()
346
  action("up","turtle.up()")
347
end
348
349
local function down()
350
  action("down","turtle.down()")
351
end
352
353
local drawMainScreen
354
355
local 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()
397
end
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", })
401
mainScreen.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})
402
mainScreen.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, })
403
mainScreen.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})
404
mainScreen.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", })
405
mainScreen.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", })
406
mainScreen.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})
407
mainScreen.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})
408
mainScreen.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="^", })
409
mainScreen.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, })
410
mainScreen.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})
411
mainScreen.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})
412
mainScreen.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, })
413
mainScreen.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
415
drawMainScreen = function()
416
  color(nil,colors.blue) term.clear()
417
  paintutils.drawImage(turtleImage,9,7)
418
  mainScreen.draw()
419
end
420
421
local 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
429
end
430
431
local 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
472
end
473
474
local 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
483
end
484
485
local 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
494
end
495
496
local function detectTools()
497
  showToolName(getRightTool(),"Right")
498
  showToolName(getLeftTool(),"Left")
499
end
500
501
--=== Program ===--
502
if not pocket then
503
  printError("This program requires a pocket computer!")
504
  return
505
elseif 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!")
511
end
512
513
do
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
521
end
522
523
local continue = true
524
while 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
558
end
559
560
local 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
  
578
end
579
580
local 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
  
613
end
614
615
local function run()
616
  parallel.waitForAny(main,heartbeat)
617
end
618
619
local state,err = pcall(run)
620
621
if 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
633
else
634
  color(colors.white,colors.black)
635
  term.clear()
636
  term.setCursorPos(1,1)
637
end