Jump to content

[TOPIC: topicViewTemplate]
[GLOBAL: userSmallPhoto]
Photo

OGT Level Manager
Started by Jack G Apr 06 2019 09:37 AM

2 replies to this topic
[TOPIC CONTROLS]
[/TOPIC CONTROLS]
[modOptionsDropdown]
[/modOptionsDropdown]
[reputationFilter]
[TOPIC: post.html]
#1

Jack G

[GLOBAL: userInfoPane.html]
Jack G
  • Observer

  • 5 posts
  • Corona SDK

Hi, I was try to see if I call find some info on making level when I came across Level Manager and I wish I never had, because it is very confusing to a beginner.  I found GGData.lua and ogt_Imdata.lua especially confusing, will I need to use this mess to code a non-animated type simple little app?

local GGData = {}
local GGData_mt = { __index = GGData }

local json = require( "json" )
local lfs = require( "lfs" )
local crypto = require( "crypto" )

-- Functions used for converting tables to strings which is used for data integrity --

function table.val_to_str ( v )
	if "string" == type( v ) then
		v = string.gsub( v, "\n", "\\n" )
		if string.match( string.gsub(v,"[^'\"]",""), '^"+$' ) then
			return "'" .. v .. "'"
		end
		return '"' .. string.gsub(v,'"', '\\"' ) .. '"'
	else
		return "table" == type( v ) and table.tostring( v ) or tostring( v )
	end
end

function table.key_to_str ( k )
	if "string" == type( k ) and string.match( k, "^[_%a][_%a%d]*$" ) then
		return k
	else
		return "[" .. table.val_to_str( k ) .. "]"
	end
end

function table.tostring( tbl )
	local result, done = {}, {}
	for k, v in ipairs( tbl ) do
		table.insert( result, table.val_to_str( v ) )
		done[ k ] = true
	end
	for k, v in pairs( tbl ) do
		if not done[ k ] then
	  		table.insert( result,
			table.key_to_str( k ) .. "=" .. table.val_to_str( v ) )
		end
	end
	return "{" .. table.concat( result, "," ) .. "}"
end

local toString = function( value )
	if type( value ) == "table" then
		return table.tostring( value )
	else
		return tostring( value )
	end
end
-----------------------------------------------

--- Initiates a new GGData object.
-- @param id The name of the GGData to create or load ( if it already exists ).
-- @param path The path to the GGData. Optional, defaults to "boxes".
-- @param baseDir The base directory for the GGData. Optional, defaults to system.DocumentsDirectory.
-- @return The new object.
function GGData:new( id, path, baseDir )

    local self = {}

    setmetatable( self, GGData_mt )

    self.id = id
    self.path = path or "boxes"
    self.baseDir = baseDir

    if self.id then
    	self:load()
    end

    return self

end

--- Loads, or reloads, this GGData object from disk.
-- @param id The id of the GGData object.
-- @param path The path to the GGData. Optional, defaults to "boxes".
-- @param baseDir The base directory for the GGData. Optional, defaults to system.DocumentsDirectory.
function GGData:load( id, path, baseDir )

	-- Set up the path
	path = path or "boxes"

	-- Pre-declare the new GGData object
	local box

	-- If no id was passed in then assume we're working with a pre-loaded GGData object so use its id
	if not id then
		id = self.id
		box = self
	end

	local data = {}

	local path = system.pathForFile( path .. "/" .. id .. ".box", baseDir or system.DocumentsDirectory )

	local file = io.open( path, "r" )

	if not file then
		return
	end

	data = json.decode( file:read( "*a" ) )
	io.close( file )

	-- If no GGData exists then we are acting on a Class function i.e. not a pre-loaded GGData object.
	if not box then
		-- Create the new GGData object.
		box = GGData:new()
	end

	-- Copy all the properties across.
	for k, v in pairs( data ) do
		box[ k ] = v
	end

	return box

end

--- Saves this GGData object to disk.
function GGData:save()

	-- Don't want this key getting saved out
	local integrityKey = self.integrityKey
	self.integrityKey = nil

	local data = {}

	-- Copy across all the properties that can be saved.
	for k, v in pairs( self ) do
		if type( v ) ~= "function" and type( v ) ~= "userdata" then
			data[ k ] = v
		end
	end

	-- Check for and create if necessary the boxes directory.
	local path = system.pathForFile( "", system.DocumentsDirectory )
	local success = lfs.chdir( path )

	if success then
		lfs.mkdir( self.path )
		path = self.path
	else
		path = ""
	end

	data = json.encode( data )

	path = system.pathForFile( self.path .. "/" .. self.id .. ".box", system.DocumentsDirectory )
	local file = io.open( path, "w" )

	if not file then
		return
	end

	file:write( data )

	io.close( file )
	file = nil

	-- Set the key back again
	self.integrityKey = integrityKey

end

--- Sets a value in this GGData object.
-- @param name The name of the value to set.
-- @param value The value to set.
function GGData:set( name, value )
	self[ name ] = value
	self:storeIntegrityHash( name, value )
end

--- Gets a value from this GGData object.
-- @param name The name of the value to get.
-- @return The value.
function GGData:get( name )
	return self[ name ]
end

--- Checks whether a value of this GGData object is higher than another value.
-- @param name The name of the first value to check.
-- @param otherValue The name of the other value to check. Can also be a number.
-- @return True if the first value is higher, false otherwise.
function GGData:isValueHigher( name, otherValue )
	if type( otherValue ) == "string" then
		otherValue = self:get( otherValue )
	end
	return self[ name ] > otherValue
end

--- Checks whether a value of this GGData object is lower than another value.
-- @param name The name of the first value to check.
-- @param otherValue The name of the other value to check. Can also be a number.
-- @return True if the first value is lower, false otherwise.
function GGData:isValueLower( name, otherValue )
	if type( otherValue ) == "string" then
		otherValue = self:get( otherValue )
	end
	return self[ name ] < otherValue
end

--- Checks whether a value of this GGData object is equal to another value.
-- @param name The name of the first value to check.
-- @param otherValue The name of the other value to check. Can also be a number.
-- @return True if the first value is equal, false otherwise.
function GGData:isValueEqual( name, otherValue )
	if type( otherValue ) == "string" then
		otherValue = self:get( otherValue )
	end
	return self[ name ] == otherValue
end

--- Checks whether this GGData object has a specific property or not.
-- @param name The name of the value to check.
-- @return True if the value exists and isn't nil, false otherwise.
function GGData:hasValue( name )
	return self[ name ] ~= nil and true or false
end

--- Sets a value on this GGData object if it is new.
-- @param name The name of the value to set.
-- @param value The value to set.
function GGData:setIfNew( name, value )
	if self[ name ] == nil then
		self[ name ] = value
		self:storeIntegrityHash( name, value )
	end
end

--- Sets a value on this GGData object if it is higher than the current value.
-- @param name The name of the value to set.
-- @param value The value to set.
function GGData:setIfHigher( name, value )
	if self[ name ] and value > self[ name ] or not self[ name ] then
		self[ name ] = value
		self:storeIntegrityHash( name, value )
	end
end

--- Sets a value on this GGData object if it is lower than the current value.
-- @param name The name of the value to set.
-- @param value The value to set.
function GGData:setIfLower( name, value )
	if self[ name ] and value < self[ name ] or not self[ name ] then
		self[ name ] = value
		self:storeIntegrityHash( name, value )
	end
end

--- Increments a value in this GGData object.
-- @param name The name of the value to increment. Must be a number. If it doesn't exist it will be set to 0 and then incremented.
-- @param amount The amount to increment. Optional, defaults to 1.
function GGData:increment( name, amount )
	if not self[ name ] then
		self:set( name, 0 )
	end
	if self[ name ] and type( self[ name ] ) == "number" then
		self[ name ] = self[ name ] + ( amount or 1 )
		self:storeIntegrityHash( name, value )
	end
end

--- Decrements a value in this GGData object.
-- @param name The name of the value to decrement. Must be a number. If it doesn't exist it will be set to 0 and then decremented.
-- @param amount The amount to decrement. Optional, defaults to 1.
function GGData:decrement( name, amount )
	if not self[ name ] then
		self:set( name, 0 )
	end
	if self[ name ] and type( self[ name ] ) == "number" then
		self[ name ] = self[ name ] - ( amount or 1 )
		self:storeIntegrityHash( name, value )
	end
end

--- Clears this GGData object.
function GGData:clear()
	for k, v in pairs( self ) do
		if k ~= "integrityControlEnabled"
			and k ~= "integrityAlgorithm"
			and k ~= "integrityKey"
			and k ~= "id"
			and type( k ) ~= "function" then
				self[ k ] = nil
		end
	end
end

--- Deletes this GGData object from disk.
-- @param id The id of the GGData to delete. Optional, only required if calling on a non-loaded object.
function GGData:delete( id )

	-- If no id was passed in then assume we're working with a pre-loaded GGData object so use its id
	if not id then
		id = self.id
	end

	local path = system.pathForFile( self.path, system.DocumentsDirectory )

	local success = lfs.chdir( path )

	os.remove( path .. "/" .. id .. ".box" )

	if not success then
		return
	end

end

--- Enables or disables the Syncing of this box.
-- @param enabled True if Sync should be enabled, false otherwise.
function GGData:setSync( enabled, id )

	-- If no id was passed in then assume we're working with a pre-loaded GGData object so use its id
	if not id then
		id = self.id
	end

	native.setSync( self.path .. "/" .. id .. ".box", { iCloudBackup = enabled } )

end

--- Checks if Syncing for this box is enabled or not.
-- @param enabled True if Sync is enabled, false otherwise.
function GGData:getSync( id )

	-- If no id was passed in then assume we're working with a pre-loaded GGData object so use its id
	if not id then
		id = self.id
	end

	return native.getSync( self.path .. "/" .. id .. ".box", { key = "iCloudBackup" } )

end

--- Enables integrity checking.
-- @param algorithm The hashing algorithm to use, see this page for possibles - http://docs.coronalabs.com/api/library/crypto/index.html
-- @param key The seed to use for the hashing algorithm.
function GGData:enableIntegrityControl( algorithm, key )
	self.integrityAlgorithm = algorithm
	self.integrityKey = key
	self.hash = self.hash or {}
	self.integrityControlEnabled = true
end

--- Disables integrity checking.
function GGData:disableIntegrityControl()
	self.integrityAlgorithm = nil
	self.integrityKey = nil
	self.hash = nil
	self.integrityControlEnabled = false
end

--- Verifies that the passed in value matches the stored hash.
-- @param name The name of the value to check.
-- @param value The value to check.
-- @return True if the value matches the hash false otherwise.
function GGData:verifyItemIntegrity( name, value )
	-- just hash the tostring() version and compare against that!
	if toString( value ) then
		local generatedHash = crypto.hmac( self.integrityAlgorithm, toString( value ), self.integrityKey, false )
		local storedHash = self.hash[ name ]
		return generatedHash == storedHash
	end
end

--- Stores the hash value of the given value to be used for integrity checks.
-- @param name The name of the value to set.
-- @param value The value to set. Optional, if not included will just pull the value from the name supplied.
function GGData:storeIntegrityHash( name, value )

	if not self.integrityControlEnabled then
		return
	end

	value = value or self[ name ]

	self.hash = self.hash or {}

	if value then
		self.hash[ name ] = crypto.hmac( self.integrityAlgorithm, toString( value ), self.integrityKey, false )
	end

end

--- Updates/sets the hash value of the all stored values.
function GGData:updateAllIntegrityHashes()

	for k, v in pairs( self ) do
		if k ~= "integrityControlEnabled"
			and k ~= "integrityAlgorithm"
			and k ~= "integrityKey"
			and k ~= "hash"
			and k ~= "id"
			and  toString( v ) then
				self:storeIntegrityHash( k, v )
		end
	end

end

--- Checks the hashed versions of all stored data. Will remove any values that don't match their hashes, i.e. they've been tampered with.
function GGData:verifyIntegrity()

	if not self.integrityControlEnabled then
		return
	end

	local corruptEntries = {}

	for k, v in pairs( self ) do
		if k ~= "integrityControlEnabled"
			and k ~= "integrityAlgorithm"
			and k ~= "integrityKey"
			and k ~= "hash"
			and k ~= "id"
			and toString( v ) then

				if not self:verifyItemIntegrity( k, v ) then
					corruptEntries[ #corruptEntries + 1 ] = { name = k, value = v }
					self[ k ] = nil
					self.hash[ k ] = nil
				end
		end
	end

	for k, v in pairs( self.hash ) do
		if not self[ k ] then
			corruptEntries[ #corruptEntries + 1 ] = { name = k, value = v }
			self[ k ] = nil
			self.hash[ k ] = nil
		end
	end

	return corruptEntries

end

--- Gets the path to the stored file. Useful if you want to upload it.
-- @return Two paramaters; the full path and then the relative path.
function GGData:getFilename()
	local relativePath = self.path .. "/" .. self.id .. ".box"
	local fullPath = system.pathForFile( relativePath, system.DocumentsDirectory )
	return fullPath, relativePath
end

--- Destroys this GGData object.
function GGData:destroy()
	self:clear()
	self = nil
end

return GGData 

Now ogt_Imdata

local k = {}

--[[ ==============================================================
When a level is selected OGTLM needs to send the user to another composer scene. 
If you use the same scene for all levels (just loading new data for each level, you'll use the k.playScene option below. 
Enter the name of the composer scene to switch to.

If you have a different composer scene for each level you'll use
	the second option below as long as the scenes are named sequentially.
	For example, level1, level2, level3, etc. Or stage1, stage2, stage3, 
		etc. in that case, enter the "base name" for the scenes, such
		as level or stage. OGTLM will add the chosen level number
		to the end when the user clicks a level.
		
Or, create a table in k.sceneNames that holds the composer scene
name for each level you have.
	-- ==============================================================]]
k.playScene = "play" -- name of scene or nil to go to sequential scenes
k.sequentialScene = "level" -- will turn into level1, level2, etc.
k.sceneNames = nil -- {"scene01", "playme", "world3"}

--[[ =========================================
 If images or audio files are in folders, define
	those here. Be sure and add a slash / to the
 end of any folder names.
	If you're not using folders, set vars to nil
--==========================================]]
k.imagePrefix = "images/"
k.audioPrefix = "audio/"

--==============================================================
-- filenames for the images used in the level manager

k.backgroundImage = "background.png" -- optional, define as nil if not used
k.customLevel = nil -- get level image from function rather than next var
k.levelSquareImage = "level-wood.png" -- required
k.lockImage = nil -- optional, define as nil if not used (--"lock.png"--)
k.starImage = nil -- optional, nil if not using stars  (--"staryellow.png"--)
k.prevImage = "prev-white.png" -- optional unless you need paging, define as nil if not used
k.nextImage = "next-white.png" -- optional unless you need paging, define as nil if not used

--==============================================================
-- filenames for the images used in the level manager

k.selectSoundFile = "levelselect.wav" -- sound when level selected, define as nil if not used
k.nextPageSoundFile = "changepage.wav"-- sound when next page clicked, define as nil if not used
k.prevPageSoundFile = "changepage.wav"-- sound when previous page clicked, define as nil if not used

k.audioOn = true

--==============================================================
-- the default font to be used for level number and score
k.fontName = "Helvetica" -- required

--==============================================================
-- info about the level number (or text) being displayed
k.showLevelNum = true
k.levelNumFontName = nil -- optional, use instead of default font for level num
k.levelNumFontSize = 50
k.levelNumFontColor = {1,1,1}
k.levelNumEmbossedFont = true
k.levelNumTextXOffset = -2 	-- horizontal offset for the level number text
k.levelNumTextYOffset = 2   -- vertical offset for the level number text
k.tileNums = nil --{"H","He","Li","Be","B","C","N","O","F","Ne","Na","Mg","Al","Si","P","S","Cl"} -- use a table of string values if desired {"A", "B", "C"}
k.showLevelNumTextWhenLocked = false

--==============================================================
-- info about the score being displayed (optional)
k.showScore = true
k.scoreFontName = nil -- optional, use instead of default font for score
k.scoreFontSize = 18
k.scoreFontColor = {1,1,1}
k.scoreEmbossedFont = true
k.scoreTextXOffset = 0 	-- horizontal offset for the score text
k.scoreTextYOffset = 0   -- vertical offset for the score text
k.scorePrefix = nil
k.scoreSuffix = " Pts"
k.showScoreWhenLocked = false

--==============================================================
-- Info about the grid itself
k.totalLevels = 10	-- total number of levels in the game. should equal (numCols * numRows) * numPages
k.numCols = 5		 -- how many columns on each page of levels
k.numRows = 2 		-- how many rows on each page of levels
k.colSpace = 10 	  -- extra spacing between each column
k.rowSpace = 40 	  -- extra spacing between each row
k.gridOffsetX = 0 	-- horizontal offset for the entire grid on the page
k.gridOffsetY = -20 	-- vertical offset for the entire grid on the page

-- misc variables

k.numUnlocked = 5 -- how many of the first levels are unlocked (minimum 1) -- was 9

k.rememberPage = true -- if true, shows page from last selected level
k.currentPage = 1 	-- probably won't change this (much) manually
k.currentLevel = 0 	-- probably won't change this (much) manually

k.swipe = true -- allow swiping left/right to page
k.minSwipeDistance = 20 -- number of pixels required to trigger paging

k.beforeLeaving = nil -- name of function to call after choosing level but before going there.

--==============================================================
-- star stuff
k.maxStars = 3
k.starOffsetX = 0
k.starOffsetY = -4
k.singleStarOffsetX = {0, 0, 0} -- number of elements must match k.maxStars
k.singleStarOffsetY = {0, 3, 0} -- number of elements must match k.maxStars
k.showMissingStar = true

-- next and prev arrows
k.nextOffsetX = -5 	-- tweak the positioning for the arrows here
k.nextOffsetY = 0
k.prevOffsetX = 5
k.prevOffsetY = 0

-- lock image
k.lockOffsetX = 0 	-- tweak position of the lock here
k.lockOffsetY = -5

-- transition effect and time for switching from level selector to chosen level.
k.sboardEffect = "crossFade"
k.sboardTime = 700

-- how fast does the level selector slide when using next/prev arrows
k.slideTime = 300 	-- milliseconds (lower number = faster slide)

k.dataFile = "ogtlm_levels"

k.userName = nil

--==============================================================
-- extra variables below that are not user-configurable.

k.displayText = nil

k.numPerPage = (k.numCols * k.numRows)
k.numPages = math.ceil ( k.totalLevels / ((k.numCols * k.numRows)) )

k.tileWidth = 100
k.tileHeight = 70

k.lockWidth = 0
k.lockHeight = 0

k.prevWidth = 0
k.prevHeight = 0

k.nextWidth = 0
k.nextHeight = 0

k.starWidth = 0 	-- will be filled in dynamically
k.starHeight = 0	-- will be filled in dynamically

k.levelInfo = {}
k.levelLocked = {}
k.starsOnLevel = {}
k.levelScores = {}

return k 

What is this OGT think?

 

Thanks



[TOPIC: post.html]
#2

XeduR @Spyric

[GLOBAL: userInfoPane.html]
XeduR @Spyric
  • Contributor

  • 616 posts
  • Corona SDK

Do you need them? I'd say no, especially since you don't even know what they do.

Those are just some code files created by someone for some purpose. I have no clue as to what they are and they are too long for me to read them to find out. :P

That being said, there's nothing forcing you to use them. You can do (almost) whatever you want with your project.

 



[TOPIC: post.html]
#3

GrahamRanson

[GLOBAL: userInfoPane.html]
GrahamRanson
  • Enthusiast

  • 92 posts
  • Corona SDK

Hi Jack,

 

I'm not sure what ogt_Imdata.lua is for however I wrote GGData.lua and it was designed to simplify data storage in games/apps ( things like scores, player names, that sort of thing ). If you look through the comments for each function this should be clear? And as stated, you don't have to use anything you don't want to - it's your project.

 

If on the other hand you decide you do need something like GGData to store things then my Puggle library has replacements for it - https://www.grahamranson.com/getting-started-with-puggle/

 

But as stated, you don't need to use anyone's code to make your game, sometimes it's just helpful to not have to reinvent the wheel.




[topic_controls]
[/topic_controls]