Develop #4

Merged
wariosolis merged 35 commits from develop into master 2017-01-04 00:04:34 +01:00
28 changed files with 165 additions and 4547 deletions
Showing only changes of commit 1b4cba7f5b - Show all commits
Binary file not shown.
Binary file not shown.
+76 -76
View File
@@ -63,100 +63,100 @@ function bell.load(game, cam)
end
function bell.update(dt, game, cam)
-- Update cam position
cam_x, cam_y = cam.gcam:getVisible()
-- Ani bells
bell_1.animation:update(dt)
bell_2.animation:update(dt)
bell_3.animation:update(dt)
-- Bells fix pos
bell_1.x = cam_x + (game.window.width / 4) - (bell_1.img:getWidth() / body.num_frames / 2)
bell_2.x = cam_x + (game.window.width / 4 * 2) - (bell_1.img:getWidth() / body.num_frames / 2)
bell_3.x = cam_x + (game.window.width / 4 * 3) - (bell_1.img:getWidth() / body.num_frames / 2)
-- Collisions fix pos
for key, bell in pairs(bells) do
for key, collision in pairs(collisions.positions) do
bell.collisions[key]:moveTo(collisions.positions[key].x + bell.x + (bell.img:getHeight() / 2) + collisions.correction, collisions.positions[key].y + bell.y + (bell.img:getHeight() / 2))
end
end
-- Check collisions
if game.bells_enable then
-- Update cam position
cam_x, cam_y = cam.gcam:getVisible()
-- Ani bells
bell_1.animation:update(dt)
bell_2.animation:update(dt)
bell_3.animation:update(dt)
-- Bells fix pos
bell_1.x = cam_x + (game.window.width / 4) - (bell_1.img:getWidth() / body.num_frames / 2)
bell_2.x = cam_x + (game.window.width / 4 * 2) - (bell_1.img:getWidth() / body.num_frames / 2)
bell_3.x = cam_x + (game.window.width / 4 * 3) - (bell_1.img:getWidth() / body.num_frames / 2)
-- Collisions fix pos
for key, bell in pairs(bells) do
for key, collision in pairs(collisions.positions) do
bell.collisions[key]:moveTo(collisions.positions[key].x + bell.x + (bell.img:getHeight() / 2) + collisions.correction, collisions.positions[key].y + bell.y + (bell.img:getHeight() / 2))
end
end
-- Check collisions
for key, bell in pairs(bells) do
for key, collision in pairs(bell.collisions) do
for shape, delta in pairs(HC.collisions(bell.collisions[key])) do
if bell.sound_loop:isPlaying() == false then
bell.sound_loop:play()
end
bell.enable[key] = true
end
end
end
for key, bell in pairs(bells) do
for key, collision in pairs(bell.collisions) do
for shape, delta in pairs(HC.collisions(bell.collisions[key])) do
if bell.sound_loop:isPlaying() == false then
bell.sound_loop:play()
end
bell.enable[key] = true
end
end
end
end
for key, bell in pairs(bells) do
for key, collision in pairs(bell.collisions) do
for shape, delta in pairs(HC.collisions(bell.collisions[key])) do
bell.enable[key] = true
end
end
end
-- Logic
local num_enable = 0
for key, bell in pairs(bells) do
for key, item in pairs(bell.enable) do
-- Count enables
num_enable = num_enable + 1
end
if num_enable > 1 then
-- Search emptys
local count_singles = 0
-- Logic
local num_enable = 0
for key, bell in pairs(bells) do
for key, item in pairs(bell.enable) do
if key > 1 and bell.enable[key] and bell.enable[key - 1] == false then
count_singles = count_singles + 1
end
-- Count enables
num_enable = num_enable + 1
end
-- Bad. Restart
if count_singles >= 2 then
for key, bell in pairs(bells) do
bell.sound_loop:stop()
end
sound_error:play()
if num_enable > 1 then
-- Search emptys
local count_singles = 0
for key, item in pairs(bell.enable) do
bell.enable[key] = false
if key > 1 and bell.enable[key] and bell.enable[key - 1] == false then
count_singles = count_singles + 1
end
end
end
-- Check good
local good = true
for key, item in pairs(bell.enable) do
if bell.enable[key] == false then
good = false
-- Bad. Restart
if count_singles >= 2 then
for key, bell in pairs(bells) do
bell.sound_loop:stop()
end
sound_error:play()
for key, item in pairs(bell.enable) do
bell.enable[key] = false
end
end
end
-- Enable animation good
if good then
bell.active = true
bell.animation:resume()
bell.sound_good:play()
bell.animation:resume()
-- Check good
local good = true
for key, item in pairs(bell.enable) do
bell.enable[key] = false
if bell.enable[key] == false then
good = false
end
end
-- Enable animation good
if good then
bell.active = true
bell.animation:resume()
bell.sound_good:play()
bell.animation:resume()
for key, item in pairs(bell.enable) do
bell.enable[key] = false
end
end
good = true
count_singles = 0
end
good = true
count_singles = 0
num_enable = 0
end
num_enable = 0
end
-- Game over
game_over = true
for key, bell in pairs(bells) do
if not bell.active then
game_over = false
-- Game over
game_over = true
for key, bell in pairs(bells) do
if not bell.active then
game_over = false
end
end
end
for key, bell in pairs(bells) do
if game_over then
bell.animation:pauseAtEnd()
game.bells_enable = false
for key, bell in pairs(bells) do
if game_over then
bell.animation:pauseAtEnd()
game.bells_enable = false
end
end
end
end
+3 -2
View File
@@ -10,6 +10,7 @@ function game.load()
game.canvas = { x = width / 2, y= 0, width = canvas_width, height = canvas_height }
game.level = 1
game.end_level = 6
game.start_screen = true
love.window.setMode(game.window.width, game.window.height)
@@ -18,12 +19,12 @@ function game.load()
local gravity = 2
love.physics.setMeter(world_meter) -- Height earth in meters
game.world = love.physics.newWorld(0, gravity * world_meter, true) -- Make earth
game.status = 1
-- Bells
game.bells_enable = false
game.music = love.audio.newSource("assets/audio/music/theme.mp3")
game:playMusic()
game.music = love.audio.newSource("assets/audio/music/theme_biblia.wav")
game.stress = false
+8 -4
View File
@@ -1,21 +1,25 @@
require 'assets/scripts/vendor/MessageInABottle'
local messages = {}
local duration = 30
messages.active = true
function messages.load()
ocean = Ocean:new()
end
function messages.update(dt)
function messages.update(dt, game)
ocean:update(dt)
if game.status == 1 and messages.active then
messages.new_message('welcome', 10)
messages.active = false
end
end
function messages.draw()
ocean:draw()
end
function messages.new_message(cap)
function messages.new_message(cap, time)
local texts = {}
texts.welcome = "I must make decision. I wont leave this room until Ive decided to kill my girlfriend or not. How has it come to this? Ive got little time. Her life is teetering through my fingers."
texts.cap1preview="Shes gorgeous in this picture. My mother has given me more than Ill ever be able to give her back. Not only did she give me the gift of life, but a reason to go on. Her lessons and strong character have made me become the man I am now. What I most want in this world is for her to be proud of me. Proud of my work and my partner. But a bitch had to screw it all up."
@@ -25,7 +29,7 @@ function messages.new_message(cap)
texts.cap4stage="I love this song. I remember now why she bought this one. It was our song. A hidden apology for me. Maybe I got too carried away. After all, shes not religious, shed never said grace. She hadnt even seen how its done. She did what anybody would have done: sit down and start to eat. Poor one. I shouldnt have accepted the album, her tears were more than enough. She meant no harm. But I did…and now I must kill her. I must do it before she starts telling lies about me and harms my family's reputation. I can't allow this to happen. I'm not gay. I'm not sick. I'm not using this key. I'll go out that door and I'll do her in. No more memories, I've made up my mind."
texts.endbad="Die, bitch."
texts.endgood="My bra. It's so uncomfortable. I've always hated wearing it. It makes my breasts hurt. On our first secret anniversary he bought me an outfit. He's got me. He's always helped me grow as a person. To break ties. Like the one I'm in now. Telling my mother I'm a woman who's into other...women. And that the Lord justs wants me to be happy. I've got to be strong. 2 Corinthians 12:10 That is why, for Christ's sake, I delight in weaknesses, in insults, in hardships, in persecutions, in difficulties. For when I am weak, then I am strong uAs strong as she made me. She laughed at me in front of my friends, but to help me overcome my shyness. She said I was a nutter to help me overcome my issues, to make me realise the obsessions Id inherited from my mother. Brute when I wasnt tolerant with others. And I just got upset…like a moron. A part of my soul still belongs to her. Thanks my love."
local bottle = TimeBottle:new(cap, texts['' .. cap], duration)
local bottle = TimeBottle:new(cap, texts['' .. cap], time)
bottle:setX(30)
bottle:setY(500)
bottle:setWidth(1220)
+34
View File
@@ -0,0 +1,34 @@
local start = {}
function start.load(game)
start.img = love.graphics.newImage('assets/sprites/menu/start.jpg')
start.music = love.audio.newSource("assets/audio/music/main_theme_menu.mp3")
start.music:play()
start.status = 1
game:stopMusic()
end
function start.update(dt, game)
if game.status == start.status then
game:stopMusic()
end
end
function start.draw(game)
if game.status == start.status then
love.graphics.draw(start.img, 0, 0)
end
end
function start.mousepressed(game)
local mos_x, mos_y = love.mouse.getPosition()
if game.status == start.status then
if mos_x > 411 and mos_x < 578 and mos_y > 400 and mos_y < 479 then
game.status = start.status + 1
start.music:stop()
game:playMusic()
end
end
end
return start
Binary file not shown.

After

Width:  |  Height:  |  Size: 59 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

-44
View File
@@ -1,44 +0,0 @@
local game = require '../assets/scripts/game'
local stages = require '../assets/scripts/stages'
local controls = require '../assets/scripts/controls'
local bells = require '../assets/scripts/bells'
local camera = require '../assets/scripts/camera'
local stress = require '../assets/scripts/stress'
local messages = require '../assets/scripts/messages'
-- LOAD
function love.load()
game.load()
camera.load(game)
stages.load(game, camera)
stress.load(game, camera)
bells.load(game, camera)
controls.load(game)
messages.load(game)
end
-- UPDATE
function love.update(dt)
--require('assets/scripts/vendor/lovebird').update()
game.world:update(dt)
game.update(dt)
camera.update(game)
stages.update(dt, game, camera)
stress.update(dt, game, camera)
bells.update(dt, game, camera)
controls.update(dt, game, camera)
messages.update(dt)
end
-- DRAW
function love.draw()
camera.gcam:draw(
function(l,t,w,h)
stages.draw()
stress.draw()
bells.draw(game)
end
)
controls.draw()
messages.draw()
end
-456
View File
@@ -1,456 +0,0 @@
--[[
ProFi v1.3, by Luke Perkin 2012. MIT Licence http://www.opensource.org/licenses/mit-license.php.
Example:
ProFi = require 'ProFi'
ProFi:start()
some_function()
another_function()
coroutine.resume( some_coroutine )
ProFi:stop()
ProFi:writeReport( 'MyProfilingReport.txt' )
API:
*Arguments are specified as: type/name/default.
ProFi:start( string/once/nil )
ProFi:stop()
ProFi:checkMemory( number/interval/0, string/note/'' )
ProFi:writeReport( string/filename/'ProFi.txt' )
ProFi:reset()
ProFi:setHookCount( number/hookCount/0 )
ProFi:setGetTimeMethod( function/getTimeMethod/os.clock )
ProFi:setInspect( string/methodName, number/levels/1 )
]]
-----------------------
-- Locals:
-----------------------
local ProFi = {}
local onDebugHook, sortByDurationDesc, sortByCallCount, getTime
local DEFAULT_DEBUG_HOOK_COUNT = 0
local FORMAT_HEADER_LINE = "| %-50s: %-40s: %-20s: %-12s: %-12s: %-12s|\n"
local FORMAT_OUTPUT_LINE = "| %s: %-12s: %-12s: %-12s|\n"
local FORMAT_INSPECTION_LINE = "> %s: %-12s\n"
local FORMAT_TOTALTIME_LINE = "| TOTAL TIME = %f\n"
local FORMAT_MEMORY_LINE = "| %-20s: %-16s: %-16s| %s\n"
local FORMAT_HIGH_MEMORY_LINE = "H %-20s: %-16s: %-16sH %s\n"
local FORMAT_LOW_MEMORY_LINE = "L %-20s: %-16s: %-16sL %s\n"
local FORMAT_TITLE = "%-50.50s: %-40.40s: %-20s"
local FORMAT_LINENUM = "%4i"
local FORMAT_TIME = "%04.3f"
local FORMAT_RELATIVE = "%03.2f%%"
local FORMAT_COUNT = "%7i"
local FORMAT_KBYTES = "%7i Kbytes"
local FORMAT_MBYTES = "%7.1f Mbytes"
local FORMAT_MEMORY_HEADER1 = "\n=== HIGH & LOW MEMORY USAGE ===============================\n"
local FORMAT_MEMORY_HEADER2 = "=== MEMORY USAGE ==========================================\n"
local FORMAT_BANNER = [[
###############################################################################################################
##### ProFi, a lua profiler. This profile was generated on: %s
##### ProFi is created by Luke Perkin 2012 under the MIT Licence, www.locofilm.co.uk
##### Version 1.3. Get the most recent version at this gist: https://gist.github.com/2838755
###############################################################################################################
]]
-----------------------
-- Public Methods:
-----------------------
--[[
Starts profiling any method that is called between this and ProFi:stop().
Pass the parameter 'once' to so that this methodis only run once.
Example:
ProFi:start( 'once' )
]]
function ProFi:start( param )
if param == 'once' then
if self:shouldReturn() then
return
else
self.should_run_once = true
end
end
self.has_started = true
self.has_finished = false
self:resetReports( self.reports )
self:startHooks()
self.startTime = getTime()
end
--[[
Stops profiling.
]]
function ProFi:stop()
if self:shouldReturn() then
return
end
self.stopTime = getTime()
self:stopHooks()
self.has_finished = true
end
function ProFi:checkMemory( interval, note )
local time = getTime()
local interval = interval or 0
if self.lastCheckMemoryTime and time < self.lastCheckMemoryTime + interval then
return
end
self.lastCheckMemoryTime = time
local memoryReport = {
['time'] = time;
['memory'] = collectgarbage('count');
['note'] = note or '';
}
table.insert( self.memoryReports, memoryReport )
self:setHighestMemoryReport( memoryReport )
self:setLowestMemoryReport( memoryReport )
end
--[[
Writes the profile report to a file.
Param: [filename:string:optional] defaults to 'ProFi.txt' if not specified.
]]
function ProFi:writeReport( filename )
if #self.reports > 0 or #self.memoryReports > 0 then
filename = filename or 'ProFi.txt'
self:sortReportsWithSortMethod( self.reports, self.sortMethod )
self:writeReportsToFilename( filename )
print( string.format("[ProFi]\t Report written to %s", filename) )
end
end
--[[
Resets any profile information stored.
]]
function ProFi:reset()
self.reports = {}
self.reportsByTitle = {}
self.memoryReports = {}
self.highestMemoryReport = nil
self.lowestMemoryReport = nil
self.has_started = false
self.has_finished = false
self.should_run_once = false
self.lastCheckMemoryTime = nil
self.hookCount = self.hookCount or DEFAULT_DEBUG_HOOK_COUNT
self.sortMethod = self.sortMethod or sortByDurationDesc
self.inspect = nil
end
--[[
Set how often a hook is called.
See http://pgl.yoyo.org/luai/i/debug.sethook for information.
Param: [hookCount:number] if 0 ProFi counts every time a function is called.
if 2 ProFi counts every other 2 function calls.
]]
function ProFi:setHookCount( hookCount )
self.hookCount = hookCount
end
--[[
Set how the report is sorted when written to file.
Param: [sortType:string] either 'duration' or 'count'.
'duration' sorts by the time a method took to run.
'count' sorts by the number of times a method was called.
]]
function ProFi:setSortMethod( sortType )
if sortType == 'duration' then
self.sortMethod = sortByDurationDesc
elseif sortType == 'count' then
self.sortMethod = sortByCallCount
end
end
--[[
By default the getTime method is os.clock (CPU time),
If you wish to use other time methods pass it to this function.
Param: [getTimeMethod:function]
]]
function ProFi:setGetTimeMethod( getTimeMethod )
getTime = getTimeMethod
end
--[[
Allows you to inspect a specific method.
Will write to the report a list of methods that
call this method you're inspecting, you can optionally
provide a levels parameter to traceback a number of levels.
Params: [methodName:string] the name of the method you wish to inspect.
[levels:number:optional] the amount of levels you wish to traceback, defaults to 1.
]]
function ProFi:setInspect( methodName, levels )
if self.inspect then
self.inspect.methodName = methodName
self.inspect.levels = levels or 1
else
self.inspect = {
['methodName'] = methodName;
['levels'] = levels or 1;
}
end
end
-----------------------
-- Implementations methods:
-----------------------
function ProFi:shouldReturn( )
return self.should_run_once and self.has_finished
end
function ProFi:getFuncReport( funcInfo )
local title = self:getTitleFromFuncInfo( funcInfo )
local funcReport = self.reportsByTitle[ title ]
if not funcReport then
funcReport = self:createFuncReport( funcInfo )
self.reportsByTitle[ title ] = funcReport
table.insert( self.reports, funcReport )
end
return funcReport
end
function ProFi:getTitleFromFuncInfo( funcInfo )
local name = funcInfo.name or 'anonymous'
local source = funcInfo.short_src or 'C_FUNC'
local linedefined = funcInfo.linedefined or 0
linedefined = string.format( FORMAT_LINENUM, linedefined )
return string.format(FORMAT_TITLE, source, name, linedefined)
end
function ProFi:createFuncReport( funcInfo )
local name = funcInfo.name or 'anonymous'
local source = funcInfo.source or 'C Func'
local linedefined = funcInfo.linedefined or 0
local funcReport = {
['title'] = self:getTitleFromFuncInfo( funcInfo );
['count'] = 0;
['timer'] = 0;
}
return funcReport
end
function ProFi:startHooks()
debug.sethook( onDebugHook, 'cr', self.hookCount )
end
function ProFi:stopHooks()
debug.sethook()
end
function ProFi:sortReportsWithSortMethod( reports, sortMethod )
if reports then
table.sort( reports, sortMethod )
end
end
function ProFi:writeReportsToFilename( filename )
local file, err = io.open( filename, 'w' )
assert( file, err )
self:writeBannerToFile( file )
if #self.reports > 0 then
self:writeProfilingReportsToFile( self.reports, file )
end
if #self.memoryReports > 0 then
self:writeMemoryReportsToFile( self.memoryReports, file )
end
file:close()
end
function ProFi:writeProfilingReportsToFile( reports, file )
local totalTime = self.stopTime - self.startTime
local totalTimeOutput = string.format(FORMAT_TOTALTIME_LINE, totalTime)
file:write( totalTimeOutput )
local header = string.format( FORMAT_HEADER_LINE, "FILE", "FUNCTION", "LINE", "TIME", "RELATIVE", "CALLED" )
file:write( header )
for i, funcReport in ipairs( reports ) do
local timer = string.format(FORMAT_TIME, funcReport.timer)
local count = string.format(FORMAT_COUNT, funcReport.count)
local relTime = string.format(FORMAT_RELATIVE, (funcReport.timer / totalTime) * 100 )
local outputLine = string.format(FORMAT_OUTPUT_LINE, funcReport.title, timer, relTime, count )
file:write( outputLine )
if funcReport.inspections then
self:writeInpsectionsToFile( funcReport.inspections, file )
end
end
end
function ProFi:writeMemoryReportsToFile( reports, file )
file:write( FORMAT_MEMORY_HEADER1 )
self:writeHighestMemoryReportToFile( file )
self:writeLowestMemoryReportToFile( file )
file:write( FORMAT_MEMORY_HEADER2 )
for i, memoryReport in ipairs( reports ) do
local outputLine = self:formatMemoryReportWithFormatter( memoryReport, FORMAT_MEMORY_LINE )
file:write( outputLine )
end
end
function ProFi:writeHighestMemoryReportToFile( file )
local memoryReport = self.highestMemoryReport
local outputLine = self:formatMemoryReportWithFormatter( memoryReport, FORMAT_HIGH_MEMORY_LINE )
file:write( outputLine )
end
function ProFi:writeLowestMemoryReportToFile( file )
local memoryReport = self.lowestMemoryReport
local outputLine = self:formatMemoryReportWithFormatter( memoryReport, FORMAT_LOW_MEMORY_LINE )
file:write( outputLine )
end
function ProFi:formatMemoryReportWithFormatter( memoryReport, formatter )
local time = string.format(FORMAT_TIME, memoryReport.time)
local kbytes = string.format(FORMAT_KBYTES, memoryReport.memory)
local mbytes = string.format(FORMAT_MBYTES, memoryReport.memory/1024)
local outputLine = string.format(formatter, time, kbytes, mbytes, memoryReport.note)
return outputLine
end
function ProFi:writeBannerToFile( file )
local banner = string.format(FORMAT_BANNER, os.date())
file:write( banner )
end
function ProFi:writeInpsectionsToFile( inspections, file )
local inspectionsList = self:sortInspectionsIntoList( inspections )
file:write('\n==^ INSPECT ^======================================================================================================== COUNT ===\n')
for i, inspection in ipairs( inspectionsList ) do
local line = string.format(FORMAT_LINENUM, inspection.line)
local title = string.format(FORMAT_TITLE, inspection.source, inspection.name, line)
local count = string.format(FORMAT_COUNT, inspection.count)
local outputLine = string.format(FORMAT_INSPECTION_LINE, title, count )
file:write( outputLine )
end
file:write('===============================================================================================================================\n\n')
end
function ProFi:sortInspectionsIntoList( inspections )
local inspectionsList = {}
for k, inspection in pairs(inspections) do
inspectionsList[#inspectionsList+1] = inspection
end
table.sort( inspectionsList, sortByCallCount )
return inspectionsList
end
function ProFi:resetReports( reports )
for i, report in ipairs( reports ) do
report.timer = 0
report.count = 0
report.inspections = nil
end
end
function ProFi:shouldInspect( funcInfo )
return self.inspect and self.inspect.methodName == funcInfo.name
end
function ProFi:getInspectionsFromReport( funcReport )
local inspections = funcReport.inspections
if not inspections then
inspections = {}
funcReport.inspections = inspections
end
return inspections
end
function ProFi:getInspectionWithKeyFromInspections( key, inspections )
local inspection = inspections[key]
if not inspection then
inspection = {
['count'] = 0;
}
inspections[key] = inspection
end
return inspection
end
function ProFi:doInspection( inspect, funcReport )
local inspections = self:getInspectionsFromReport( funcReport )
local levels = 5 + inspect.levels
local currentLevel = 5
while currentLevel < levels do
local funcInfo = debug.getinfo( currentLevel, 'nS' )
if funcInfo then
local source = funcInfo.short_src or '[C]'
local name = funcInfo.name or 'anonymous'
local line = funcInfo.linedefined
local key = source..name..line
local inspection = self:getInspectionWithKeyFromInspections( key, inspections )
inspection.source = source
inspection.name = name
inspection.line = line
inspection.count = inspection.count + 1
currentLevel = currentLevel + 1
else
break
end
end
end
function ProFi:onFunctionCall( funcInfo )
local funcReport = ProFi:getFuncReport( funcInfo )
funcReport.callTime = getTime()
funcReport.count = funcReport.count + 1
if self:shouldInspect( funcInfo ) then
self:doInspection( self.inspect, funcReport )
end
end
function ProFi:onFunctionReturn( funcInfo )
local funcReport = ProFi:getFuncReport( funcInfo )
if funcReport.callTime then
funcReport.timer = funcReport.timer + (getTime() - funcReport.callTime)
end
end
function ProFi:setHighestMemoryReport( memoryReport )
if not self.highestMemoryReport then
self.highestMemoryReport = memoryReport
else
if memoryReport.memory > self.highestMemoryReport.memory then
self.highestMemoryReport = memoryReport
end
end
end
function ProFi:setLowestMemoryReport( memoryReport )
if not self.lowestMemoryReport then
self.lowestMemoryReport = memoryReport
else
if memoryReport.memory < self.lowestMemoryReport.memory then
self.lowestMemoryReport = memoryReport
end
end
end
-----------------------
-- Local Functions:
-----------------------
getTime = os.clock
onDebugHook = function( hookType )
local funcInfo = debug.getinfo( 2, 'nS' )
if hookType == "call" then
ProFi:onFunctionCall( funcInfo )
elseif hookType == "return" then
ProFi:onFunctionReturn( funcInfo )
end
end
sortByDurationDesc = function( a, b )
return a.timer > b.timer
end
sortByCallCount = function( a, b )
return a.count > b.count
end
-----------------------
-- Return Module:
-----------------------
ProFi:reset()
return ProFi
-208
View File
@@ -1,208 +0,0 @@
-- gamera.lua v1.0.1
-- Copyright (c) 2012 Enrique García Cota
-- 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.
-- Based on YaciCode, from Julien Patte and LuaObject, from Sebastien Rocca-Serra
local gamera = {}
-- Private attributes and methods
local gameraMt = {__index = gamera}
local abs, min, max = math.abs, math.min, math.max
local function clamp(x, minX, maxX)
return x < minX and minX or (x>maxX and maxX or x)
end
local function checkNumber(value, name)
if type(value) ~= 'number' then
error(name .. " must be a number (was: " .. tostring(value) .. ")")
end
end
local function checkPositiveNumber(value, name)
if type(value) ~= 'number' or value <=0 then
error(name .. " must be a positive number (was: " .. tostring(value) ..")")
end
end
local function checkAABB(l,t,w,h)
checkNumber(l, "l")
checkNumber(t, "t")
checkPositiveNumber(w, "w")
checkPositiveNumber(h, "h")
end
local function getVisibleArea(self, scale)
scale = scale or self.scale
local sin, cos = abs(self.sin), abs(self.cos)
local w,h = self.w / scale, self.h / scale
w,h = cos*w + sin*h, sin*w + cos*h
return min(w,self.ww), min(h, self.wh)
end
local function cornerTransform(self, x,y)
local scale, sin, cos = self.scale, self.sin, self.cos
x,y = x - self.x, y - self.y
x,y = -cos*x + sin*y, -sin*x - cos*y
return self.x - (x/scale + self.l), self.y - (y/scale + self.t)
end
local function adjustPosition(self)
local wl,wt,ww,wh = self.wl, self.wt, self.ww, self.wh
local w,h = getVisibleArea(self)
local w2,h2 = w*0.5, h*0.5
local left, right = wl + w2, wl + ww - w2
local top, bottom = wt + h2, wt + wh - h2
self.x, self.y = clamp(self.x, left, right), clamp(self.y, top, bottom)
end
local function adjustScale(self)
local w,h,ww,wh = self.w, self.h, self.ww, self.wh
local rw,rh = getVisibleArea(self, 1) -- rotated frame: area around the window, rotated without scaling
local sx,sy = rw/ww, rh/wh -- vert/horiz scale: minimun scales that the window needs to occupy the world
local rscale = max(sx,sy)
self.scale = max(self.scale, rscale)
end
-- Public interface
function gamera.new(l,t,w,h)
local sw,sh = love.graphics.getWidth(), love.graphics.getHeight()
local cam = setmetatable({
x=0, y=0,
scale=1,
angle=0, sin=math.sin(0), cos=math.cos(0),
l=0, t=0, w=sw, h=sh, w2=sw*0.5, h2=sh*0.5
}, gameraMt)
cam:setWorld(l,t,w,h)
return cam
end
function gamera:setWorld(l,t,w,h)
checkAABB(l,t,w,h)
self.wl, self.wt, self.ww, self.wh = l,t,w,h
adjustPosition(self)
end
function gamera:setWindow(l,t,w,h)
checkAABB(l,t,w,h)
self.l, self.t, self.w, self.h, self.w2, self.h2 = l,t,w,h, w*0.5, h*0.5
adjustPosition(self)
end
function gamera:setPosition(x,y)
checkNumber(x, "x")
checkNumber(y, "y")
self.x, self.y = x,y
adjustPosition(self)
end
function gamera:setScale(scale)
checkNumber(scale, "scale")
self.scale = scale
adjustScale(self)
adjustPosition(self)
end
function gamera:setAngle(angle)
checkNumber(angle, "angle")
self.angle = angle
self.cos, self.sin = math.cos(angle), math.sin(angle)
adjustScale(self)
adjustPosition(self)
end
function gamera:getWorld()
return self.wl, self.wt, self.ww, self.wh
end
function gamera:getWindow()
return self.l, self.t, self.w, self.h
end
function gamera:getPosition()
return self.x, self.y
end
function gamera:getScale()
return self.scale
end
function gamera:getAngle()
return self.angle
end
function gamera:getVisible()
local w,h = getVisibleArea(self)
return self.x - w*0.5, self.y - h*0.5, w, h
end
function gamera:getVisibleCorners()
local x,y,w2,h2 = self.x, self.y, self.w2, self.h2
local x1,y1 = cornerTransform(self, x-w2,y-h2)
local x2,y2 = cornerTransform(self, x+w2,y-h2)
local x3,y3 = cornerTransform(self, x+w2,y+h2)
local x4,y4 = cornerTransform(self, x-w2,y+h2)
return x1,y1,x2,y2,x3,y3,x4,y4
end
function gamera:draw(f)
love.graphics.setScissor(self:getWindow())
love.graphics.push()
local scale = self.scale
love.graphics.scale(scale)
love.graphics.translate((self.w2 + self.l) / scale, (self.h2+self.t) / scale)
love.graphics.rotate(-self.angle)
love.graphics.translate(-self.x, -self.y)
f(self:getVisible())
love.graphics.pop()
love.graphics.setScissor()
end
function gamera:toWorld(x,y)
local scale, sin, cos = self.scale, self.sin, self.cos
x,y = (x - self.w2 - self.l) / scale, (y - self.h2 - self.t) / scale
x,y = cos*x - sin*y, sin*x + cos*y
return x + self.x, y + self.y
end
function gamera:toScreen(x,y)
local scale, sin, cos = self.scale, self.sin, self.cos
x,y = x - self.x, y - self.y
x,y = cos*x + sin*y, -sin*x + cos*y
return scale * x + self.w2 + self.l, scale * y + self.h2 + self.t
end
return gamera
-47
View File
@@ -1,47 +0,0 @@
HUMP - Helper Utilities for Massive Progression
===============================================
__HUMP__ is a small collection of tools for developing games with L&Ouml;VE.
Contents:
------------
* *gamestate.lua*: Easy gamestate management.
* *timer.lua*: Delayed and time-limited function calls and tweening functionality.
* *vector.lua*: 2D vector math.
* *vector-light.lua*: Lightweight 2D vector math (for optimisation purposes - leads to potentially ugly code).
* *class.lua*: Lightweight object orientation (class or prototype based).
* *signal.lua*: Simple Signal/Slot (aka. Observer) implementation.
* *camera.lua*: Move-, zoom- and rotatable camera.
Documentation
=============
You can find the documentation here: [http://vrld.github.com/hump/](http://vrld.github.com/hump/)
License
=======
> Copyright (c) 2010-2013 Matthias Richter
>
> 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.
>
> Except as contained in this notice, the name(s) of the above copyright holders
> shall not be used in advertising or otherwise to promote the sale, use or
> other dealings in this Software without prior written authorization.
>
> 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.
-117
View File
@@ -1,117 +0,0 @@
--[[
Copyright (c) 2010-2013 Matthias Richter
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.
Except as contained in this notice, the name(s) of the above copyright holders
shall not be used in advertising or otherwise to promote the sale, use or
other dealings in this Software without prior written authorization.
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 _PATH = (...):match('^(.*[%./])[^%.%/]+$') or ''
local cos, sin = math.cos, math.sin
local camera = {}
camera.__index = camera
local function new(x,y, zoom, rot)
x,y = x or love.graphics.getWidth()/2, y or love.graphics.getHeight()/2
zoom = zoom or 1
rot = rot or 0
return setmetatable({x = x, y = y, scale = zoom, rot = rot}, camera)
end
function camera:lookAt(x,y)
self.x, self.y = x,y
return self
end
function camera:move(x,y)
self.x, self.y = self.x + x, self.y + y
return self
end
function camera:pos()
return self.x, self.y
end
function camera:rotate(phi)
self.rot = self.rot + phi
return self
end
function camera:rotateTo(phi)
self.rot = phi
return self
end
function camera:zoom(mul)
self.scale = self.scale * mul
return self
end
function camera:zoomTo(zoom)
self.scale = zoom
return self
end
function camera:attach()
local cx,cy = love.graphics.getWidth()/(2*self.scale), love.graphics.getHeight()/(2*self.scale)
love.graphics.push()
love.graphics.scale(self.scale)
love.graphics.translate(cx, cy)
love.graphics.rotate(self.rot)
love.graphics.translate(-self.x, -self.y)
end
function camera:detach()
love.graphics.pop()
end
function camera:draw(func)
self:attach()
func()
self:detach()
end
function camera:cameraCoords(x,y)
-- x,y = ((x,y) - (self.x, self.y)):rotated(self.rot) * self.scale + center
local w,h = love.graphics.getWidth(), love.graphics.getHeight()
local c,s = cos(self.rot), sin(self.rot)
x,y = x - self.x, y - self.y
x,y = c*x - s*y, s*x + c*y
return x*self.scale + w/2, y*self.scale + h/2
end
function camera:worldCoords(x,y)
-- x,y = (((x,y) - center) / self.scale):rotated(-self.rot) + (self.x,self.y)
local w,h = love.graphics.getWidth(), love.graphics.getHeight()
local c,s = cos(-self.rot), sin(-self.rot)
x,y = (x - w/2) / self.scale, (y - h/2) / self.scale
x,y = c*x - s*y, s*x + c*y
return x+self.x, y+self.y
end
function camera:mousepos()
return self:worldCoords(love.mouse.getPosition())
end
-- the module
return setmetatable({new = new},
{__call = function(_, ...) return new(...) end})
-94
View File
@@ -1,94 +0,0 @@
--[[
Copyright (c) 2010-2013 Matthias Richter
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.
Except as contained in this notice, the name(s) of the above copyright holders
shall not be used in advertising or otherwise to promote the sale, use or
other dealings in this Software without prior written authorization.
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 function include_helper(to, from, seen)
if from == nil then
return to
elseif type(from) ~= 'table' then
return from
elseif seen[from] then
return seen[from]
end
seen[from] = to
for k,v in pairs(from) do
k = include_helper({}, k, seen) -- keys might also be tables
if not to[k] then
to[k] = include_helper({}, v, seen)
end
end
return to
end
-- deeply copies `other' into `class'. keys in `other' that are already
-- defined in `class' are omitted
local function include(class, other)
return include_helper(class, other, {})
end
-- returns a deep copy of `other'
local function clone(other)
return setmetatable(include({}, other), getmetatable(other))
end
local function new(class)
-- mixins
local inc = class.__includes or {}
if getmetatable(inc) then inc = {inc} end
for _, other in ipairs(inc) do
include(class, other)
end
-- class implementation
class.__index = class
class.init = class.init or class[1] or function() end
class.include = class.include or include
class.clone = class.clone or clone
-- constructor call
return setmetatable(class, {__call = function(c, ...)
local o = setmetatable({}, c)
o:init(...)
return o
end})
end
-- interface for cross class-system compatibility (see https://github.com/bartbes/Class-Commons).
if class_commons ~= false and not common then
common = {}
function common.class(name, prototype, parent)
return new{__includes = {prototype, parent}}
end
function common.instance(class, ...)
return class(...)
end
end
-- the module
return setmetatable({new = new, include = include, clone = clone},
{__call = function(_,...) return new(...) end})
-97
View File
@@ -1,97 +0,0 @@
--[[
Copyright (c) 2010-2013 Matthias Richter
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.
Except as contained in this notice, the name(s) of the above copyright holders
shall not be used in advertising or otherwise to promote the sale, use or
other dealings in this Software without prior written authorization.
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 function __NULL__() end
-- default gamestate produces error on every callback
local state_init = setmetatable({leave = __NULL__},
{__index = function() error("Gamestate not initialized. Use Gamestate.switch()") end})
local stack = {state_init}
local GS = {}
function GS.new(t) return t or {} end -- constructor - deprecated!
function GS.switch(to, ...)
assert(to, "Missing argument: Gamestate to switch to")
assert(to ~= GS, "Can't call switch with colon operator")
local pre = stack[#stack]
;(pre.leave or __NULL__)(pre)
;(to.init or __NULL__)(to)
to.init = nil
stack[#stack] = to
return (to.enter or __NULL__)(to, pre, ...)
end
function GS.push(to, ...)
assert(to, "Missing argument: Gamestate to switch to")
assert(to ~= GS, "Can't call push with colon operator")
local pre = stack[#stack]
;(to.init or __NULL__)(to)
to.init = nil
stack[#stack+1] = to
return (to.enter or __NULL__)(to, pre, ...)
end
function GS.pop(...)
assert(#stack > 1, "No more states to pop!")
local pre = stack[#stack]
stack[#stack] = nil
;(pre.leave or __NULL__)(pre)
return (stack[#stack].resume or __NULL__)(pre, ...)
end
function GS.current()
return stack[#stack]
end
local all_callbacks = {
'draw', 'errhand', 'focus', 'keypressed', 'keyreleased', 'mousefocus',
'mousepressed', 'mousereleased', 'quit', 'resize', 'textinput',
'threaderror', 'update', 'visible', 'gamepadaxis', 'gamepadpressed',
'gamepadreleased', 'joystickadded', 'joystickaxis', 'joystickhat',
'joystickpressed', 'joystickreleased', 'joystickremoved'
}
function GS.registerEvents(callbacks)
local registry = {}
callbacks = callbacks or all_callbacks
for _, f in ipairs(callbacks) do
registry[f] = love[f] or __NULL__
love[f] = function(...)
registry[f](...)
return GS[f](...)
end
end
end
-- forward any undefined functions
setmetatable(GS, {__index = function(_, func)
return function(...)
return (stack[#stack][func] or __NULL__)(stack[#stack], ...)
end
end})
return GS
-95
View File
@@ -1,95 +0,0 @@
--[[
Copyright (c) 2012-2013 Matthias Richter
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.
Except as contained in this notice, the name(s) of the above copyright holders
shall not be used in advertising or otherwise to promote the sale, use or
other dealings in this Software without prior written authorization.
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 Registry = {}
Registry.__index = function(self, key)
return Registry[key] or (function()
local t = {}
rawset(self, key, t)
return t
end)()
end
function Registry:register(s, f)
self[s][f] = f
return f
end
function Registry:emit(s, ...)
for f in pairs(self[s]) do
f(...)
end
end
function Registry:remove(s, ...)
local f = {...}
for i = 1,select('#', ...) do
self[s][f[i]] = nil
end
end
function Registry:clear(...)
local s = {...}
for i = 1,select('#', ...) do
self[s[i]] = {}
end
end
function Registry:emit_pattern(p, ...)
for s in pairs(self) do
if s:match(p) then self:emit(s, ...) end
end
end
function Registry:remove_pattern(p, ...)
for s in pairs(self) do
if s:match(p) then self:remove(s, ...) end
end
end
function Registry:clear_pattern(p)
for s in pairs(self) do
if s:match(p) then self[s] = {} end
end
end
-- the module
local function new()
local registry = setmetatable({}, Registry)
return setmetatable({
new = new,
register = function(...) return registry:register(...) end,
emit = function(...) registry:emit(...) end,
remove = function(...) registry:remove(...) end,
clear = function(...) registry:clear(...) end,
emit_pattern = function(...) registry:emit_pattern(...) end,
remove_pattern = function(...) registry:remove_pattern(...) end,
clear_pattern = function(...) registry:clear_pattern(...) end,
}, {__call = new})
end
return new()
-188
View File
@@ -1,188 +0,0 @@
--[[
Copyright (c) 2010-2013 Matthias Richter
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.
Except as contained in this notice, the name(s) of the above copyright holders
shall not be used in advertising or otherwise to promote the sale, use or
other dealings in this Software without prior written authorization.
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 Timer = {}
Timer.__index = Timer
local function _nothing_() end
function Timer:update(dt)
local to_remove = {}
for handle, delay in pairs(self.functions) do
delay = delay - dt
if delay <= 0 then
to_remove[#to_remove+1] = handle
end
self.functions[handle] = delay
handle.func(dt, delay)
end
for _,handle in ipairs(to_remove) do
self.functions[handle] = nil
handle.after(handle.after)
end
end
function Timer:do_for(delay, func, after)
local handle = {func = func, after = after or _nothing_}
self.functions[handle] = delay
return handle
end
function Timer:add(delay, func)
return self:do_for(delay, _nothing_, func)
end
function Timer:addPeriodic(delay, func, count)
local count, handle = count or math.huge -- exploit below: math.huge - 1 = math.huge
handle = self:add(delay, function(f)
if func(func) == false then return end
count = count - 1
if count > 0 then
self.functions[handle] = delay
end
end)
return handle
end
function Timer:cancel(handle)
self.functions[handle] = nil
end
function Timer:clear()
self.functions = {}
end
Timer.tween = setmetatable({
-- helper functions
out = function(f) -- 'rotates' a function
return function(s, ...) return 1 - f(1-s, ...) end
end,
chain = function(f1, f2) -- concatenates two functions
return function(s, ...) return (s < .5 and f1(2*s, ...) or 1 + f2(2*s-1, ...)) * .5 end
end,
-- useful tweening functions
linear = function(s) return s end,
quad = function(s) return s*s end,
cubic = function(s) return s*s*s end,
quart = function(s) return s*s*s*s end,
quint = function(s) return s*s*s*s*s end,
sine = function(s) return 1-math.cos(s*math.pi/2) end,
expo = function(s) return 2^(10*(s-1)) end,
circ = function(s) return 1 - math.sqrt(1-s*s) end,
back = function(s,bounciness)
bounciness = bounciness or 1.70158
return s*s*((bounciness+1)*s - bounciness)
end,
bounce = function(s) -- magic numbers ahead
local a,b = 7.5625, 1/2.75
return math.min(a*s^2, a*(s-1.5*b)^2 + .75, a*(s-2.25*b)^2 + .9375, a*(s-2.625*b)^2 + .984375)
end,
elastic = function(s, amp, period)
amp, period = amp and math.max(1, amp) or 1, period or .3
return (-amp * math.sin(2*math.pi/period * (s-1) - math.asin(1/amp))) * 2^(10*(s-1))
end,
}, {
-- register new tween
__call = function(tween, self, len, subject, target, method, after, ...)
-- recursively collects fields that are defined in both subject and target into a flat list
local function tween_collect_payload(subject, target, out)
for k,v in pairs(target) do
local ref = subject[k]
assert(type(v) == type(ref), 'Type mismatch in field "'..k..'".')
if type(v) == 'table' then
tween_collect_payload(ref, v, out)
else
local ok, delta = pcall(function() return (v-ref)*1 end)
assert(ok, 'Field "'..k..'" does not support arithmetic operations')
out[#out+1] = {subject, k, delta}
end
end
return out
end
method = tween[method or 'linear'] -- see __index
local payload, t, args = tween_collect_payload(subject, target, {}), 0, {...}
local last_s = 0
return self:do_for(len, function(dt)
t = t + dt
local s = method(math.min(1, t/len), unpack(args))
local ds = s - last_s
last_s = s
for _, info in ipairs(payload) do
local ref, key, delta = unpack(info)
ref[key] = ref[key] + delta * ds
end
end, after)
end,
-- fetches function and generated compositions for method `key`
__index = function(tweens, key)
if type(key) == 'function' then return key end
assert(type(key) == 'string', 'Method must be function or string.')
if rawget(tweens, key) then return rawget(tweens, key) end
local function construct(pattern, f)
local method = rawget(tweens, key:match(pattern))
if method then return f(method) end
return nil
end
local out, chain = rawget(tweens,'out'), rawget(tweens,'chain')
return construct('^in%-([^-]+)$', function(...) return ... end)
or construct('^out%-([^-]+)$', out)
or construct('^in%-out%-([^-]+)$', function(f) return chain(f, out(f)) end)
or construct('^out%-in%-([^-]+)$', function(f) return chain(out(f), f) end)
or error('Unknown interpolation method: ' .. key)
end})
-- the module
local function new()
local timer = setmetatable({functions = {}, tween = Timer.tween}, Timer)
return setmetatable({
new = new,
update = function(...) return timer:update(...) end,
do_for = function(...) return timer:do_for(...) end,
add = function(...) return timer:add(...) end,
addPeriodic = function(...) return timer:addPeriodic(...) end,
cancel = function(...) return timer:cancel(...) end,
clear = function(...) return timer:clear(...) end,
tween = setmetatable({}, {
__index = Timer.tween,
__newindex = function(_,k,v) Timer.tween[k] = v end,
__call = function(t,...) return timer:tween(...) end,
})
}, {__call = new})
end
return new()
-161
View File
@@ -1,161 +0,0 @@
--[[
Copyright (c) 2012-2013 Matthias Richter
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.
Except as contained in this notice, the name(s) of the above copyright holders
shall not be used in advertising or otherwise to promote the sale, use or
other dealings in this Software without prior written authorization.
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 sqrt, cos, sin, atan2 = math.sqrt, math.cos, math.sin, math.atan2
local function str(x,y)
return "("..tonumber(x)..","..tonumber(y)..")"
end
local function mul(s, x,y)
return s*x, s*y
end
local function div(s, x,y)
return x/s, y/s
end
local function add(x1,y1, x2,y2)
return x1+x2, y1+y2
end
local function sub(x1,y1, x2,y2)
return x1-x2, y1-y2
end
local function permul(x1,y1, x2,y2)
return x1*x2, y1*y2
end
local function dot(x1,y1, x2,y2)
return x1*x2 + y1*y2
end
local function det(x1,y1, x2,y2)
return x1*y2 - y1*x2
end
local function eq(x1,y1, x2,y2)
return x1 == x2 and y1 == y2
end
local function lt(x1,y1, x2,y2)
return x1 < x2 or (x1 == x2 and y1 < y2)
end
local function le(x1,y1, x2,y2)
return x1 <= x2 and y1 <= y2
end
local function len2(x,y)
return x*x + y*y
end
local function len(x,y)
return sqrt(x*x + y*y)
end
local function dist2(x1,y1, x2,y2)
return len2(x1-x2, y1-y2)
end
local function dist(x1,y1, x2,y2)
return len(x1-x2, y1-y2)
end
local function normalize(x,y)
local l = len(x,y)
if l > 0 then
return x/l, y/l
end
return x,y
end
local function rotate(phi, x,y)
local c, s = cos(phi), sin(phi)
return c*x - s*y, s*x + c*y
end
local function perpendicular(x,y)
return -y, x
end
local function project(x,y, u,v)
local s = (x*u + y*v) / (u*u + v*v)
return s*u, s*v
end
local function mirror(x,y, u,v)
local s = 2 * (x*u + y*v) / (u*u + v*v)
return s*u - x, s*v - y
end
-- ref.: http://blog.signalsondisplay.com/?p=336
local function trim(maxLen, x, y)
local s = maxLen * maxLen / len2(x, y)
s = s > 1 and 1 or math.sqrt(s)
return x * s, y * s
end
local function angleTo(x,y, u,v)
if u and v then
return atan2(y, x) - atan2(v, u)
end
return atan2(y, x)
end
-- the module
return {
str = str,
-- arithmetic
mul = mul,
div = div,
add = add,
sub = sub,
permul = permul,
dot = dot,
det = det,
cross = det,
-- relation
eq = eq,
lt = lt,
le = le,
-- misc operations
len2 = len2,
len = len,
dist2 = dist2,
dist = dist,
normalize = normalize,
rotate = rotate,
perpendicular = perpendicular,
project = project,
mirror = mirror,
trim = trim,
angleTo = angleTo,
}
-191
View File
@@ -1,191 +0,0 @@
--[[
Copyright (c) 2010-2013 Matthias Richter
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.
Except as contained in this notice, the name(s) of the above copyright holders
shall not be used in advertising or otherwise to promote the sale, use or
other dealings in this Software without prior written authorization.
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 assert = assert
local sqrt, cos, sin, atan2 = math.sqrt, math.cos, math.sin, math.atan2
local vector = {}
vector.__index = vector
local function new(x,y)
return setmetatable({x = x or 0, y = y or 0}, vector)
end
local zero = new(0,0)
local function isvector(v)
return type(v) == 'table' and type(v.x) == 'number' and type(v.y) == 'number'
end
function vector:clone()
return new(self.x, self.y)
end
function vector:unpack()
return self.x, self.y
end
function vector:__tostring()
return "("..tonumber(self.x)..","..tonumber(self.y)..")"
end
function vector.__unm(a)
return new(-a.x, -a.y)
end
function vector.__add(a,b)
assert(isvector(a) and isvector(b), "Add: wrong argument types (<vector> expected)")
return new(a.x+b.x, a.y+b.y)
end
function vector.__sub(a,b)
assert(isvector(a) and isvector(b), "Sub: wrong argument types (<vector> expected)")
return new(a.x-b.x, a.y-b.y)
end
function vector.__mul(a,b)
if type(a) == "number" then
return new(a*b.x, a*b.y)
elseif type(b) == "number" then
return new(b*a.x, b*a.y)
else
assert(isvector(a) and isvector(b), "Mul: wrong argument types (<vector> or <number> expected)")
return a.x*b.x + a.y*b.y
end
end
function vector.__div(a,b)
assert(isvector(a) and type(b) == "number", "wrong argument types (expected <vector> / <number>)")
return new(a.x / b, a.y / b)
end
function vector.__eq(a,b)
return a.x == b.x and a.y == b.y
end
function vector.__lt(a,b)
return a.x < b.x or (a.x == b.x and a.y < b.y)
end
function vector.__le(a,b)
return a.x <= b.x and a.y <= b.y
end
function vector.permul(a,b)
assert(isvector(a) and isvector(b), "permul: wrong argument types (<vector> expected)")
return new(a.x*b.x, a.y*b.y)
end
function vector:len2()
return self.x * self.x + self.y * self.y
end
function vector:len()
return sqrt(self.x * self.x + self.y * self.y)
end
function vector.dist(a, b)
assert(isvector(a) and isvector(b), "dist: wrong argument types (<vector> expected)")
local dx = a.x - b.x
local dy = a.y - b.y
return sqrt(dx * dx + dy * dy)
end
function vector.dist2(a, b)
assert(isvector(a) and isvector(b), "dist: wrong argument types (<vector> expected)")
local dx = a.x - b.x
local dy = a.y - b.y
return (dx * dx + dy * dy)
end
function vector:normalize_inplace()
local l = self:len()
if l > 0 then
self.x, self.y = self.x / l, self.y / l
end
return self
end
function vector:normalized()
return self:clone():normalize_inplace()
end
function vector:rotate_inplace(phi)
local c, s = cos(phi), sin(phi)
self.x, self.y = c * self.x - s * self.y, s * self.x + c * self.y
return self
end
function vector:rotated(phi)
local c, s = cos(phi), sin(phi)
return new(c * self.x - s * self.y, s * self.x + c * self.y)
end
function vector:perpendicular()
return new(-self.y, self.x)
end
function vector:projectOn(v)
assert(isvector(v), "invalid argument: cannot project vector on " .. type(v))
-- (self * v) * v / v:len2()
local s = (self.x * v.x + self.y * v.y) / (v.x * v.x + v.y * v.y)
return new(s * v.x, s * v.y)
end
function vector:mirrorOn(v)
assert(isvector(v), "invalid argument: cannot mirror vector on " .. type(v))
-- 2 * self:projectOn(v) - self
local s = 2 * (self.x * v.x + self.y * v.y) / (v.x * v.x + v.y * v.y)
return new(s * v.x - self.x, s * v.y - self.y)
end
function vector:cross(v)
assert(isvector(v), "cross: wrong argument types (<vector> expected)")
return self.x * v.y - self.y * v.x
end
-- ref.: http://blog.signalsondisplay.com/?p=336
function vector:trim_inplace(maxLen)
local s = maxLen * maxLen / self:len2()
s = (s > 1 and 1) or math.sqrt(s)
self.x, self.y = self.x * s, self.y * s
return self
end
function vector:angleTo(other)
if other then
return atan2(self.y, self.x) - atan2(other.y, other.x)
end
return atan2(self.y, self.x)
end
function vector:trimmed(maxLen)
return self:clone():trim_inplace(maxLen)
end
-- the module
return setmetatable({new = new, isvector = isvector, zero = zero},
{__call = function(_, ...) return new(...) end})
-388
View File
@@ -1,388 +0,0 @@
# Change Log
## 2016-01-12: v0.14.1.12
* Added: Basic support for object layers in Bump plugin (thanks @premek)
* Changed: New line token from CRLF to LF
* Fixed: Sprite batches should now respect the map draw order
## 2016-01-01: v0.14.1.11
* Fixed: Various bugs in the Box2D plugin (thanks @ChrisWeisiger)
* Fixed: Various bugs in the Bump plugin (thanks @bobbyjoness)
## 2015-12-31: v0.14.1.10
* Fixed: Box2D plugin was not recognizing a tile's embedded object group
## 2015-11-19: v0.14.1.9
* Changed: key in image cache to formatted path of image
## 2015-11-16: v0.14.1.8
* Added: image cache to STI module [sponsored by Binary Cocoa]
* Added: STI:flush() to clear out image cache
## 2015-11-15: v0.14.1.7
* Added: support for offsetting maps [sponsored by Binary Cocoa]
* Changed: Map.setDrawRange is more optimized via recycling tables
* Changed: render order now defaults to "right-down"
## 2015-11-07: v0.14.1.6
* Fixed: tileset images not being properly filtered
* Fixed: bump.lua plugin missing world argument in draw
## 2015-10-14: v0.14.1.5
* Added: bump.lua plugin (thanks @bobbyjoness)
## 2015-10-12: v0.14.1.4
* Fixed: removing a layer now properly removes tile and object instances
* Fixed: box2d plugin now properly removes collision objects
## 2015-10-09: v0.14.1.3
* Fixed: flipping animated tiles properly display
* Fixed: rotating animated tiles properly display
* Fixed: rotating tile objects properly display
* Fixed: box2d plugin properly creates rotated and flipped tile objects
* Fixed: box2d plugin no longer crashes when drawing a line with two vertices
## 2015-10-07: v0.14.1.2
* Added: support for all render orders (rd, ru, ld, lu)
* Added: support for sensors in the box2d plugin (only works on individual tiles and objects; sensor = true)
* Changed: addCustomLayer's index argument is now optional and defaults to the end of the array
* Fixed: a crash when using Base64 (uncompressed) with LOVE 0.9.2
## 2015-10-03: v0.14.1.1
* Added: support for gzip compressed maps (requires LOVE 0.10.0+)
## 2015-09-30: v0.14.1.0
* Added: support for Base64 compressed maps (requires LuaJIT)
* Added: support for zlib compressed maps (requires LOVE 0.10.0+)
## 2015-09-28: v0.14.0.1
* Added: Support for all staggered types (x/y, even/odd, iso/hex)
## 2015-09-27: v0.14.0.0
* Added: Hexagonal map support (thanks EntranceJew!)
* Added: Error message for compressed maps
* Fixed: box2d plugin threw an error in some cases (thanks maxha651!)
## 2015-09-17: v0.13.1.4
* Changed: sanity checks now search for love.physics instead of love.physics.*
## 2015-09-16: v0.13.1.3
* Changed: Improved documentation
## 2015-09-16: v0.13.1.2
* Changed: Simplified plugins
* Changed: Namespaced the box2d plugin
* Removed: Non-LOVE frameworks (they didn't work)
## 2015-09-16: v0.13.1.1
* Added: LDoc documentation
* Added: Plugin system where devs can extend STI
* Added: Reinstated the Box2D integration as a plugin
## 2015-09-15: v0.13.1.0
* Added: Map:convertToCustomLayer() now returns the layer
* Changed: Tightened localization of some functions
* Removed: Box2D collision integration
* Removed: Unused functions
## 2015-07-31: v0.12.3.0
* Added: Tiled version number to Map.tiledversion
* Added: Map.objects table indexed by unique object IDs
* Added: A better error message when trying to use Tile Collections
* Changed: Version number should now match Tiled's version number
* Changed: You must now add ".lua" in the filename of a new map as this is consistent with other libraries
* Changed: Renamed "pure" framework to "lua" (still doesn't work, though!)
* Changed: Map:setDrawRange no longer inverts tx and ty for you, do it yourself!
* Changed: Map:draw no longer accepts scale values, use love.graphics.scale!
* Fixed: A bug where tile objects were drawing an object border
* Removed: Corona framework file
## 2015-03-22: v0.9.8
* Fixed: A bug where Tiles without a Properties list would crash
## 2015-02-02: v0.9.7
* Added: userdata to Box2D fixtures
* Changed: changelog.txt -> CHANGELOG.md
* Changed: Flipping tiles now happens in both tile layers and object layers
* Fixed: A bug where tile objects were drawing oddly in some cases
* Fixed: A bug where circles would error if physics was disabled
## 2015-01-28: v0.9.6
* Added: getLayerProperties(), getTileProperties(), and getObjectProperties()
* Fixed: A bug where flipped tiles crashed STI during initCollision()
* Fixed: Flipped collision tiles now have correct offset
* Removed: Reverted the change in v0.9.3 that filled in empty tiles with false
## 2014-12-15: v0.9.5
* Fixed: A bug where tile collision objects were using the wrong size in some cases
* Fixed: A bug where flipped tiles weren't always creating collision objects
## 2014-12-05: v0.9.4
* Changed: STI's canvas plays nicely with other libraries
* Changed: addCustomLayer() now returns a handle on the created layer
## 2014-12-03: v0.9.3
* Added: Local Tile IDs to Tile objects
* Added: Terrain information
* Fixed: Some conversion functions
* Changed: Tile Layers now contain "false" instead of "nil" where there is no tile
* Changed: Added \_LICENSE, \_URL, \_VERSION, and \_DESCRIPTION properties to core STI object
## 2014-09-29: v0.9.2
* Added: Support for drawing tiles in object layers
* Fixed: Incorrect calculation of some collision objects
## 2014-09-26: v0.9.1
* Fixed: A crash when a collidable tile is initialized but not used
* Removed: Public access to formatPath(), rotateVertex(), and convertEllipseToPolygon()
## 2014-09-24: v0.9.0
* Added: Animated tiles! (Thanks to Clean3d)
* Fixed: A crash when a collidable rectangle has no rotation value
* Fixed: Incorrect values given to orthogonal collision objects
## 2014-09-24: v0.8.3
* Added: Map:convertScreenToTile() and Map:convertTileToScreen()
* Added: Map:convertScreenToIsometricTile() and Map:convertIsometricTileToScreen()
* Added: Map:convertScreenToStaggeredTile() and Map:convertStaggeredTileToScreen()
* Fixed: Map:removeLayer() now works properly
* Changed: Tile Objects now use the tile's collision map by default
## 2014-09-22: v0.8.2
* Added: "collidable" property for objects, tiles, and layers
* if collidable is set to true in Tiled, STI will pick it up and set all appropriate entities to collidable objects
* Fixed: Physics module no long required if not needed.
* Fixed: Whitespace discrepencies
* Changed: Map:initWorldCollision() now supports a whole lot more
## 2014-09-21: v0.8.1
* Added: README now lists minimum requirements
* Changed: README updated with new collision system
* Changed: Map:enableCollision() renamed to Map:initWorldCollision()
* Changed: Map:drawCollisionMap() renamed to Map:drawWorldCollision()
* Changed: Updated framework files (still no real Lua/Corona support)
* Changed: Tidied up collision code
* Removed: Map:getCollisionMap()
## 2014-09-20: v0.8.0
* Added: Box2D collision via Map:enableCollision()
* Added: Map:convertEllipseToPolygon()
## 2014-09-17: v0.7.6
* Added: Map:convertScreenToIsometric and Map:convertIsometricToScreen
* Added: Map:setObjectCoordinates
* Added: Map:rotateVertex
* Fixed: Adjusted map positioning for Isometric and Staggered maps
* Fixed: Object positioning in Isometric maps
* Removed: Temporary fix for Tiled 0.9.1
## 2014-08-05: v0.7.5
* Fixed: Properties offset by 1
* Fixed: Drawing a single Layer can now use Layer's name/index
## 2014-04-28: v0.7.4
* Fixed: Canvas resize type
## 2014-04-18: v0.7.3
* Fixed: Canvas using wrong filter
## 2014-04-08: v0.7.2
* Removed: Dependency for LuaJIT's bitwise operations
## 2014-04-08: v0.7.1
* Added: Map:resize(w, h)
* Changed: Map:draw() now takes two optional arguments: ScaleX and ScaleY
* Changed: STI now draws to a Canvas before drawing to screen (fixes scaling oddities)
## 2014-04-07: v0.7.0
* Added: Files for Corona and Pure Lua implementation
* Changed: Restructured sti.lua into several files
* Changed: Library is now LOVE agnostic and should allow for implementation of other frameworks
## 2014-03-1 : v0.6.16
* Changed: Ellipses now use polygons instead of... Not polygons.
## 2014-03-1 : v0.6.15
* Fixed: Tile spacing calculated properly in all cases
## 2014-02-0 : v0.6.14
* Fixed: Tile properties ACTUALLY being added now!
## 2014-01-2 : v0.6.13
* Added: Missing Tile Flag
## 2014-01-2 : v0.6.12
* Added: drawCollisionMap() now supports Isometric and Staggered maps
* Changed: drawCollisionMap() now requires a collision map parameter
* Changed: setCollisionMap() renamed to getCollisionMap()
* Changed: getCollisionMap() now returns the collision map
* Fixed: Tile properties not being added
* Removed: Map.collision table removed
## 2014-01-2 : v0.6.11
* Added: Descriptive error messages
* Fixed: Image filters for scaling
## 2014-01-2 : v0.6.10
* Fixed: Optimized load time
## 2014-01-25: v0.6.9
* Fixed: Parallax Scrolling
## 2014-01-25: v0.6.8
* Changed: Revised and restructured code
* Changed: createCollisionMap() renamed to setCollisionMap()
* Changed: newCustomLayer() renamed to addCustomLayer()
## 2014-01-24: v0.6.7
* Fixed: Number of tiles wasn't calculated properly
## 2014-01-24: v0.6.6
* Fixed: Spacing wasn't calculated properly
## 2014-01-24: v0.6.5
* Added: Staggered Maps
## 2014-01-24: v0.6.4
* Added: Isometric Maps
## 2014-01-20: v0.6.3
* Added: Tile Flags (flip/rotation)
## 2014-01-20: v0.6.2
* Fixed: A scaling bug
## 2014-01-19: v0.6.1
* Fixed: A bug causing the Collision Map to be nil
## 2014-01-19: v0.6.0
* Added: Sprite Batches
## 2014-01-19: v0.5.0
* Added: Draw Range optimization
## 2014-01-18: v0.4.3
* Added: Layer draw offsets
## 2014-01-17: v0.4.2
* Changed: Organized library a little better
## 2014-01-17: v0.4.1
* Fixed: Tiles incorrectly offset
* Fixed: Drawing concave polygons
## 2014-01-17: v0.4.0
* Added: Draw Object Layers
## 2014-01-16: v0.3.3
* Added: Create new Custom Layer
* Added: Callbacks for all layers
* Added: Remove Layer
* Changed: Simplified sti.new()
## 2014-01-16: v0.3.2
* Fixed: Crash if using Tiled 0.9.1
* Changed: Map structure to remove "map" table
## 2014-01-16: v0.3.1
* Added: Update callback to Custom Layers
## 2014-01-16: v0.3.0
* Added: Support for converting layers to Custom Layers
* Changed: sti.new() no longer requires the file extension
## 2014-01-15: v0.2.2
* Added: Support for basic collision layer
## 2014-01-15: v0.2.1
* Added: Support for map instances
* Added: Name alias to layer indices
* Changed: Sandboxed map environment
* Changed: Data structures are more efficient
* Removed: Unnecessary update function
Thanks to JarrettBillingsley for many of these changes
## 2014-01-14: v0.2.0
* Fixed: Drawing Tile Offset
* Changed: Tile Layer data structure is more efficient
* Changed: Simplified Quad generation
## 2014-01-14: v0.1.0
* Initial Commit
* Added: Orthogonal Map support
* Added: Draw Tile Layers
* Added: Draw Image Layers
* Added: Ignore Hidden Layers
* Added: Layer Opacity
-26
View File
@@ -1,26 +0,0 @@
# Simple Tiled Implementation
This code is licensed under the [**MIT/X11 Open Source License**][MIT].
Copyright (c) 2014 Landon Manning - LManning17@gmail.com - [LandonManning.com][LM]
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.
[MIT]: http://www.opensource.org/licenses/mit-license.html
[LM]: http://LandonManning.com
-96
View File
@@ -1,96 +0,0 @@
# Simple Tiled Implementation
Simple Tiled Implementation is a [**Tiled**][Tiled] map loader and renderer designed for the **\*awesome\*** [**LÖVE**][LOVE] framework. Please read the [**documentation**][dox] to learn how it works!
## Quick Example
```lua
-- This example uses the default Box2D (love.physics) plugin!!
local sti = require "sti"
function love.load()
-- Grab window size
windowWidth = love.graphics.getWidth()
windowHeight = love.graphics.getHeight()
-- Set world meter size (in pixels)
love.physics.setMeter(32)
-- Load a map exported to Lua from Tiled
map = sti.new("assets/maps/map01.lua", { "box2d" })
-- Prepare physics world with horizontal and vertical gravity
world = love.physics.newWorld(0, 0)
-- Prepare collision objects
map:box2d_init(world)
-- Create a Custom Layer
map:addCustomLayer("Sprite Layer", 3)
-- Add data to Custom Layer
local spriteLayer = map.layers["Sprite Layer"]
spriteLayer.sprites = {
player = {
image = love.graphics.newImage("assets/sprites/player.png"),
x = 64,
y = 64,
r = 0,
}
}
-- Update callback for Custom Layer
function spriteLayer:update(dt)
for _, sprite in pairs(self.sprites) do
sprite.r = sprite.r + math.rad(90 * dt)
end
end
-- Draw callback for Custom Layer
function spriteLayer:draw()
for _, sprite in pairs(self.sprites) do
local x = math.floor(sprite.x)
local y = math.floor(sprite.y)
local r = sprite.r
love.graphics.draw(sprite.image, x, y, r)
end
end
end
function love.update(dt)
map:update(dt)
end
function love.draw()
-- Translation would normally be based on a player's x/y
local translateX = 0
local translateY = 0
-- Draw Range culls unnecessary tiles
map:setDrawRange(-translateX, -translateY, windowWidth, windowHeight)
-- Draw the map and all objects within
map:draw()
-- Draw Collision Map (useful for debugging)
love.graphics.setColor(255, 0, 0, 255)
map:box2d_draw()
-- Reset color
love.graphics.setColor(255, 255, 255, 255)
end
```
## Requirements
This library recommends LÖVE 0.9.2 or 0.10.0 and Tiled 0.14.1. If you are updating from an older version of Tiled, please re-export your Lua map files.
## License
This code is licensed under the [**MIT/X11 Open Source License**][MIT]. Check out the LICENSE file for more information.
[Tiled]: http://www.mapeditor.org/
[LOVE]: https://www.love2d.org/
[dox]: http://karai17.github.io/Simple-Tiled-Implementation/
[MIT]: http://www.opensource.org/licenses/mit-license.html
-53
View File
@@ -1,53 +0,0 @@
--- Simple and fast Tiled map loader and renderer.
-- @module sti
-- @author Landon Manning
-- @copyright 2015
-- @license MIT/X11
local STI = {
_LICENSE = "MIT/X11",
_URL = "https://github.com/karai17/Simple-Tiled-Implementation",
_VERSION = "0.14.1.12",
_DESCRIPTION = "Simple Tiled Implementation is a Tiled Map Editor library designed for the *awesome* LÖVE framework.",
cache = {}
}
local path = (...):gsub('%.init$', '') .. "."
local Map = require(path .. "map")
--- Instance a new map.
-- @param path Path to the map file.
-- @param plugins A list of plugins to load.
-- @param ox Offset of map on the X axis (in pixels)
-- @param oy Offset of map on the Y axis (in pixels)
-- @return table The loaded Map.
function STI.new(map, plugins, ox, oy)
-- Check for valid map type
local ext = map:sub(-4, -1)
assert(ext == ".lua", string.format(
"Invalid file type: %s. File must be of type: lua.",
ext
))
-- Get path to map
local path = map:reverse():find("[/\\]") or ""
if path ~= "" then
path = map:sub(1, 1 + (#map - path))
end
-- Load map
map = love.filesystem.load(map)
setfenv(map, {})
map = setmetatable(map(), {__index = Map})
map:init(STI, path, plugins, ox, oy)
return map
end
--- Flush image cache.
function STI:flush()
self.cache = {}
end
return STI
-1388
View File
File diff suppressed because it is too large Load Diff
-349
View File
@@ -1,349 +0,0 @@
--- Box2D plugin for STI
-- @module box2d
-- @author Landon Manning
-- @copyright 2015
-- @license MIT/X11
return {
box2d_LICENSE = "MIT/X11",
box2d_URL = "https://github.com/karai17/Simple-Tiled-Implementation",
box2d_VERSION = "2.3.2.1",
box2d_DESCRIPTION = "Box2D hooks for STI.",
--- Initialize Box2D physics world.
-- @param world The Box2D world to add objects to.
-- @return nil
box2d_init = function(map, world)
assert(love.physics, "To use the Box2D plugin, please enable the love.physics module.")
local body = love.physics.newBody(world, map.offsetx, map.offsety)
local collision = {
body = body,
}
local function convertEllipseToPolygon(x, y, w, h, max_segments)
local function calc_segments(segments)
local function vdist(a, b)
local c = {
x = a.x - b.x,
y = a.y - b.y,
}
return c.x * c.x + c.y * c.y
end
segments = segments or 64
local vertices = {}
local v = { 1, 2, math.ceil(segments/4-1), math.ceil(segments/4) }
local m
if love.physics then
m = love.physics.getMeter()
else
m = 32
end
for _, i in ipairs(v) do
local angle = (i / segments) * math.pi * 2
local px = x + w / 2 + math.cos(angle) * w / 2
local py = y + h / 2 + math.sin(angle) * h / 2
table.insert(vertices, { x = px / m, y = py / m })
end
local dist1 = vdist(vertices[1], vertices[2])
local dist2 = vdist(vertices[3], vertices[4])
-- Box2D threshold
if dist1 < 0.0025 or dist2 < 0.0025 then
return calc_segments(segments-2)
end
return segments
end
local segments = calc_segments(max_segments)
local vertices = {}
table.insert(vertices, { x = x + w / 2, y = y + h / 2 })
for i=0, segments do
local angle = (i / segments) * math.pi * 2
local px = x + w / 2 + math.cos(angle) * w / 2
local py = y + h / 2 + math.sin(angle) * h / 2
table.insert(vertices, { x = px, y = py })
end
return vertices
end
local function rotateVertex(v, x, y, cos, sin, oy)
oy = oy or 0
local vertex = {
x = v.x,
y = v.y - oy,
}
vertex.x = vertex.x - x
vertex.y = vertex.y - y
local vx = cos * vertex.x - sin * vertex.y
local vy = sin * vertex.x + cos * vertex.y
return vx + x, vy + y + oy
end
local function addObjectToWorld(objshape, vertices, userdata, object)
local shape
if objshape == "polyline" then
shape = love.physics.newChainShape(false, unpack(vertices))
else
shape = love.physics.newPolygonShape(unpack(vertices))
end
local fixture = love.physics.newFixture(body, shape)
fixture:setUserData(userdata)
if userdata.properties.sensor == "true" then
fixture:setSensor(true)
end
local obj = {
object = object,
shape = shape,
fixture = fixture,
}
table.insert(collision, obj)
end
local function getPolygonVertices(object)
local vertices = {}
for _, vertex in ipairs(object.polygon) do
table.insert(vertices, vertex.x)
table.insert(vertices, vertex.y)
end
return vertices
end
local function calculateObjectPosition(object, tile)
local o = {
shape = object.shape,
x = object.dx or object.x,
y = object.dy or object.y,
w = object.width,
h = object.height,
polygon = object.polygon or object.polyline or object.ellipse or object.rectangle
}
local userdata = {
object = o,
properties = object.properties
}
if o.shape == "rectangle" then
o.r = object.rotation or 0
local cos = math.cos(math.rad(o.r))
local sin = math.sin(math.rad(o.r))
local oy = 0
if object.gid then
local tileset = map.tilesets[map.tiles[object.gid].tileset]
local lid = object.gid - tileset.firstgid
local tile = {}
-- This fixes a height issue
o.y = o.y + map.tiles[object.gid].offset.y
oy = tileset.tileheight
for _, t in ipairs(tileset.tiles) do
if t.id == lid then
tile = t
break
end
end
if tile.objectGroup then
for _, obj in ipairs(tile.objectGroup.objects) do
-- Every object in the tile
calculateObjectPosition(obj, object)
end
return
else
o.w = map.tiles[object.gid].width
o.h = map.tiles[object.gid].height
end
end
o.polygon = {
{ x=o.x+0, y=o.y+0 },
{ x=o.x+o.w, y=o.y+0 },
{ x=o.x+o.w, y=o.y+o.h },
{ x=o.x+0, y=o.y+o.h },
}
for _, vertex in ipairs(o.polygon) do
if map.orientation == "isometric" then
vertex.x, vertex.y = map:convertIsometricToScreen(vertex.x, vertex.y)
end
vertex.x, vertex.y = rotateVertex(vertex, o.x, o.y, cos, sin, oy)
end
local vertices = getPolygonVertices(o)
addObjectToWorld(o.shape, vertices, userdata, tile or object)
elseif o.shape == "ellipse" then
if not o.polygon then
o.polygon = convertEllipseToPolygon(o.x, o.y, o.w, o.h)
end
local vertices = getPolygonVertices(o)
local triangles = love.math.triangulate(vertices)
for _, triangle in ipairs(triangles) do
addObjectToWorld(o.shape, triangle, userdata, tile or object)
end
elseif o.shape == "polygon" then
local vertices = getPolygonVertices(o)
local triangles = love.math.triangulate(vertices)
for _, triangle in ipairs(triangles) do
addObjectToWorld(o.shape, triangle, userdata, tile or object)
end
elseif o.shape == "polyline" then
local vertices = getPolygonVertices(o)
addObjectToWorld(o.shape, vertices, userdata, tile or object)
end
end
for _, tile in pairs(map.tiles) do
local tileset = map.tilesets[tile.tileset]
-- Every object in every instance of a tile
if tile.objectGroup then
if map.tileInstances[tile.gid] then
for _, instance in ipairs(map.tileInstances[tile.gid]) do
for _, object in ipairs(tile.objectGroup.objects) do
object.dx = object.x + instance.x
object.dy = object.y + instance.y
calculateObjectPosition(object, instance)
end
end
end
-- Every instance of a tile
elseif tile.properties and tile.properties.collidable == "true" and map.tileInstances[tile.gid] then
for _, instance in ipairs(map.tileInstances[tile.gid]) do
local object = {
shape = "rectangle",
x = instance.x,
y = instance.y,
width = tileset.tilewidth,
height = tileset.tileheight,
properties = tile.properties
}
calculateObjectPosition(object, instance)
end
end
end
for _, layer in ipairs(map.layers) do
-- Entire layer
if layer.properties.collidable == "true" then
if layer.type == "tilelayer" then
for gid, tiles in pairs(map.tileInstances) do
local tile = map.tiles[gid]
local tileset = map.tilesets[tile.tileset]
for _, instance in ipairs(tiles) do
if instance.layer == layer then
local object = {
shape = "rectangle",
x = instance.x,
y = instance.y,
width = tileset.tilewidth,
height = tileset.tileheight,
properties = tile.properties
}
calculateObjectPosition(object, instance)
end
end
end
elseif layer.type == "objectgroup" then
for _, object in ipairs(layer.objects) do
calculateObjectPosition(object)
end
elseif layer.type == "imagelayer" then
local object = {
shape = "rectangle",
x = layer.x or 0,
y = layer.y or 0,
width = layer.width,
height = layer.height,
properties = layer.properties
}
calculateObjectPosition(object)
end
end
-- Individual objects
if layer.type == "objectgroup" then
for _, object in ipairs(layer.objects) do
if object.properties.collidable == "true" then
calculateObjectPosition(object)
end
end
end
end
map.box2d_collision = collision
end,
--- Remove Box2D fixtures and shapes from world.
-- @param index The index or name of the layer being removed
-- @return nil
box2d_removeLayer = function(map, index)
local layer = assert(map.layers[index], "Layer not found: " .. index)
local collision = map.box2d_collision
-- Remove collision objects
for i=#collision, 1, -1 do
local obj = collision[i]
if obj.object.layer == layer then
obj.fixture:destroy()
table.remove(collision, i)
end
end
end,
--- Draw Box2D physics world.
-- @return nil
box2d_draw = function(map)
local collision = map.box2d_collision
for _, obj in ipairs(collision) do
local points = {collision.body:getWorldPoints(obj.shape:getPoints())}
if #points == 4 then
love.graphics.line(points)
else
love.graphics.polygon("line", points)
end
end
end,
}
--- Custom Properties in Tiled are used to tell this plugin what to do.
-- @table Properties
-- @field collidable set to "true", can be used on any Layer, Tile, or Object
-- @field sensor set to "true", can be used on any Tile or Object that is also collidable
-103
View File
@@ -1,103 +0,0 @@
--- Bump.lua plugin for STI
-- @module bump.lua
-- @author David Serrano (BobbyJones|FrenchFryLord)
-- @copyright 2016
-- @license MIT/X11
return {
bump_LICENSE = "MIT/X11",
bump_URL = "https://github.com/karai17/Simple-Tiled-Implementation",
bump_VERSION = "3.1.5.2",
bump_DESCRIPTION = "Bump hooks for STI.",
--- Adds each collidable tile to the Bump world.
-- @param world The Bump world to add objects to.
-- @return collidables table containing the handles to the objects in the Bump world.
bump_init = function(map, world)
local collidables = {}
for _, tileset in ipairs(map.tilesets) do
for _, tile in ipairs(tileset.tiles) do
local gid = tileset.firstgid + tile.id
-- Every object in every instance of a tile
if tile.properties and tile.properties.collidable == "true" and map.tileInstances[gid] then
for _, instance in ipairs(map.tileInstances[gid]) do
local t = {properties = tile.properties, x = instance.x + map.offsetx, y = instance.y + map.offsety, width = map.tilewidth, height = map.tileheight, layer = instance.layer }
world:add(t, t.x,t.y, t.width,t.height)
table.insert(collidables,t)
end
end
end
end
for _, layer in ipairs(map.layers) do
-- Entire layer
if layer.properties.collidable == "true" then
if layer.type == "tilelayer" then
for y, tiles in ipairs(layer.data) do
for x, tile in pairs(tiles) do
local t = {properties = tile.properties, x = x * map.tilewidth + tile.offset.x + map.offsetx, y = y * map.tileheight + tile.offset.y + map.offsety, width = tile.width, height = tile.height, layer = layer }
world:add(t, t.x,t.y, t.width,t.height )
table.insert(collidables,t)
end
end
elseif layer.type == "imagelayer" then
world:add(layer, layer.x,layer.y, layer.width,layer.height)
table.insert(collidables,layer)
end
end
-- individual collidable objects in a layer that is not "collidable"
-- or whole collidable objects layer
if layer.type == "objectgroup" then
for _, obj in ipairs(layer.objects) do
if (layer.properties and layer.properties.collidable == "true")
or (obj.properties and obj.properties.collidable == "true") then
if obj.shape == "rectangle" then
local t = {properties = obj.properties, x = obj.x, y = obj.y, width = obj.width, height = obj.height, type = obj.type, name = obj.name, id = obj.id, gid = obj.gid, layer = layer }
if obj.gid then t.y = t.y - obj.height end
world:add(t, t.x,t.y, t.width,t.height )
table.insert(collidables,t)
end -- TODO implement other object shapes?
end
end
end
end
map.bump_collidables = collidables
end,
--- Remove layer
-- @params index to layer to be removed
-- @params world bump world the holds the tiles
-- @return nil
bump_removeLayer = function(map, index, world)
local layer = assert(map.layers[index], "Layer not found: " .. index)
local collidables = map.bump_collidables
-- Remove collision objects
for i=#collidables, 1, -1 do
local obj = collidables[i]
if obj.layer == layer
and (
layer.properties.collidable == "true"
or obj.properties.collidable == "true"
) then
world:remove(obj)
table.remove(collidables, i)
end
end
end,
--- Draw bump collisions world.
-- @params world bump world holding the tiles geometry
-- @return nil
bump_draw = function(map, world)
for k,collidable in pairs(map.bump_collidables) do
love.graphics.rectangle("line",world:getRect(collidable))
end
end
}
+44 -364
View File
@@ -1,372 +1,52 @@
require "lib/postshader"
local LightWorld = require "lib"
local ProFi = require 'game.vendor.ProFi'
exf = {}
exf.current = nil
exf.available = {}
local game = require 'assets/scripts/game'
local stages = require 'assets/scripts/stages'
local controls = require 'assets/scripts/controls'
local bells = require 'assets/scripts/bells'
local camera = require 'assets/scripts/camera'
local stress = require 'assets/scripts/stress'
local messages = require 'assets/scripts/messages'
local start = require 'assets/scripts/start'
-- LOAD
function love.load()
exf.list = List:new()
exf.smallfont = love.graphics.newFont(12)
exf.mediumfont = love.graphics.newFont(18)
exf.bigfont = love.graphics.newFont(40)
exf.list.font = exf.smallfont
game.load()
camera.load(game)
stages.load(game, camera)
stress.load(game, camera)
bells.load(game, camera)
controls.load(game)
messages.load(game)
start.load(game)
end
exf.bigball = love.graphics.newImage("game/gfx/not-estudio.png")
exf.music = love.audio.newSource("assets/audio/music/theme.mp3", "static")
exf.music:setVolume(0.9) -- 90% of ordinary volume
exf.music:setPitch(0.5) -- one octave lower
exf:playMusic()
-- Find available demos.
local files = love.filesystem.getDirectoryItems("game")
local n = 0
-- UPDATE
function love.update(dt)
--require('assets/scripts/vendor/lovebird').update()
game.world:update(dt)
game.update(dt)
camera.update(game)
stages.update(dt, game, camera)
stress.update(dt, game, camera)
bells.update(dt, game, camera)
controls.update(dt, game, camera)
messages.update(dt, game)
start.update(dt, game)
end
for i, v in ipairs(files) do
is_file = love.filesystem.isFile("game/".. v )
if is_file then
n = n + 1
table.insert(exf.available, v);
local file = love.filesystem.newFile(v, love.file_read)
file:open("r")
local contents = love.filesystem.read("game/" .. v, 100)
local s, e, c = string.find(contents, "Example: ([%a%p ]-)[\r\n]")
file:close(file)
if not c then c = "Untitled" end
local title = " Inicia la Aventura!"
exf.list:add(title, v)
-- DRAW
function love.draw()
camera.gcam:draw(
function(l,t,w,h)
stages.draw()
stress.draw()
bells.draw(game)
end
end
exf.list:done()
exf.resume()
)
controls.draw()
messages.draw()
start.draw(game)
end
function love.update(dt) end
function love.draw() end
function love.keypressed(k) end
function love.keyreleased(k) end
function love.mousepressed(x, y, b) end
function love.mousereleased(x, y, b) end
function exf.empty() end
function exf.update(dt)
exf.list:update(dt)
lightMouse:setPosition(love.mouse.getX(), love.mouse.getY())
end
function exf.draw()
lightWorld:draw(function()
love.graphics.setBackgroundColor(0, 0, 0)
love.graphics.setColor(10, 110,110)
love.graphics.rectangle("fill", 0, 0, love.graphics.getWidth(), love.graphics.getHeight())
love.graphics.setColor(255, 255, 255, 191)
love.graphics.setFont(exf.bigfont)
love.graphics.print("Shall I Kill Her", 500, 50)
love.graphics.setFont(exf.mediumfont)
love.graphics.print("Un hombre atormentado por oscuros sentimientos de debate", 380, 140)
love.graphics.print("entre el bien y el mal. Debe tomar la decisión entre matar o no a ", 360, 160)
love.graphics.print("su actual pareja. Para ello recorre sus recuerdos en busca de una razón.", 335, 180)
exf.list:draw()
love.graphics.setColor(255, 255, 255)
love.graphics.draw(exf.bigball, 760 - 128, 600 - 128, love.timer.getTime(), 1, 1, exf.bigball:getWidth() * 0.5, exf.bigball:getHeight() * 0.5)
end)
end
function exf.keypressed(k)
end
function exf.keyreleased(k)
end
function exf.mousepressed(x, y, b)
exf.list:mousepressed(x, y, b)
end
function exf.mousereleased(x, y, b)
exf.list:mousereleased(x, y, b)
end
function exf.intable(t, e)
for k, v in ipairs(t) do
if v == e then return true end
end
return false
end
function exf.start(item, file)
local e_id = string.sub(item, 1, 4)
local e_rest = string.sub(item, 5)
local unused1, unused2, n = string.find(item, "(%s)%.lua")
exf:stopMusic()
if exf.intable(exf.available, file) then
if not love.filesystem.exists("game/" .. file) then
print("Could not load game .. " .. file)
else
-- Clear all callbacks.
love.load = exf.empty
love.update = exf.empty
love.draw = exf.empty
love.keypressed = exf.empty
love.keyreleased = exf.empty
love.mousepressed = exf.empty
love.mousereleased = exf.empty
love.filesystem.load("game/" .. file)()
exf.clear()
--love.window.setTitle(e_rest)
-- Redirect keypress
local o_keypressed = love.keypressed
love.keypressed =
function(k)
if k == "escape" then
exf.resume()
end
o_keypressed(k)
end
love.load()
end
else
print("Game ".. e_id .. " does not exist.")
end
end
function exf.clear()
love.graphics.setBackgroundColor(0,0,0)
love.graphics.setColor(255, 255, 255)
love.graphics.setLineWidth(1)
love.graphics.setLineStyle("smooth")
love.graphics.setBlendMode("alpha")
love.mouse.setVisible(true)
end
function exf.resume()
load = nil
love.update = exf.update
love.draw = exf.draw
love.keypressed = exf.keypressed
love.keyreleased = exf.keyreleased
love.mousepressed = exf.mousepressed
love.mousereleased = exf.mousereleased
love.mouse.setVisible(true)
love.window.setTitle("Shall I Kill Her")
-- create light world
lightWorld = LightWorld({
ambient = {127, 127, 127}
})
-- create light
lightMouse = lightWorld:newLight(0, 0, 255, 127, 63, 500)
lightMouse:setSmooth(2)
-- create shadow bodys
circleTest = lightWorld:newCircle(760 - 128, 600 - 128, exf.bigball:getWidth()*0.5)
end
function inside(mx, my, x, y, w, h)
return mx >= x and mx <= (x+w) and my >= y and my <= (y+h)
end
----------------------
-- List object
----------------------
List = {}
function List:new()
o = {}
setmetatable(o, self)
self.__index = self
o.items = {}
o.files = {}
o.x = 520
o.y = 300
o.width = 220
o.height = 160
o.item_height = 23
o.sum_item_height = 0
o.bar_size = 20
o.bar_pos = 0
o.bar_max_pos = 0
o.bar_width = 15
o.bar_lock = nil
return o
end
function List:add(item, file)
table.insert(self.items, item)
table.insert(self.files, file)
end
function List:done()
self.items.n = #self.items
-- Recalc bar size.
self.bar_pos = 0
local num_items = (self.height/self.item_height)
local ratio = num_items/self.items.n
self.bar_size = self.height * ratio
self.bar_max_pos = self.height - self.bar_size - 3
-- Calculate height of everything.
self.sum_item_height = (self.item_height+1) * self.items.n + 2
end
function List:hasBar()
return self.sum_item_height > self.height
end
function List:getBarRatio()
return self.bar_pos/self.bar_max_pos
end
function List:getOffset()
local ratio = self.bar_pos/self.bar_max_pos
return math.floor((self.sum_item_height-self.height)*ratio + 0.5)
end
function List:update(dt)
if self.bar_lock then
local dy = math.floor(love.mouse.getY()-self.bar_lock.y+0.5)
self.bar_pos = self.bar_pos + dy
if self.bar_pos < 0 then
self.bar_pos = 0
elseif self.bar_pos > self.bar_max_pos then
self.bar_pos = self.bar_max_pos
end
self.bar_lock.y = love.mouse.getY()
end
end
function List:mousepressed(mx, my, b)
if self:hasBar() then
if b == 1 then
local x, y, w, h = self:getBarRect()
if inside(mx, my, x, y, w, h) then
self.bar_lock = { x = mx, y = my }
end
end
local per_pixel = (self.sum_item_height-self.height)/self.bar_max_pos
local bar_pixel_dt = math.floor(((self.item_height)*3)/per_pixel + 0.5)
if b == "wd" then
self.bar_pos = self.bar_pos + bar_pixel_dt
if self.bar_pos > self.bar_max_pos then
self.bar_pos = self.bar_max_pos
end
elseif b == "wu" then
self.bar_pos = self.bar_pos - bar_pixel_dt
if self.bar_pos < 0 then
self.bar_pos = 0
end
end
end
if b == 1 and inside(mx, my, self.x+2, self.y+1, self.width-3, self.height-3) then
local tx, ty = mx-self.x, my + self:getOffset() - self.y
local index = math.floor((ty/self.sum_item_height)*self.items.n)
local i = self.items[index+1]
local f = self.files[index+1]
if f then
exf.start(i, f)
end
end
end
function List:mousereleased(x, y, b)
if self:hasBar() then
if b == 1 then
self.bar_lock = nil
end
end
end
function List:getBarRect()
return self.x+self.width+2, self.y+1+self.bar_pos,
self.bar_width-3, self.bar_size
end
function List:getItemRect(i)
return self.x+2, self.y+((self.item_height+1)*(i-1)+1)-self:getOffset(),
self.width-3, self.item_height
end
function List:draw()
love.graphics.setLineWidth(2)
love.graphics.setLineStyle("rough")
love.graphics.setFont(self.font)
love.graphics.setColor(48, 156, 225)
local mx, my = love.mouse.getPosition()
-- Get interval to display.
local start_i = math.floor( self:getOffset()/(self.item_height+1) ) + 1
local end_i = start_i+math.floor( self.height/(self.item_height+1) ) + 1
if end_i > self.items.n then
end_i = self.items.n
end
love.graphics.setScissor(self.x, self.y, self.width, self.height)
-- Items.
for i = start_i,end_i do
local x, y, w, h = self:getItemRect(i)
local hover = inside(mx, my, x, y, w, h)
if hover then
love.graphics.setColor(0, 0, 0, 127)
else
love.graphics.setColor(0, 0, 0, 63)
end
love.graphics.rectangle("fill", x+1, y+i+1, w-3, h)
if hover then
love.graphics.setColor(255, 255, 255)
else
love.graphics.setColor(255, 255, 255, 127)
end
local e_id = string.sub(self.items[i], 1, 5)
local e_rest = string.sub(self.items[i], 5)
love.graphics.print(e_id, x+10, y+i+6) --Updated y placement -- Used to change position of Example IDs
love.graphics.print(e_rest, x+50, y+i+6) --Updated y placement -- Used to change position of Example Titles
end
love.graphics.setScissor()
end
function exf:playMusic()
exf.music:play()
end
function exf:stopMusic()
exf.music:stop()
function love.mousepressed(x, y, button, istouch)
start.mousepressed(game)
end