puju.lua 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204
  1. local puju = {}
  2. function puju:idle()
  3. self.target = self:closest('minion', 'shruju')
  4. if self.target then
  5. self.state = 'move'
  6. end
  7. end
  8. function puju:move()
  9. if self.target and self.target.isMinion and self.target.dead then
  10. self.state = 'idle'
  11. self.target = nil
  12. return
  13. end
  14. self.target = self:closest('minion', 'shruju')
  15. if self.target then
  16. sign = self:signTo(self.target)
  17. else
  18. sign = util.sign(self.destination.x - self.position.x)
  19. end
  20. if sign ~= 0 then
  21. self.animation.flipped = sign > 0
  22. end
  23. local distance = self:distanceTo(self.target)
  24. local angle = self:directionTo(self.target)
  25. local targetVelocityX = math.cos(angle) * self.config.speed * lib.tick.rate
  26. local targetVelocityY = math.sin(angle) * self.config.speed * lib.tick.rate
  27. if self.target.isShruju then
  28. self.velocity.x = util.lerp(self.velocity.x, targetVelocityX, lib.tick.getLerpFactor(self.config.acceleration))
  29. self.velocity.y = util.lerp(self.velocity.y, targetVelocityY, lib.tick.getLerpFactor(self.config.acceleration))
  30. if self:isCarryingShruju(self.target) then
  31. self.state = 'run'
  32. end
  33. if self:distanceTo(self.target) <= self.config.radius + self.target.config.radius then
  34. if self:pickupShruju(self.target) then
  35. self.state = 'run'
  36. end
  37. end
  38. return
  39. end
  40. local distanceFactor = util.clamp((distance - self.config.range) / (self.config.range / 4), 0, 1)
  41. self.velocity.x = util.lerp(self.velocity.x, targetVelocityX, lib.tick.getLerpFactor(self.config.acceleration))
  42. self.velocity.y = util.lerp(self.velocity.y, targetVelocityY, lib.tick.getLerpFactor(self.config.acceleration))
  43. if self:isInRangeOf(self.target) and love.math.random() < lib.tick.rate * 2 and not self.target.isShruju then
  44. self.state = 'attack'
  45. end
  46. end
  47. function puju:attack()
  48. self.attackThread = self.attackThread or lib.quilt.add(function()
  49. self.attackDirection = self:directionTo(self.target)
  50. self.chargeStart = lib.tick.index
  51. self.animation:set('mouthsuck')
  52. coroutine.yield(self.config.chargeTime)
  53. self.animation:set('mouthblow')
  54. self.chargeStart = nil
  55. app.context:addObject(app.spells.seed, {
  56. position = util.copy(self.position),
  57. direction = self.attackDirection,
  58. owner = self
  59. })
  60. self.velocity.x = util.dx(self.config.recoil, self.attackDirection + math.pi)
  61. self.velocity.y = util.dy(self.config.recoil, self.attackDirection + math.pi)
  62. self.yank = -self.velocity.x / 2
  63. coroutine.yield(self.config.attackCooldown)
  64. self.state = 'idle'
  65. self.attackDirection = nil
  66. self.attackThread = nil
  67. end)
  68. self.velocity.x = util.lerp(self.velocity.x, 0, lib.tick.getLerpFactor(self.config.acceleration))
  69. self.velocity.y = util.lerp(self.velocity.y, 0, lib.tick.getLerpFactor(self.config.acceleration))
  70. end
  71. function puju:run()
  72. local targetX, targetY
  73. if self.position.x < app.context.scene.width / 2 then
  74. targetX = 0
  75. targetY = app.context.scene.height / 2
  76. else
  77. targetX = app.context.scene.width
  78. targetY = app.context.scene.height / 2
  79. end
  80. local distance = self:distanceToPoint(targetX, targetY)
  81. local angle = self:directionToPoint(targetX, targetY)
  82. local velocityX = math.cos(angle) * self.config.speed / 3 * lib.tick.rate
  83. local velocityY = math.sin(angle) * self.config.speed / 3 * lib.tick.rate
  84. self.velocity.x = util.lerp(self.velocity.x, velocityX, lib.tick.getLerpFactor(self.config.acceleration))
  85. self.velocity.y = util.lerp(self.velocity.y, velocityY, lib.tick.getLerpFactor(self.config.acceleration))
  86. if distance < 50 then
  87. self.target:die()
  88. self.state = 'idle'
  89. end
  90. end
  91. function puju:isHovered(x, y)
  92. local hoverAllowanceFactor = 1.5
  93. local dis = util.distance(self.position.x, self.position.y, x, y)
  94. local dir = util.angle(self.position.x, self.position.y, x, y)
  95. local ellipseHover = dis < self.config.radius * hoverAllowanceFactor / (2 - math.abs(math.cos(dir)))
  96. local image = app.art.puju
  97. local baseScale = self:getBaseScale()
  98. local scale = g.imageScale(image, 35 * baseScale)
  99. local width, height = self.config.radius * 2, image:getHeight() * scale
  100. local offset = math.sin(lib.tick.index * lib.tick.rate * 3) * 4
  101. local x1 = self.position.x - width / 2
  102. local y1 = self.position.y - 20 + offset - height
  103. local mouseHover = util.inside(x, y, x1, y1, width, height)
  104. return self:isTargetable() and (mouseHover or ellipseHover)
  105. end
  106. function puju:drift()
  107. self.velocity.x = self.velocity.x + math.sin((self.floatOffset + lib.tick.index) * lib.tick.rate * 2) * lib.tick.rate
  108. self.velocity.y = self.velocity.y + math.cos((self.floatOffset + lib.tick.index) * lib.tick.rate * 2) * lib.tick.rate
  109. self.position.x = self.position.x + self.velocity.x
  110. self.position.y = self.position.y + self.velocity.y
  111. local yank = util.clamp(self.velocity.x / (self.config.speed * lib.tick.rate), -1, 1)
  112. self.yank = util.lerp(self.yank, yank, lib.tick.getLerpFactor(.02))
  113. end
  114. function puju:getBaseScale()
  115. return self.chargeStart and 1 + ((lib.tick.index - self.chargeStart) * lib.tick.rate / self.config.chargeTime) * .5 or 1
  116. end
  117. function puju:draw()
  118. local baseScale = self:getBaseScale()
  119. local image = app.art.shadow
  120. local offset = math.sin(lib.tick.index * lib.tick.rate * 3) * 4
  121. local scale = g.imageScale(image, (70 + offset) * baseScale)
  122. g.white(70 * self.alpha)
  123. g.draw(image, self.position.x, self.position.y, 0, scale, scale / 1.5, image:getWidth() / 2, image:getHeight() / 2)
  124. self:drawRing(255, 40, 40)
  125. g.white()
  126. self.animation:tick(lib.tick.delta)
  127. self.animation.skeleton:findBone('body').rotation = 270 + self.yank * 20 * (self.animation.flipped and 1 or -1)
  128. if util.timeSince(self.lastHurt) < self.config.damageFlashDuration then
  129. self.animation:draw(self.position.x, self.position.y)
  130. app.shaders.colorize:send('color', { 1, 1, 1, 1 - util.timeSince(self.lastHurt) / self.config.damageFlashDuration })
  131. g.setShader(app.shaders.colorize)
  132. self.animation:draw(self.position.x, self.position.y)
  133. g.setShader()
  134. else
  135. self.animation:draw(self.position.x, self.position.y)
  136. end
  137. local image = app.art.puju
  138. local scale = g.imageScale(image, 35 * baseScale)
  139. g.white(self.alpha * 255)
  140. --g.draw(image, self.position.x, self.position.y - 20 + offset - image:getHeight() * scale, self.yank * .4, scale * self.direction, scale, image:getWidth() / 2, 0)
  141. return -self.position.y
  142. end
  143. function puju:die()
  144. if self.attackThread then
  145. lib.quilt.remove(self.attackThread)
  146. end
  147. if self.dead then return end
  148. self.dead = true
  149. self:dropShruju()
  150. lib.flux.to(self, .4, { alpha = 0 }):ease('cubicout'):oncomplete(function()
  151. self:remove()
  152. end)
  153. end
  154. return puju