ComputerCraft Archive

pocketgps - waypoints, heading and distance!

computer networking unknown forum

Description

Here's a quick script I whipped up tonight to take advantage of the wireless pocket computers. Its features include a waypoint system, the users's last heading, and the coordinates of and distance to ...

Installation

Copy one of these commands into your ComputerCraft terminal:

Pastebin:pastebin get wQaGzaK7 pocketgps_-_waypoints,_heading_and_distance!
wget:wget https://pastebin.com/raw/wQaGzaK7 pocketgps_-_waypoints,_heading_and_distance!
Archive:wget https://cc.shobie.xyz/cc/get/pb-wQaGzaK7 pocketgps_-_waypoints,_heading_and_distance!
Quick Install: wget https://cc.shobie.xyz/cc/get/pb-wQaGzaK7 pocketgps - waypoints, heading and distance!

Usage

Run the program after downloading

Tags

networkingforumpocket-programs

Source

View Original Source

Code Preview

--[[
The MIT License (MIT)
 
Copyright (c) 2014 Lyqyd
 
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
 
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
 
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
--]]

local places = {}
local destination, current, heading, distance
local placesOffset = 1
local screenHandler, screenDraw
local newPlace, newPlaceFocus = {x = "", y = "", z = "", name = ""}, "name"
local goto, gotoFocus = {x = "", y = "", z = ""}, "x"
local tracing, traces = false, {}
local openDoors = false
local unpack = unpack or table.unpack

if fs.exists(".places") then
	local handle = io.open(".places", "r")
	if handle then
		for line in handle:lines() do
			local entry = {}
			entry.x, entry.y, entry.z, entry.name = string.match(line, "(%-?%d+),(%-?%d+),(%-?%d+),(.*)")
			if entry.x and entry.y and entry.z and entry.name then
				table.insert(places, entry)
			end
		end
		handle:close()
	end
end

local function savePlaces()
	local handle = io.open(".places", "w")
	if handle then
		for i = 1, #places do
			handle:write(places[i].x..","..places[i].y..","..places[i].z..","..places[i].name.."\n")
		end
		handle:close()
	else
		error("Couldn't open file for writing!")
	end
end

local function saveTrace()
	local name
	if fs.exists("trace") then
		local count = 1
		repeat
			count = count + 1
		until not fs.exists("trace"..count)
		name = "trace"..count
	else
		name = "trace"
	end
	local handle = io.open(name, "w")
	if handle then
		for i = 1, #traces do
			handle:write(traces[i].x..","..traces[i].y..","..traces[i].z.."\n")
		end
		handle:close()
	end
end


local function printCenter(text)
	text = tostring(text)
	local x, y = term.getSize()
	local xCur, yCur = term.getCursorPos()
	term.setCursorPos((x - #text) / 2 + 1, yCur)
	term.write(text)
end

local function menuBar()
	term.setCursorPos(1,1)
	term.setBackgroundColor(colors.gray)
	term.setTextColor(colors.white)
	term.write("  Places    Goto    Exit  ")
end

local function clearState()
	term.setCursorBlink(false)
	placesOffset = 1
	newPlace = {x = "", y = "", z = "", name = ""}
	newPlaceFocus = "name"
end

local function drawDestinationLine(index)
	local x, y = term.getSize()
	term.setBackgroundColor(colors.red)
	term.setTextColor(colors.white)
	term.write("x")
	term.setBackgroundColor(index % 2 == 0 and colors.lightGray or colors.white)
	term.setTextColor(colors.black)
	local destCoords = tostring(places[index].x)..","..tostring(places[index].y)..","..tostring(places[index].z)
	local nameLen = x - (#destCoords + 4) --4 for spacing and initial X and scrollbar
	local nameStr = string.sub(places[index].name..string.rep(" ", x), 1, nameLen)
	term.write(" "..nameStr.." "..destCoords)
end


local function mainScreen()
	local x, y = term.getSize()
	term.setBackgroundColor(colors.lightGray)
	term.setTextColor(colors.black)
	if heading then
		term.setCursorPos(3, 4)
		term.write("Heading: "..heading)
	end
	if current then
		term.setCursorPos(3, 6)
		term.write("Current Location:")
		term.setCursorPos(1, 7)
		printCenter(current)
	end
	if destination then
		term.setCursorPos(3, 9)
		term.write("Destination: ")
		if destinationName then
			term.write(destinationName)
		end
		term.setCursorPos(1, 10)
		printCenter(destination)
	end
	if distance then
		term.setCursorPos(3, 12)
		term.write("Distance: "..distance.."m")
	end
	term.setCursorPos(3, y - 1)
	term.setTextColor(tracing and colors.lime or colors.red)
	term.write("Tracing    ")
	if fs.exists("/.fingerprint") then
		term.setTextColor(openDoors and colors.lime or colors.red)
		term.write("Open Doors")
	end
	term.setTextColor(colors.black)
	term.setCursorPos(1, y)
end

local function handleMain(event)
	local x, y = term.getSize()
	if event[1] == "char" then
		if event[2] == "t" then
			tracing = not tracing
			if not tracing then
				--toggled off, save traces.
				saveTrace()
			end
		elseif event[2] == "f" then
			if not fs.exists("/.fingerprint") then
				local handle = io.open("/.fingerprint", "w")
				if handle then
					for i = 1, 256 do
						handle:write(string.char(math.random(32, 126)))
					end
					handle:close()
				end
			end
		end
	elseif event[1] == "mouse_click" then
		if event[4] == y - 1 and event[3] >= 3 and event[3] <= 9 then
			tracing = not tracing
			if not tracing then
				saveTrace()
			end
		elseif event[4] == y - 1 and event[3] >= 14 and event[3] <= 23 and fs.exists("/.fingerprint") then
			openDoors = not openDoors
		end
	end
end

local placesScreen, handlePlaces

local function newPlaceScreen()
	local x, y = term.getSize()
	local curX, curY
	term.setBackgroundColor(colors.lightGray)
	term.setTextColor(colors.black)
	term.setCursorPos(3, 4)
	term.write("Name: ")
	if newPlace.name then
		term.write(newPlace.name)
	end
	if newPlaceFocus == "name" then
		curX, curY = term.getCursorPos()
	end
	term.setCursorPos(3, 6)
	term.write("X: ")
	if newPlace.x then
		term.write(string.sub(tostring(newPlace.x), 0-(x - 7)))
	end
	if newPlaceFocus == "x" then
		curX, curY = term.getCursorPos()
	end
	term.setCursorPos(3, 7)
	term.write("Y: ")
	if newPlace.y then
		term.write(string.sub(tostring(newPlace.y), 0-(x - 7)))
	end
	if newPlaceFocus == "y" then
		curX, curY = term.getCursorPos()
	end
	term.setCursorPos(3, 8)
	term.write("Z: ")
	if newPlace.z then
		term.write(string.sub(tostring(newPlace.z), 0-(x - 7)))
	end
	if newPlaceFocus == "z" then
		curX, curY = term.getCursorPos()
	end
	term.setCursorPos(x - 5, 10)
	term.write("Done")
	if curX and curY then
		term.setCursorPos(curX, curY)
		term.setCursorBlink(true)
	end
end

local function handleNewPlace(event)
	local x, y = term.getSize()
	if event[1] == "mouse_click" then
		if event[4] == 4 then
			newPlaceFocus = "name"
		elseif event[4] == 6 then
			newPlaceFocus = "x"
		elseif event[4] == 7 then
			newPlaceFocus = "y"
		elseif event[4] == 8 then
			newPlaceFocus = "z"
		elseif event[4] == 10 and event[3] >= x - 5 and event[3] <= x - 1 then
			--clicked Done
			if tonumber(newPlace.x) and tonumber(newPlace.y) and tonumber(newPlace.z) and #newPlace.name > 0 then
				local entry = {x = newPlace.x, y = newPlace.y, z = newPlace.z, name = newPlace.name}
				table.insert(places, entry)
				savePlaces()
				clearState()
				screenHandler = handlePlaces
				screenDraw = placesScreen
			end
		end
	elseif event[1] == "char" then
		if event[2] == "-" and (newPlaceFocus == "x" or newPlaceFocus == "y" or newPlaceFocus == "z") then
			if string.sub(newPlace[newPlaceFocus], 1, 1) == "-" then
				newPlace[newPlaceFocus] = string.sub(newPlace[newPlaceFocus], 2)
			else
				newPlace[newPlaceFocus] = "-"..newPlace[newPlaceFocus]
			end
		else
			newPlace[newPlaceFocus] = newPlace[newPlaceFocus]..event[2]
		end
	elseif event[1] == "key" and event[2] == 14 then
		--backspace
		newPlace[newPlaceFocus] = string.sub(newPlace[newPlaceFocus], 1, #newPlace[newPlaceFocus] - 1)
	end
end

function placesScreen()
	term.setBackgroundColor(colors.lightGray)
	term.setTextColor(colors.black)
	term.setCursorPos(1, 3)
	term.write("  New:   Manual  ")
	if current and type(current) == "table" then
		term.write("Current")
	end
	term.setCursorPos(1, 5)
	local x, y = term.getSize()
	for i = 1, math.min(#places, y - 4) do
		term.setCursorPos(1, i + 4)
		drawDestinationLine(i + placesOffset - 1)
	end
	term.setCursorPos(x, 5)
	term.setBackgroundColor(colors.gray)
	term.setTextColor(colors.white)
	term.write("^")
	term.setCursorPos(x, y)
	term.write("v")
end

function handlePlaces(event)
	if event[1] == "mouse_click" then
		local x, y = term.getSize()
		if event[4] >= 5 and event[4] <= y then
			--clicked somewhere in the places list
			if event[3] == 1 then
				--removing a place from the list, clicked the x
				local index = placesOffset + event[4] - 5 --5 due to the starting y coord of the list
				if index <= #places then
					if event[2] == 1 then
						table.remove(places, index)
						savePlaces()
						placesScreen()
					elseif event[2] == 2 then
						--right-clicked, open place for editing!
						clearState()
						newPlace.x = tostring(places[index].x)
						newPlace.y = tostring(places[index].y)
						newPlace.z = tostring(places[index].z)
						newPlace.name = places[index].name
						table.remove(places, index)
						savePlaces()
						screenHandler = handleNewPlace
						screenDraw = newPlaceScreen
					end
				end
			elseif event[3] == x then
				--clicked the scroll bar.
				if event[4] == 5 then
					--up
					if placesOffset > 1 then
						placesOffset = placesOffset - 1
					end
				elseif event[4] == y then
					if placesOffset + y - 5 < #places then
						placesOffset = placesOffset + 1
					end
				end
			else
				--clicked on a place!
				local index = placesOffset + event[4] - 5
				if index <= #places then
					destination = vector.new(places[index].x, places[index].y, places[index].z)
					destinationName = places[index].name
					screenHandler = handleMain
					screenDraw = mainScreen
				end			
			end
		elseif event[4] == 3 then
			--clicked one of our new places options
			if event[3] >= 10 and event[3] <= 15 then
				--clicked manual
				clearState()
				screenHandler = handleNewPlace
				screenDraw = newPlaceScreen
			elseif event[3] >= 18 and event[3] <= 24 then
				--clicked current
				if current and type(current) == "table" then
					clearState()
					newPlace.x = tostring(current.x)
					newPlace.y = tostring(current.y)
					newPlace.z = tostring(current.z)
					screenHandler = handleNewPlace
					screenDraw = newPlaceScreen
				end
			end
		end
	end
end

local function gotoScreen()
	local x, y = term.getSize()
	local curX, curY
	term.setBackgroundColor(colors.lightGray)
	term.setTextColor(colors.black)
	term.setCursorPos(3, 4)
	term.write("X: ")
	if goto.x then
		term.write(string.sub(tostring(goto.x), 0-(x - 7)))
	end
	if gotoFocus == "x" then
		curX, curY = term.getCursorPos()
	end
	term.setCursorPos(3, 5)
	term.write("Y: ")
	if goto.y then
		term.write(string.sub(tostring(goto.y), 0-(x - 7)))
	end
	if gotoFocus == "y" then
		curX, curY = term.getCursorPos()
	end
	term.setCursorPos(3, 6)
	term.write("Z: ")
	if goto.z then
		term.write(string.sub(tostring(goto.z), 0-(x - 7)))
	end
	if gotoFocus == "z" then
		curX, curY = term.getCursorPos()
	end
	term.setCursorPos(x - 5, 8)
	term.write("Done")
	if curX and curY then
		term.setCursorPos(curX, curY)
		term.setCursorBlink(true)
	end
end

local function handleGoto(event)
	local x, y = term.getSize()
	if event[1] == "mouse_click" then
		if event[4] == 4 then
			gotoFocus = "x"
		elseif event[4] == 5 then
			gotoFocus = "y"
		elseif event[4] == 6 then
			gotoFocus = "z"
		elseif event[4] == 8 and event[3] >= x - 5 and event[3] <= x - 1 then
			--clicked Done
			if tonumber(goto.x) and tonumber(goto.y) and tonumber(goto.z) then
				destinationName = nil
				destination = vector.new(tonumber(goto.x), tonumber(goto.y), tonumber(goto.z))
				clearState()
				screenHandler = handleMain
				screenDraw = mainScreen
			end
		end
	elseif event[1] == "char" then
		if event[2] == "-" and (gotoFocus == "x" or gotoFocus == "y" or gotoFocus == "z") then
			if string.sub(goto[gotoFocus], 1, 1) == "-" then
				goto[gotoFocus] = string.sub(goto[gotoFocus], 2)
			else
				goto[gotoFocus] = "-"..goto[gotoFocus]
			end
		else
			goto[gotoFocus] = goto[gotoFocus]..event[2]
		end
	elseif event[1] == "key" and event[2] == 14 then
		--backspace
		goto[gotoFocus] = string.sub(goto[gotoFocus], 1, #goto[gotoFocus] - 1)
	end
end

local function handleEvents()
	while true do
		term.setBackgroundColor(colors.lightGray)
		term.clear()
		menuBar()
		screenDraw()
		local event = {os.pullEvent()}
		if event[1] == "mouse_click" then
			if event[4] == 1 then
				--clicked the menu bar at the top
				if event[3] >= 3 and event[3] <= 8 then
					--clicked places
					clearState()
					screenHandler = handlePlaces
					screenDraw = placesScreen
				elseif event[3] >= 13 and event[3] <= 16 then
					--clicked goto
					clearState()
					screenHandler = handleGoto
					screenDraw = gotoScreen
				elseif event[3] >= 21 and event[3] <= 24 then
					--clicked exit
					if screenHandler ~= handleMain then
						--not on main screen, return to main screen
						clearState()
						screenHandler = handleMain
						screenDraw = mainScreen
					else
						if tracing then
							saveTrace()
						end
						return
					end
				end
			else
				screenHandler(event)
			end
		elseif event[1] == "key" or event[1] == "char" or event[1] == "mouse_scroll" or event[1] == "mouse_drag" then
			screenHandler(event)
		end
	end
end

local function doGPS()
	local loc, oldLoc
	while true do
		oldLoc = loc
		loc = nil
		local result = {gps.locate()}
		if #result == 3 then
			loc = vector.new(unpack(result))
		end
		if loc then
			current = loc
			if destination then
				distance = math.sqrt((destination.x - current.x)^2 + (destination.y - current.y)^2 + (destination.z - current.z)^2)
			end
			if tracing then
				if not oldLoc or (oldLoc and (loc.x ~= oldLoc.x or loc.y ~= oldLoc.y or loc.z ~= oldLoc.z)) then
					table.insert(traces, loc)
				end
			end
		elseif current then
			current = "GPS Signal Lost"
		end
		if loc and oldLoc then
			local head = loc - oldLoc
			if math.abs(head.z) > math.abs(head.x) and math.abs(head.z) > 1 then
				if head.z == math.abs(head.z) then
					--positive z, south
					heading = "South"
				else
					heading = "North"
				end
			elseif math.abs(head.x) > 1 then
				if head.x == math.abs(head.x) then
					--positive x, east
					heading = "East"
				else
					heading = "West"
				end
			end
		end
		sleep(1)
	end
end

local function openDoor()
	local modem = peripheral.wrap("back")
	modem.open(4210)
	local handle = io.open("/.fingerprint")
	local fingerprint
	if handle then
		fingerprint = handle:read("*a")
		handle:close()
	end
	while true do
		local event = {os.pullEvent()}
		if openDoors then
			if event[1] == "modem_message" and event[3] == 4210 then
				modem.transmit(4211, 4212, textutils.serialize({content = textutils.serialize(fingerprint), senderID = os.getComputerID(), senderName = os.getComputerLabel(), channel = 4211, replyChannel = 4212, messageID = messageID or math.random(10000), destinationID = event[5].senderID}))
			end
		end
	end
end
				

screenHandler = handleMain
screenDraw = mainScreen
parallel.waitForAny(handleEvents, doGPS, openDoor)