first commit
This commit is contained in:
57
src/Animation.lua
Normal file
57
src/Animation.lua
Normal file
@@ -0,0 +1,57 @@
|
||||
--[[
|
||||
GD50
|
||||
Legend of Zelda
|
||||
|
||||
-- Animation Class --
|
||||
|
||||
Author: Colton Ogden
|
||||
cogden@cs50.harvard.edu
|
||||
]]
|
||||
|
||||
Animation = Class{}
|
||||
|
||||
function Animation:init(def)
|
||||
self.frames = def.frames
|
||||
self.interval = def.interval
|
||||
self.texture = def.texture
|
||||
self.looping = def.looping or true
|
||||
|
||||
self.timer = 0
|
||||
self.currentFrame = 1
|
||||
|
||||
-- used to see if we've seen a whole loop of the animation
|
||||
self.timesPlayed = 0
|
||||
end
|
||||
|
||||
function Animation:refresh()
|
||||
self.timer = 0
|
||||
self.currentFrame = 1
|
||||
self.timesPlayed = 0
|
||||
end
|
||||
|
||||
function Animation:update(dt)
|
||||
-- if not a looping animation and we've played at least once, exit
|
||||
if not self.looping and self.timesPlayed > 0 then
|
||||
return
|
||||
end
|
||||
|
||||
-- no need to update if animation is only one frame
|
||||
if #self.frames > 1 then
|
||||
self.timer = self.timer + dt
|
||||
|
||||
if self.timer > self.interval then
|
||||
self.timer = self.timer % self.interval
|
||||
|
||||
self.currentFrame = math.max(1, (self.currentFrame + 1) % (#self.frames + 1))
|
||||
|
||||
-- if we've looped back to the beginning, record
|
||||
if self.currentFrame == 1 then
|
||||
self.timesPlayed = self.timesPlayed + 1
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function Animation:getCurrentFrame()
|
||||
return self.frames[self.currentFrame]
|
||||
end
|
||||
69
src/Ball.lua
Normal file
69
src/Ball.lua
Normal file
@@ -0,0 +1,69 @@
|
||||
|
||||
Ball = Class{}
|
||||
|
||||
function Ball:init(x, y, dx, dy)
|
||||
self.x = x
|
||||
self.y = y
|
||||
self.width = 8
|
||||
self.height = 8
|
||||
|
||||
-- these variables are for keeping track of our velocity on both the
|
||||
-- X and Y axis, since the ball can move in two dimensions
|
||||
self.dy = dx
|
||||
self.dx = dy
|
||||
self.remove = false
|
||||
self.hitPlayer = false
|
||||
end
|
||||
function Ball:reset()
|
||||
self.x = VIRTUAL_WIDTH / 2 - 2
|
||||
self.y = VIRTUAL_HEIGHT / 2 - 2
|
||||
self.dx = 0
|
||||
self.dy = 0
|
||||
end
|
||||
|
||||
function Ball:update(dt,level)
|
||||
local tempX = self.x + self.dx * dt
|
||||
local tempY = self.y + self.dy * dt
|
||||
|
||||
local trailSeg = level:pointOnEdge(tempX,tempY,level.player.trailSegments)
|
||||
if trailSeg then
|
||||
self.hitPlayer = true
|
||||
gSounds['hit']:play()
|
||||
end
|
||||
|
||||
local i, segment = level:getTouchingSegment(tempX,tempY)
|
||||
if segment then
|
||||
local direction = self:getCollisionDirection(segment, tempX, tempY)
|
||||
if direction == 'up' or direction == 'down' then
|
||||
self.dy = - self.dy
|
||||
elseif direction == 'right' or direction == 'left' then
|
||||
self.dx = -self.dx
|
||||
elseif level:pointOnEdge(tempX,tempY) then
|
||||
self.dx = -self.dx
|
||||
self.dy = -self.dy
|
||||
elseif not level:insideBounds(tempX,tempY) then
|
||||
self.dx = -self.dx
|
||||
self.dy = -self.dy
|
||||
end
|
||||
gSounds['blip']:stop()
|
||||
gSounds['blip']:play()
|
||||
end
|
||||
|
||||
if not (level:insideBounds(tempX,tempY) or level:pointOnEdge(tempX,tempY)) then
|
||||
self.remove = true
|
||||
end
|
||||
|
||||
self.x = self.x + self.dx * dt
|
||||
self.y = self.y + self.dy * dt
|
||||
|
||||
|
||||
end
|
||||
|
||||
function Ball:render()
|
||||
love.graphics.setColor(255,255,255,255)
|
||||
love.graphics.circle('fill', self.x, self.y, self.width/2)
|
||||
end
|
||||
|
||||
function Ball:getCollisionDirection(segment,x,y)
|
||||
return segment:sideOfSegment(x,y)
|
||||
end
|
||||
64
src/Dependencies.lua
Normal file
64
src/Dependencies.lua
Normal file
@@ -0,0 +1,64 @@
|
||||
--
|
||||
-- libraries
|
||||
--
|
||||
|
||||
Class = require 'lib/class'
|
||||
Event = require 'lib/knife.event'
|
||||
push = require 'lib/push'
|
||||
Timer = require 'lib/knife.timer'
|
||||
|
||||
require 'src/Animation'
|
||||
require 'src/constants'
|
||||
require 'src/StateMachine'
|
||||
require 'src/Util'
|
||||
|
||||
require 'src/Level'
|
||||
require 'src/Segment'
|
||||
require 'src/Player'
|
||||
require 'src/Ball'
|
||||
|
||||
require 'src/entity/entity_defs'
|
||||
require 'src/entity/Entity'
|
||||
|
||||
require 'src/states/BaseState'
|
||||
require 'src/states/StateStack'
|
||||
|
||||
require 'src/states/entity/EntityBaseState'
|
||||
require 'src/states/entity/EntityIdleState'
|
||||
require 'src/states/entity/EntityWalkState'
|
||||
require 'src/states/entity/EntityWalkEdgeState'
|
||||
|
||||
require 'src/states/game/StartState'
|
||||
require 'src/states/game/FadeInState'
|
||||
require 'src/states/game/FadeOutState'
|
||||
require 'src/states/game/PlayState'
|
||||
require 'src/states/game/GameOverState'
|
||||
|
||||
gTextures = {
|
||||
['entities'] = love.graphics.newImage('graphics/entities.png')
|
||||
}
|
||||
|
||||
gFrames = {
|
||||
['entities'] = GenerateQuads(gTextures['entities'], 16, 16)
|
||||
}
|
||||
|
||||
gFonts = {
|
||||
['small'] = love.graphics.newFont('fonts/font.ttf', 8),
|
||||
['medium'] = love.graphics.newFont('fonts/font.ttf', 16),
|
||||
['large'] = love.graphics.newFont('fonts/font.ttf', 32)
|
||||
}
|
||||
|
||||
gSounds = {
|
||||
['music'] = love.audio.newSource('sounds/music.wav', 'static'),
|
||||
['field-music'] = love.audio.newSource('sounds/field_music.wav', 'static'),
|
||||
['battle-music'] = love.audio.newSource('sounds/battle_music.mp3', 'static'),
|
||||
['blip'] = love.audio.newSource('sounds/blip.wav', 'static'),
|
||||
['powerup'] = love.audio.newSource('sounds/powerup.wav', 'static'),
|
||||
['hit'] = love.audio.newSource('sounds/hit.wav', 'static'),
|
||||
['run'] = love.audio.newSource('sounds/run.wav', 'static'),
|
||||
['heal'] = love.audio.newSource('sounds/heal.wav', 'static'),
|
||||
['exp'] = love.audio.newSource('sounds/exp.wav', 'static'),
|
||||
['levelup'] = love.audio.newSource('sounds/levelup.wav', 'static'),
|
||||
['victory-music'] = love.audio.newSource('sounds/victory.wav', 'static'),
|
||||
['intro-music'] = love.audio.newSource('sounds/intro.mp3', 'static')
|
||||
}
|
||||
728
src/Level.lua
Normal file
728
src/Level.lua
Normal file
@@ -0,0 +1,728 @@
|
||||
|
||||
-- require('mobdebug').start()
|
||||
|
||||
Level = Class{}
|
||||
|
||||
function Level:init()
|
||||
|
||||
self.segments = self:createLevel()
|
||||
self.polygon = self:createPolygon()
|
||||
self.player = {}
|
||||
|
||||
end
|
||||
|
||||
function Level:update(dt)
|
||||
end
|
||||
|
||||
function Level:render()
|
||||
self:renderOuterSegments()
|
||||
end
|
||||
|
||||
function Level:renderOuterSegments()
|
||||
for k,segment in pairs(self.segments) do
|
||||
segment:render()
|
||||
end
|
||||
end
|
||||
|
||||
function Level:createLevel()
|
||||
local level = {
|
||||
[1] = Segment({LEVEL_RENDER_OFFSET,LEVEL_RENDER_OFFSET_TOP},{VIRTUAL_WIDTH - LEVEL_RENDER_OFFSET, LEVEL_RENDER_OFFSET_TOP},.5,'down'),
|
||||
[2] = Segment({VIRTUAL_WIDTH - LEVEL_RENDER_OFFSET, LEVEL_RENDER_OFFSET_TOP},{VIRTUAL_WIDTH - LEVEL_RENDER_OFFSET, VIRTUAL_HEIGHT - LEVEL_RENDER_OFFSET},.5,'left'),
|
||||
[3] = Segment({VIRTUAL_WIDTH - LEVEL_RENDER_OFFSET, VIRTUAL_HEIGHT - LEVEL_RENDER_OFFSET},{LEVEL_RENDER_OFFSET, VIRTUAL_HEIGHT - LEVEL_RENDER_OFFSET},.5,'up'),
|
||||
[4] = Segment({LEVEL_RENDER_OFFSET, VIRTUAL_HEIGHT - LEVEL_RENDER_OFFSET},{LEVEL_RENDER_OFFSET,LEVEL_RENDER_OFFSET_TOP},.5,'right')
|
||||
}
|
||||
-- local level = {
|
||||
-- [1] = Segment({LEVEL_RENDER_OFFSET,LEVEL_RENDER_OFFSET_TOP},{VIRTUAL_WIDTH - LEVEL_RENDER_OFFSET, LEVEL_RENDER_OFFSET_TOP},.5),
|
||||
-- [2] = Segment({VIRTUAL_WIDTH - LEVEL_RENDER_OFFSET, LEVEL_RENDER_OFFSET_TOP},{VIRTUAL_WIDTH - LEVEL_RENDER_OFFSET, VIRTUAL_HEIGHT - LEVEL_RENDER_OFFSET},.5),
|
||||
-- [3] = Segment({VIRTUAL_WIDTH - LEVEL_RENDER_OFFSET, VIRTUAL_HEIGHT - LEVEL_RENDER_OFFSET},{VIRTUAL_WIDTH / 2 + 10, VIRTUAL_HEIGHT - LEVEL_RENDER_OFFSET},.5),
|
||||
-- [4] = Segment({VIRTUAL_WIDTH / 2 + 10, VIRTUAL_HEIGHT - LEVEL_RENDER_OFFSET},{VIRTUAL_WIDTH / 2 + 10, VIRTUAL_HEIGHT / 2},.5),
|
||||
-- [5] = Segment({VIRTUAL_WIDTH / 2 + 10, VIRTUAL_HEIGHT / 2},{LEVEL_RENDER_OFFSET,VIRTUAL_HEIGHT / 2},.5),
|
||||
-- [6] = Segment({LEVEL_RENDER_OFFSET,VIRTUAL_HEIGHT / 2},{LEVEL_RENDER_OFFSET,LEVEL_RENDER_OFFSET_TOP},.5)
|
||||
-- }
|
||||
return level
|
||||
end
|
||||
|
||||
function Level:insideBounds2(x,y)
|
||||
local shape = love.physics.newPolygonShape(self.polygonPoints)
|
||||
return shape:testPoint(x,y)
|
||||
end
|
||||
|
||||
function Level:insideBounds(x,y, segments)
|
||||
if not segments then
|
||||
segments = self.segments
|
||||
end
|
||||
|
||||
local up,down,left,right = false
|
||||
local upSeg, downSeg, leftSeg, rightSeg = nil
|
||||
|
||||
-- find closest segments
|
||||
for i, segment in pairs(segments) do
|
||||
-- check raycast on y axis
|
||||
if segment.vertical then
|
||||
if y <= math.max(segment.firstPointY, segment.secondPointY) and
|
||||
y >= math.min(segment.firstPointY, segment.secondPointY) then
|
||||
if x <= segment.firstPointX then
|
||||
if not rightSeg then
|
||||
rightSeg = segment
|
||||
elseif math.abs(segment.firstPointX - x) < math.abs(rightSeg.firstPointX - x) then
|
||||
rightSeg = segment
|
||||
end
|
||||
else
|
||||
if not leftSeg then
|
||||
leftSeg = segment
|
||||
elseif math.abs(segment.firstPointX - x) < math.abs(leftSeg.firstPointX - x) then
|
||||
leftSeg = segment
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
if segment.horizontal then
|
||||
if x <= math.max(segment.firstPointX, segment.secondPointX) and
|
||||
x >= math.min(segment.firstPointX, segment.secondPointX) then
|
||||
if y <= segment.firstPointY then
|
||||
if not downSeg then
|
||||
downSeg = segment
|
||||
elseif math.abs(segment.firstPointY - y) < math.abs(downSeg.firstPointY - y) then
|
||||
downSeg = segment
|
||||
end
|
||||
else
|
||||
if not upSeg then
|
||||
upSeg = segment
|
||||
elseif math.abs(segment.firstPointY - y) < math.abs(upSeg.firstPointY - y) then
|
||||
upSeg = segment
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
if not rightSeg or not leftSeg or not upSeg or not downSeg then
|
||||
return false
|
||||
end
|
||||
|
||||
if rightSeg.face ~= 'left' or leftSeg.face ~= 'right' or upSeg.face ~= 'down' or downSeg.face ~= 'up' then
|
||||
return false
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
function Level:insideBounds3(x,y)
|
||||
x = math.floor(x)
|
||||
y = math.floor(y)
|
||||
local oddNodes = false
|
||||
local j = #self.polygon
|
||||
local polygon = self.polygon
|
||||
for i = 1, #polygon do
|
||||
if (polygon[i].y < y and polygon[j].y >= y or polygon[j].y < y and polygon[i].y >= y) then
|
||||
if (polygon[i].x + ( y - polygon[i].y ) / (polygon[j].y - polygon[i].y) * (polygon[j].x - polygon[i].x) < x) then
|
||||
oddNodes = not oddNodes
|
||||
end
|
||||
end
|
||||
j = i
|
||||
end
|
||||
return oddNodes
|
||||
end
|
||||
|
||||
function Level:createPolygon()
|
||||
local polygon = {}
|
||||
local polygonPoints = {}
|
||||
local j = 1
|
||||
for i,segment in ipairs(self.segments) do
|
||||
polygon[i] = {}
|
||||
polygon[i+1] = {}
|
||||
polygon[i].x, polygon[i].y, polygon[i+1].x, polygon[i+1].y = segment:segmentToPoints()
|
||||
polygonPoints[j] = polygon[i].x
|
||||
polygonPoints[j+1] = polygon[i].y
|
||||
polygonPoints[j+2] = polygon[i+1].x
|
||||
polygonPoints[j+3] = polygon[i+1].y
|
||||
j = j + 4
|
||||
i = i + 1
|
||||
end
|
||||
self.polygonPoints = polygonPoints
|
||||
return polygon
|
||||
end
|
||||
|
||||
function Level:pointOnEdge(x,y,segments)
|
||||
if not segments then
|
||||
segments = self.segments
|
||||
end
|
||||
local margin = 2
|
||||
for i,segment in pairs(segments) do
|
||||
if segment.vertical then --vertical line
|
||||
if x >= segment.firstPointX - margin and x <= segment.firstPointX + margin and
|
||||
y <= math.max(segment.firstPointY, segment.secondPointY) and
|
||||
y >= math.min(segment.firstPointY, segment.secondPointY)
|
||||
then
|
||||
return true
|
||||
end
|
||||
elseif segment.horizontal then --horizontal line
|
||||
if y >= segment.firstPointY - margin and y <= segment.firstPointY + margin and
|
||||
x <= math.max(segment.firstPointX, segment.secondPointX) and
|
||||
x >= math.min(segment.firstPointX, segment.secondPointX)
|
||||
then
|
||||
return true
|
||||
end
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
function Level:getTouchingSegment(x,y)
|
||||
local margin = 2
|
||||
for i,segment in pairs(self.segments) do
|
||||
if segment.vertical then --vertical line
|
||||
if x >= segment.firstPointX - margin and x <= segment.firstPointX + margin and
|
||||
y <= math.max(segment.firstPointY, segment.secondPointY) and
|
||||
y >= math.min(segment.firstPointY, segment.secondPointY)
|
||||
then
|
||||
return i, segment
|
||||
end
|
||||
elseif segment.horizontal then --horizontal line
|
||||
if y >= segment.firstPointY - margin and y <= segment.firstPointY + margin and
|
||||
x <= math.max(segment.firstPointX, segment.secondPointX) and
|
||||
x >= math.min(segment.firstPointX, segment.secondPointX)
|
||||
then
|
||||
return i, segment
|
||||
end
|
||||
end
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
function Level:cutLevel()
|
||||
|
||||
local newSegs = self.player.trailSegments
|
||||
local startSegi, startSeg = self.player.trailStartSegment[1],self.player.trailStartSegment[2]
|
||||
local endSegi, endSeg = self.player.trailFinishSegment[1],self.player.trailFinishSegment[2]
|
||||
local first = 0
|
||||
local last = 0
|
||||
local firstFace = ''
|
||||
|
||||
--check if it is same start and finish segment
|
||||
if startSegi == endSegi then
|
||||
-- print("cutlevel same start and end segment: "..tostring(startSegi)..' - '..tostring(endSegi))
|
||||
local s = {}
|
||||
-- split the start/end segment
|
||||
local new = {}
|
||||
local split = self.segments[startSegi]:copy()
|
||||
|
||||
newSegs[1]:joinPerpendicular(split)
|
||||
newSegs[#newSegs]:joinPerpendicular(split)
|
||||
-- print("-- joined perpendicular start and end segment from new segments")
|
||||
newSegs[1]:debug()
|
||||
newSegs[#newSegs]:debug()
|
||||
local previous = #self.segments
|
||||
if startSegi > 1 and startSegi < #self.segments then
|
||||
previous = startSegi - 1
|
||||
end
|
||||
local part1, part2, temp = {}
|
||||
local inOrder = false
|
||||
local j,k = 1,1
|
||||
local insertedNewSegs = false
|
||||
-- print("-- Create new segments table")
|
||||
-- create the new set of segments
|
||||
while k <= #self.segments do
|
||||
-- print(" segment to be inserted "..tostring(k))
|
||||
if k == startSegi and not insertedNewSegs then
|
||||
-- print("Reached the segment being cut")
|
||||
-- print_r(self.segments[k])
|
||||
-- print("split the segment")
|
||||
part1, temp, part2 = self.segments[k]:splitInThreeWithSegments(newSegs[1],newSegs[#newSegs])
|
||||
-- print("part1")
|
||||
part1:debug()
|
||||
-- print("part2")
|
||||
part2:debug()
|
||||
|
||||
-- print("insert first part")
|
||||
new[j] = part1
|
||||
-- print_r(new)
|
||||
j = j + 1
|
||||
-- insert new segs
|
||||
-- print("-- insert new segments into new set")
|
||||
for i, segment in pairs(newSegs) do
|
||||
if i + 1 < #newSegs then
|
||||
newSegs[i+1]:joinPerpendicular(segment)
|
||||
end
|
||||
new[j] = segment:copy()
|
||||
j = j + 1
|
||||
end
|
||||
insertedNewSegments = true
|
||||
new[j] = part2
|
||||
j = j + 1
|
||||
-- print_r(new)
|
||||
-- print("-- finished inserting new segments and parts")
|
||||
else
|
||||
new[j] = self.segments[k]:copy()
|
||||
j = j + 1
|
||||
end
|
||||
k = k + 1
|
||||
end
|
||||
|
||||
-- print("order segments in cutlevel same start and end segment")
|
||||
new = self:orderSegments(new)
|
||||
-- print_r(new)
|
||||
-- print("calculate faces")
|
||||
self:calculateSegmentFaces(new,new[1].face)
|
||||
-- print_r(new)
|
||||
self.segments = new
|
||||
else
|
||||
-- create two set of segments, one to one side of the cut, one to the other
|
||||
local board1 = {}
|
||||
local board2 = {}
|
||||
|
||||
-- create first board
|
||||
local k = 1
|
||||
local j = 1
|
||||
local last = #self.segments
|
||||
local insertedNewSegments = false
|
||||
local savedStart,savedEnd = {}
|
||||
local board2StartSegi,board2EndSegi = 0
|
||||
-- create first board
|
||||
while k <= #self.segments do
|
||||
-- print("-----start Loop for "..tostring(k).."-----")
|
||||
if k == startSegi and not insertedNewSegments then
|
||||
board2StartSegi = k
|
||||
-- print("-- this segment is the start segment and not inserted new segments yet")
|
||||
-- print("newsegs[1] is joined perpendicular to k segment:")
|
||||
newSegs[1]:debug()
|
||||
newSegs[1]:joinPerpendicular(self.segments[k])
|
||||
newSegs[1]:debug()
|
||||
-- print("newSegs[1] is joined -- finished")
|
||||
local startPart1, startPart2 = self.segments[k]:splitInTwoWithSegment(newSegs[1])
|
||||
-- print("-- Segment k split into 2 segments by newsegs[1]: ")
|
||||
startPart1:debug()
|
||||
startPart2:debug()
|
||||
|
||||
if self.segments[last]:endEqualsStartOf(self.segments[k]) or
|
||||
self.segments[last]:startEqualsStartOf(self.segments[k]) then -- last one is linked to the start of this one
|
||||
--keep first part of segment
|
||||
-- print("-- keep first part of the k segment")
|
||||
self.segments[k] = startPart1
|
||||
savedStart = startPart2
|
||||
else -- keep second part of the segment (which is the first one touching the last segment)
|
||||
-- print("-- keep second part of the k segment")
|
||||
self.segments[k] = startPart2
|
||||
savedStart = startPart1
|
||||
end
|
||||
|
||||
board1[j] = self.segments[k]
|
||||
j = j + 1
|
||||
|
||||
-- print("-- before checking for inserted new segments - Board1")
|
||||
-- print_r(board1)
|
||||
if not insertedNewSegments then
|
||||
k = endSegi -- skip to last segment to cut and insert it as well
|
||||
last = k - 1
|
||||
board2EndSegi = k
|
||||
newSegs[#newSegs]:joinPerpendicular(self.segments[k])
|
||||
|
||||
-- print("-- not inserted items, proceed to insert in order new segments")
|
||||
for i, newseg in ipairs(newSegs) do
|
||||
board1[j] = newseg:copy()
|
||||
j = j + 1
|
||||
end
|
||||
-- print_r(board1)
|
||||
|
||||
local endPart1, endPart2 = self.segments[k]:splitInTwoWithSegment(newSegs[#newSegs])
|
||||
-- print("-- proceed to insert the second split segment")
|
||||
if self.segments[last]:endEqualsEndOf(self.segments[k]) or
|
||||
self.segments[last]:startEqualsEndOf(self.segments[k]) then -- next one is linked to the start of this one
|
||||
self.segments[k] = endPart1
|
||||
savedEnd = endPart2
|
||||
-- print("-- keep first part of the k segment")
|
||||
else
|
||||
self.segments[k] = endPart2
|
||||
savedEnd = endPart1
|
||||
-- print("-- keep second part of the k segment")
|
||||
end
|
||||
|
||||
board1[j] = self.segments[k]:copy()
|
||||
j = j + 1
|
||||
-- print_r(board1)
|
||||
insertedNewSegments = true
|
||||
end
|
||||
elseif k == endSegi and not insertedNewSegments then
|
||||
board2StartSegi = k
|
||||
-- print("-- this segment is the end segment and not inserted new segments yet")
|
||||
-- print("newsegs[#newSegs] is joined perpendicular to k segment:")
|
||||
newSegs[#newSegs]:debug()
|
||||
newSegs[#newSegs]:joinPerpendicular(self.segments[k])
|
||||
newSegs[#newSegs]:debug()
|
||||
-- print("newSegs[#newSegs] is joined -- finished")
|
||||
local endPart1, endPart2 = self.segments[k]:splitInTwoWithSegment(newSegs[#newSegs])
|
||||
-- print("-- Segment k split into 2 segments by newsegs[#newSegs]: ")
|
||||
endPart1:debug()
|
||||
endPart2:debug()
|
||||
|
||||
if self.segments[last]:endEqualsStartOf(self.segments[k]) or
|
||||
self.segments[last]:startEqualsStartOf(self.segments[k]) then -- last one is linked to the start of this one
|
||||
-- keep first part of segment
|
||||
self.segments[k] = endPart1
|
||||
savedEnd = endPart2
|
||||
else -- keep second part of the segment (which is the first one touching the last segment)
|
||||
self.segments[k] = endPart2
|
||||
savedEnd = endPart1
|
||||
end
|
||||
|
||||
board1[j] = self.segments[k]
|
||||
j = j + 1
|
||||
|
||||
if not insertedNewSegments then
|
||||
k = startSegi -- skip to last segment to cut and insert it as well
|
||||
last = k - 1
|
||||
board2EndSegi = k
|
||||
newSegs[1]:joinPerpendicular(self.segments[k])
|
||||
-- print("-- not inserted items, proceed to insert in reverse order new segments")
|
||||
|
||||
local i = #newSegs
|
||||
while i >= 1 do
|
||||
board1[j] = newSegs[i]:copy()
|
||||
board1[j]:switchDirection()
|
||||
j = j + 1
|
||||
i = i - 1
|
||||
end
|
||||
-- print_r(board1)
|
||||
|
||||
local startPart1, startPart2 = self.segments[k]:splitInTwoWithSegment(newSegs[1])
|
||||
-- print("-- Segment k split into 2 segments by newsegs[1]: ")
|
||||
startPart1:debug()
|
||||
startPart2:debug()
|
||||
|
||||
if self.segments[last]:endEqualsEndOf(self.segments[k]) or
|
||||
self.segments[last]:startEqualsEndOf(self.segments[k]) then -- next one is linked to the start of this one
|
||||
self.segments[k] = startPart1
|
||||
savedstart = startPart2
|
||||
else
|
||||
self.segments[k] = startPart2
|
||||
savedStart= startPart1
|
||||
end
|
||||
|
||||
board1[j] = self.segments[k]:copy()
|
||||
j = j + 1
|
||||
-- print_r(board1)
|
||||
insertedNewSegments = true
|
||||
end
|
||||
|
||||
elseif k ~= startSegi and k ~= endSegi then
|
||||
board1[j] = self.segments[k]:copy()
|
||||
j = j + 1
|
||||
end
|
||||
|
||||
last = k
|
||||
k = k + 1
|
||||
end
|
||||
-- print("order segments in cutlevel board1")
|
||||
board1 = self:orderSegments(board1)
|
||||
self:calculateSegmentFaces(board1,board1[1].face)
|
||||
-- print("PREPARE BOARD 1 -- FINAL")
|
||||
-- print_r(board1)
|
||||
|
||||
-- create second board
|
||||
-- print('---- Create Second board from '..tostring(board2StartSegi)..' to '..tostring(board2EndSegi))
|
||||
j = 1
|
||||
if board2StartSegi < board2EndSegi then
|
||||
board2[j] = savedStart
|
||||
j = j + 1
|
||||
-- print_r(board2)
|
||||
|
||||
-- print('-- inserted first segment, proceed with skipped segments in first board, start < end')
|
||||
k = board2StartSegi + 1
|
||||
|
||||
while k < board2EndSegi do
|
||||
board2[j] = self.segments[k]:copy()
|
||||
k = k + 1
|
||||
j = j + 1
|
||||
end
|
||||
-- print_r(board2)
|
||||
-- print('-- inserted original skipped segments')
|
||||
board2[j] = savedEnd
|
||||
-- print('-- inserted saved end segment')
|
||||
-- print_r(board2)
|
||||
j = j + 1
|
||||
-- insert new segments in order
|
||||
-- print("-- proceed to insert in order new segments")
|
||||
for i, newseg in ipairs(newSegs) do
|
||||
board2[j] = newseg:copy()
|
||||
j = j + 1
|
||||
end
|
||||
-- print_r(board2)
|
||||
|
||||
else
|
||||
-- print('-- inserted first segment, proceed with skipped segments in first board, end < start')
|
||||
board2[j] = savedEnd
|
||||
j = j + 1
|
||||
-- print('inserted saved end')
|
||||
-- print_r(board2)
|
||||
|
||||
k = board2EndSegi + 1
|
||||
-- print('-- insert the skipped segments')
|
||||
while k < board2StartSegi do
|
||||
board2[j] = self.segments[k]:copy()
|
||||
k = k + 1
|
||||
j = j + 1
|
||||
end
|
||||
-- print_r(board2)
|
||||
-- print('insert the saved start segment')
|
||||
-- insert new segments reverse order
|
||||
board2[j] = savedStart
|
||||
j = j + 1
|
||||
-- print_r(board2)
|
||||
-- print('-- insert the new segments in reverse order')
|
||||
local i = #newSegs
|
||||
while i >= 1 do
|
||||
board2[j] = newSegs[i]:copy()
|
||||
board2[j]:switchDirection()
|
||||
j = j + 1
|
||||
i = i - 1
|
||||
end
|
||||
-- print('-- inserted new segments, final board2 before ordering')
|
||||
-- print_r(board2)
|
||||
end
|
||||
|
||||
-- print("order segments in cutlevel board2")
|
||||
board2 = self:orderSegments(board2)
|
||||
self:calculateSegmentFaces(board2,board2[1].face)
|
||||
-- print("PREPARE BOARD 2 -- FINAL")
|
||||
-- print_r(board2)
|
||||
|
||||
if self:containsBalls(board1) == 0 then
|
||||
self.segments = board2
|
||||
elseif self:containsBalls(board2) == 0 then
|
||||
self.segments = board1
|
||||
else
|
||||
self.segments = self:getPerimeter(board1) > self:getPerimeter(board2) and board1 or board2
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
function Level:orderSegments(segs)
|
||||
--returns the list of segments in the level linked to each other in order and perpendicular to each other
|
||||
local new = {}
|
||||
-- -- print('----- order Segments : ')
|
||||
-- print_s(segs)
|
||||
|
||||
new[1] = segs[1]
|
||||
table.remove(segs,1)
|
||||
|
||||
local found = false
|
||||
local k = #segs
|
||||
local i = 1
|
||||
while i <= k do
|
||||
-- -- print('----- segment i = '..tostring(i) .. '#segs '..tostring(#segs))
|
||||
-- print_s(new)
|
||||
-- -- print(' --new search')
|
||||
-- print_s(segs)
|
||||
for j, s in ipairs(segs) do
|
||||
-- -- print("test if j is next segment to i, j = "..tostring(j))
|
||||
if new[i]:endEqualsStartOf(s) then
|
||||
found = true
|
||||
|
||||
-- -- print("new[i] end equals start of ")
|
||||
-- s:debug()
|
||||
if new[i].vertical == s.vertical and new[i].horizontal == s.horizontal then
|
||||
-- -- print("join contiguous "..tostring(i).. 'with'..tostring(j))
|
||||
new[i]:joinContiguous(s)
|
||||
table.remove(segs,j)
|
||||
i = i - 1
|
||||
k = k - 1
|
||||
break
|
||||
else
|
||||
-- -- print("join perpendicular "..tostring(i).. 'with'..tostring(j))
|
||||
s:joinPerpendicular(new[i])
|
||||
new[i+1] = s
|
||||
table.remove(segs,j)
|
||||
break
|
||||
end
|
||||
elseif new[i]:endEqualsEndOf(s) then
|
||||
found = true
|
||||
|
||||
-- -- print("new[i] end equals end of ")
|
||||
-- s:debug()
|
||||
s:switchDirection()
|
||||
if new[i].vertical == s.vertical and new[i].horizontal == s.horizontal then
|
||||
-- -- print("join contiguous "..tostring(i).. 'with'..tostring(j))
|
||||
new[i]:joinContiguous(s)
|
||||
table.remove(segs,j)
|
||||
i = i - 1
|
||||
k = k - 1
|
||||
break
|
||||
else
|
||||
-- -- print("join perpendicular "..tostring(i).. 'with'..tostring(j))
|
||||
s:joinPerpendicular(new[i])
|
||||
new[i+1] = s
|
||||
table.remove(segs,j)
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local fi, fs = 0,{}
|
||||
|
||||
if not found then
|
||||
-- -- print("search didnt yield any segment")
|
||||
local margin = 2
|
||||
while not found and margin < 5 do
|
||||
fi, fs = self:findNearestSegment(new[i].secondPointX,new[i].secondPointY,segs,margin)
|
||||
|
||||
if not fs then
|
||||
fi, fs = self:findNearestSegment(new[i].firstPointX,new[i].firstPointY,segs,margin)
|
||||
|
||||
if not fs then
|
||||
margin = margin + 1
|
||||
else
|
||||
found = true
|
||||
end
|
||||
else
|
||||
found = true
|
||||
end
|
||||
end
|
||||
|
||||
if found then
|
||||
if new[i].vertical == fs.vertical then
|
||||
-- -- print("join contiguous "..tostring(i).. 'with'..tostring(fi))
|
||||
new[i]:joinContiguous(fs)
|
||||
table.remove(segs,fi)
|
||||
i = i - 1
|
||||
k = k - 1
|
||||
else
|
||||
-- -- print("join perpendicular "..tostring(i).. 'with'..tostring(fi))
|
||||
fs:joinPerpendicular(new[i])
|
||||
new[i+1] = fs
|
||||
table.remove(segs,fi)
|
||||
end
|
||||
end
|
||||
end
|
||||
if not found then
|
||||
-- print("ERROR -- order segments does not find next segment to ")
|
||||
new[i]:debug("number: " ..tostring(i))
|
||||
break
|
||||
else
|
||||
found = false
|
||||
end
|
||||
|
||||
i = i + 1
|
||||
end
|
||||
-- -- print("finished ordering: #new " ..tostring(#new))
|
||||
return new
|
||||
end
|
||||
|
||||
function Level:calculateSegmentFaces(segments, firstFace)
|
||||
-- start segment, end segment, list of segments to calculate faces for (when cutting the map)
|
||||
local face = firstFace
|
||||
for k, s in ipairs(segments) do
|
||||
s.face = face
|
||||
if k + 1 <= #segments then
|
||||
if face == 'up' then
|
||||
if s.direction == 'right' then
|
||||
if segments[k+1].direction == 'up' then
|
||||
face = 'left'
|
||||
else
|
||||
face = 'right'
|
||||
end
|
||||
else
|
||||
if segments[k+1].direction == 'up' then
|
||||
face = 'right'
|
||||
else
|
||||
face = 'left'
|
||||
end
|
||||
end
|
||||
elseif face == 'down' then
|
||||
if s.direction == 'right' then
|
||||
if segments[k+1].direction == 'up' then
|
||||
face = 'right'
|
||||
else
|
||||
face = 'left'
|
||||
end
|
||||
else
|
||||
if segments[k+1].direction == 'up' then
|
||||
face = 'left'
|
||||
else
|
||||
face = 'right'
|
||||
end
|
||||
end
|
||||
elseif face == 'right' then
|
||||
if s.direction == 'up' then
|
||||
if segments[k+1].direction == 'right' then
|
||||
face = 'down'
|
||||
else
|
||||
face = 'up'
|
||||
end
|
||||
else
|
||||
if segments[k+1].direction == 'right' then
|
||||
face = 'up'
|
||||
else
|
||||
face = 'down'
|
||||
end
|
||||
end
|
||||
elseif face == 'left' then
|
||||
if s.direction == 'up' then
|
||||
if segments[k+1].direction == 'right' then
|
||||
face = 'up'
|
||||
else
|
||||
face = 'down'
|
||||
end
|
||||
else
|
||||
if segments[k+1].direction == 'right' then
|
||||
face = 'down'
|
||||
else
|
||||
face = 'up'
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function Level:getPerimeter(segments)
|
||||
local p = 0
|
||||
if not segments then
|
||||
segments = self.segments
|
||||
end
|
||||
for k, s in pairs(segments) do
|
||||
p = p + s:length()
|
||||
end
|
||||
return p
|
||||
end
|
||||
|
||||
function Level:findNearestSegment(x,y,segments,margin)
|
||||
if not segments then
|
||||
segments = self.segments
|
||||
end
|
||||
if not margin then
|
||||
margin = 2
|
||||
end
|
||||
-- find a touching segment within margins
|
||||
for i,segment in pairs(segments) do
|
||||
if segment.vertical then --vertical line
|
||||
if x >= segment.firstPointX - margin and x <= segment.firstPointX + margin and
|
||||
y <= math.max(segment.firstPointY, segment.secondPointY) and
|
||||
y >= math.min(segment.firstPointY, segment.secondPointY)
|
||||
then
|
||||
return i, segment
|
||||
end
|
||||
elseif segment.horizontal then --horizontal line
|
||||
if y >= segment.firstPointY - margin and y <= segment.firstPointY + margin and
|
||||
x <= math.max(segment.firstPointX, segment.secondPointX) and
|
||||
x >= math.min(segment.firstPointX, segment.secondPointX)
|
||||
then
|
||||
return i, segment
|
||||
end
|
||||
end
|
||||
end
|
||||
return nil, nil
|
||||
end
|
||||
|
||||
function Level:containsBalls(segments)
|
||||
-- returns numbere of balls inside the bounds passed in or self.segments
|
||||
if not segments then
|
||||
segments = self.segments
|
||||
end
|
||||
local counter = 0
|
||||
|
||||
for k,b in pairs(self.balls) do
|
||||
if self:insideBounds(b.x + 4, b.y + 4,segments) or self:pointOnEdge(b.x+4, b.y+4, seegments) then
|
||||
counter = counter + 1
|
||||
end
|
||||
end
|
||||
return counter
|
||||
end
|
||||
213
src/Player.lua
Normal file
213
src/Player.lua
Normal file
@@ -0,0 +1,213 @@
|
||||
|
||||
-- require('mobdebug').start()
|
||||
|
||||
Player = Class{__includes = Entity}
|
||||
|
||||
function Player:init(def, level )
|
||||
Entity.init(self,def)
|
||||
|
||||
self.x = 1
|
||||
self.y = LEVEL_RENDER_OFFSET_TOP - TILE_SIZE / 2 + 1
|
||||
|
||||
self.trail = {}
|
||||
self.level = level
|
||||
self.trailSegments = {}
|
||||
self.trailStartSegment = {}
|
||||
self.trailFinishSegment = {}
|
||||
self.score = 0
|
||||
self.multiplier = 1
|
||||
|
||||
Event.on('on-edge',function()
|
||||
self:onEdge()
|
||||
-- self:playerDebug("after on-edge event ")
|
||||
end)
|
||||
|
||||
Event.on('walking',function()
|
||||
self:onWalking()
|
||||
-- self:playerDebug("after walking event ")
|
||||
end)
|
||||
|
||||
Event.on('change-direction', function()
|
||||
self:onChangeDirection()
|
||||
-- self:playerDebug("after change-direction event ")
|
||||
end)
|
||||
|
||||
Event.on('back-to-wall', function ()
|
||||
self:onBackToWall()
|
||||
-- self:playerDebug("after back-to-wall event ")
|
||||
end)
|
||||
end
|
||||
|
||||
function Player:changeState(state)
|
||||
Entity.changeState(self,state)
|
||||
end
|
||||
|
||||
function Player:update(dt)
|
||||
Entity.update(self, dt)
|
||||
|
||||
end
|
||||
|
||||
function Player:createAnimations(animations)
|
||||
return Entity.createAnimations(self,animations)
|
||||
end
|
||||
|
||||
function Player:changeAnimation(name)
|
||||
Entity.changeAnimation(self,name)
|
||||
end
|
||||
|
||||
function Player:render()
|
||||
Entity.render(self)
|
||||
self:renderTrailSegments()
|
||||
|
||||
-- love.graphics.setColor(255, 0, 255, 255)
|
||||
-- love.graphics.rectangle('line', self.x, self.y, self.width, self.height)
|
||||
-- love.graphics.setColor(255, 255, 255, 255)
|
||||
end
|
||||
|
||||
function Player:renderTrailSegments()
|
||||
for i,segment in pairs (self.trailSegments) do
|
||||
segment:render()
|
||||
-- print_r(segment)
|
||||
end
|
||||
end
|
||||
|
||||
function Player:insideBounds()
|
||||
return self.level:insideBounds(self.x + TILE_SIZE / 2, self.y + TILE_SIZE / 2)
|
||||
end
|
||||
|
||||
function Player:onEdge()
|
||||
self.trail = {}
|
||||
self.trailSegments = {}
|
||||
self.trailFinishSegment = {}
|
||||
self.trailStartSegment = {}
|
||||
self.trailStartSegment[1], self.trailStartSegment[2] = self.level:getTouchingSegment(self.x + TILE_SIZE / 2, self.y + TILE_SIZE / 2)
|
||||
table.insert(self.trail,
|
||||
{['x'] = self.x + self.width/2,['y'] = self.y + self.height/2}
|
||||
)
|
||||
|
||||
end
|
||||
|
||||
function Player:onWalking()
|
||||
if #self.trail > 1 then
|
||||
table.remove(self.trail)
|
||||
table.remove(self.trailSegments)
|
||||
end
|
||||
table.insert(self.trail,
|
||||
{['x'] = self.x + self.width/2,['y'] = self.y + self.height/2}
|
||||
)
|
||||
if #self.trail == 2 then
|
||||
table.insert(self.trailSegments,
|
||||
Segment(
|
||||
{self.trail[1].x, self.trail[1].y},
|
||||
{self.trail[2].x, self.trail[2].y},
|
||||
.5,'',{r=250,g=150,b=150}
|
||||
)
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
function Player:onChangeDirection()
|
||||
if self:insideBounds() then
|
||||
if #self.trail > 1 then
|
||||
table.remove(self.trail)
|
||||
table.remove(self.trailSegments)
|
||||
end
|
||||
|
||||
local k = #self.trailSegments
|
||||
local j = #self.trail + 1
|
||||
|
||||
table.insert(self.trail,
|
||||
{['x'] = self.x + self.width/2,['y'] = self.y + self.height/2}
|
||||
)
|
||||
if #self.trail == 2 then
|
||||
|
||||
table.insert(self.trailSegments,
|
||||
Segment(
|
||||
{self.trail[1].x, self.trail[1].y},
|
||||
{self.trail[2].x, self.trail[2].y},
|
||||
.5,'',{r=250,g=150,b=150}
|
||||
)
|
||||
)
|
||||
end
|
||||
self.trail = {}
|
||||
table.insert(self.trail,
|
||||
{['x'] = self.x + self.width/2,['y'] = self.y + self.height/2}
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
function Player:onBackToWall()
|
||||
if self:onTheEdge() and #self.trail > 0 then
|
||||
self.trailFinishSegment[1], self.trailFinishSegment[2] = self.level:getTouchingSegment(self.x + TILE_SIZE / 2, self.y + TILE_SIZE / 2)
|
||||
local k = #self.trailSegments
|
||||
local j = #self.trail + 1
|
||||
if #self.trail > 1 then
|
||||
table.remove(self.trail)
|
||||
end
|
||||
table.insert(self.trail,
|
||||
{['x'] = self.x + self.width/2,['y'] = self.y + self.height/2}
|
||||
)
|
||||
if #self.trail == 2 then
|
||||
table.remove(self.trailSegments)
|
||||
table.insert(self.trailSegments,
|
||||
Segment(
|
||||
{self.trail[1].x, self.trail[1].y},
|
||||
{self.trail[2].x, self.trail[2].y},
|
||||
.5,'',{r=250,g=150,b=150}
|
||||
)
|
||||
)
|
||||
end
|
||||
self:resetTrail()
|
||||
end
|
||||
end
|
||||
|
||||
function Player:reset()
|
||||
self.x = 1
|
||||
self.y = LEVEL_RENDER_OFFSET_TOP - TILE_SIZE / 2 + 1
|
||||
self.trail = {}
|
||||
self.trailSegments = {}
|
||||
self.trailStartSegment = {}
|
||||
self.trailFinishSegment = {}
|
||||
end
|
||||
|
||||
function Player:resetTrail()
|
||||
self:playerDebug('Player:resetTrail')
|
||||
if #self.trailStartSegment > 0 and #self.trailFinishSegment > 0 then
|
||||
self.level:cutLevel()
|
||||
end
|
||||
self.trail = {}
|
||||
self.trailStartSegment = {}
|
||||
self.trailFinishSegment = {}
|
||||
self.trailSegments = {}
|
||||
end
|
||||
|
||||
function Player:onTheEdge()
|
||||
return self.level:pointOnEdge(self.x + TILE_SIZE / 2, self.y + TILE_SIZE / 2)
|
||||
end
|
||||
|
||||
function Player:playerDebug(msg)
|
||||
-- print('------------------------------------')
|
||||
if msg then
|
||||
-- print(msg)
|
||||
end
|
||||
-- print("Player Position x,y = " ..tostring(self.x)..','..tostring(self.y))
|
||||
-- print("Player Center x,y = " ..tostring(self.x+8)..','..tostring(self.y+8))
|
||||
-- print("Player direction : "..self.direction .. ' previous: '..self.previousDirection)
|
||||
-- print('......................')
|
||||
-- print("Player trail points: ")
|
||||
-- print_r(self.trail)
|
||||
-- print('......................')
|
||||
-- print("Player segments: ")
|
||||
-- print_s(self.trailSegments)
|
||||
-- print('......................')
|
||||
-- print("Trail Start")
|
||||
if self.trailStartSegment[2] ~= nil then
|
||||
self.trailStartSegment[2]:debug(tostring(self.trailStartSegment[1]))
|
||||
end
|
||||
-- print('......................')
|
||||
-- print("Trail Finish")
|
||||
if self.trailFinishSegment[2] ~= nil then
|
||||
self.trailFinishSegment[2]:debug(tostring(self.trailFinishSegment[1]))
|
||||
end
|
||||
-- print('------------------------------------')
|
||||
end
|
||||
294
src/Segment.lua
Normal file
294
src/Segment.lua
Normal file
@@ -0,0 +1,294 @@
|
||||
-- require('mobdebug').start()
|
||||
|
||||
Segment = Class{}
|
||||
|
||||
local symbols = {['up'] = 207, ['down'] = 209, ['right'] = 199, ['left'] = 182}
|
||||
|
||||
function Segment:init(firstPoint, secondPoint,lineWidth, face, color)
|
||||
self.firstPointX = firstPoint[1]
|
||||
self.firstPointY = firstPoint[2]
|
||||
self.secondPointX = secondPoint[1]
|
||||
self.secondPointY = secondPoint[2]
|
||||
self.lineWidth = lineWidth or 3
|
||||
self.vertical = self.firstPointX == self.secondPointX
|
||||
self.horizontal = self.firstPointY == self.secondPointY
|
||||
self.face = face or ''
|
||||
self.direction = self:calculateDirection()
|
||||
self.color = color or {r=255,g=255,b=255}
|
||||
end
|
||||
|
||||
function Segment:render()
|
||||
love.graphics.setColor(self.color.r,self.color.g, self.color.b,255)
|
||||
love.graphics.setLineWidth(self.lineWidth)
|
||||
love.graphics.line(self.firstPointX,self.firstPointY,self.secondPointX, self.secondPointY)
|
||||
|
||||
love.graphics.setColor(255,255, 255, 255)
|
||||
|
||||
-- debug
|
||||
-- love.graphics.setFont(gFonts['small'])
|
||||
-- love.graphics.printf(self.face,self.firstPointX + 2, self.firstPointY + 2 / 2,25,'left' )
|
||||
|
||||
end
|
||||
|
||||
function Segment:segmentToPoints()
|
||||
return self.firstPointX, self.firstPointY, self.secondPointX, self.secondPointY
|
||||
end
|
||||
|
||||
function Segment:length()
|
||||
return math.sqrt( (self.secondPointX - self.firstPointX) * (self.secondPointX - self.firstPointX) + (self.secondPointY - self.firstPointY) * (self.secondPointY - self.firstPointY) )
|
||||
end
|
||||
|
||||
function Segment:sideOfSegment(x,y)
|
||||
if y < self.firstPointY and y < self.secondPointY then -- above
|
||||
return 'up'
|
||||
end
|
||||
if y > self.firstPointY and y > self.secondPointY then -- below
|
||||
return 'down'
|
||||
end
|
||||
if x < self.firstPointX and x < self.secondPointX then -- left
|
||||
return 'left'
|
||||
end
|
||||
if x > self.firstPointX and x > self.secondPointX then -- right
|
||||
return 'right'
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
function Segment:debug(msg)
|
||||
if not msg then
|
||||
msg = ''
|
||||
end
|
||||
-- print("Segment:debug ".. msg .." ...............")
|
||||
-- print("FirstPoint: "..tostring(self.firstPointX)..','..tostring(self.firstPointY))
|
||||
-- print("SecondPoint: "..tostring(self.secondPointX)..','..tostring(self.secondPointY))
|
||||
-- print("face: "..self.face.. ' - Direction: '..self.direction)
|
||||
end
|
||||
|
||||
function Segment:copy()
|
||||
local copy = {}
|
||||
copy.firstPointX = self.firstPointX
|
||||
copy.firstPointY = self.firstPointY
|
||||
copy.secondPointX = self.secondPointX
|
||||
copy.secondPointY = self.secondPointY
|
||||
copy.lineWidth = self.lineWidth
|
||||
copy.face = self.face
|
||||
return Segment({copy.firstPointX,copy.firstPointY},{copy.secondPointX,copy.secondPointY},copy.lineWidth, copy.face)
|
||||
end
|
||||
|
||||
function Segment:endEqualsStartOf(s)
|
||||
-- segments are linked at the end of this one
|
||||
if self.secondPointX == s.firstPointX and
|
||||
self.secondPointY == s.firstPointY then
|
||||
return true
|
||||
end
|
||||
return false
|
||||
end
|
||||
function Segment:endEqualsEndOf(s)
|
||||
-- segments are linked at the end of this one
|
||||
if self.secondPointX == s.secondPointX and self.secondPointY == s.secondPointY then
|
||||
return true
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
function Segment:startEqualsEndOf(s)
|
||||
-- segments are linked at the beginning of this one
|
||||
if self.firstPointX == s.secondPointX and self.firstPointY == s.secondPointY then
|
||||
return true
|
||||
end
|
||||
return false
|
||||
end
|
||||
function Segment:startEqualsStartOf(s)
|
||||
-- segments are linked at the beginning of this one
|
||||
if self.firstPointX == s.firstPointX and self.firstPointY == s.PointY then
|
||||
return true
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
function Segment:calculateDirection()
|
||||
local dir = ''
|
||||
|
||||
if self.vertical then
|
||||
if self.firstPointY < self.secondPointY then
|
||||
dir = 'down'
|
||||
else
|
||||
dir = 'up'
|
||||
end
|
||||
else
|
||||
if self.firstPointX < self.secondPointX then
|
||||
dir = 'right'
|
||||
else
|
||||
dir = 'left'
|
||||
end
|
||||
end
|
||||
return dir
|
||||
end
|
||||
|
||||
function Segment:splitInTwoWithPoint(point) -- number
|
||||
local s1, s2 = {}
|
||||
if point > 0 then
|
||||
if self.vertical then
|
||||
s1 = Segment(
|
||||
{self.firstPointX, self.firstPointY},
|
||||
{self.secondPointX, point},
|
||||
self.lineWidth, self.face
|
||||
)
|
||||
s2 = Segment(
|
||||
{self.firstPointX, point},
|
||||
{self.secondPointX,self.secondPointY},
|
||||
self.lineWidth, self.face
|
||||
)
|
||||
else
|
||||
s1 = Segment(
|
||||
{self.firstPointX, self.firstPointY},
|
||||
{point, self.secondPointY},
|
||||
self.lineWidth, self.face
|
||||
)
|
||||
s2 = Segment(
|
||||
{point, self.firstPointY},
|
||||
{self.secondPointX,self.secondPointY},
|
||||
self.lineWidth, self.face
|
||||
)
|
||||
end
|
||||
else
|
||||
return nil, nil
|
||||
end
|
||||
|
||||
return s1, s2
|
||||
end
|
||||
|
||||
function Segment:splitInTwoWithSegment(segment)
|
||||
local point = 0
|
||||
if self.vertical then
|
||||
if self.firstPointY == segment.firstPointY then
|
||||
point = segment.firstPointY
|
||||
else
|
||||
point = segment.secondPointY
|
||||
end
|
||||
elseif self.horizontal then
|
||||
if self.firstPointX == segment.firstPointX then
|
||||
point = segment.firstPointX
|
||||
else
|
||||
point = segment.secondPointX
|
||||
end
|
||||
end
|
||||
|
||||
return self:splitInTwoWithPoint(point)
|
||||
end
|
||||
|
||||
function Segment:joinPerpendicular(s)
|
||||
-- changes the self, not the parameter
|
||||
if s.vertical then
|
||||
local sx = s.firstPointX
|
||||
if math.abs(self.firstPointX - sx) <
|
||||
math.abs(self.secondPointX - sx) then
|
||||
self.firstPointX = sx
|
||||
else
|
||||
self.secondPointX = sx
|
||||
end
|
||||
else
|
||||
local sy = s.firstPointY
|
||||
if math.abs(self.firstPointY - sy) <
|
||||
math.abs(self.secondPointY - sy) then
|
||||
self.firstPointY = sy
|
||||
else
|
||||
self.secondPointY = sy
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function Segment:switchDirection()
|
||||
local temp = {}
|
||||
temp.firstPointX = self.firstPointX
|
||||
temp.firstPointY = self.firstPointY
|
||||
self.firstPointX = self.secondPointX
|
||||
self.firstPointY = self.secondPointY
|
||||
self.secondPointX = temp.firstPointX
|
||||
self.secondPointY = temp.firstPointY
|
||||
self:notDirection()
|
||||
end
|
||||
|
||||
function Segment:notDirection()
|
||||
if self.direction == 'down' then
|
||||
self.direction = 'up'
|
||||
elseif self.direction == 'up' then
|
||||
self.direction = 'down'
|
||||
elseif self.direction == 'right' then
|
||||
self.direction = 'left'
|
||||
elseif self.direction == 'left' then
|
||||
self.direction = 'right'
|
||||
end
|
||||
end
|
||||
|
||||
function Segment:notFace()
|
||||
if self.face == 'up' then
|
||||
self.face = 'down'
|
||||
elseif self.face == 'down' then
|
||||
self.face = 'up'
|
||||
elseif self.face == 'right' then
|
||||
self.face = 'left'
|
||||
elseif self.face == 'left' then
|
||||
self.face = 'right'
|
||||
end
|
||||
end
|
||||
|
||||
function Segment:joinContiguous(s)
|
||||
-- second segment becomes part of first one
|
||||
if self.vertical then
|
||||
self.secondPointY = s.secondPointY
|
||||
else
|
||||
self.secondPointX = s.secondPointX
|
||||
end
|
||||
end
|
||||
|
||||
function Segment:compare(s)
|
||||
-- returns >0 if self is to the right or below
|
||||
-- returns <0 if self is to the left or above
|
||||
-- returns 0 if both are same point
|
||||
if self.vertical and s.vertical then
|
||||
if self.firstPointX == s.firstPointX then
|
||||
return 0
|
||||
else
|
||||
return self.firstPointX - s.firstPointX
|
||||
end
|
||||
elseif self.horizontal and s.horizontal then
|
||||
if self.firstPointY == s.firstPointY then
|
||||
return 0
|
||||
else
|
||||
return self.firstPointY - s.firstPointY
|
||||
end
|
||||
else
|
||||
return (self.firstPointX + self.firstPointY +
|
||||
self.secondPointX + self.secondPointY) - (
|
||||
s.firstPointX + s.firstPointY + s.secondPointX
|
||||
+s.secondPointY)
|
||||
end
|
||||
end
|
||||
|
||||
function Segment:splitInThreeWithSegments(s1,s2)
|
||||
local p1, p2, p3, t = {}
|
||||
if self.direction == 'down' or self.direction == 'right' then
|
||||
if s1:compare(s2) < 0 then
|
||||
p1, t = self:splitInTwoWithSegment(s1)
|
||||
p2, p3 = t:splitInTwoWithSegment(s2)
|
||||
else
|
||||
p1, t = self:splitInTwoWithSegment(s2)
|
||||
p2, p3 = t:splitInTwoWithSegment(s1)
|
||||
end
|
||||
else
|
||||
if s1:compare(s2) < 0 then
|
||||
p1, t = self:splitInTwoWithSegment(s2)
|
||||
p2, p3 = t:splitInTwoWithSegment(s1)
|
||||
else
|
||||
p1, t = self:splitInTwoWithSegment(s1)
|
||||
p2, p3 = t:splitInTwoWithSegment(s2)
|
||||
end
|
||||
end
|
||||
|
||||
return p1,p2,p3
|
||||
end
|
||||
|
||||
function Segment:insert(list)
|
||||
|
||||
end
|
||||
37
src/StateMachine.lua
Normal file
37
src/StateMachine.lua
Normal file
@@ -0,0 +1,37 @@
|
||||
-- require('mobdebug').start()
|
||||
|
||||
StateMachine = Class{}
|
||||
|
||||
function StateMachine:init(states)
|
||||
self.empty = {
|
||||
render = function() end,
|
||||
update = function() end,
|
||||
processAI = function() end,
|
||||
enter = function() end,
|
||||
exit = function() end
|
||||
}
|
||||
self.states = states or {} -- [name] -> [function that returns states]
|
||||
self.current = self.empty
|
||||
end
|
||||
|
||||
function StateMachine:change(stateName, enterParams)
|
||||
assert(self.states[stateName]) -- state must exist!
|
||||
self.current:exit()
|
||||
self.current = self.states[stateName]()
|
||||
self.current:enter(enterParams)
|
||||
end
|
||||
|
||||
function StateMachine:update(dt)
|
||||
self.current:update(dt)
|
||||
end
|
||||
|
||||
function StateMachine:render()
|
||||
self.current:render()
|
||||
end
|
||||
|
||||
--[[
|
||||
Used for states that can be controlled by the AI to influence update logic.
|
||||
]]
|
||||
function StateMachine:processAI(params, dt)
|
||||
self.current:processAI(params, dt)
|
||||
end
|
||||
73
src/Util.lua
Normal file
73
src/Util.lua
Normal file
@@ -0,0 +1,73 @@
|
||||
|
||||
-- require('mobdebug').start()
|
||||
--[[
|
||||
Given an "atlas" (a texture with multiple sprites), as well as a
|
||||
width and a height for the tiles therein, split the texture into
|
||||
all of the quads by simply dividing it evenly.
|
||||
]]
|
||||
function GenerateQuads(atlas, tilewidth, tileheight)
|
||||
local sheetWidth = atlas:getWidth() / tilewidth
|
||||
local sheetHeight = atlas:getHeight() / tileheight
|
||||
|
||||
local sheetCounter = 1
|
||||
local spritesheet = {}
|
||||
|
||||
for y = 0, sheetHeight - 1 do
|
||||
for x = 0, sheetWidth - 1 do
|
||||
spritesheet[sheetCounter] =
|
||||
love.graphics.newQuad(x * tilewidth, y * tileheight, tilewidth,
|
||||
tileheight, atlas:getDimensions())
|
||||
sheetCounter = sheetCounter + 1
|
||||
end
|
||||
end
|
||||
|
||||
return spritesheet
|
||||
end
|
||||
|
||||
--[[
|
||||
Recursive table printing function.
|
||||
https://coronalabs.com/blog/2014/09/02/tutorial-printing-table-contents/
|
||||
]]
|
||||
function print_r ( t )
|
||||
local print_r_cache={}
|
||||
local function sub_print_r(t,indent)
|
||||
if (print_r_cache[tostring(t)]) then
|
||||
print(indent.."*"..tostring(t))
|
||||
else
|
||||
print_r_cache[tostring(t)]=true
|
||||
if (type(t)=="table") then
|
||||
for pos,val in pairs(t) do
|
||||
if (type(val)=="table") then
|
||||
print(indent.."["..pos.."] => "..tostring(t).." {")
|
||||
sub_print_r(val,indent..string.rep(" ",string.len(pos)+8))
|
||||
print(indent..string.rep(" ",string.len(pos)+6).."}")
|
||||
elseif (type(val)=="string") then
|
||||
print(indent.."["..pos..'] => "'..val..'"')
|
||||
else
|
||||
print(indent.."["..pos.."] => "..tostring(val))
|
||||
end
|
||||
end
|
||||
else
|
||||
print(indent..tostring(t))
|
||||
end
|
||||
end
|
||||
end
|
||||
if (type(t)=="table") then
|
||||
print(tostring(t).." {")
|
||||
sub_print_r(t," ")
|
||||
print("}")
|
||||
else
|
||||
sub_print_r(t," ")
|
||||
end
|
||||
print()
|
||||
end
|
||||
|
||||
function math.round(x,precision)
|
||||
return x + precision - (x + precision) % 1
|
||||
end
|
||||
|
||||
function print_s(segments)
|
||||
for k, s in ipairs(segments) do
|
||||
s:debug(tostring(k))
|
||||
end
|
||||
end
|
||||
12
src/constants.lua
Normal file
12
src/constants.lua
Normal file
@@ -0,0 +1,12 @@
|
||||
-- require('mobdebug').start()
|
||||
|
||||
VIRTUAL_WIDTH = 384
|
||||
VIRTUAL_HEIGHT = 216
|
||||
|
||||
WINDOW_WIDTH = 1280
|
||||
WINDOW_HEIGHT = 720
|
||||
|
||||
LEVEL_RENDER_OFFSET_TOP = 30
|
||||
LEVEL_RENDER_OFFSET = 10
|
||||
|
||||
TILE_SIZE = 16
|
||||
57
src/entity/Entity.lua
Normal file
57
src/entity/Entity.lua
Normal file
@@ -0,0 +1,57 @@
|
||||
|
||||
Entity = Class{}
|
||||
|
||||
function Entity:init(def)
|
||||
self.direction = 'down'
|
||||
self.previousDirection = ''
|
||||
self.walkingSpeed = def.walkingSpeed
|
||||
self.animations = self:createAnimations(def.animations)
|
||||
|
||||
self.currentAnimation = self.animations['idle-down']
|
||||
|
||||
self.stateMachine = StateMachine {
|
||||
['walk'] = function() return EntityWalkState(self) end,
|
||||
['edge'] = function() return EntityWalkEdgeState(self) end,
|
||||
['idle'] = function() return EntityIdleState(self) end
|
||||
}
|
||||
self.stateMachine:change('idle')
|
||||
self.width = def.width
|
||||
self.height = def.height
|
||||
|
||||
end
|
||||
|
||||
function Entity:changeState(name)
|
||||
self.stateMachine:change(name)
|
||||
-- printChangeState to "..name)
|
||||
end
|
||||
|
||||
function Entity:changeAnimation(name)
|
||||
self.currentAnimation = self.animations[name]
|
||||
end
|
||||
|
||||
function Entity:createAnimations(animations)
|
||||
local animationsReturned = {}
|
||||
|
||||
for k, animationDef in pairs(animations) do
|
||||
animationsReturned[k] = Animation {
|
||||
texture = animationDef.texture or 'entities',
|
||||
frames = animationDef.frames,
|
||||
interval = animationDef.interval
|
||||
}
|
||||
end
|
||||
|
||||
return animationsReturned
|
||||
end
|
||||
|
||||
function Entity:processAI(params, dt)
|
||||
self.stateMachine:processAI(params, dt)
|
||||
end
|
||||
|
||||
function Entity:update(dt)
|
||||
self.currentAnimation:update(dt)
|
||||
self.stateMachine:update(dt)
|
||||
end
|
||||
|
||||
function Entity:render()
|
||||
self.stateMachine:render()
|
||||
end
|
||||
47
src/entity/entity_defs.lua
Normal file
47
src/entity/entity_defs.lua
Normal file
@@ -0,0 +1,47 @@
|
||||
|
||||
|
||||
ENTITY_DEFS = {
|
||||
['player'] = {
|
||||
width = 16,
|
||||
height = 16,
|
||||
walkingSpeed = 100,
|
||||
animations = {
|
||||
['walk-left'] = {
|
||||
frames = {67, 68, 69},
|
||||
interval = 0.15,
|
||||
texture = 'entities'
|
||||
},
|
||||
['walk-right'] = {
|
||||
frames = {79, 80, 81},
|
||||
interval = 0.15,
|
||||
texture = 'entities'
|
||||
},
|
||||
['walk-down'] = {
|
||||
frames = {55, 56, 57},
|
||||
interval = 0.15,
|
||||
texture = 'entities'
|
||||
},
|
||||
['walk-up'] = {
|
||||
frames = {91, 92, 93},
|
||||
interval = 0.15,
|
||||
texture = 'entities'
|
||||
},
|
||||
['idle-left'] = {
|
||||
frames = {67},
|
||||
texture = 'entities'
|
||||
},
|
||||
['idle-right'] = {
|
||||
frames = {79},
|
||||
texture = 'entities'
|
||||
},
|
||||
['idle-down'] = {
|
||||
frames = {55},
|
||||
texture = 'entities'
|
||||
},
|
||||
['idle-up'] = {
|
||||
frames = {91},
|
||||
texture = 'entities'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
9
src/states/BaseState.lua
Normal file
9
src/states/BaseState.lua
Normal file
@@ -0,0 +1,9 @@
|
||||
-- require('mobdebug').start()
|
||||
|
||||
BaseState = Class{}
|
||||
|
||||
function BaseState:init() end
|
||||
function BaseState:enter() end
|
||||
function BaseState:exit() end
|
||||
function BaseState:update(dt) end
|
||||
function BaseState:render() end
|
||||
36
src/states/StateStack.lua
Normal file
36
src/states/StateStack.lua
Normal file
@@ -0,0 +1,36 @@
|
||||
-- require('mobdebug').start()
|
||||
|
||||
StateStack = Class{}
|
||||
|
||||
function StateStack:init()
|
||||
self.states = {}
|
||||
end
|
||||
|
||||
function StateStack:update(dt)
|
||||
self.states[#self.states]:update(dt)
|
||||
end
|
||||
|
||||
function StateStack:processAI(params, dt)
|
||||
self.states[#self.states]:processAI(params, dt)
|
||||
end
|
||||
|
||||
function StateStack:render()
|
||||
for i, state in ipairs(self.states) do
|
||||
state:render()
|
||||
end
|
||||
end
|
||||
|
||||
function StateStack:clear()
|
||||
self.states = {}
|
||||
end
|
||||
|
||||
function StateStack:push(state)
|
||||
table.insert(self.states, state)
|
||||
state:enter()
|
||||
end
|
||||
|
||||
function StateStack:pop()
|
||||
-- print("pop state: " .. self.states[#self.states].name)
|
||||
self.states[#self.states]:exit()
|
||||
table.remove(self.states)
|
||||
end
|
||||
18
src/states/entity/EntityBaseState.lua
Normal file
18
src/states/entity/EntityBaseState.lua
Normal file
@@ -0,0 +1,18 @@
|
||||
-- require('mobdebug').start()
|
||||
|
||||
EntityBaseState = Class{}
|
||||
|
||||
function EntityBaseState:init(entity)
|
||||
self.entity = entity
|
||||
end
|
||||
|
||||
function EntityBaseState:update(dt) end
|
||||
function EntityBaseState:enter() end
|
||||
function EntityBaseState:exit() end
|
||||
function EntityBaseState:processAI(params, dt) end
|
||||
|
||||
function EntityBaseState:render()
|
||||
local anim = self.entity.currentAnimation
|
||||
love.graphics.draw(gTextures[anim.texture], gFrames[anim.texture][anim:getCurrentFrame()],
|
||||
math.floor(self.entity.x), math.floor(self.entity.y))
|
||||
end
|
||||
32
src/states/entity/EntityIdleState.lua
Normal file
32
src/states/entity/EntityIdleState.lua
Normal file
@@ -0,0 +1,32 @@
|
||||
-- require('mobdebug').start()
|
||||
|
||||
EntityIdleState = Class{__includes = BaseState}
|
||||
|
||||
function EntityIdleState:init(entity)
|
||||
self.entity = entity
|
||||
|
||||
self.entity:changeAnimation('idle-' .. self.entity.direction)
|
||||
|
||||
end
|
||||
|
||||
function EntityIdleState:update(dt)
|
||||
if love.keyboard.isDown('left') or love.keyboard.isDown('right') or
|
||||
love.keyboard.isDown('up') or love.keyboard.isDown('down') then
|
||||
if not self.entity:onTheEdge() then
|
||||
Event.dispatch('walking')
|
||||
self.entity:changeState('walk')
|
||||
else
|
||||
Event.dispatch('on-edge')
|
||||
self.entity:changeState('edge')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function EntityIdleState:render()
|
||||
local anim = self.entity.currentAnimation
|
||||
love.graphics.draw(gTextures[anim.texture], gFrames[anim.texture][anim:getCurrentFrame()],self.entity.x, self.entity.y)
|
||||
|
||||
-- love.graphics.setColor(255, 0, 255, 255)
|
||||
-- love.graphics.rectangle('line', self.entity.x, self.entity.y, self.entity.width, self.entity.height)
|
||||
-- love.graphics.setColor(255, 255, 255, 255)
|
||||
end
|
||||
100
src/states/entity/EntityWalkEdgeState.lua
Normal file
100
src/states/entity/EntityWalkEdgeState.lua
Normal file
@@ -0,0 +1,100 @@
|
||||
-- require('mobdebug').start()
|
||||
|
||||
|
||||
EntityWalkEdgeState = Class{__includes = BaseState}
|
||||
|
||||
function EntityWalkEdgeState:init(entity)
|
||||
self.entity = entity
|
||||
self.entity:changeAnimation('walk-'..self.entity.direction)
|
||||
end
|
||||
|
||||
function EntityWalkEdgeState:update(dt)
|
||||
|
||||
local dy = self.entity.walkingSpeed * dt
|
||||
local dx = self.entity.walkingSpeed * dt
|
||||
|
||||
if self.entity.direction == 'left' then
|
||||
if self.entity.level:pointOnEdge(self.entity.x - dx + TILE_SIZE / 2, self.entity.y + TILE_SIZE / 2) then
|
||||
self.entity.x = self.entity.x - dx
|
||||
Event.dispatch('on-edge')
|
||||
else
|
||||
if self.entity.level:insideBounds(self.entity.x + TILE_SIZE / 2 - dx, self.entity.y + TILE_SIZE / 2) then
|
||||
self.entity:changeState('walk')
|
||||
Event.dispatch('walking')
|
||||
self.entity.x = self.entity.x - dx
|
||||
end
|
||||
end
|
||||
elseif self.entity.direction == 'right' then
|
||||
if self.entity.level:pointOnEdge(self.entity.x + dx + TILE_SIZE / 2, self.entity.y + TILE_SIZE / 2) then
|
||||
self.entity.x = self.entity.x + dx
|
||||
Event.dispatch('on-edge')
|
||||
else
|
||||
if self.entity.level:insideBounds(self.entity.x + TILE_SIZE / 2 + dx, self.entity.y + TILE_SIZE / 2) then
|
||||
self.entity:changeState('walk')
|
||||
Event.dispatch('walking')
|
||||
self.entity.x = self.entity.x + dx
|
||||
end
|
||||
end
|
||||
elseif self.entity.direction == 'up' then
|
||||
if self.entity.level:pointOnEdge(self.entity.x + TILE_SIZE / 2, self.entity.y - dy + TILE_SIZE / 2) then
|
||||
self.entity.y = self.entity.y - dy
|
||||
Event.dispatch('on-edge')
|
||||
else
|
||||
if self.entity.level:insideBounds(self.entity.x + TILE_SIZE / 2, self.entity.y + TILE_SIZE / 2 - dy) then
|
||||
self.entity:changeState('walk')
|
||||
Event.dispatch('walking')
|
||||
self.entity.y = self.entity.y - dy
|
||||
end
|
||||
end
|
||||
elseif self.entity.direction == 'down' then
|
||||
if self.entity.level:pointOnEdge(self.entity.x + TILE_SIZE / 2, self.entity.y + dy + TILE_SIZE / 2) then
|
||||
self.entity.y = self.entity.y + dy
|
||||
Event.dispatch('on-edge')
|
||||
else
|
||||
if self.entity.level:insideBounds(self.entity.x + TILE_SIZE / 2, self.entity.y + TILE_SIZE / 2 + dy) then
|
||||
self.entity:changeState('walk')
|
||||
Event.dispatch('walking')
|
||||
self.entity.y = self.entity.y + dy
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if love.keyboard.isDown('left') then
|
||||
if self.entity.direction ~= 'left' then
|
||||
self.entity.previousDirection = self.entity.direction
|
||||
end
|
||||
self.entity.direction = 'left'
|
||||
self.entity:changeAnimation('walk-left')
|
||||
elseif love.keyboard.isDown('right') then
|
||||
if self.entity.direction ~= 'right' then
|
||||
self.entity.previousDirection = self.entity.direction
|
||||
end
|
||||
self.entity.direction = 'right'
|
||||
self.entity:changeAnimation('walk-right')
|
||||
elseif love.keyboard.isDown('up') then
|
||||
if self.entity.direction ~= 'up' then
|
||||
self.entity.previousDirection = self.entity.direction
|
||||
end
|
||||
self.entity.direction = 'up'
|
||||
self.entity:changeAnimation('walk-up')
|
||||
elseif love.keyboard.isDown('down') then
|
||||
if self.entity.direction ~= 'down' then
|
||||
self.entity.previousDirection = self.entity.direction
|
||||
end
|
||||
self.entity.direction = 'down'
|
||||
self.entity:changeAnimation('walk-down')
|
||||
else
|
||||
self.entity:changeState('idle')
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
|
||||
function EntityWalkEdgeState:render()
|
||||
local anim = self.entity.currentAnimation
|
||||
love.graphics.draw(gTextures[anim.texture], gFrames[anim.texture][anim:getCurrentFrame()],self.entity.x, self.entity.y)
|
||||
|
||||
-- love.graphics.setColor(255, 0, 255, 255)
|
||||
-- love.graphics.rectangle('line', self.entity.x, self.entity.y, self.entity.width, self.entity.height)
|
||||
-- love.graphics.setColor(255, 255, 255, 255)
|
||||
end
|
||||
93
src/states/entity/EntityWalkState.lua
Normal file
93
src/states/entity/EntityWalkState.lua
Normal file
@@ -0,0 +1,93 @@
|
||||
-- require('mobdebug').start()
|
||||
|
||||
|
||||
EntityWalkState = Class{__includes = BaseState}
|
||||
|
||||
function EntityWalkState:init(entity)
|
||||
self.entity = entity
|
||||
self.entity:changeAnimation('walk-'..self.entity.direction)
|
||||
end
|
||||
|
||||
function EntityWalkState:update(dt)
|
||||
if self.entity.direction == 'up' then
|
||||
self.entity.y = self.entity.y - self.entity.walkingSpeed * dt
|
||||
if self.entity.y + TILE_SIZE / 2 < LEVEL_RENDER_OFFSET_TOP - 2 then
|
||||
self.entity.y = self.entity.y + self.entity.walkingSpeed * dt
|
||||
Event.dispatch('back-to-wall')
|
||||
self.entity:changeState('edge')
|
||||
end
|
||||
elseif self.entity.direction == 'down' then
|
||||
self.entity.y = self.entity.y + self.entity.walkingSpeed * dt
|
||||
if self.entity.y + TILE_SIZE / 2 > VIRTUAL_HEIGHT - LEVEL_RENDER_OFFSET + 2 then
|
||||
self.entity.y = self.entity.y - self.entity.walkingSpeed * dt
|
||||
Event.dispatch('back-to-wall')
|
||||
self.entity:changeState('edge')
|
||||
end
|
||||
elseif self.entity.direction == 'right' then
|
||||
self.entity.x = self.entity.x + self.entity.walkingSpeed * dt
|
||||
if self.entity.x + TILE_SIZE / 2 > VIRTUAL_WIDTH - TILE_SIZE / 2 + 2 or self.entity.previousdirection == 'left' then
|
||||
self.entity.x = self.entity.x - self.entity.walkingSpeed * dt
|
||||
Event.dispatch('back-to-wall')
|
||||
self.entity:changeState('edge')
|
||||
end
|
||||
elseif self.entity.direction == 'left' then
|
||||
self.entity.x = self.entity.x - self.entity.walkingSpeed * dt
|
||||
if self.entity.x < 0 or self.entity.previousdirection == 'right' then
|
||||
self.entity.x = self.entity.x + self.entity.walkingSpeed * dt
|
||||
Event.dispatch('back-to-wall')
|
||||
self.entity:changeState('edge')
|
||||
end
|
||||
end
|
||||
if love.keyboard.isDown('left') then
|
||||
if self.entity.direction ~= 'left' then
|
||||
self.entity.previousDirection = self.entity.direction
|
||||
Event.dispatch('change-direction')
|
||||
else
|
||||
Event.dispatch('walking')
|
||||
end
|
||||
self.entity.direction = 'left'
|
||||
self.entity:changeAnimation('walk-left')
|
||||
elseif love.keyboard.isDown('right') then
|
||||
if self.entity.direction ~= 'right' then
|
||||
self.entity.previousDirection = self.entity.direction
|
||||
Event.dispatch('change-direction')
|
||||
else
|
||||
Event.dispatch('walking')
|
||||
end
|
||||
self.entity.direction = 'right'
|
||||
self.entity:changeAnimation('walk-right')
|
||||
elseif love.keyboard.isDown('up') then
|
||||
if self.entity.direction ~= 'up' then
|
||||
self.entity.previousDirection = self.entity.direction
|
||||
Event.dispatch('change-direction')
|
||||
else
|
||||
Event.dispatch('walking')
|
||||
end
|
||||
self.entity.direction = 'up'
|
||||
self.entity:changeAnimation('walk-up')
|
||||
elseif love.keyboard.isDown('down') then
|
||||
if self.entity.direction ~= 'down' then
|
||||
self.entity.previousDirection = self.entity.direction
|
||||
Event.dispatch('change-direction')
|
||||
else
|
||||
Event.dispatch('walking')
|
||||
end
|
||||
self.entity.direction = 'down'
|
||||
self.entity:changeAnimation('walk-down')
|
||||
else
|
||||
self.entity:changeState('idle')
|
||||
end
|
||||
if not self.entity:insideBounds() then
|
||||
Event.dispatch('back-to-wall')
|
||||
self.entity:changeState('edge')
|
||||
end
|
||||
end
|
||||
|
||||
function EntityWalkState:render()
|
||||
local anim = self.entity.currentAnimation
|
||||
love.graphics.draw(gTextures[anim.texture], gFrames[anim.texture][anim:getCurrentFrame()],self.entity.x, self.entity.y)
|
||||
|
||||
-- love.graphics.setColor(255, 0, 255, 255)
|
||||
-- love.graphics.rectangle('line', self.entity.x, self.entity.y, self.entity.width, self.entity.height)
|
||||
-- love.graphics.setColor(255, 255, 255, 255)
|
||||
end
|
||||
33
src/states/game/FadeInState.lua
Normal file
33
src/states/game/FadeInState.lua
Normal file
@@ -0,0 +1,33 @@
|
||||
--[[
|
||||
GD50
|
||||
Pokemon
|
||||
|
||||
Author: Colton Ogden
|
||||
cogden@cs50.harvard.edu
|
||||
]]
|
||||
|
||||
FadeInState = Class{__includes = BaseState}
|
||||
|
||||
function FadeInState:init(color, time, onFadeComplete)
|
||||
self.name = 'FadeInState'
|
||||
self.r = color.r
|
||||
self.g = color.g
|
||||
self.b = color.b
|
||||
self.opacity = 0
|
||||
self.time = time
|
||||
|
||||
Timer.tween(self.time, {
|
||||
[self] = {opacity = 255}
|
||||
})
|
||||
:finish(function()
|
||||
gStateStack:pop()
|
||||
onFadeComplete()
|
||||
end)
|
||||
end
|
||||
|
||||
function FadeInState:render()
|
||||
love.graphics.setColor(self.r, self.g, self.b, self.opacity)
|
||||
love.graphics.rectangle('fill', 0, 0, VIRTUAL_WIDTH, VIRTUAL_HEIGHT)
|
||||
|
||||
love.graphics.setColor(255, 255, 255, 255)
|
||||
end
|
||||
37
src/states/game/FadeOutState.lua
Normal file
37
src/states/game/FadeOutState.lua
Normal file
@@ -0,0 +1,37 @@
|
||||
--[[
|
||||
GD50
|
||||
Pokemon
|
||||
|
||||
Author: Colton Ogden
|
||||
cogden@cs50.harvard.edu
|
||||
]]
|
||||
|
||||
FadeOutState = Class{__includes = BaseState}
|
||||
|
||||
function FadeOutState:init(color, time, onFadeComplete)
|
||||
self.name = 'FadeOutState'
|
||||
self.opacity = 255
|
||||
self.r = color.r
|
||||
self.g = color.g
|
||||
self.b = color.b
|
||||
self.time = time
|
||||
|
||||
Timer.tween(self.time, {
|
||||
[self] = {opacity = 0}
|
||||
})
|
||||
:finish(function()
|
||||
gStateStack:pop()
|
||||
onFadeComplete()
|
||||
end)
|
||||
end
|
||||
|
||||
function FadeOutState:update(dt)
|
||||
|
||||
end
|
||||
|
||||
function FadeOutState:render()
|
||||
love.graphics.setColor(self.r, self.g, self.b, self.opacity)
|
||||
love.graphics.rectangle('fill', 0, 0, VIRTUAL_WIDTH, VIRTUAL_HEIGHT)
|
||||
|
||||
love.graphics.setColor(255, 255, 255, 255)
|
||||
end
|
||||
44
src/states/game/GameOverState.lua
Normal file
44
src/states/game/GameOverState.lua
Normal file
@@ -0,0 +1,44 @@
|
||||
-- require('mobdebug').start()
|
||||
|
||||
GameOverState = Class{__includes = BaseState}
|
||||
|
||||
function GameOverState:init(score)
|
||||
self.name = 'GameOverState'
|
||||
gSounds['intro-music']:play()
|
||||
self.score = score or 0
|
||||
end
|
||||
|
||||
function GameOverState:update(dt)
|
||||
if love.keyboard.wasPressed('enter') or love.keyboard.wasPressed('return') then
|
||||
gStateStack:push(FadeInState({
|
||||
r = 0, g = 0, b = 0
|
||||
}, 1,
|
||||
function()
|
||||
gSounds['intro-music']:stop()
|
||||
|
||||
gStateStack:push(StartState(),function()end, true)
|
||||
gStateStack:push(FadeOutState({
|
||||
r = 0, g = 0, b = 0
|
||||
}, 1,
|
||||
function() end))
|
||||
end))
|
||||
end
|
||||
end
|
||||
|
||||
function GameOverState:render()
|
||||
love.graphics.clear(0, 0, 0, 255)
|
||||
|
||||
love.graphics.setColor(56, 56, 56, 255)
|
||||
love.graphics.setFont(gFonts['large'])
|
||||
love.graphics.printf('GAME OVER', 0, VIRTUAL_HEIGHT / 2 - 72, VIRTUAL_WIDTH, 'center')
|
||||
love.graphics.setFont(gFonts['medium'])
|
||||
|
||||
love.graphics.printf('Score: '..tostring(self.score), 0, VIRTUAL_HEIGHT / 2, VIRTUAL_WIDTH, 'center')
|
||||
|
||||
love.graphics.printf('Try keeping away from the balls but do not lose them all!!!', 0, VIRTUAL_HEIGHT / 2 + 48, VIRTUAL_WIDTH, 'center')
|
||||
love.graphics.printf('Press Enter', 0, VIRTUAL_HEIGHT / 2 + 88, VIRTUAL_WIDTH, 'center')
|
||||
love.graphics.setFont(gFonts['small'])
|
||||
|
||||
love.graphics.setColor(255, 255, 255, 255)
|
||||
-- love.graphics.draw(gTextures[self.sprite], self.spriteX, self.spriteY)
|
||||
end
|
||||
115
src/states/game/PlayState.lua
Normal file
115
src/states/game/PlayState.lua
Normal file
@@ -0,0 +1,115 @@
|
||||
-- require('mobdebug').start()
|
||||
|
||||
PlayState = Class{__includes = BaseState}
|
||||
|
||||
function PlayState:init()
|
||||
self.name = 'PlayState'
|
||||
self.stage = 1
|
||||
self.level = Level()
|
||||
self.player = Player ( ENTITY_DEFS['player'] , self.level)
|
||||
self.level.player = self.player
|
||||
self.balls = {}
|
||||
self:createBalls()
|
||||
self.level.balls = self.balls
|
||||
|
||||
gSounds['music']:setLooping(true)
|
||||
gSounds['music']:play()
|
||||
|
||||
end
|
||||
|
||||
function PlayState:enter()
|
||||
end
|
||||
|
||||
function PlayState:update(dt)
|
||||
-- self.level:update(dt)
|
||||
self.player:update(dt)
|
||||
self:updateBalls(dt, self.level)
|
||||
end
|
||||
|
||||
function PlayState:render()
|
||||
love.graphics.clear(0, 0, 0, 255)
|
||||
love.graphics.setColor(40, 45, 52,255)
|
||||
|
||||
love.graphics.setFont(gFonts['small'])
|
||||
love.graphics.printf('Score '..tostring(self.player.score),
|
||||
0, 5, VIRTUAL_WIDTH, 'left')
|
||||
|
||||
love.graphics.setFont(gFonts['medium'])
|
||||
love.graphics.printf('Stage '..tostring(self.stage),
|
||||
0, 5, VIRTUAL_WIDTH, 'center')
|
||||
|
||||
self.level:render()
|
||||
self.player:render()
|
||||
self:renderBalls(dt)
|
||||
end
|
||||
|
||||
function PlayState:createBalls()
|
||||
self.balls = {}
|
||||
|
||||
for i = 1, self.stage do
|
||||
table.insert(self.balls,Ball(
|
||||
math.random(LEVEL_RENDER_OFFSET, VIRTUAL_WIDTH - LEVEL_RENDER_OFFSET), --X
|
||||
math.random(LEVEL_RENDER_OFFSET_TOP, VIRTUAL_HEIGHT - LEVEL_RENDER_OFFSET), --Y
|
||||
math.random(20,90),math.random(20,90) -- speed
|
||||
))
|
||||
end
|
||||
end
|
||||
|
||||
function PlayState:updateBalls(dt, level)
|
||||
for k,ball in pairs(self.balls) do
|
||||
ball:update(dt,level)
|
||||
end
|
||||
-- remove balls outside bounds
|
||||
for k,ball in pairs(self.balls) do
|
||||
if ball.remove then
|
||||
table.remove(self.balls,k)
|
||||
self.player.score = self.player.score - 1
|
||||
elseif ball.hitPlayer then
|
||||
-- check if any ball hit the player trail segments
|
||||
self:gameOver()
|
||||
end
|
||||
end
|
||||
|
||||
if #self.balls <= 0 then
|
||||
-- game over - reset level
|
||||
self:gameOver()
|
||||
end
|
||||
|
||||
-- check perimeter of level, if low enough, create new level move on to the next stage
|
||||
if level:getPerimeter() < 500 then
|
||||
self:nextStage()
|
||||
end
|
||||
end
|
||||
|
||||
function PlayState:renderBalls(dt)
|
||||
for k,ball in pairs(self.balls) do
|
||||
ball:render()
|
||||
end
|
||||
end
|
||||
|
||||
function PlayState:gameOver()
|
||||
gStateStack:pop()
|
||||
gSounds['music']:stop()
|
||||
gStateStack:push(GameOverState(self.player.score),function() end)
|
||||
end
|
||||
|
||||
function PlayState:nextStage()
|
||||
gStateStack:push(FadeOutState({
|
||||
r = 255, g = 255, b = 255}, 1,function()
|
||||
self.stage = self.stage + 1
|
||||
self.level = Level()
|
||||
self.player:reset()
|
||||
self.player.score = self.player.score + self.player.multiplier * #self.balls
|
||||
self.player.multiplier = self.player.multiplier + #self.balls
|
||||
self.balls = {}
|
||||
self:createBalls()
|
||||
self.level.player = self.player
|
||||
self.player.level = self.level
|
||||
self.level.balls = self.balls
|
||||
gSounds['music']:stop()
|
||||
gSounds['heal']:play()
|
||||
gStateStack:push(FadeOutState({r = 255, g = 255, b = 255}, 1,function()
|
||||
gSounds['music']:play()
|
||||
end))
|
||||
end))
|
||||
end
|
||||
40
src/states/game/StartState.lua
Normal file
40
src/states/game/StartState.lua
Normal file
@@ -0,0 +1,40 @@
|
||||
-- require('mobdebug').start()
|
||||
|
||||
StartState = Class{__includes = BaseState}
|
||||
|
||||
function StartState:init()
|
||||
self.name = 'StartState'
|
||||
gSounds['intro-music']:play()
|
||||
|
||||
end
|
||||
|
||||
function StartState:update(dt)
|
||||
if love.keyboard.wasPressed('enter') or love.keyboard.wasPressed('return') then
|
||||
gStateStack:push(FadeInState({
|
||||
r = 255, g = 255, b = 255
|
||||
}, 1,
|
||||
function()
|
||||
gSounds['intro-music']:stop()
|
||||
|
||||
gStateStack:push(PlayState(),function()end, true)
|
||||
-- gStateStack:push(FadeOutState({
|
||||
-- r = 255, g = 255, b = 255
|
||||
-- }, 1,
|
||||
-- function() end))
|
||||
end))
|
||||
end
|
||||
end
|
||||
|
||||
function StartState:render()
|
||||
-- print("StartSate : setting colors")
|
||||
love.graphics.clear(0, 0, 0, 255)
|
||||
love.graphics.setColor(56, 56, 56, 255)
|
||||
love.graphics.setFont(gFonts['large'])
|
||||
love.graphics.printf('Spider Cut!', 0, VIRTUAL_HEIGHT / 2 - 72, VIRTUAL_WIDTH, 'center')
|
||||
love.graphics.setFont(gFonts['medium'])
|
||||
love.graphics.printf('Press Enter', 0, VIRTUAL_HEIGHT / 2 + 68, VIRTUAL_WIDTH, 'center')
|
||||
love.graphics.setFont(gFonts['small'])
|
||||
-- print("StartState : end")
|
||||
-- love.graphics.setColor(255, 255, 255, 255)
|
||||
-- love.graphics.draw(gTextures[self.sprite], self.spriteX, self.spriteY)
|
||||
end
|
||||
Reference in New Issue
Block a user