manager.lua 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255
  1. local abilities = lib.object.create()
  2. local function isLeft(x, y, b)
  3. return b == 1
  4. end
  5. local function hasTouch(id)
  6. return util.find(love.touch.getTouches(), id) or (id == 'm' and love.mouse.isDown(1))
  7. end
  8. function abilities:isCasting()
  9. return self.casting
  10. end
  11. function abilities:init()
  12. self.selected = nil
  13. self.casts = {}
  14. -- TODO group these eventually once UI is decided
  15. self.list = {
  16. app.abilities.summon:new(),
  17. app.abilities.heal:new(),
  18. app.abilities.burst:new()
  19. }
  20. end
  21. function abilities:bind()
  22. local abilityCast, autoCast = love.touchpressed
  23. :map(function(id, x, y)
  24. return id, lib.target.objectAtPosition(app.context.view:worldPoint(x, y))
  25. end)
  26. :filter(function(id, owner)
  27. return owner and (owner.isMinion or owner == app.context.objects.muju)
  28. end)
  29. :partition(function()
  30. return self.selected
  31. end)
  32. return {
  33. abilityCast
  34. :filter(function(id, owner)
  35. return owner:canCast(self.selected)
  36. end)
  37. :tap(function(id, owner)
  38. local ox, oy = app.context.view:worldPoint(love.touch.getPosition(id))
  39. self.casts[id] = {
  40. id = id,
  41. ability = self.selected,
  42. owner = owner,
  43. ox = ox,
  44. oy = oy,
  45. active = true,
  46. factor = 0,
  47. tick = lib.tick.index
  48. }
  49. lib.flux.to(self.casts[id], .3, { factor = 1 }):ease('backinout')
  50. end)
  51. :flatMap(function(id, owner)
  52. return love.touchreleased
  53. :filter(f.eq(id))
  54. :take(1)
  55. end)
  56. :subscribe(function(id, x, y)
  57. local mx, my = app.context.view:worldPoint(x, y)
  58. local cast = self.casts[id]
  59. if not cast then return end
  60. cast.active = false
  61. cast.x = mx
  62. cast.y = my
  63. cast.ability:cast(cast.owner, mx, my)
  64. lib.flux.to(cast, .35, { factor = 0 })
  65. :ease('cubicout')
  66. :oncomplete(function()
  67. if not hasTouch(id) then
  68. self.casts[id] = nil
  69. end
  70. end)
  71. self.selected = nil
  72. end),
  73. love.touchreleased
  74. :filter(function(id)
  75. local casting = util.match(self.casts, function(cast) return cast.active and cast.id == id end)
  76. return self.selected and not casting
  77. end)
  78. :subscribe(function()
  79. self.selected = nil
  80. end),
  81. autoCast
  82. :filter(function(id, owner)
  83. return owner.isMinion or owner:canCast(self.list[1])
  84. end)
  85. :tap(function(id, owner)
  86. local ox, oy = app.context.view:worldPoint(love.touch.getPosition(id))
  87. self.casts[id] = {
  88. id = id,
  89. ability = nil,
  90. owner = owner,
  91. ox = ox,
  92. oy = oy,
  93. active = true,
  94. factor = 0,
  95. tick = lib.tick.index
  96. }
  97. lib.flux.to(self.casts[id], .3, { factor = 1 }):ease('backinout')
  98. end)
  99. :flatMap(function(id)
  100. return love.touchreleased
  101. :filter(f.eq(id))
  102. :take(1)
  103. end)
  104. :subscribe(function(id, x, y)
  105. local mx, my = app.context.view:worldPoint(x, y)
  106. local cast = self.casts[id]
  107. cast.active = false
  108. cast.x = mx
  109. cast.y = my
  110. if cast.owner.isMinion then
  111. cast.owner:command(mx, my)
  112. else
  113. cast.owner:cast(self.list[1], mx, my)
  114. end
  115. lib.flux.to(cast, .35, { factor = 0 })
  116. :ease('cubicout')
  117. :oncomplete(function()
  118. if not hasTouch(id) then
  119. self.casts[id] = nil
  120. end
  121. end)
  122. end),
  123. app.context.view.draw:subscribe(self:wrap(self.draw))
  124. }
  125. end
  126. function abilities:draw()
  127. util.each(self.casts, function(cast)
  128. if cast.factor > 0 and cast.owner then
  129. local ox, oy = cast.owner.position.x, cast.owner.position.y
  130. local points = {}
  131. local radius = 35
  132. local tx, ty
  133. if cast.active and (util.find(love.touch.getTouches(), cast.id) or cast.id == 'm') then
  134. tx, ty = app.context.view:worldPoint(love.touch.getPosition(cast.id))
  135. else
  136. radius = radius + 20 * (1 - cast.factor)
  137. tx = cast.x
  138. ty = cast.y
  139. end
  140. if not tx or not ty then return end
  141. local entity = lib.target.objectAtPosition(tx, ty)
  142. if entity and (entity.isEnemy or entity.isShruju) then
  143. tx = util.lerp(tx, entity.position.x, .5)
  144. ty = util.lerp(ty, entity.position.y, .5)
  145. radius = radius + 10
  146. end
  147. local dir = util.angle(ox, oy, tx, ty)
  148. local pointCount = 80
  149. for i = 1, 80 do
  150. local x = tx + util.dx(radius, dir + (2 * math.pi * (i / 80)))
  151. local y = ty + util.dy(radius, dir + (2 * math.pi * (i / 80)))
  152. local mouseDir = util.angle(x, y, tx, ty)
  153. if util.distance(ox, oy, tx, ty) >= radius then
  154. local max = math.pi / 2 + (math.pi / 2) * util.distance(ox, oy, tx, ty) / 500 -- how bulbous it is
  155. local dif = (max - util.clamp(math.abs(util.anglediff(mouseDir, dir)), 0, max)) / max
  156. if cast.active then
  157. x = util.lerp(ox, x, cast.factor ^ 2)
  158. y = util.lerp(oy, y, cast.factor ^ 2)
  159. end
  160. x = util.lerp(x, ox, dif ^ 5)
  161. y = util.lerp(y, oy, dif ^ 5)
  162. end
  163. if util.distance(x, y, ox, oy) < 1 then
  164. table.insert(points, x)
  165. table.insert(points, y)
  166. else
  167. if util.distance(ox, oy, tx, ty) >= radius then
  168. local sign = util.sign(util.anglediff(mouseDir, dir))
  169. x = x + 2 * math.cos(dir + (math.pi / 2) * sign)
  170. y = y + 2 * math.sin(dir + (math.pi / 2) * sign)
  171. end
  172. table.insert(points, x)
  173. table.insert(points, y)
  174. end
  175. end
  176. if #points >= 3 then
  177. g.white(40 * cast.factor)
  178. g.setLineWidth(3)
  179. g.polygon('fill', points)
  180. g.setLineWidth(1)
  181. end
  182. local image, color, angle
  183. if self.selected then
  184. image = app.art.icons[self.selected.tag]
  185. color = { 255, 255, 255 }
  186. angle = 0
  187. else
  188. if cast.owner.isMinion then
  189. image = app.art.icons.command
  190. color = (entity and entity.isEnemy) and { 255, 140, 140 } or { 140, 255, 140 }
  191. angle = dir - math.pi / 4
  192. else
  193. image = app.art.icons.summon
  194. color = { 255, 255, 255 }
  195. angle = 0
  196. end
  197. end
  198. local w, h = image:getDimensions()
  199. local size = .75 * 2 * radius * cast.factor
  200. local scale = size / ((w > h) and w or h)
  201. g.setColor(g.alpha(color, 255 * cast.factor ^ 3))
  202. g.draw(image, tx, ty, angle, scale, scale, w / 2, h / 2)
  203. end
  204. end)
  205. return -1000
  206. end
  207. function abilities:isValidCastTarget(entity)
  208. --[[util.match(self.casts, function(cast)
  209. print(cast.ability)
  210. return cast.ability and cast.ability:canCast(entity)
  211. end)]]
  212. return self.selected and self.selected:canCast(entity)
  213. end
  214. return abilities