Created
September 30, 2012 00:44
-
-
Save reefwing/3805511 to your computer and use it in GitHub Desktop.
Tutorial 18 - Saving and Loading Complicated Tables
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
--# Cell | |
Cell = class() | |
-- dGenerator Cell Class | |
-- Reefwing Software | |
-- | |
-- Version 1.2 (Modified from MineSweeper Cell Class) | |
-- | |
-- Each element in the dGenerator two dimensional grid{} | |
-- consists of a cell object. Each cell is responsible for | |
-- tracking its own state and drawing the appropriate sprite | |
-- based on this state. | |
-- | |
-- States available are: Obstacle, Clear, Start or Finish. | |
-- There can only be one start and one finish cell in the | |
-- matrix. | |
function Cell:init(i, j, state, x, y) | |
-- Cell Initialisation. | |
self.index = vec2(i, j) -- location of cell within the grid{} table | |
self.state = state -- contents of cell | |
self.pos = vec2(x, y) -- position of cell on the screen | |
self.action = nil -- call back function when cell tapped | |
self.size = vec2(32, 32) -- size of cell on screen | |
self.showGrid = false -- if true a border will be drawn around the cell | |
end | |
-- Cell Data Export and Import Functions (for saving and retrieving data) | |
function Cell:dataToString() | |
return self.state | |
end | |
function Cell:dataFromString(str) | |
self.state = str | |
end | |
-- Cell Draw Functions | |
function Cell:draw() | |
-- Codea does not automatically call this method | |
-- Draw the appropriate cell image based on its state. | |
if self.state == stateObstacle then | |
sprite("Dropbox:obstacleSprite", self.pos.x, self.pos.y) | |
elseif self.state == stateClear then | |
sprite("Dropbox:clearSprite", self.pos.x, self.pos.y) | |
elseif self.state == stateStart then | |
sprite("Dropbox:startSprite", self.pos.x, self.pos.y) | |
elseif self.state == stateEnd then | |
sprite("Dropbox:endSprite", self.pos.x, self.pos.y) | |
elseif self.state == statePath then | |
sprite("Dropbox:pathSprite", self.pos.x, self.pos.y) | |
end | |
-- If showGrid is true we draw a border around the cell. | |
if self.showGrid then | |
pushStyle() | |
noSmooth() | |
fill(clearColor) | |
stroke(darkGrayColor) | |
strokeWidth(1) | |
rectMode(CENTER) | |
rect(self.pos.x, self.pos.y, self.size.x, self.size.y) | |
popStyle() | |
end | |
end | |
-- Cell Touch Handling | |
function Cell:hit(p) | |
-- Was the touch on this cell? | |
-- Note code repurposed from the original button class | |
-- provide in the Codea examples. | |
local l = self.pos.x - self.size.x/2 | |
local r = self.pos.x + self.size.x/2 | |
local t = self.pos.y + self.size.y/2 | |
local b = self.pos.y - self.size.y/2 | |
if p.x > l and p.x < r and | |
p.y > b and p.y < t then | |
return true | |
end | |
return false | |
end | |
function Cell:touched(touch) | |
-- Codea does not automatically call this method | |
if touch.state == ENDED and self:hit(vec2(touch.x,touch.y)) then | |
if self.action then | |
-- call back method called. | |
self.action(self.index) | |
end | |
end | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
--# Colors | |
-- Make available the predefined colors from UIColor | |
-- | |
-- Version 1.1 | |
blackColor = color(0, 0, 0) | |
darkGrayColor = color(85, 85, 85) | |
lightGrayColor = color(170, 170, 170) | |
whiteColor = color(255, 255, 255) | |
grayColor = color(128, 128, 128) | |
redColor = color(255, 0, 0) | |
greenColor = color(0, 255, 0) | |
blueColor = color(0, 0, 255) | |
cyanColor = color(0, 255, 255) | |
yellowColor = color(255, 255, 0) | |
magentaColor = color(255, 0, 255) | |
orangeColor = color(255, 128, 0) | |
purpleColor = color(128, 0, 128) | |
brownColor = color(153, 102, 51) | |
clearColor = color(0, 0, 0, 0) | |
lightTextColor = color(255, 255, 255, 153) | |
darkTextColor = color(0, 0, 0) | |
-- Reefwing specific Colours | |
lightRedColor = color(243, 157, 33) | |
lightBlueColor = color(0, 0, 255, 128) | |
codeaDarkBackground = color(40, 40, 50) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
--# DataDumper | |
--[[ DataDumper.lua | |
Copyright (c) 2007 Olivetti-Engineering SA | |
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 dumplua_closure = [[ | |
local closures = {} | |
local function closure(t) | |
closures[#closures+1] = t | |
t[1] = assert(loadstring(t[1])) | |
return t[1] | |
end | |
for _,t in pairs(closures) do | |
for i = 2,#t do | |
debug.setupvalue(t[1], i-1, t[i]) | |
end | |
end | |
]] | |
local lua_reserved_keywords = { | |
'and', 'break', 'do', 'else', 'elseif', 'end', 'false', 'for', | |
'function', 'if', 'in', 'local', 'nil', 'not', 'or', 'repeat', | |
'return', 'then', 'true', 'until', 'while' } | |
local function keys(t) | |
local res = {} | |
local oktypes = { stringstring = true, numbernumber = true } | |
local function cmpfct(a,b) | |
if oktypes[type(a)..type(b)] then | |
return a < b | |
else | |
return type(a) < type(b) | |
end | |
end | |
for k in pairs(t) do | |
res[#res+1] = k | |
end | |
table.sort(res, cmpfct) | |
return res | |
end | |
local c_functions = {} | |
for _,lib in pairs{'_G', 'string', 'table', 'math', | |
'io', 'os', 'coroutine', 'package', 'debug'} do | |
local t = _G[lib] or {} | |
lib = lib .. "." | |
if lib == "_G." then lib = "" end | |
for k,v in pairs(t) do | |
if type(v) == 'function' and not pcall(string.dump, v) then | |
c_functions[v] = lib..k | |
end | |
end | |
end | |
function DataDumper(value, varname, fastmode, ident) | |
local defined, dumplua = {} | |
-- Local variables for speed optimization | |
local string_format, type, string_dump, string_rep = | |
string.format, type, string.dump, string.rep | |
local tostring, pairs, table_concat = | |
tostring, pairs, table.concat | |
local keycache, strvalcache, out, closure_cnt = {}, {}, {}, 0 | |
setmetatable(strvalcache, {__index = function(t,value) | |
local res = string_format('%q', value) | |
t[value] = res | |
return res | |
end}) | |
local fcts = { | |
string = function(value) return strvalcache[value] end, | |
number = function(value) return value end, | |
boolean = function(value) return tostring(value) end, | |
['nil'] = function(value) return 'nil' end, | |
['function'] = function(value) | |
return string_format("loadstring(%q)", string_dump(value)) | |
end, | |
userdata = function() error("Cannot dump userdata") end, | |
thread = function() error("Cannot dump threads") end, | |
} | |
local function test_defined(value, path) | |
if defined[value] then | |
if path:match("^getmetatable.*%)$") then | |
out[#out+1] = string_format("s%s, %s)\n", path:sub(2,-2), defined[value]) | |
else | |
out[#out+1] = path .. " = " .. defined[value] .. "\n" | |
end | |
return true | |
end | |
defined[value] = path | |
end | |
local function make_key(t, key) | |
local s | |
if type(key) == 'string' and key:match('^[_%a][_%w]*$') then | |
s = key .. "=" | |
else | |
s = "[" .. dumplua(key, 0) .. "]=" | |
end | |
t[key] = s | |
return s | |
end | |
for _,k in ipairs(lua_reserved_keywords) do | |
keycache[k] = '["'..k..'"] = ' | |
end | |
if fastmode then | |
fcts.table = function (value) | |
-- Table value | |
local numidx = 1 | |
out[#out+1] = "{" | |
for key,val in pairs(value) do | |
if key == numidx then | |
numidx = numidx + 1 | |
else | |
out[#out+1] = keycache[key] | |
end | |
local str = dumplua(val) | |
out[#out+1] = str.."," | |
end | |
if string.sub(out[#out], -1) == "," then | |
out[#out] = string.sub(out[#out], 1, -2); | |
end | |
out[#out+1] = "}" | |
return "" | |
end | |
else | |
fcts.table = function (value, ident, path) | |
if test_defined(value, path) then return "nil" end | |
-- Table value | |
local sep, str, numidx, totallen = " ", {}, 1, 0 | |
local meta, metastr = (debug or getfenv()).getmetatable(value) | |
if meta then | |
ident = ident + 1 | |
metastr = dumplua(meta, ident, "getmetatable("..path..")") | |
totallen = totallen + #metastr + 16 | |
end | |
for _,key in pairs(keys(value)) do | |
local val = value[key] | |
local s = "" | |
local subpath = path | |
if key == numidx then | |
subpath = subpath .. "[" .. numidx .. "]" | |
numidx = numidx + 1 | |
else | |
s = keycache[key] | |
if not s:match "^%[" then subpath = subpath .. "." end | |
subpath = subpath .. s:gsub("%s*=%s*$","") | |
end | |
s = s .. dumplua(val, ident+1, subpath) | |
str[#str+1] = s | |
totallen = totallen + #s + 2 | |
end | |
if totallen > 80 then | |
sep = "\n" .. string_rep(" ", ident+1) | |
end | |
str = "{"..sep..table_concat(str, ","..sep).." "..sep:sub(1,-3).."}" | |
if meta then | |
sep = sep:sub(1,-3) | |
return "setmetatable("..sep..str..","..sep..metastr..sep:sub(1,-3)..")" | |
end | |
return str | |
end | |
fcts['function'] = function (value, ident, path) | |
if test_defined(value, path) then return "nil" end | |
if c_functions[value] then | |
return c_functions[value] | |
elseif debug == nil or debug.getupvalue(value, 1) == nil then | |
return string_format("loadstring(%q)", string_dump(value)) | |
end | |
closure_cnt = closure_cnt + 1 | |
local res = {string.dump(value)} | |
for i = 1,math.huge do | |
local name, v = debug.getupvalue(value,i) | |
if name == nil then break end | |
res[i+1] = v | |
end | |
return "closure " .. dumplua(res, ident, "closures["..closure_cnt.."]") | |
end | |
end | |
function dumplua(value, ident, path) | |
return fcts[type(value)](value, ident, path) | |
end | |
if varname == nil then | |
varname = "return " | |
elseif varname:match("^[%a_][%w_]*$") then | |
varname = varname .. " = " | |
end | |
if fastmode then | |
setmetatable(keycache, {__index = make_key }) | |
out[1] = varname | |
table.insert(out,dumplua(value, 0)) | |
return table.concat(out) | |
else | |
setmetatable(keycache, {__index = make_key }) | |
local items = {} | |
for i=1,10 do items[i] = '' end | |
items[3] = dumplua(value, ident or 0, "t") | |
if closure_cnt > 0 then | |
items[1], items[6] = dumplua_closure:match("(.*\n)\n(.*)") | |
out[#out+1] = "" | |
end | |
if #out > 0 then | |
items[2], items[4] = "local t = ", "\n" | |
items[5] = table.concat(out) | |
items[7] = varname .. "t" | |
else | |
items[2] = varname | |
end | |
return table.concat(items) | |
end | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
--# FileManager | |
-- Define Directory Types | |
ProjectInfo = 1 | |
ProjectData = 2 | |
LocalData = 3 | |
GlobalData = 4 | |
-- Codea FileManager v1.2 | |
-- | |
-- A simple file manager to examine the Codea provided | |
-- persistent storage. Currently handles Local Data, Project Data, | |
-- Project Information and Global Data. | |
-- | |
-- Reefwing Software | |
-- 15 September 2012 | |
-- | |
-- This class requires the ListScroll and TextItem class written by @sanit and | |
-- the MenuBar class written by @dave1707 (with some modifications). | |
-- | |
-- WARNING - FILE DELETION CAN NOT BE UNDONE AND THE CLASS DOES NOT ASK FOR CONFIRMATION. | |
-- USE WITH EXTREME CAUTION AND BACK UP YOUR DATA BEFORE USING THIS | |
-- FILE MANAGER CLASS. | |
FileManager = class() | |
function FileManager:init() | |
-- Initialise the FileManager parameters and | |
-- load up the currently supported data stores. | |
directoryList = ListScroll(vec2(25, HEIGHT - 500), 200, 400) | |
directoryContents = {"Project Info", "Project Data", "Local Data", "Global Data"} | |
for i = 1, #directoryContents do | |
local item = TextItem(i, directoryContents[i]) | |
if i == 1 then | |
-- pre select Project Info "Directory" | |
item.selected = true | |
end | |
directoryList:add(item) | |
end | |
currentDirectory = ProjectInfo | |
dataKeyList = ListScroll(vec2(227, HEIGHT - 500), 260, 400) | |
-- Pre-fetch results | |
infoKeyTable = {"Description", "Author", "Date", "Version", "Comments"} | |
loadProjectKeys() | |
loadLocalKeys() | |
loadGlobalKeys() | |
displayKeys(infoKeyTable) | |
currentKey = 1 | |
valueString = readProjectInfo(infoKeyTable[currentKey]) | |
-- Setup the MenuBar | |
MenuSetup() | |
end | |
-- Functions to read keys from the available pLists | |
function loadProjectKeys() | |
projectKeyTable = listProjectData() | |
if #projectKeyTable == 0 then | |
projectKeyTable = {"nil"} | |
end | |
end | |
function loadLocalKeys() | |
localKeyTable = listLocalData() | |
if #localKeyTable == 0 then | |
localKeyTable = {"nil"} | |
end | |
end | |
function loadGlobalKeys() | |
globalKeyTable = listGlobalData() | |
if #globalKeyTable == 0 then | |
globalKeyTable = {"nil"} | |
end | |
end | |
-- This function displays the keys passed in keyTable within the dataKeyList ListScroll | |
function displayKeys(keyTable) | |
dataKeyList.items = {} | |
for i = 1, #keyTable do | |
local displayKey = string.truncate(keyTable[i], 14) | |
local item = TextItem(i, displayKey) | |
if i == 1 then | |
-- pre select first key in Project Info | |
item.selected = true | |
end | |
dataKeyList:add(item) | |
end | |
end | |
-- Setup the menu items at the top of the ListScrolls | |
-- Note that the call back functions are in the Main tab. | |
function MenuSetup() | |
pointer2 = nil | |
pointer3 = nil | |
b1tab = {} | |
local y = HEIGHT - 80 | |
-- Create tables for main menu bar | |
table.insert(b1tab, MenuBar(80, y, 100, 30, "Load", loadFile)) | |
table.insert(b1tab, MenuBar(180, y, 100, 30, "Cancel", cancelFileManager)) | |
table.insert(b1tab, MenuBar(280, y, 100, 30, "About", aboutFileManager)) | |
table.insert(b1tab, MenuBar(380, y, 100, 30, "Delete", deleteFile)) | |
pointer1 = b1tab | |
-- Global variables to prevent touches on scroll list | |
-- while subMenu is visible and to update the key list | |
-- if the directory changes. | |
subMenuShown = false | |
directoryChanged = false | |
end | |
-- Update functions for List Scrolls | |
function updateDirectorySelection() | |
local dlsi = directoryList.selectedItem | |
if dlsi ~= currentDirectory then | |
if dlsi == nil then | |
-- No Items Selected assign to default | |
dlsi = ProjectInfo | |
displayKeys(infoKeyTable) | |
elseif dlsi == ProjectData then | |
displayKeys(projectKeyTable) | |
elseif dlsi == ProjectInfo then | |
displayKeys(infoKeyTable) | |
elseif dlsi == LocalData then | |
displayKeys(localKeyTable) | |
elseif dlsi == GlobalData then | |
displayKeys(globalKeyTable) | |
end | |
currentDirectory = dlsi | |
directoryChanged = true | |
end | |
end | |
function updateKeySelection() | |
local dksi = dataKeyList.selectedItem | |
if dksi ~= currentKey or directoryChanged then | |
if directoryChanged then | |
dataKeyList.selectedItem = 1 | |
currentKey = 1 | |
directoryChanged = false | |
else | |
currentKey = dksi | |
end | |
if currentDirectory == nil then | |
-- No Directory Selected | |
valueString = "" | |
elseif currentDirectory == ProjectData and projectKeyTable[currentKey] ~= nil then | |
valueString = readProjectData(projectKeyTable[currentKey]) or "nil" | |
elseif currentDirectory == ProjectInfo and infoKeyTable[currentKey] ~= nil then | |
valueString = readProjectInfo(infoKeyTable[currentKey]) or "nil" | |
elseif currentDirectory == LocalData and localKeyTable[currentKey] ~= nil then | |
valueString = readLocalData(localKeyTable[currentKey]) or "nil" | |
elseif currentDirectory == GlobalData and globalKeyTable[currentKey] ~= nil then | |
valueString = readGlobalData(globalKeyTable[currentKey]) or "nil" | |
end | |
valueString = string.truncate(valueString, 150) | |
end | |
end | |
function FileManager:draw() | |
-- Codea does not automatically call this method | |
-- Handle Directory Selection | |
updateDirectorySelection() | |
updateKeySelection() | |
directoryList:draw() | |
dataKeyList:draw() | |
-- Draw Menu Bar | |
pushStyle() | |
fill(0,0,255,255) | |
rect(25, HEIGHT - 98, 725, 36) | |
popStyle() | |
MenuBar:menu1() | |
-- Draw the results window | |
pushStyle() | |
textWrapWidth(250) | |
textMode(CORNER) | |
font("Courier") | |
fontSize(24) | |
fill(255) | |
rect(489, HEIGHT - 500, 260, 400) | |
fill(0) | |
local _,h = textSize(valueString) | |
text(valueString, 496, HEIGHT - 100 - h) | |
popStyle() | |
end | |
function FileManager:touched(touch) | |
-- Codea does not automatically call this method | |
if touch.state == ENDED then | |
MenuBar:check(touch) | |
end | |
if pointer2 ~= nil then | |
-- Sub menu visible | |
subMenuShown = true | |
elseif subMenuShown and touch.state == ENDED then | |
subMenuShown = false | |
else | |
-- Sub menu is not visible so handle scroll touches | |
directoryList:touched(touch) | |
dataKeyList:touched(touch) | |
end | |
end | |
-- String Helper | |
function string.truncate(str, num) | |
if string.len(str) > num then | |
str = string.sub(str,1,num) .. "..." | |
end | |
return str | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
--# FindPath | |
-- A* Path Finding Function | |
-- | |
-- Based on the Algorithm posted at mobile.tutsplus.com | |
-- Corona SDK: Game Development Path Finding | |
-- | |
-- Version 1.0 | |
-- | |
-- Modified by Reefwing Software (www.reefwing.com.au) | |
-- | |
-- Modifications: | |
-- | |
-- - Ported to Codea from Corona | |
-- - Changed isObstacle variable to a more general state variable | |
function CalcMoves(board, startX, startY, targetX, targetY) | |
local openlist = {} -- Possible Moves | |
local closedlist = {} -- Checked Squares | |
local listk = 1 -- open list counter | |
local closedk = 0 -- Closedlist counter | |
local tempH = math.abs(startX-targetX) + math.abs(startY-targetY) | |
local tempG = 0 | |
openlist[1] = {x = startX, y = startY, g = 0, h = tempH, f = 0 + tempH ,par = 1} | |
local xsize = table.getn(board[1]) | |
local ysize = table.getn(board) | |
local curSquare = {} | |
local curSquareIndex = 1 -- Index of current base | |
while listk > 0 do | |
local lowestF = openlist[listk].f | |
curSquareIndex = listk | |
for k = listk, 1, -1 do | |
if openlist[k].f < lowestF then | |
lowestF = openlist[k].f | |
curSquareIndex = k | |
end | |
end | |
closedk = closedk + 1 | |
table.insert(closedlist,closedk,openlist[curSquareIndex]) | |
curSquare = closedlist[closedk] -- define current base from which to grow list | |
local rightOK = true | |
local leftOK = true -- Booleans defining if they're OK to add | |
local downOK = true -- (must be reset for each while loop) | |
local upOK = true | |
-- Look through closedlist. Makes sure that the path doesn't double back | |
if closedk > 0 then | |
for k = 1, closedk do | |
if closedlist[k].x == curSquare.x + 1 and closedlist[k].y == curSquare.y then | |
rightOK = false | |
end | |
if closedlist[k].x == curSquare.x-1 and closedlist[k].y == curSquare.y then | |
leftOK = false | |
end | |
if closedlist[k].x == curSquare.x and closedlist[k].y == curSquare.y + 1 then | |
downOK = false | |
end | |
if closedlist[k].x == curSquare.x and closedlist[k].y == curSquare.y - 1 then | |
upOK = false | |
end | |
end | |
end | |
-- Check if next points are on the map and within moving distance | |
if curSquare.x + 1 > xsize then | |
rightOK = false | |
end | |
if curSquare.x - 1 < 1 then | |
leftOK = false | |
end | |
if curSquare.y + 1 > ysize then | |
downOK = false | |
end | |
if curSquare.y - 1 < 1 then | |
upOK = false | |
end | |
-- If it is on the map, check map for obstacles | |
-- Lua returns an error if you try to access a table position | |
-- that doesn't exist, so you can't combine it with above. | |
if curSquare.x + 1 <= xsize and | |
board[curSquare.x+1][curSquare.y].state == stateObstacle then | |
rightOK = false | |
end | |
if curSquare.x - 1 >= 1 and | |
board[curSquare.x-1][curSquare.y].state == stateObstacle then | |
leftOK = false | |
end | |
if curSquare.y + 1 <= ysize and | |
board[curSquare.x][curSquare.y+1].state == stateObstacle then | |
downOK = false | |
end | |
if curSquare.y - 1 >= 1 and | |
board[curSquare.x][curSquare.y-1].state == stateObstacle then | |
upOK = false | |
end | |
-- check if the move from the current base is shorter then from the former parent | |
tempG = curSquare.g + 1 | |
for k = 1,listk do | |
if rightOK and openlist[k].x==curSquare.x+1 and openlist[k].y==curSquare.y | |
and openlist[k].g>tempG then | |
tempH=math.abs((curSquare.x+1)-targetX)+math.abs(curSquare.y-targetY) | |
table.insert(openlist,k,{x=curSquare.x+1, y=curSquare.y, g=tempG, | |
h=tempH, f=tempG+tempH, par=closedk}) | |
rightOK=false | |
end | |
if leftOK and openlist[k].x==curSquare.x-1 and openlist[k].y==curSquare.y | |
and openlist[k].g>tempG then | |
tempH=math.abs((curSquare.x-1)-targetX)+math.abs(curSquare.y-targetY) | |
table.insert(openlist,k,{x=curSquare.x-1, y=curSquare.y, g=tempG, | |
h=tempH, f=tempG+tempH, par=closedk}) | |
leftOK=false | |
end | |
if downOK and openlist[k].x==curSquare.x and openlist[k].y==curSquare.y+1 | |
and openlist[k].g>tempG then | |
tempH=math.abs((curSquare.x)-targetX)+math.abs(curSquare.y+1-targetY) | |
table.insert(openlist,k,{x=curSquare.x, y=curSquare.y+1, g=tempG, | |
h=tempH, f=tempG+tempH, par=closedk}) | |
downOK=false | |
end | |
if upOK and openlist[k].x==curSquare.x and openlist[k].y==curSquare.y-1 | |
and openlist[k].g>tempG then | |
tempH=math.abs((curSquare.x)-targetX)+math.abs(curSquare.y-1-targetY) | |
table.insert(openlist,k,{x=curSquare.x, y=curSquare.y-1, g=tempG, | |
h=tempH, f=tempG+tempH, par=closedk}) | |
upOK=false | |
end | |
end | |
-- Add points to openlist | |
-- Add point to the right of current base point | |
if rightOK then | |
listk=listk+1 | |
tempH=math.abs((curSquare.x+1)-targetX)+math.abs(curSquare.y-targetY) | |
table.insert(openlist,listk,{x=curSquare.x+1, y=curSquare.y, g=tempG, | |
h=tempH, f=tempG+tempH, par=closedk}) | |
end | |
-- Add point to the left of current base point | |
if leftOK then | |
listk=listk+1 | |
tempH=math.abs((curSquare.x-1)-targetX)+math.abs(curSquare.y-targetY) | |
table.insert(openlist,listk,{x=curSquare.x-1, y=curSquare.y, g=tempG, | |
h=tempH, f=tempG+tempH, par=closedk}) | |
end | |
-- Add point on the top of current base point | |
if downOK then | |
listk=listk+1 | |
tempH=math.abs(curSquare.x-targetX)+math.abs((curSquare.y+1)-targetY) | |
table.insert(openlist,listk,{x=curSquare.x, y=curSquare.y+1, g=tempG, | |
h=tempH, f=tempG+tempH, par=closedk}) | |
end | |
-- Add point on the bottom of current base point | |
if upOK then | |
listk=listk+1 | |
tempH=math.abs(curSquare.x-targetX)+math.abs((curSquare.y-1)-targetY) | |
table.insert(openlist,listk,{x=curSquare.x, y=curSquare.y-1, g=tempG, | |
h=tempH, f=tempG+tempH, par=closedk}) | |
end | |
table.remove(openlist,curSquareIndex) | |
listk=listk-1 | |
if closedlist[closedk].x==targetX and closedlist[closedk].y==targetY then | |
return closedlist | |
end | |
end | |
return nil | |
end | |
function CalcPath(closedlist) | |
if closedlist == nil then | |
return nil | |
end | |
local path = {} | |
local pathIndex = {} | |
local last = table.getn(closedlist) | |
table.insert(pathIndex, 1, last) | |
local i = 1 | |
while pathIndex[i] > 1 do | |
i = i + 1 | |
table.insert(pathIndex, i, closedlist[pathIndex[i - 1]].par) | |
end | |
for n = table.getn(pathIndex), 1, -1 do | |
table.insert(path,{x=closedlist[pathIndex[n]].x, y=closedlist[pathIndex[n]].y}) | |
end | |
closedlist = nil | |
return path | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
--# ListScroll | |
-- List Scroll class | |
-- | |
-- Developed by @sanit February 6, 2012 | |
-- | |
-- Version 1.0 | |
ListScroll = class() | |
function ListScroll:init(pos,w,h) | |
-- you can accept and set parameters here | |
self.pos = pos | |
self.width= w | |
self.height= h | |
self.itemHeight = 0 | |
self.borderColor = color(100,100,100,255) | |
self.borderWidth = 2 | |
self.backColor = color(255,255,255,255) | |
self.sepColor = color(100,100,100,255) | |
self.rowColor1 = color (255,255,255,255) | |
self.rowColor2 = color(186, 186, 186, 100) | |
self.allowEdit = false | |
self.allowDelete = true | |
self.allowInsert = false | |
self.multiSelected = false | |
self.items = {} | |
self.posY = self.pos.y+self.height | |
self.prevState = nil | |
self.selectedItem = 1 | |
end | |
function ListScroll:add(i) | |
self.itemHeight = i.height | |
i.width = self.width | |
table.insert(self.items,i) | |
end | |
function ListScroll:selectedItem() | |
return self.selectedItem | |
end | |
function ListScroll:selectedValue() | |
if self.selectedItem==nil then | |
return nil | |
end | |
return self.items[self.selectedItem].value | |
end | |
function ListScroll:itemCount() | |
return #self.items | |
end | |
function ListScroll:draw() | |
-- Codea does not automatically call this method | |
pushStyle() | |
strokeWidth(self.borderWidth) | |
fill(self.backColor) | |
stroke(self.borderColor) | |
rect(self.pos.x,self.pos.y,self.width,self.height) | |
clip(self.pos.x,self.pos.y,self.width,self.height) | |
for i=1,#self.items do | |
strokeWidth(1) | |
if i%2==0 then | |
fill(self.rowColor1) | |
else | |
fill(self.rowColor2) | |
end | |
self.items[i]:draw(self.pos.x,self.posY-i*self.itemHeight) | |
end | |
noClip() | |
popStyle() | |
end | |
function ListScroll:touched(touch) | |
-- Codea does not automatically call this method | |
if not (touch.x>=self.pos.x and touch.x<=self.pos.x+self.width and | |
touch.y>=self.pos.y and touch.y<=self.pos.y+self.height) then | |
return | |
end | |
if touch.state==MOVING then | |
self.prevState=MOVING | |
self.posY = self.posY + touch.deltaY | |
if self.posY>=self.pos.y+self.height+self.itemHeight*(#self.items-1) then | |
self.posY=self.pos.y+self.height+self.itemHeight*(#self.items-1) | |
end | |
elseif touch.state==ENDED then | |
if self.prevState==MOVING then | |
if self.posY<self.pos.y+self.height then | |
self.posY=self.pos.y+self.height | |
end | |
self.prevState=nil | |
else | |
local i = math.ceil((self.posY-touch.y)/self.itemHeight) | |
if i<=#self.items then | |
self.selectedItem = i | |
self.items[i]:touched(touch) | |
if not self.multiSelected then | |
for r=1,#self.items do | |
if r ~= i then | |
self.items[r].selected=false | |
end | |
end | |
end | |
else | |
self.selectedItem = nil | |
end | |
end | |
end | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
--# Main | |
-- dGenerator | |
-- | |
-- This program is a WYSIWYG Level Editor which can be used | |
-- in a tower defence or Rogue type game. | |
-- | |
-- We will use it to demonstrate our A* pathfinding tutorial. | |
supportedOrientations(ANY) | |
DEBUG = true -- If true additional debug information is printed. | |
-- Define the grid cell and App states | |
stateNil = 0 | |
stateStart = 1 | |
stateEnd = 2 | |
stateClear = 3 | |
stateObstacle = 4 | |
stateCheck = 5 | |
stateToggleGrid = 6 | |
stateToggleDebug = 7 | |
stateShowFileManager = 8 | |
currentState = stateNil | |
function setup() | |
version = 1.1 | |
saveProjectInfo("Description", "Level Generator for Tower Defence Game v"..version) | |
saveProjectInfo("Author", "Reefwing Software") | |
saveProjectInfo("Date", "30 August 2012") | |
saveProjectInfo("Version", version) | |
saveProjectInfo("Comments", "Original Release.") | |
print("-- dGenerator v"..version.."\n") | |
-- We will keep track of whether we have selected the | |
-- start and finish cell and if so the co-ordinates of | |
-- these two cells. This will save us having to iterate | |
-- through the entire table to find them. | |
startCellSelected = false | |
endCellSelected = false | |
startPos = nil | |
endPos = nil | |
-- Create a global variable to hold the current tap state | |
tapState = stateStart | |
-- Initialise the grid matrix, we will reuse a lot of the | |
-- code from our MineSweeper App. We have selected a sprite | |
-- size of 32 so you can use Spritely to generate your sprites | |
-- if you want. We used Sprite Something this time. | |
mSpriteSize = 32 | |
gridWidth = 15 | |
gridHeight = 15 | |
-- baseX = WIDTH/2 - (mSpriteSize * gridWidth) / 2 + 15 | |
baseX = 30 | |
grid = {} | |
createGrid() | |
-- Create the 4 tap state menu select buttons & 4 action buttons | |
local bX = baseX + 33 | |
local bY = 50 | |
startButton = Button(bX, bY, true, "Start") | |
startButton.tag = stateStart | |
startButton.action = function()buttonPressed(stateStart) end | |
endButton = Button(bX + 74, bY, false, "End") | |
endButton.tag = stateEnd | |
endButton.action = function()buttonPressed(stateEnd) end | |
clearButton = Button(bX + 148, bY, false, "Path") | |
clearButton.tag = stateClear | |
clearButton.action = function()buttonPressed(stateClear) end | |
wallButton = Button(bX + 222, bY, false, "Wall") | |
wallButton.tag = stateWall | |
wallButton.action = function()buttonPressed(stateObstacle) end | |
gridButton = Button(bX + 326, bY, false, "Grid") | |
gridButton.tag = stateToggleGrid | |
gridButton.action = function()buttonPressed(stateToggleGrid) end | |
checkButton = Button(bX + 400, bY, false, "A*") | |
checkButton.tag = stateCheck | |
checkButton.action = function()buttonPressed(stateCheck) end | |
debugButton = Button(bX + 474, bY, true, "dBug") | |
debugButton.tag = stateToggleDebug | |
debugButton.action = function()buttonPressed(stateToggleDebug) end | |
exitButton = Button(bX + 548, bY, false, "Exit") | |
exitButton.action = function()close() end | |
-- Unlike the other buttons, we want the checkButton to be | |
-- momentary. To create this illusion we will use a counter | |
-- called checkCounter which will turn off the button after | |
-- 0.5 seconds. | |
checkCounter = 0 | |
-- It is actually easier to use the procedurally generated | |
-- mesh buttons, so we will use these for the Load, Save and | |
-- Reset buttons. | |
local x, y = baseX - mSpriteSize/2, HEIGHT - 128 | |
resetButton = MeshButton("Reset", x, y, 128, 50) | |
resetButton.action = function() resetGrid() end | |
loadButton = MeshButton("Load", x + 178, y, 128, 50) | |
loadButton.action = function() loadLevel() end | |
saveButton = MeshButton("Save", x + 356, y, 128, 50) | |
saveButton.action = function() saveLevel() end | |
-- Create the game name and level text boxes | |
y = HEIGHT - 50 | |
gameNameTextBox = TextBox(x, y, 178+128, "<Enter Game Name>") | |
levelTextBox = TextBox(x + 356, y, 128, "<Level>") | |
-- Initialise the FileManager class | |
FileManager:init() | |
end | |
-- Menu Bar Call Back Methods | |
function cancelFileManager() | |
-- Custom implementation goes here | |
currentState = stateNil | |
end | |
function aboutFileManager() | |
-- Custom implementation goes here | |
end | |
function saveFile() | |
-- Custom implementation goes here | |
end | |
function loadFile() | |
-- Once file is selected load level | |
if currentDirectory == GlobalData and globalKeyTable[currentKey] ~= nil then | |
local keyString = globalKeyTable[currentKey] | |
local valString = readGlobalData(globalKeyTable[currentKey]) | |
if DEBUG then | |
print("-- Loading Key: " .. keyString) | |
end | |
if string.starts(keyString, "dGEN") then | |
print("dGen - valid data key found.") | |
local keyArray = explode(",", keyString) | |
gameNameTextBox.text = keyArray[2] | |
levelTextBox.text = keyArray[3] | |
if string.match(keyArray[4], "true") == "true" then | |
print("dGen - start cell selected.") | |
startCellSelected = true | |
if startPos == nil then | |
startPos = vec2(keyArray[5], keyArray[6]) | |
else | |
startPos.x = keyArray[5] | |
startPos.y = keyArray[6] | |
end | |
else | |
print("dGen - start cell not selected.") | |
startCellSelected = false | |
startPos.x = nil | |
startPos.y = nil | |
end | |
if string.match(keyArray[7], "true") == "true" then | |
print("dGen - end cell selected.") | |
endCellSelected = true | |
if endPos == nil then | |
endPos = vec2(keyArray[8], keyArray[9]) | |
else | |
startPos.x = keyArray[8] | |
startPos.y = keyArray[9] | |
end | |
else | |
print("dGen - end cell not selected.") | |
endCellSelected = false | |
endPos.x = nil | |
endPos.y = nil | |
end | |
generateGrid = loadstring(valString) | |
createGrid(generateGrid()) | |
print("dGen - Grid loaded.") | |
else | |
print("dGen ERROR - invalid file type.") | |
end | |
end | |
currentState = stateNil | |
end | |
function deleteFile() | |
if currentDirectory == nil then | |
-- No Directory Selected | |
elseif currentDirectory == ProjectData and projectKeyTable[currentKey] ~= nil then | |
saveProjectData(projectKeyTable[currentKey], nil) | |
loadProjectKeys() | |
displayKeys(projectKeyTable) | |
currentKey = 1 | |
valueString = string.truncate(readProjectData(projectKeyTable[currentKey]) or "nil",150) | |
elseif currentDirectory == ProjectInfo and infoKeyTable[currentKey] ~= nil then | |
-- delete is not currently supported for ProjectInfo because we can't | |
-- get a list of the keys in this pList. | |
elseif currentDirectory == LocalData and localKeyTable[currentKey] ~= nil then | |
saveLocalData(localKeyTable[currentKey], nil) | |
loadLocalKeys() | |
displayKeys(localKeyTable) | |
currentKey = 1 | |
valueString = string.truncate(readLocalData(localKeyTable[currentKey]) or "nil",150) | |
elseif currentDirectory == GlobalData and globalKeyTable[currentKey] ~= nil then | |
saveGlobalData(globalKeyTable[currentKey], nil) | |
loadGlobalKeys() | |
displayKeys(globalKeyTable) | |
currentKey = 1 | |
valueString = string.truncate(readGlobalData(globalKeyTable[currentKey]) or "nil",150) | |
end | |
end | |
-- dGenerator Draw() functions | |
function draw() | |
-- This sets a dark background color | |
background(codeaDarkBackground) | |
if currentState == stateFileManager then | |
FileManager:draw() | |
else | |
-- Draw the grid | |
drawGrid() | |
-- Draw the tap state button tool bar | |
pushStyle() | |
local toolBarX = baseX - mSpriteSize/2 | |
fill(lightGrayColor) | |
stroke(whiteColor) | |
strokeWidth(4) | |
rect(toolBarX, 7, 320, 84) | |
lineCapMode(SQUARE) | |
stroke(blackColor) | |
strokeWidth(6) | |
line(toolBarX + 2, 7, toolBarX + 2, 91) | |
line(toolBarX, 90, toolBarX + 320, 90) | |
-- If the checkButton is pressed start the checkCounter timer | |
-- this will unpress the button after 0.5 secs. | |
if checkButton.pressed then | |
checkCounter = checkCounter + DeltaTime | |
if checkCounter > 1 then | |
checkButton.pressed = false | |
checkCounter = 0 | |
end | |
end | |
-- Draw the buttons | |
startButton:draw() | |
endButton:draw() | |
clearButton:draw() | |
wallButton:draw() | |
gridButton:draw() | |
checkButton:draw() | |
debugButton:draw() | |
exitButton:draw() | |
resetButton:draw() | |
loadButton:draw() | |
saveButton:draw() | |
-- And the Text Boxes | |
gameNameTextBox:draw() | |
levelTextBox:draw() | |
popStyle() | |
end | |
end | |
function drawGrid() | |
-- Iterate through the grid matrix and draw each cell | |
for i = 1, gridWidth do | |
for j = 1, gridHeight do | |
grid[i][j]: draw() | |
end | |
end | |
end | |
-- Load and Save Level Functions | |
function explode(div,str) | |
if (div=='') then return false end | |
local pos,arr = 0,{} | |
-- for each divider found | |
for st,sp in function() return string.find(str,div,pos,true) end do | |
table.insert(arr,string.sub(str,pos,st-1)) -- Attach chars left of current divider | |
pos = sp + 1 -- Jump past current divider | |
end | |
table.insert(arr,string.sub(str,pos)) -- Attach chars right of last divider | |
return arr | |
end | |
function saveLevel() | |
print("-- Saving Level Data.") | |
-- Create and populate a table (saveGrid) to save the grid state. | |
-- This way we can save and retrieve the level data. | |
-- | |
-- Save Data string format: | |
-- | |
-- key = "DGEN,gameName,levelNumberstartCellSelected,startPos.x,startPos.y, | |
-- endCellSelected,endPos.x,endPos.y" | |
-- | |
-- value = "saveGrid" | |
local saveGrid = {} | |
for i = 1, gridWidth do | |
saveGrid[i] = {} -- create a new row | |
for j = 1, gridHeight do | |
saveGrid[i][j] = grid[i][j]:dataToString() | |
end | |
end | |
-- Note that no checks are made regarding whether game name or | |
-- level number has been entered by the user. Overwriting previous | |
-- levels also isn't checked. | |
local key = "dGEN," .. gameNameTextBox.text .. "," .. levelTextBox.text .."," | |
local suffix | |
if startCellSelected and startPos ~= nil then | |
suffix = "true," .. startPos.x .. "," .. startPos.y .. "," | |
else | |
suffix = "false,nil,nil," | |
end | |
if endCellSelected and endPos ~= nil then | |
suffix = suffix .. "true," .. endPos.x .. "," .. endPos.y .. "," | |
else | |
suffix = suffix .. "false,nil,nil," | |
end | |
key = key .. suffix | |
local saveString = DataDumper(saveGrid) | |
if DEBUG then | |
print("Key: " .. key) | |
print("Value: " .. saveString) | |
end | |
saveGlobalData(key, saveString) | |
end | |
function loadLevel() | |
print("-- Loading Level Data.") | |
print("-- Available Keys:") | |
currentState = stateFileManager | |
globalDataKeyTable = listGlobalData() | |
for i, v in ipairs(globalDataKeyTable) do | |
if string.starts(v, "dGEN") then | |
print(i..": "..v) | |
elseif i == #globalDataKeyTable and DEBUG then | |
print("-- End of Global Data File.") | |
end | |
end | |
end | |
-- Handle iPad Orientation Changes | |
function updateGridLocation(newOrientation) | |
-- This function is required to reposition the grid | |
-- if the iPad orientation changes. | |
baseX = 30 | |
local y = HEIGHT/2 - (mSpriteSize * gridHeight) / 2 | |
local x = baseX | |
for i = 1, gridWidth do | |
for j = 1, gridHeight do | |
grid[i][j].pos.x = x | |
grid[i][j].pos.y = y | |
x = x + mSpriteSize | |
end | |
x = baseX | |
y = y + mSpriteSize | |
end | |
local bX = baseX + 33 | |
startButton.x = bX | |
endButton.x = bX + 74 | |
clearButton.x = bX + 148 | |
wallButton.x = bX + 222 | |
local rbX, rbY = baseX - mSpriteSize/2, HEIGHT - 128 | |
resetButton.x, resetButton.y = rbX, rbY | |
loadButton.x, loadButton.y = rbX + 178, rbY | |
saveButton.x, saveButton.y = rbX + 356, rbY | |
local tbY = HEIGHT - 50 | |
gameNameTextBox.x, gameNameTextBox.y = rbX, tbY | |
levelTextBox.x, levelTextBox.y = rbX + 356, tbY | |
if newOrientation == LANDSCAPE_LEFT or newOrientation == LANDSCAPE_RIGHT then | |
gridButton.x = bX + 326 | |
checkButton.x = bX + 400 | |
debugButton.x = bX + 474 | |
debugButton.y = checkButton.y | |
exitButton.x = bX + 548 | |
exitButton.y = gridButton.y | |
else | |
local rbY = rbY - 130 | |
gridButton.x = bX + 320 | |
exitButton.x = gridButton.x | |
exitButton.y = gridButton.y + 74 | |
checkButton.x = bX + 394 | |
debugButton.x = checkButton.x | |
debugButton.y = checkButton.y + 74 | |
resetButton.y = rbY | |
loadButton.y = rbY | |
saveButton.y = rbY | |
end | |
end | |
function orientationChanged(newOrientation) | |
if currentState == stateShowFileManager then | |
-- Update ListScroll co-ordinates for new orientation | |
local y = HEIGHT - 500 | |
if directoryList ~= nil then | |
directoryList.pos.y = y | |
end | |
if dataKeyList ~= nil then | |
dataKeyList.pos.y = y | |
end | |
-- Update Menu Bar co-ordinates for new orientation | |
y = HEIGHT - 80 | |
if b1tab ~= nil then | |
for i = 1, #b1tab do | |
b1tab[i].y = y | |
end | |
end | |
y = HEIGHT - 110 | |
if b2tab ~= nil then | |
for i = 1, #b2tab do | |
b2tab[i].y = y | |
y = y - 30 | |
end | |
end | |
else | |
updateGridLocation(newOrientation) | |
end | |
end | |
-- Grid creation and reset functions | |
function createGrid(obstacleGrid) | |
local y = HEIGHT/2 - (mSpriteSize * gridHeight) / 2 | |
local x = baseX | |
-- Create the grid using nested tables. | |
-- It operates as a two dimensional array (or matrix) | |
if DEBUG then | |
if obstacleGrid == nil then | |
print("-- dGen: Creating Empty Grid.") | |
else | |
print("-- dGen: Creating Grid from File.") | |
end | |
end | |
for i = 1, gridWidth do | |
grid[i] = {} -- create a new row | |
for j = 1, gridHeight do | |
if obstacleGrid == nil then | |
grid[i][j] = Cell(i, j, stateObstacle, x, y) | |
else | |
grid[i][j] = Cell(i, j, obstacleGrid[i][j], x, y) | |
end | |
grid[i][j].action = function() handleCellTouch(grid[i][j].index) end | |
x = x + mSpriteSize | |
end | |
x = baseX | |
y = y + mSpriteSize | |
end | |
end | |
function resetGrid() | |
-- When the reset button is tapped this function will | |
-- reset the table | |
if DEBUG then | |
print("-- dGen: Resetting Grid.") | |
end | |
for i = 1, gridWidth do | |
for j = 1, gridHeight do | |
grid[i][j] = nil | |
end | |
end | |
grid = {} | |
createGrid() | |
if gridButton.pressed then | |
toggleGridState() | |
end | |
startCellSelected = false | |
endCellSelected = false | |
startPos = nil | |
endPos = nil | |
end | |
-- Cell Related Functions | |
-- | |
-- We discussed closure functions in a separate tutorial, but | |
-- for now to understand what is going on in the count neighbouring cell | |
-- functions you need to know that when a function is enclosed in | |
-- another function, it has full access to local variables from the | |
-- enclosing function. In this example, inNeighbourCells() increments the local | |
-- variable obstacleNum in countObstacles(). | |
function inNeighbourCells(startX, endX, startY, endY, closure) | |
for i = math.max(startX, 1), math.min(endX, gridWidth) do | |
for j = math.max(startY, 1), math.min(endY, gridHeight) do | |
closure(i, j) | |
end | |
end | |
end | |
function countObstacles(index) | |
local obstacleNum = 0 | |
inNeighbourCells(index.x - 1, index.x + 1, index.y - 1, index.y + 1, | |
function(x, y) if grid[x][y].state == stateObstacle then | |
obstacleNum = obstacleNum + 1 end | |
end) | |
return obstacleNum | |
end | |
-- Button Action Methods | |
function toggleGridState() | |
-- This function will toggle whether the grid overlay is shown. | |
for i = 1, gridWidth do | |
for j = 1, gridHeight do | |
grid[i][j].showGrid = not grid[i][j].showGrid | |
end | |
end | |
end | |
function toggleDebugState() | |
-- This function will toggle whether the Debug window is shown. | |
if debugButton.pressed then | |
displayMode(STANDARD) | |
DEBUG = true | |
else | |
displayMode(FULLSCREEN_NO_BUTTONS) | |
DEBUG = false | |
end | |
end | |
function clearPath() | |
-- Clear the path from the Grid. | |
if DEBUG then | |
print("-- dGEN: Clearing Path from Grid") | |
end | |
for i = 1, gridWidth do | |
for j = 1, gridHeight do | |
if grid[i][j].state == statePath then | |
grid[i][j].state = stateClear | |
end | |
end | |
end | |
end | |
function findPath() | |
-- If a start and end cell has been defined, use | |
-- the A* algorithm to find and display a path. | |
if startCellSelected then | |
if endCellSelected then | |
print("-- dGEN: Calculating A* Path.") | |
path = CalcPath(CalcMoves(grid, startPos.x, startPos.y, endPos.x, endPos.y)) | |
if path == nil then | |
print("-- dGEN No path found.") | |
else | |
print("-- dGEN: Path found:\n") | |
if DEBUG then | |
print(to_string(path, 2)) | |
end | |
-- start and end one cell early so we dont overwrite | |
-- the start and end cell status. | |
for i = 2, table.getn(path) - 1 do | |
grid[path[i].x][path[i].y].state = statePath | |
end | |
end | |
else | |
print("--dGEN ERROR: Path can't be found until end cell is selected.") | |
end | |
else | |
print("--dGEN ERROR: Path can't be found until start cell is selected.") | |
end | |
end | |
-- Touch Handling | |
function buttonPressed(index) | |
if index < stateCheck then | |
tapState = index | |
end | |
if index == stateStart then | |
endButton.pressed = false | |
clearButton.pressed = false | |
wallButton.pressed = false | |
elseif index == stateEnd then | |
startButton.pressed = false | |
clearButton.pressed = false | |
wallButton.pressed = false | |
elseif index == stateClear then | |
startButton.pressed = false | |
endButton.pressed = false | |
wallButton.pressed = false | |
elseif index == stateObstacle then | |
startButton.pressed = false | |
endButton.pressed = false | |
clearButton.pressed = false | |
elseif index == stateToggleGrid then | |
toggleGridState() | |
elseif index == stateCheck then | |
findPath() | |
elseif index == stateToggleDebug then | |
toggleDebugState() | |
else | |
print("-- dGEN WARNING: Unknown button index pressed.") | |
end | |
end | |
function touched(touch) | |
if currentState == stateFileManager then | |
FileManager:touched(touch) | |
else | |
-- Pass through touch handling to buttons, textbox and the grid cells | |
startButton:touched(touch) | |
endButton:touched(touch) | |
clearButton:touched(touch) | |
wallButton:touched(touch) | |
gridButton:touched(touch) | |
checkButton:touched(touch) | |
debugButton:touched(touch) | |
exitButton:touched(touch) | |
resetButton:touched(touch) | |
saveButton:touched(touch) | |
loadButton:touched(touch) | |
gameNameTextBox:touched(touch) | |
levelTextBox:touched(touch) | |
for i = 1, gridWidth do | |
for j = 1, gridHeight do | |
grid[i][j]:touched(touch) | |
end | |
end | |
end | |
end | |
function handleCellTouch(index) | |
if tapState == stateStart and startCellSelected then | |
print("-- dGEN Warning: Only one cell may be assigned as the start cell.") | |
elseif tapState == stateEnd and endCellSelected then | |
print("-- dGEN Warning: Only one cell may be assigned as the end cell.") | |
else | |
if tapState == stateStart then | |
startCellSelected = true | |
startPos = vec2(index.x, index.y) | |
elseif tapState == stateEnd then | |
endCellSelected = true | |
endPos = vec2(index.x, index.y) | |
end | |
if grid[index.x][index.y].state == stateStart then | |
startCellSelected = false | |
startPos = nil | |
clearPath() | |
elseif grid[index.x][index.y].state == stateEnd then | |
endCellSelected = false | |
endPos = nil | |
clearPath() | |
end | |
grid[index.x][index.y].state = tapState | |
end | |
end | |
-- KeyBoard handling function | |
-- Used to enter game name and level | |
function keyboard(key) | |
-- Add text to the textbox which has focus | |
local mTextBox = gameNameTextBox | |
if levelTextBox.hasFocus then | |
mTextBox = levelTextBox | |
end | |
if key ~= nil then | |
if string.byte(key) == 10 then -- <RETURN> Key pressed | |
hideKeyboard() | |
mTextBox.hasFocus = false | |
elseif string.byte(key) ~= 44 then -- filter out commas | |
mTextBox:acceptKey(key) | |
end | |
end | |
end | |
-- Some String Helper Functions: | |
function string.starts(String, Start) | |
return string.sub(String, 1, string.len(Start)) == Start | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
--# TextItem | |
-- Text Item class | |
-- | |
-- Developed by @sanit February 6, 2012 | |
-- | |
-- Version 1.0 | |
TextItem = class() | |
function TextItem:init(id,value) | |
-- you can accept and set parameters here | |
self.pos = vec2(0,0) | |
self.id = id | |
self.value=value | |
self.selected=false | |
self.selectedColor = color(223, 218, 150, 255) | |
self.width = 0 | |
self.height = 0 | |
self.textColor = color(0,0,0,255) | |
self.textAlign=LEFT | |
self.fontName = "Courier" | |
self.fontSize=24 | |
font(self.fontName) | |
fontSize(self.fontSize) | |
_,self.height = textSize(self.fontName) | |
end | |
function TextItem:draw(x,y) | |
-- Codea does not automatically call this method | |
self.pos = vec2(x,y) | |
pushStyle() | |
strokeWidth(1) | |
stroke(stroke()) | |
if self.selected then | |
fill(self.selectedColor) | |
end | |
rect(self.pos.x,self.pos.y,self.width,self.height) | |
font(self.fontName) | |
fontSize(self.fontSize) | |
textMode(CORNER) | |
textAlign(self.textAlign) | |
textWrapWidth(self.width) | |
fill(self.textColor) | |
text(self.value,self.pos.x+5,self.pos.y) | |
popStyle() | |
end | |
function TextItem:touched(touch) | |
-- Codea does not automatically call this method | |
if self.selected then | |
self.selected=false | |
else | |
self.selected=true | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment