entity.lua 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262
  1. local entity = {}
  2. function entity:isHovered(x, y)
  3. local hoverAllowanceFactor = 2
  4. local dis = util.distance(self.position.x, self.position.y, x, y)
  5. local dir = util.angle(self.position.x, self.position.y, x, y)
  6. local ellipseHover = dis < self.config.radius * hoverAllowanceFactor / (2 - math.abs(math.cos(dir)))
  7. return self:isTargetable() and ((self.animation and self.animation:contains(x, y)) or ellipseHover)
  8. end
  9. function entity:isTargetable()
  10. return true
  11. end
  12. function entity:drawRing(r, gg, b)
  13. local mx, my = app.context.view:worldPoint(love.mouse.getPosition())
  14. local isCasting = app.context.abilities:isCasting() and app.context.abilities.owner == self
  15. local isTargeted = app.context.abilities:isCasting() and self.isEnemy and lib.target.objectAtPosition(mx, my) == self
  16. local alpha = (isCasting or isTargeted) and 1 or 0.5
  17. self.ringAlpha = util.lerp(self.ringAlpha or 0, alpha, 8 * lib.tick.delta)
  18. local radius = self.config.radius * (1 + .5 * (self.ringAlpha - .5))
  19. if app.context.abilities:isValidCastTarget(self) then
  20. radius = radius * (1 + math.abs(math.sin(lib.tick.index * lib.tick.rate * 4)) * .2)
  21. g.white(50)
  22. g.ellipse('fill', self.position.x, self.position.y, radius, radius / 2)
  23. else
  24. radius = radius * (1 + math.abs(math.sin(lib.tick.index * lib.tick.rate * 4)) * .05)
  25. end
  26. g.setColor(r, gg, b, self.ringAlpha * 80)
  27. g.setLineWidth(4 + 3 * alpha)
  28. g.ellipse('line', self.position.x, self.position.y, radius, radius / 2)
  29. g.white(self.ringAlpha * 160)
  30. g.setLineWidth(2 + 2 * (alpha - .5))
  31. g.ellipse('line', self.position.x, self.position.y, radius, radius / 2)
  32. g.setLineWidth(1)
  33. end
  34. function entity.closest(source, ...)
  35. local getEntries = {
  36. enemy = function(source, result)
  37. util.each(util.filter(app.context.objects, 'isEnemy'), function(enemy)
  38. if source ~= enemy then
  39. table.insert(result, {enemy, entity.distanceTo(source, enemy)})
  40. end
  41. end)
  42. end,
  43. minion = function(source, result)
  44. util.each(util.filter(app.context.objects, 'isMinion'), function(minion)
  45. if source ~= minion then
  46. table.insert(result, {minion, entity.distanceTo(source, minion)})
  47. end
  48. end)
  49. end,
  50. player = function(source, result)
  51. local player = app.context.objects.muju
  52. if source ~= player then
  53. table.insert(result, {player, entity.distanceTo(source, player)})
  54. end
  55. end,
  56. shruju = function(source, result)
  57. util.each(util.filter(app.context.objects, 'isShruju'), function(shruju)
  58. if source ~= shruju and not shruju.carrier then
  59. table.insert(result, {shruju, entity.distanceTo(source, shruju)})
  60. end
  61. end)
  62. end
  63. }
  64. local kinds = {...}
  65. local targets = {}
  66. util.each(kinds, function(kind) getEntries[kind](source, targets) end)
  67. table.sort(targets, function(a, b) return a[2] < b[2] end)
  68. if targets[1] then return unpack(targets[1]) end
  69. return nil
  70. end
  71. function entity.closestToPoint(x, y, ...)
  72. local getEntries = {
  73. enemy = function(result)
  74. util.each(util.filter(app.context.objects, 'isEnemy'), function(enemy)
  75. table.insert(result, {enemy, entity.distanceToPoint(enemy, x, y)})
  76. end)
  77. end,
  78. minion = function(result)
  79. util.each(util.filter(app.context.objects, 'isMinion'), function(minion)
  80. table.insert(result, {minion, entity.distanceToPoint(minion, x, y)})
  81. end)
  82. end,
  83. player = function(result)
  84. local player = app.context.objects.muju
  85. table.insert(result, {player, entity.distanceToPoint(player, x, y)})
  86. end,
  87. shruju = function(result)
  88. util.each(util.filter(app.context.objects, 'isShruju'), function(shruju)
  89. if not shruju.carrier then
  90. table.insert(result, {shruju, entity.distanceToPoint(shruju, x, y)})
  91. end
  92. end)
  93. end
  94. }
  95. local kinds = {...}
  96. local targets = {}
  97. util.each(kinds, function(kind) getEntries[kind](targets) end)
  98. table.sort(targets, function(a, b) return a[2] < b[2] end)
  99. if targets[1] then return unpack(targets[1]) end
  100. return nil
  101. end
  102. function entity.inRange(x, y, range, ...)
  103. local getEntries = {
  104. enemy = function(result)
  105. util.each(util.filter(app.context.objects, 'isEnemy'), function(enemy)
  106. local distance = entity.distanceToPoint(enemy, x, y)
  107. if distance <= range then
  108. table.insert(result, enemy)
  109. end
  110. end)
  111. end,
  112. minion = function(result)
  113. util.each(util.filter(app.context.objects, 'isMinion'), function(minion)
  114. local distance = entity.distanceToPoint(minion, x, y)
  115. if distance <= range then
  116. table.insert(result, minion)
  117. end
  118. end)
  119. end,
  120. player = function(result)
  121. local player = app.context.objects.muju
  122. local distance = entity.distanceToPoint(player, x, y)
  123. if distance <= range then
  124. table.insert(result, player)
  125. end
  126. end,
  127. shruju = function(result)
  128. util.each(util.filter(app.context.objects, 'isShruju'), function(shruju)
  129. local distance = entity.distanceToPoint(shruju, x, y)
  130. if distance <= range and not shruju.carrier then
  131. table.insert(result, shruju)
  132. end
  133. end)
  134. end
  135. }
  136. local kinds = {...}
  137. local targets = {}
  138. util.each(kinds, function(kind) getEntries[kind](targets) end)
  139. return targets
  140. end
  141. function entity:distanceTo(other)
  142. return entity.distanceToPoint(self, other.position.x, other.position.y)
  143. end
  144. function entity:distanceToPoint(x, y)
  145. return util.distance(self.position.x, self.position.y, x, y)
  146. end
  147. function entity:directionTo(other)
  148. return entity.directionToPoint(self, other.position.x, other.position.y)
  149. end
  150. function entity:directionToPoint(x, y)
  151. return util.angle(self.position.x, self.position.y, x, y)
  152. end
  153. function entity:signTo(other)
  154. return -util.sign(self.position.x - other.position.x)
  155. end
  156. function entity:isInRangeOf(other)
  157. return self:distanceTo(other) < self.config.range
  158. end
  159. function entity:moveIntoRangeOf(other, speed)
  160. if not entity.isInRangeOf(self, other) then
  161. entity.moveTowards(self, other, speed)
  162. end
  163. end
  164. function entity:moveTowards(other, speed)
  165. return entity.moveTowardsPoint(self, other.position.x, other.position.y, speed)
  166. end
  167. function entity:moveTowardsPoint(x, y, speed)
  168. local distance, direction = lib.entity.distanceToPoint(self, x, y), lib.entity.directionToPoint(self, x, y)
  169. speed = math.min(distance, speed)
  170. self.position.x = self.position.x + math.cos(direction) * speed
  171. self.position.y = self.position.y + math.sin(direction) * speed
  172. end
  173. function entity:moveWithSpeed(speed, y)
  174. local x
  175. if y then
  176. x = speed
  177. else
  178. x, y = speed.x, speed.y
  179. end
  180. self.position.x = self.position.x + x * lib.tick.rate
  181. self.position.y = self.position.y + y * lib.tick.rate
  182. end
  183. function entity:moveInDirection(direction, speed)
  184. self:moveWithSpeed(util.dx(speed, direction), util.dy(speed, direction))
  185. end
  186. function entity:isEscaped()
  187. if self.config.shape == 'circle' or self.config.shape == 'ellipse' then
  188. local r = self.config.radius
  189. local x, y = self.position.x, self.position.y
  190. local w, h = app.context.scene.width, app.context.scene.height
  191. local x1, y1, x2, y2 = x - r, y - r, x + r, y + r
  192. return x1 < 0 or y1 < 0 or x2 > w or y2 > h
  193. end
  194. return false
  195. end
  196. function entity:enclose()
  197. if self.config.shape == 'circle' or self.config.shape == 'ellipse' then
  198. local r = self.config.radius
  199. local x, y = self.position.x, self.position.y
  200. local w, h = app.context.scene.width, app.context.scene.height
  201. self.position.x = util.clamp(x, r, w - r)
  202. self.position.y = util.clamp(y, r, h - r)
  203. end
  204. end
  205. function entity:remove()
  206. self:unbind()
  207. app.context:removeObject(self)
  208. end
  209. -- Sugar
  210. function entity:canCast(ability, ...)
  211. return ability:canCast(self, ...)
  212. end
  213. function entity:cast(ability, ...)
  214. return ability:cast(self, ...)
  215. end
  216. return entity