/src/serpent.lua

https://code.google.com/p/area-serpentis/ · Lua · 240 lines · 209 code · 30 blank · 1 comment · 65 complexity · eb29d035a198c523cd0c32b806664b78 MD5 · raw file

  1. Serpent = {}
  2. Serpent_mt = { __index = Serpent }
  3. function Serpent.create(args)
  4. local self = {}
  5. setmetatable(self, Serpent_mt)
  6. self.x = args.x
  7. self.y = args.y
  8. self.dx = args.dx
  9. self.dy = args.dy
  10. self.speed = 200
  11. self.color = args.color
  12. self.text = args.text
  13. self.keys = args.keys
  14. self.ownedPoints = 0
  15. self.captureSound = args.captureSound
  16. self.slowedSound = args.slowedSound
  17. self:resetSegments()
  18. self.intCoords = { }
  19. self.areaTable = { }
  20. self.lastAreaTable = { }
  21. return self
  22. end
  23. function Serpent:resetSegments()
  24. self.segments = { Segment.create(self) }
  25. self.length = 0
  26. self.lenLimit = 300
  27. self.lenLimitDelta = 0
  28. end
  29. AREA_MAX_AGE = 60
  30. function Serpent:update(dt)
  31. local newSegment = false
  32. local distance = self.speed * dt
  33. self.segments[1]:elongate(distance)
  34. self.x = self.x + self.dx * distance
  35. self.y = self.y + self.dy * distance
  36. if love.keyboard.isDown(self.keys.up) and self.dy ~= 1 and self.dy ~= -1 then
  37. self.dx = 0
  38. self.dy = -1
  39. newSegment = true
  40. end
  41. if love.keyboard.isDown(self.keys.down) and self.dy ~= -1 and self.dy ~= 1 then
  42. self.dx = 0
  43. self.dy = 1
  44. newSegment = true
  45. end
  46. if love.keyboard.isDown(self.keys.left) and self.dx ~= 1 and self.dx ~= -1 then
  47. self.dx = -1
  48. self.dy = 0
  49. newSegment = true
  50. end
  51. if love.keyboard.isDown(self.keys.right) and self.dx ~= -1 and self.dx ~= 1 then
  52. self.dx = 1
  53. self.dy = 0
  54. newSegment = true
  55. end
  56. if newSegment then
  57. table.insert(self.segments, 1, Segment.create(self))
  58. sounds.turn:stop()
  59. sounds.turn:setPitch(0.8 + math.random() * 0.4)
  60. sounds.turn:play()
  61. end
  62. local endPos = table.maxn(self.segments)
  63. self.length = self.length + distance
  64. self.lenLimit = self.lenLimit + self.lenLimitDelta
  65. if self.length > self.lenLimit then
  66. distance = self.length - self.lenLimit
  67. self.length = self.lenLimit
  68. while distance > self.segments[endPos].length do
  69. distance = distance - self.segments[endPos].length
  70. table.remove(self.segments, endPos)
  71. endPos = endPos - 1
  72. end
  73. self.segments[endPos]:shorten(distance)
  74. end
  75. self:areaIntersection()
  76. if self:isOutOfBounds() then
  77. self:crashAndRespawn()
  78. elseif self:haveLoop() then
  79. self:splitSerpent()
  80. self:capturePoints()
  81. self:resetSegments()
  82. end
  83. for index, areaSegment in pairs(self.areaTable) do
  84. areaSegment.time = areaSegment.time + dt
  85. if areaSegment:getTimeRemaining() < 0 then
  86. table.remove(self.areaTable, index)
  87. end
  88. end
  89. end
  90. function Serpent:areaIntersection()
  91. local intCoords = {x, y, segment1, t, s }
  92. local breakLoop = false
  93. for index0, selfSegment in pairs(self.segments) do
  94. for index1, otherSerpent in pairs(snakes) do
  95. for index2, otherSegment in pairs(otherSerpent.areaTable) do
  96. if otherSerpent ~= self then
  97. intCoords = selfSegment:isIntersecting(otherSegment)
  98. if intCoords.x ~= -1 and intCoords.y ~= -1 then
  99. self.speed = 50
  100. self.slowedSound:stop()
  101. breakLoop = true
  102. break
  103. else
  104. self.speed = 200
  105. if self.slowedSound:isStopped() then
  106. self.slowedSound:play()
  107. end
  108. end
  109. end
  110. end
  111. if breakLoop then
  112. break
  113. end
  114. end
  115. if breakLoop then
  116. break
  117. end
  118. end
  119. end
  120. function Serpent:splitSerpent()
  121. local vector1X
  122. local vector1Y
  123. self.lastAreaTable = {}
  124. for index, segment in ipairs(self.segments) do
  125. if index <= self.intCoords.segment1 then
  126. if index == 1 then
  127. segment.length = self.intCoords.s
  128. vector1X = segment.x + segment.dx * segment.length
  129. vector1Y = segment.y + segment.dy * segment.length
  130. elseif index == self.intCoords.segment1 then
  131. segment:shorten(self.intCoords.t)
  132. end
  133. table.insert(self.areaTable, 1, segment)
  134. table.insert(self.lastAreaTable, segment)
  135. table.remove(self.segments, index)
  136. table.insert(self.segments, 1, Segment.create(self))
  137. end
  138. end
  139. end
  140. function Serpent:capturePoints()
  141. local capturedPoints = 0
  142. for index, point in ipairs(points) do
  143. if point:isInsideSegmentLoop(self.lastAreaTable) then
  144. point:setOwner(self)
  145. capturedPoints = capturedPoints + 1
  146. end
  147. end
  148. if capturedPoints > 0 then
  149. self.captureSound:stop()
  150. self.captureSound:play()
  151. end
  152. sounds.createLoop:stop()
  153. sounds.createLoop:play()
  154. end
  155. function Serpent:haveLoop()
  156. for index, segment in ipairs(self.segments) do
  157. intCoords = self.segments[1]:isIntersecting(segment)
  158. if index >= 4 and intCoords.x ~= -1 and intCoords.y ~= -1 then
  159. intCoords.segment1 = index
  160. self.intCoords = intCoords
  161. return true
  162. end
  163. end
  164. return false
  165. end
  166. function Serpent:isOutOfBounds()
  167. return (self.x < 0 or self.x > WIDTH or self.y < 0 or self.y > HEIGHT)
  168. end
  169. Serpent.headSize = 3
  170. Serpent.bodyWidth = 2
  171. function Serpent:crashAndRespawn()
  172. if self.x < 0 then self.x = 0 end
  173. if self.x > WIDTH then self.x = WIDTH end
  174. if self.y < 0 then self.y = 0 end
  175. if self.y > HEIGHT then self.y = HEIGHT end
  176. self.dx = -self.dx
  177. self.dy = -self.dy
  178. self:resetSegments()
  179. sounds.crash:stop()
  180. sounds.crash:play()
  181. end
  182. function Serpent:draw()
  183. love.graphics.setColor(self.color[1], self.color[2], self.color[3], 255)
  184. for index, segment in ipairs(self.segments) do
  185. love.graphics.setLineWidth(self.bodyWidth)
  186. love.graphics.line(segment.x, segment.y, segment:getEndX(), segment:getEndY())
  187. end
  188. for index, segment in ipairs(self.areaTable) do
  189. love.graphics.setLineWidth(1)
  190. local time = segment:getTimeRemaining()
  191. if (time < 5 and math.fmod(time, 1) > 0.5) then
  192. love.graphics.setColor(self.color[1], self.color[2], self.color[3], 128)
  193. else
  194. love.graphics.setColor(self.color[1], self.color[2], self.color[3], 255)
  195. end
  196. love.graphics.line(segment.x, segment.y, segment:getEndX(), segment:getEndY())
  197. end
  198. love.graphics.setColor(self.color[1], self.color[2], self.color[3], 255)
  199. love.graphics.circle("fill", self.x, self.y, self.headSize)
  200. love.graphics.setFont(fonts.serpentFont)
  201. love.graphics.print(self.text, self.x + self.headSize, self.y + self.headSize)
  202. end
  203. function Serpent:changeOwnedPoints(delta)
  204. self.ownedPoints = self.ownedPoints + delta
  205. --Debug:log(self.text .. ": " .. self.ownedPoints .. " points")
  206. end
  207. WINNING_POINTS_FRACTION = 0.2
  208. function Serpent:isWinner()
  209. return self.ownedPoints > table.maxn(points) * WINNING_POINTS_FRACTION
  210. end