ghost-player.lua 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147
  1. GhostPlayer = class()
  2. GhostPlayer.radius = 48
  3. GhostPlayer.first = true
  4. function GhostPlayer:init(owner)
  5. self.owner = owner
  6. self.x = owner.x
  7. self.y = owner.y + owner.height
  8. self.alpha = 0
  9. self.vx = 0
  10. self.vy = -600
  11. self.prevx = self.x
  12. self.prevy = self.y
  13. self.prevalpha = self.alpha
  14. self.image = data.media.graphics.spiritMuju
  15. self.angle = -math.pi / 2
  16. self.maxRange = ctx.map.height - ctx.map.groundHeight
  17. self.maxDis = lume.lerp(self.maxRange, 0, (1 - (self.owner.deathTimer / self.owner.deathDuration)) ^ 5)
  18. local sound = ctx.sound:play('spirit')
  19. if sound then sound:setVolume(.12) end
  20. if not ctx.effects.active then ctx.view:register(self) end
  21. end
  22. function GhostPlayer:update()
  23. self.prevx = self.x
  24. self.prevy = self.y
  25. self.prevalpha = self.alpha
  26. local px, py = self.owner.x, self.owner.y + self.owner.height
  27. if lume.distance(self.x, self.y, px, py) < self.radius + 16 and self.owner:hasShruju('diffuse') and self.owner.deathDuration - self.owner.deathTimer > 1 then
  28. self.owner:spawn()
  29. return
  30. end
  31. local speed = 140 * self.owner.ghostSpeedMultiplier
  32. local gx, gy = 0, 0
  33. if self.owner.joystick then
  34. gx, gy = self.owner.joystick:getGamepadAxis('leftx'), self.owner.joystick:getGamepadAxis('lefty')
  35. if math.abs(gx) < .1 then gx = 0 end
  36. if math.abs(gy) < .1 then gy = 0 end
  37. end
  38. if gx ~= 0 or gy ~= 0 then
  39. self.vx = lume.lerp(self.vx, speed * gx, 8 * ls.tickrate)
  40. self.vy = lume.lerp(self.vy, speed * gy, 8 * ls.tickrate)
  41. if gx < 0 then
  42. self.angle = math.anglerp(self.angle, -math.pi / 2 - (math.pi / 7 * (self.vx / -speed)), 12 * ls.tickrate)
  43. elseif gx > 0 then
  44. self.angle = math.anglerp(self.angle, -math.pi / 2 + (math.pi / 7 * (self.vx / speed)), 12 * ls.tickrate)
  45. end
  46. else
  47. if love.keyboard.isDown('left', 'a') then
  48. self.vx = lume.lerp(self.vx, -speed, 8 * ls.tickrate)
  49. self.angle = math.anglerp(self.angle, -math.pi / 2 - (math.pi / 7 * (self.vx / -speed)), 12 * ls.tickrate)
  50. elseif love.keyboard.isDown('right', 'd') then
  51. self.vx = lume.lerp(self.vx, speed, 8 * ls.tickrate)
  52. self.angle = math.anglerp(self.angle, -math.pi / 2 + (math.pi / 7 * (self.vx / speed)), 12 * ls.tickrate)
  53. else
  54. self.vx = lume.lerp(self.vx, 0, 2 * ls.tickrate)
  55. end
  56. if love.keyboard.isDown('up', 'w') then
  57. self.vy = lume.lerp(self.vy, -speed, 8 * ls.tickrate)
  58. elseif love.keyboard.isDown('down', 's') then
  59. self.vy = lume.lerp(self.vy, speed, 8 * ls.tickrate)
  60. else
  61. self.vy = lume.lerp(self.vy, 0, 2 * ls.tickrate)
  62. end
  63. end
  64. local len = (self.vx ^ 2 + self.vy ^ 2) ^ .5
  65. if len > 0 and self.owner.deathTimer < self.owner.deathDuration - 1 then
  66. self.vx = (self.vx / len) * math.min(len, speed)
  67. self.vy = (self.vy / len) * math.min(len, speed)
  68. end
  69. self.x = self.x + self.vx * ls.tickrate
  70. self.y = self.y + self.vy * ls.tickrate
  71. local contained = false
  72. self.maxDis = lume.lerp(self.maxRange, 0, (1 - (self.owner.deathTimer / self.owner.deathDuration)) ^ 5)
  73. if lume.distance(self.x, self.y, px, py) + self.radius > self.maxDis then
  74. local angle = lume.angle(px, py, self.x, self.y)
  75. self.x = lume.lerp(self.x, px + math.dx(self.maxDis - self.radius, angle), 1)
  76. self.y = lume.lerp(self.y, py + math.dy(self.maxDis - self.radius, angle), 1)
  77. contained = true
  78. end
  79. self.x = math.clamp(self.x, self.radius, ctx.map.width - self.radius)
  80. self.y = math.clamp(self.y, self.radius, ctx.map.height - self.radius - ctx.map.groundHeight)
  81. local scale = math.min(self.owner.deathTimer, 2) / 2
  82. if self.owner.deathDuration - self.owner.deathTimer < 1 then
  83. scale = self.owner.deathDuration - self.owner.deathTimer
  84. end
  85. if ctx.tutorial.active then scale = 1 end
  86. scale = .4 + scale * .4
  87. self.radius = 40 * scale
  88. self.alpha = math.min(self.alpha + ls.tickrate, 1)
  89. ctx.particles:emit('ghosttrail', self.x, self.y, math.min(lume.round(len / speed, 1) + (contained and 1 or 0)), {
  90. linearAcceleration = {-self.vx * 1, -self.vy * 1, -self.vx * 1.5, -self.vy * 1.5}
  91. })
  92. end
  93. function GhostPlayer:despawn()
  94. GhostPlayer.first = false
  95. if not ctx.effects.active then ctx.view:unregister(self) end
  96. end
  97. function GhostPlayer:draw()
  98. local g = love.graphics
  99. local x, y = lume.lerp(self.prevx, self.x, ls.accum / ls.tickrate), lume.lerp(self.prevy, self.y, ls.accum / ls.tickrate)
  100. local alpha = lume.lerp(self.prevalpha, self.alpha, ls.accum / ls.tickrate)
  101. local scale = math.min(self.owner.deathTimer, 2) / 2
  102. if self.owner.deathDuration - self.owner.deathTimer < 1 then
  103. scale = self.owner.deathDuration - self.owner.deathTimer
  104. end
  105. if ctx.tutorial.active then scale = 1 end
  106. scale = .4 + scale * .4
  107. local alphaScale = math.min(self.owner.deathTimer * 6 / self.owner.deathDuration, 1)
  108. local color = {128, 0, 255}
  109. color = table.interpolate(color, {255, 255, 255}, .8)
  110. color[4] = 200 * alpha * alphaScale
  111. g.setColor(color)
  112. g.draw(self.image, x, y, self.angle, .6 * scale, .6 * scale, self.image:getWidth() / 2, self.image:getHeight() / 2)
  113. g.setBlendMode('additive')
  114. color[4] = 75 * alpha * alphaScale
  115. g.setColor(color)
  116. g.draw(self.image, x, y, self.angle, .75 * scale, .75 * scale, self.image:getWidth() / 2, self.image:getHeight() / 2)
  117. color[4] = 30 * alpha * alphaScale
  118. g.setColor(color)
  119. g.draw(self.image, x, y, self.angle, 1 * scale, 1 * scale, self.image:getWidth() / 2, self.image:getHeight() / 2)
  120. g.setBlendMode('alpha')
  121. g.setColor(255, 255, 255, 15)
  122. g.arc('fill', self.owner.x, self.owner.y + self.owner.height, self.maxDis, 0, -math.pi, self.maxDis / 2)
  123. end