tracking down problem with insidebounds check

This commit is contained in:
2021-12-29 23:22:17 +01:00
parent 0aeb0139d3
commit e46316cff8
10 changed files with 731 additions and 267 deletions

View File

@@ -4,10 +4,10 @@ Lua game inspired by an old arcade game(Qix/Gals Panic) using LOVE2D for Linux.
## TO-DO
- Stop player from creating crossed lines
- Fix creation of the lines / polygon / points
- Add more pictures (manga?)
- Fix balls sometimes attaching to the lines
- Add spiders moving around in the board, player collision kills

View File

@@ -65,6 +65,11 @@ function love.keypressed(key)
push:switchFullscreen()
end
if key == 'r' then
gStateStack:clear()
gStateStack:push(StartState())
end
love.keyboard.keysPressed[key] = true
end

View File

@@ -22,13 +22,15 @@ function Ball:reset()
end
function Ball:update(dt,level)
local tempX = self.x + self.dx * dt
local tempY = self.y + self.dy * dt
local tempX = math.floor((self.x + self.dx * dt) + 0.5)
local tempY = math.floor((self.y + self.dy * dt) + 0.5)
local trailSeg = level:pointOnEdge(tempX,tempY,level.player.trailSegments)
if trailSeg then
self.hitPlayer = true
-- print("ball hits player")
gSounds['hit']:play()
return
end
local i, segment = level:getTouchingSegment(tempX,tempY)
@@ -49,8 +51,9 @@ function Ball:update(dt,level)
gSounds['blip']:play()
end
if not (level:insideBounds(tempX,tempY) or level:pointOnEdge(tempX,tempY)) then
if not level:insideBounds(tempX,tempY) and not level:pointOnEdge(tempX,tempY) then
self.remove = true
print("ball outside or touching segment")
end
self.x = self.x + self.dx * dt
@@ -60,8 +63,9 @@ function Ball:update(dt,level)
end
function Ball:render()
love.graphics.setColor(255/255,1/255,1/255,255/255)
love.graphics.setColor(1,0.1,0.1,1)
love.graphics.circle('fill', self.x, self.y, self.width/2)
love.graphics.setColor(1,1,1,1)
end
function Ball:getCollisionDirection(segment,x,y)

View File

@@ -26,8 +26,8 @@ function Clock:render()
love.graphics.setColor(self.color[1], self.color[2], self.color[3],
self.color[4])
end
love.graphics.setFont(gFonts['medium'])
love.graphics.printf('Time: ' .. self.currentTime, 0, 5, VIRTUAL_WIDTH,
love.graphics.setFont(gFonts['small'])
love.graphics.printf('Time: ' .. self.currentTime, 0, LEVEL_RENDER_OFFSET_TOP/2, VIRTUAL_WIDTH - LEVEL_RENDER_OFFSET,
'right')
love.graphics.setFont(gFonts['small'])
love.graphics.setColor(1, 1, 1, 1)

View File

@@ -5,35 +5,46 @@ function Level:init(def, stage)
self.stage = stage
self.points = {}
self.player = {}
self.segments = self:createLevelFromDef(def, stage)
self.segments = self:createLevelFromDef(def)
self.polygon = self:createPolygon()
self.mesh = poly2mesh(self.points)
self.balls = self:createBallsFromDef(def)
self.clock = Clock(def.timeLimit, "down")
end
function Level:update(dt)
self.mesh = poly2mesh(self.points)
self:updateBalls(dt)
self.clock:update(dt)
end
function Level:render()
self:renderBackground()
self:renderOuterSegments()
self:renderBalls()
self.clock:render()
end
function Level:renderOuterSegments()
for k, segment in pairs(self.segments) do segment:render() end
for k, segment in pairs(self.segments) do
segment:render()
end
end
function Level:renderBackground()
love.graphics.draw(gImages[(self.stage % #gImages)], LEVEL_RENDER_OFFSET,
LEVEL_RENDER_OFFSET_TOP)
local nextImg = self.stage % (#gImages + 1)
if nextImg == 0 then
nextImg = 1
end
love.graphics.draw(gImages[nextImg], LEVEL_RENDER_OFFSET, LEVEL_RENDER_OFFSET_TOP)
if self.mesh:type() == "Mesh" then
love.graphics.draw(self.mesh, 0, 0)
end
end
function Level:createLevelFromDef(def, stage)
function Level:createLevelFromDef(def)
local level = {}
for i, seg in pairs(def[1].segments) do
for i, seg in pairs(def.segments) do
table.insert(level, Segment(seg.p1, seg.p2, seg.width, seg.face))
end
return level
@@ -41,22 +52,45 @@ 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
}, {
[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({
},
.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')
},
{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),
@@ -74,8 +108,10 @@ function Level:insideBounds2(x, y)
return shape:testPoint(x, y)
end
function Level:insideBounds(x, y, segments)
if not segments then segments = self.segments end
function Level:insideBounds4(x, y, segments)
if not segments then
segments = self.segments
end
local up, down, left, right = false
local upSeg, downSeg, leftSeg, rightSeg = nil
@@ -84,40 +120,40 @@ function Level:insideBounds(x, y, 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
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
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
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
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
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
elseif math.abs(segment.firstPointY - y) < math.abs(upSeg.firstPointY - y) then
upSeg = segment
end
end
@@ -125,11 +161,15 @@ function Level:insideBounds(x, y, segments)
end
end
if not rightSeg or not leftSeg or not upSeg or not downSeg then
print("ball outside - seg check")
return false
end
if rightSeg.face ~= "left" or leftSeg.face ~= "right" or upSeg.face ~= "down" or downSeg.face ~= "up" then
print("ball outside - face check")
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
@@ -140,17 +180,26 @@ function Level:insideBounds3(x, y)
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
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:insideBounds(x, y, segments)
if not segments then
segments = self.segments
end
-- prepare shape
print_r(self.polygon)
return PointWithinShape(self.polygon, x, y)
end
function Level:createPolygon()
local polygon = {}
local polygonPoints = {}
@@ -159,16 +208,16 @@ function Level:createPolygon()
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()
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
if polygon[i + 1].x ~= polygon[i].x or polygon[i + 1].y ~= polygon[i].y then
table.insert(pointlist, polygon[i].x)
table.insert(pointlist, polygon[i].y)
end
j = j + 4
i = i + 1
end
@@ -178,25 +227,30 @@ function Level:createPolygon()
end
function Level:pointOnEdge(x, y, segments)
if not segments then segments = self.segments end
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
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
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
-- print("ball pointOnEdge is false")
return false
end
@@ -204,17 +258,19 @@ 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
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
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
@@ -223,15 +279,12 @@ function Level:getTouchingSegment(x, y)
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 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 = ''
local firstFace = ""
-- check if it is same start and finish segment
if startSegi == endSegi then
@@ -259,11 +312,12 @@ function Level:cutLevel()
while k <= #self.segments do
-- print(" segment to be inserted "..tostring(k))
if k == startSegi and not insertedNewSegs then
-- print_r(new)
-- print("-- finished inserting new segments and parts")
-- 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])
part1, temp, part2 = self.segments[k]:splitInThreeWithSegments(newSegs[1], newSegs[#newSegs])
-- print("part1")
part1:debug()
-- print("part2")
@@ -285,8 +339,6 @@ function Level:cutLevel()
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
@@ -324,14 +376,15 @@ function Level:cutLevel()
newSegs[1]:joinPerpendicular(self.segments[k])
newSegs[1]:debug()
-- print("newSegs[1] is joined -- finished")
local startPart1, startPart2 =
self.segments[k]:splitInTwoWithSegment(newSegs[1])
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
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
@@ -360,18 +413,19 @@ function Level:cutLevel()
end
-- print_r(board1)
local endPart1, endPart2 =
self.segments[k]:splitInTwoWithSegment(newSegs[#newSegs])
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
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
-- print("-- keep first part of the k segment")
self.segments[k] = endPart1
savedEnd = endPart2
-- print("-- keep first part of the k segment")
else
-- print("-- keep second part of the k segment")
self.segments[k] = endPart2
savedEnd = endPart1
-- print("-- keep second part of the k segment")
end
board1[j] = self.segments[k]:copy()
@@ -387,14 +441,15 @@ function Level:cutLevel()
newSegs[#newSegs]:joinPerpendicular(self.segments[k])
newSegs[#newSegs]:debug()
-- print("newSegs[#newSegs] is joined -- finished")
local endPart1, endPart2 =
self.segments[k]:splitInTwoWithSegment(newSegs[#newSegs])
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
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
@@ -422,14 +477,15 @@ function Level:cutLevel()
end
-- print_r(board1)
local startPart1, startPart2 =
self.segments[k]:splitInTwoWithSegment(newSegs[1])
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
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
@@ -442,7 +498,6 @@ function Level:cutLevel()
-- print_r(board1)
insertedNewSegments = true
end
elseif k ~= startSegi and k ~= endSegi then
board1[j] = self.segments[k]:copy()
j = j + 1
@@ -461,6 +516,7 @@ function Level:cutLevel()
-- print('---- Create Second board from '..tostring(board2StartSegi)..' to '..tostring(board2EndSegi))
j = 1
if board2StartSegi < board2EndSegi then
-- print_r(board2)
board2[j] = savedStart
j = j + 1
-- print_r(board2)
@@ -485,9 +541,9 @@ function Level:cutLevel()
board2[j] = newseg:copy()
j = j + 1
end
-- print_r(board2)
else
-- print('-- inserted new segments, final board2 before ordering')
-- print_r(board2)
-- print('-- inserted first segment, proceed with skipped segments in first board, end < start')
board2[j] = savedEnd
j = j + 1
@@ -515,8 +571,6 @@ function Level:cutLevel()
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")
@@ -525,13 +579,12 @@ function Level:cutLevel()
-- print("PREPARE BOARD 2 -- FINAL")
-- print_r(board2)
if self:containsBalls(board1) == 0 then
self.segments = board2
elseif self:containsBalls(board2) == 0 then
local b1balls = self:containsBalls(board1)
local b2balls = self:containsBalls(board2)
if b1balls > b2balls then
self.segments = board1
else
self.segments = self:getPerimeter(board1) >
self:getPerimeter(board2) and board1 or board2
self.segments = board2
end
end
@@ -563,8 +616,7 @@ function Level:orderSegments(segs)
-- -- print("new[i] end equals start of ")
-- s:debug()
if new[i].vertical == s.vertical and new[i].horizontal ==
s.horizontal then
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)
@@ -584,8 +636,7 @@ function Level:orderSegments(segs)
-- -- print("new[i] end equals end of ")
-- s:debug()
s:switchDirection()
if new[i].vertical == s.vertical and new[i].horizontal ==
s.horizontal then
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)
@@ -608,14 +659,10 @@ function Level:orderSegments(segs)
-- -- 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)
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)
fi, fs = self:findNearestSegment(new[i].firstPointX, new[i].firstPointY, segs, margin)
if not fs then
margin = margin + 1
@@ -662,60 +709,60 @@ function Level:calculateSegmentFaces(segments, 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'
if face == "up" then
if s.direction == "right" then
if segments[k + 1].direction == "up" then
face = "left"
else
face = 'right'
face = "right"
end
else
if segments[k + 1].direction == 'up' then
face = 'right'
if segments[k + 1].direction == "up" then
face = "right"
else
face = 'left'
face = "left"
end
end
elseif face == 'down' then
if s.direction == 'right' then
if segments[k + 1].direction == 'up' then
face = 'right'
elseif face == "down" then
if s.direction == "right" then
if segments[k + 1].direction == "up" then
face = "right"
else
face = 'left'
face = "left"
end
else
if segments[k + 1].direction == 'up' then
face = 'left'
if segments[k + 1].direction == "up" then
face = "left"
else
face = 'right'
face = "right"
end
end
elseif face == 'right' then
if s.direction == 'up' then
if segments[k + 1].direction == 'right' then
face = 'down'
elseif face == "right" then
if s.direction == "up" then
if segments[k + 1].direction == "right" then
face = "down"
else
face = 'up'
face = "up"
end
else
if segments[k + 1].direction == 'right' then
face = 'up'
if segments[k + 1].direction == "right" then
face = "up"
else
face = 'down'
face = "down"
end
end
elseif face == 'left' then
if s.direction == 'up' then
if segments[k + 1].direction == 'right' then
face = 'up'
elseif face == "left" then
if s.direction == "up" then
if segments[k + 1].direction == "right" then
face = "up"
else
face = 'down'
face = "down"
end
else
if segments[k + 1].direction == 'right' then
face = 'down'
if segments[k + 1].direction == "right" then
face = "down"
else
face = 'up'
face = "up"
end
end
end
@@ -725,28 +772,38 @@ 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
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
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
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
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
@@ -755,15 +812,62 @@ function Level:findNearestSegment(x, y, segments, margin)
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
-- returns number 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
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
-- return #self.balls
end
function Level:createBalls(n)
local balls = {}
for i = 1, n do
table.insert(
balls,
Ball(
math.random(LEVEL_RENDER_OFFSET + 5, VIRTUAL_WIDTH - LEVEL_RENDER_OFFSET - 5), --X
math.random(LEVEL_RENDER_OFFSET_TOP + 5, VIRTUAL_HEIGHT - LEVEL_RENDER_OFFSET - 5), --Y
math.random(20, 90),
math.random(20, 90) -- speed
)
)
end
return balls
end
function Level:createBallsFromDef(def)
local balls = {}
for i = 1, def.balls do
table.insert(
balls,
Ball(
math.random(LEVEL_RENDER_OFFSET + 5, VIRTUAL_WIDTH - LEVEL_RENDER_OFFSET - 5), --X
math.random(LEVEL_RENDER_OFFSET_TOP + 5, VIRTUAL_HEIGHT - LEVEL_RENDER_OFFSET - 5), --Y
math.random(def.ballSpeed - 10, def.ballSpeed + 10),
math.random(def.ballSpeed - 10, def.ballSpeed + 10) -- speed
)
)
end
return balls
end
function Level:updateBalls(dt)
for k, ball in pairs(self.balls) do
ball:update(dt, self)
end
end
function Level:renderBalls()
for k, ball in pairs(self.balls) do
ball:render()
end
end

View File

@@ -98,7 +98,6 @@ function Player:onEdge()
end
function Player:onWalking()
-- if position of player collides with its own trail, stop motion
if #self.trail > 1 then
table.remove(self.trail)
table.remove(self.trailSegments)

View File

@@ -74,7 +74,8 @@ end
-- convert a list of points forming a polygon {x1, y1, x2, y2, ...} into a mesh
function poly2mesh(points)
-- remove duplicates???
-- print("util - poly2mesh: ")
-- print_r(points)
if #points > 2 then
local polypts = love.math.triangulate(points)
local tlist
@@ -119,3 +120,155 @@ function poly2mesh(points)
return nil
end
end
-- point in shape calculation
function PointWithinShape(shape, tx, ty)
if #shape == 0 then
return false
elseif #shape == 1 then
return shape[1].x == tx and shape[1].y == ty
elseif #shape == 2 then
return PointWithinLine(shape, tx, ty)
else
return CrossingsMultiplyTest(shape, tx, ty)
end
end
function BoundingBox(box, tx, ty)
return (box[2].x >= tx and box[2].y >= ty)
and (box[1].x <= tx and box[1].y <= ty)
or (box[1].x >= tx and box[2].y >= ty)
and (box[2].x <= tx and box[1].y <= ty)
end
function colinear(line, x, y, e)
e = e or 0.1
m = (line[2].y - line[1].y) / (line[2].x - line[1].x)
local function f(x) return line[1].y + m*(x - line[1].x) end
return math.abs(y - f(x)) <= e
end
function PointWithinLine(line, tx, ty, e)
e = e or 0.66
if BoundingBox(line, tx, ty) then
return colinear(line, tx, ty, e)
else
return false
end
end
-------------------------------------------------------------------------
-- The following function is based off code from
-- [ http://erich.realtimerendering.com/ptinpoly/ ]
--
--[[
======= Crossings Multiply algorithm ===================================
* This version is usually somewhat faster than the original published in
* Graphics Gems IV; by turning the division for testing the X axis crossing
* into a tricky multiplication test this part of the test became faster,
* which had the additional effect of making the test for "both to left or
* both to right" a bit slower for triangles than simply computing the
* intersection each time. The main increase is in triangle testing speed,
* which was about 15% faster; all other polygon complexities were pretty much
* the same as before. On machines where division is very expensive (not the
* case on the HP 9000 series on which I tested) this test should be much
* faster overall than the old code. Your mileage may (in fact, will) vary,
* depending on the machine and the test data, but in general I believe this
* code is both shorter and faster. This test was inspired by unpublished
* Graphics Gems submitted by Joseph Samosky and Mark Haigh-Hutchinson.
* Related work by Samosky is in:
*
* Samosky, Joseph, "SectionView: A system for interactively specifying and
* visualizing sections through three-dimensional medical image data",
* M.S. Thesis, Department of Electrical Engineering and Computer Science,
* Massachusetts Institute of Technology, 1993.
*
--]]
--[[ Shoot a test ray along +X axis. The strategy is to compare vertex Y values
* to the testing point's Y and quickly discard edges which are entirely to one
* side of the test ray. Note that CONVEX and WINDING code can be added as
* for the CrossingsTest() code; it is left out here for clarity.
*
* Input 2D polygon _pgon_ with _numverts_ number of vertices and test point
* _point_, returns 1 if inside, 0 if outside.
--]]
function CrossingsMultiplyTest(pgon, tx, ty)
local i, yflag0, yflag1, inside_flag
local vtx0, vtx1
local numverts = #pgon
vtx0 = pgon[numverts]
vtx1 = pgon[1]
-- get test bit for above/below X axis
yflag0 = ( vtx0.y >= ty )
inside_flag = false
for i=2,numverts+1 do
yflag1 = ( vtx1.y >= ty )
--[[ Check if endpoints straddle (are on opposite sides) of X axis
* (i.e. the Y's differ); if so, +X ray could intersect this edge.
* The old test also checked whether the endpoints are both to the
* right or to the left of the test point. However, given the faster
* intersection point computation used below, this test was found to
* be a break-even proposition for most polygons and a loser for
* triangles (where 50% or more of the edges which survive this test
* will cross quadrants and so have to have the X intersection computed
* anyway). I credit Joseph Samosky with inspiring me to try dropping
* the "both left or both right" part of my code.
--]]
if ( yflag0 ~= yflag1 ) then
--[[ Check intersection of pgon segment with +X ray.
* Note if >= point's X; if so, the ray hits it.
* The division operation is avoided for the ">=" test by checking
* the sign of the first vertex wrto the test point; idea inspired
* by Joseph Samosky's and Mark Haigh-Hutchinson's different
* polygon inclusion tests.
--]]
if ( ((vtx1.y - ty) * (vtx0.x - vtx1.x) >= (vtx1.x - tx) * (vtx0.y - vtx1.y)) == yflag1 ) then
inside_flag = not inside_flag
end
end
-- Move to the next pair of vertices, retaining info as possible.
yflag0 = yflag1
vtx0 = vtx1
vtx1 = pgon[i]
end
return inside_flag
end
function GetIntersect( points )
local g1 = points[1].x
local h1 = points[1].y
local g2 = points[2].x
local h2 = points[2].y
local i1 = points[3].x
local j1 = points[3].y
local i2 = points[4].x
local j2 = points[4].y
local xk = 0
local yk = 0
if checkIntersect({x=g1, y=h1}, {x=g2, y=h2}, {x=i1, y=j1}, {x=i2, y=j2}) then
local a = h2-h1
local b = (g2-g1)
local v = ((h2-h1)*g1) - ((g2-g1)*h1)
local d = i2-i1
local c = (j2-j1)
local w = ((j2-j1)*i1) - ((i2-i1)*j1)
xk = (1/((a*d)-(b*c))) * ((d*v)-(b*w))
yk = (-1/((a*d)-(b*c))) * ((a*w)-(c*v))
end
return xk, yk
end

View File

@@ -75,5 +75,207 @@ LEVELS_DEF = {
face = 'right'
},
},
spiders = 0,
timeLimit = 300,
balls = 1,
ballSpeed = 20,
},
[2] = {
segments =
{
{
p1 = {LEVEL_RENDER_OFFSET, LEVEL_RENDER_OFFSET_TOP},
p2 = {VIRTUAL_WIDTH - LEVEL_RENDER_OFFSET, LEVEL_RENDER_OFFSET_TOP},
width = LINE_WIDTH,
face = 'down'
},
{
p1 = {VIRTUAL_WIDTH - LEVEL_RENDER_OFFSET, LEVEL_RENDER_OFFSET_TOP},
p2 = {VIRTUAL_WIDTH - LEVEL_RENDER_OFFSET, VIRTUAL_HEIGHT - LEVEL_RENDER_OFFSET},
width = LINE_WIDTH,
face = 'left'
},
{
p1 = {VIRTUAL_WIDTH - LEVEL_RENDER_OFFSET, VIRTUAL_HEIGHT - LEVEL_RENDER_OFFSET},
p2 = {LEVEL_RENDER_OFFSET, VIRTUAL_HEIGHT - LEVEL_RENDER_OFFSET},
width = LINE_WIDTH,
face = 'up'
},
{
p1 = {LEVEL_RENDER_OFFSET, VIRTUAL_HEIGHT - LEVEL_RENDER_OFFSET},
p2 = {LEVEL_RENDER_OFFSET, LEVEL_RENDER_OFFSET_TOP},
width= LINE_WIDTH,
face = 'right'
},
},
spiders = 0,
timeLimit = 35,
balls = 2,
ballSpeed = 40,
},
[3] = {
segments =
{
{
p1 = {LEVEL_RENDER_OFFSET, LEVEL_RENDER_OFFSET_TOP},
p2 = {VIRTUAL_WIDTH - LEVEL_RENDER_OFFSET, LEVEL_RENDER_OFFSET_TOP},
width = LINE_WIDTH,
face = 'down'
},
{
p1 = {VIRTUAL_WIDTH - LEVEL_RENDER_OFFSET, LEVEL_RENDER_OFFSET_TOP},
p2 = {VIRTUAL_WIDTH - LEVEL_RENDER_OFFSET, VIRTUAL_HEIGHT - LEVEL_RENDER_OFFSET},
width = LINE_WIDTH,
face = 'left'
},
{
p1 = {VIRTUAL_WIDTH - LEVEL_RENDER_OFFSET, VIRTUAL_HEIGHT - LEVEL_RENDER_OFFSET},
p2 = {LEVEL_RENDER_OFFSET, VIRTUAL_HEIGHT - LEVEL_RENDER_OFFSET},
width = LINE_WIDTH,
face = 'up'
},
{
p1 = {LEVEL_RENDER_OFFSET, VIRTUAL_HEIGHT - LEVEL_RENDER_OFFSET},
p2 = {LEVEL_RENDER_OFFSET, LEVEL_RENDER_OFFSET_TOP},
width= LINE_WIDTH,
face = 'right'
},
},
spiders = 0,
timeLimit = 45,
balls = 3,
ballSpeed = 45,
},
[4] = {
segments =
{
{
p1 = {LEVEL_RENDER_OFFSET, LEVEL_RENDER_OFFSET_TOP},
p2 = {VIRTUAL_WIDTH - LEVEL_RENDER_OFFSET, LEVEL_RENDER_OFFSET_TOP},
width = LINE_WIDTH,
face = 'down'
},
{
p1 = {VIRTUAL_WIDTH - LEVEL_RENDER_OFFSET, LEVEL_RENDER_OFFSET_TOP},
p2 = {VIRTUAL_WIDTH - LEVEL_RENDER_OFFSET, VIRTUAL_HEIGHT - LEVEL_RENDER_OFFSET},
width = LINE_WIDTH,
face = 'left'
},
{
p1 = {VIRTUAL_WIDTH - LEVEL_RENDER_OFFSET, VIRTUAL_HEIGHT - LEVEL_RENDER_OFFSET},
p2 = {LEVEL_RENDER_OFFSET, VIRTUAL_HEIGHT - LEVEL_RENDER_OFFSET},
width = LINE_WIDTH,
face = 'up'
},
{
p1 = {LEVEL_RENDER_OFFSET, VIRTUAL_HEIGHT - LEVEL_RENDER_OFFSET},
p2 = {LEVEL_RENDER_OFFSET, LEVEL_RENDER_OFFSET_TOP},
width= LINE_WIDTH,
face = 'right'
},
},
spiders = 1,
timeLimit = 60,
balls = 3,
ballSpeed = 45,
},
[5] = {
segments =
{
{
p1 = {LEVEL_RENDER_OFFSET, LEVEL_RENDER_OFFSET_TOP},
p2 = {VIRTUAL_WIDTH - LEVEL_RENDER_OFFSET, LEVEL_RENDER_OFFSET_TOP},
width = LINE_WIDTH,
face = 'down'
},
{
p1 = {VIRTUAL_WIDTH - LEVEL_RENDER_OFFSET, LEVEL_RENDER_OFFSET_TOP},
p2 = {VIRTUAL_WIDTH - LEVEL_RENDER_OFFSET, VIRTUAL_HEIGHT - LEVEL_RENDER_OFFSET},
width = LINE_WIDTH,
face = 'left'
},
{
p1 = {VIRTUAL_WIDTH - LEVEL_RENDER_OFFSET, VIRTUAL_HEIGHT - LEVEL_RENDER_OFFSET},
p2 = {LEVEL_RENDER_OFFSET, VIRTUAL_HEIGHT - LEVEL_RENDER_OFFSET},
width = LINE_WIDTH,
face = 'up'
},
{
p1 = {LEVEL_RENDER_OFFSET, VIRTUAL_HEIGHT - LEVEL_RENDER_OFFSET},
p2 = {LEVEL_RENDER_OFFSET, LEVEL_RENDER_OFFSET_TOP},
width= LINE_WIDTH,
face = 'right'
},
},
spiders = 1,
timeLimit = 65,
balls = 4,
ballSpeed = 50,
},
[6] = {
segments =
{
{
p1 = {LEVEL_RENDER_OFFSET, LEVEL_RENDER_OFFSET_TOP},
p2 = {VIRTUAL_WIDTH - LEVEL_RENDER_OFFSET, LEVEL_RENDER_OFFSET_TOP},
width = LINE_WIDTH,
face = 'down'
},
{
p1 = {VIRTUAL_WIDTH - LEVEL_RENDER_OFFSET, LEVEL_RENDER_OFFSET_TOP},
p2 = {VIRTUAL_WIDTH - LEVEL_RENDER_OFFSET, VIRTUAL_HEIGHT - LEVEL_RENDER_OFFSET},
width = LINE_WIDTH,
face = 'left'
},
{
p1 = {VIRTUAL_WIDTH - LEVEL_RENDER_OFFSET, VIRTUAL_HEIGHT - LEVEL_RENDER_OFFSET},
p2 = {LEVEL_RENDER_OFFSET, VIRTUAL_HEIGHT - LEVEL_RENDER_OFFSET},
width = LINE_WIDTH,
face = 'up'
},
{
p1 = {LEVEL_RENDER_OFFSET, VIRTUAL_HEIGHT - LEVEL_RENDER_OFFSET},
p2 = {LEVEL_RENDER_OFFSET, LEVEL_RENDER_OFFSET_TOP},
width= LINE_WIDTH,
face = 'right'
},
},
spiders = 2,
timeLimit = 75,
balls = 4,
ballSpeed = 50,
},
[7] = {
segments =
{
{
p1 = {LEVEL_RENDER_OFFSET, LEVEL_RENDER_OFFSET_TOP},
p2 = {VIRTUAL_WIDTH - LEVEL_RENDER_OFFSET, LEVEL_RENDER_OFFSET_TOP},
width = LINE_WIDTH,
face = 'down'
},
{
p1 = {VIRTUAL_WIDTH - LEVEL_RENDER_OFFSET, LEVEL_RENDER_OFFSET_TOP},
p2 = {VIRTUAL_WIDTH - LEVEL_RENDER_OFFSET, VIRTUAL_HEIGHT - LEVEL_RENDER_OFFSET},
width = LINE_WIDTH,
face = 'left'
},
{
p1 = {VIRTUAL_WIDTH - LEVEL_RENDER_OFFSET, VIRTUAL_HEIGHT - LEVEL_RENDER_OFFSET},
p2 = {LEVEL_RENDER_OFFSET, VIRTUAL_HEIGHT - LEVEL_RENDER_OFFSET},
width = LINE_WIDTH,
face = 'up'
},
{
p1 = {LEVEL_RENDER_OFFSET, VIRTUAL_HEIGHT - LEVEL_RENDER_OFFSET},
p2 = {LEVEL_RENDER_OFFSET, LEVEL_RENDER_OFFSET_TOP},
width= LINE_WIDTH,
face = 'right'
},
},
spiders = 2,
timeLimit = 90,
balls = 5,
ballSpeed = 60,
},
}

View File

@@ -10,8 +10,8 @@ end
function EntityWalkEdgeState:update(dt)
local dy = self.entity.walkingSpeed * dt
local dx = self.entity.walkingSpeed * dt
local dy = math.floor(self.entity.walkingSpeed * dt + 0.5)
local dx = math.floor(self.entity.walkingSpeed * dt + 0.5)
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

View File

@@ -3,19 +3,17 @@
PlayState = Class {__includes = BaseState}
function PlayState:init()
self.name = 'PlayState'
self.name = "PlayState"
self.stage = 1
self.level = Level(LEVELS_DEF, self.stage)
self.player = Player ( ENTITY_DEFS['player'] , self.level)
self.level = Level(LEVELS_DEF[1], self.stage)
self.player = Player(ENTITY_DEFS["player"], self.level)
self.level.player = self.player
self.balls = {}
self:createBalls()
self.level.balls = self.balls
self.clock = Clock(30,"down")
gSounds['music']:setLooping(true)
gSounds['music']:play()
self.balls = self.level.balls
self.clock = self.level.clock
gSounds["music"]:setPitch(1)
gSounds["music"]:setLooping(true)
gSounds["music"]:play()
end
function PlayState:enter()
@@ -24,45 +22,49 @@ end
function PlayState:update(dt)
self.level:update(dt)
self.player:update(dt)
self.clock:update(dt)
self:updateBalls(dt, self.level)
self:checkEndLevel(self.level)
end
function PlayState:render()
love.graphics.clear(0, 0, 0, 255 / 255)
love.graphics.setColor(40/255, 45/255, 52/255,255/255)
love.graphics.setColor(56 / 255, 56 / 255, 56 / 255, 1)
love.graphics.setFont(gFonts['small'])
love.graphics.printf('Score '..tostring(self.player.score),
0, 5, VIRTUAL_WIDTH, 'left')
love.graphics.setFont(gFonts["small"])
love.graphics.printf("Score " .. tostring(self.player.score), LEVEL_RENDER_OFFSET, LEVEL_RENDER_OFFSET_TOP/2, VIRTUAL_WIDTH, "left")
love.graphics.setFont(gFonts['medium'])
love.graphics.printf('Stage '..tostring(self.stage),
0, 5, VIRTUAL_WIDTH, 'center')
love.graphics.setFont(gFonts["medium"])
love.graphics.printf("Stage " .. tostring(self.stage), 0, 5, VIRTUAL_WIDTH, "center")
self.clock:render()
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 + 5, VIRTUAL_WIDTH - LEVEL_RENDER_OFFSET - 5), --X
math.random(LEVEL_RENDER_OFFSET_TOP + 5, VIRTUAL_HEIGHT - LEVEL_RENDER_OFFSET - 5), --Y
math.random(20,90),math.random(20,90) -- speed
))
end
function PlayState:checkEndLevel(level)
if #self.balls <= 0 then
-- game over - reset level
print("game over due to 0 balls")
self:gameOver()
end
function PlayState:updateBalls(dt, level)
for k,ball in pairs(self.balls) do
ball:update(dt,level)
-- check perimeter of level, if low enough, create new level move on to the next stage
if level:getPerimeter() < 500 then
print("next stage due to perimeter goal reached")
self:nextStage()
end
-- check if clock finished counting limit time
if self.clock.direction == "up" and self.clock.currentTime * 1 > self.clock.timeLimit then
print("game over due to time limit reached")
self:gameOver()
elseif self.clock.direction == "down" and self.clock.currentTime * 1 <= 0 then
print("game over due to time limit reached")
self:gameOver()
elseif self.clock.direction == "up" and self.clock.currentTime * 1 > self.clock.timeLimit - 10 then
gSounds["music"]:setPitch(1.25)
elseif self.clock.direction == "down" and self.clock.currentTime * 1 <= 10 then
gSounds["music"]:setPitch(1.25)
end
-- remove balls outside bounds
for k, ball in pairs(self.balls) do
if ball.remove then
@@ -70,61 +72,56 @@ function PlayState:updateBalls(dt, level)
self.player.score = self.player.score - 1
elseif ball.hitPlayer then
-- check if any ball hit the player trail segments
print("game over due to player hit by ball")
self:gameOver()
end
end
end
function PlayState:checkEndLevel (level)
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
-- check if clock finished counting limit time
if self.clock.direction == 'up' and self.clock.currentTime * 1 > self.clock.timeLimit then
self:gameOver()
elseif self.clock.direction == 'down' and self.clock.currentTime * 1 <= 0 then
self:gameOver()
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)
gSounds["music"]:stop()
gStateStack:push(
GameOverState(self.player.score),
function()
end
)
end
function PlayState:nextStage()
gStateStack:push(FadeOutState({
r = 255/255, g = 255/255, b = 255/255}, 1,function()
gStateStack:push(
FadeOutState(
{
r = 255 / 255,
g = 255 / 255,
b = 255 / 255
},
1,
function()
self.stage = self.stage + 1
self.level = Level(LEVELS_DEF,self.stage)
self.clock = Clock(self.clock.timeLimit + 3 * #self.balls,"down")
local nextDef = self.stage % (#LEVELS_DEF + 1)
if nextDef == 0 then nextDef = 1 end
self.level = Level(LEVELS_DEF[nextDef], self.stage)
self.balls = self.level.balls
self.clock = self.level.clock
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 = 1/255, g = 1/255, b = 1/255}, 1,function()
gSounds['music']:play()
end))
end))
gSounds["music"]:stop()
gSounds["heal"]:play()
gStateStack:push(
FadeOutState(
{r = 1 / 255, g = 1 / 255, b = 1 / 255},
1,
function()
gSounds["music"]:setPitch(1)
gSounds["music"]:play()
end
)
)
end
)
)
end