unit.lua 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181
  1. local unit = {}
  2. function unit:command(x, y)
  3. local entity = lib.target.objectAtPosition(x, y)
  4. -- Interrupt attacks
  5. if self.attackCooldown then
  6. lib.quilt.remove(self.attackCooldown)
  7. self.attackCooldown = nil
  8. self.state = 'move'
  9. self.attacking = nil
  10. end
  11. if entity and (entity.isEnemy or entity.isShruju) then
  12. self.target = entity
  13. else
  14. -- Moving to a juju picks it up
  15. if util.isa(entity, app.juju) then
  16. self.target = entity
  17. return
  18. end
  19. local muju = app.context.objects.muju
  20. local distance, angle = util.vector(x, y, muju.position.x, muju.position.y)
  21. local minDistance = self.config.radius + muju.config.radius
  22. if distance * (2 - math.abs(math.sin(angle))) < minDistance * (2 - math.abs(math.sin(angle))) then
  23. x = muju.position.x + util.dx(minDistance + 4, angle + math.pi)
  24. y = muju.position.y + util.dy(minDistance + 4, angle + math.pi) / 2
  25. end
  26. self.destination.x = x
  27. self.destination.y = y
  28. self.target = nil
  29. end
  30. end
  31. function unit:resolveCollision(other, dx, dy)
  32. if other.isMinion or other.isEnemy then
  33. local x1, y1, x2, y2 = self.position.x, self.position.y, other.position.x, other.position.y
  34. local myFactor, theirFactor
  35. local selfIsMoving, otherIsMoving = self:isMoving(), other:isMoving()
  36. if selfIsMoving and not otherIsMoving then
  37. myFactor, theirFactor = 0, .75
  38. elseif not selfIsMoving and otherIsMoving then
  39. myFactor, theirFactor = .75, 0
  40. elseif selfIsMoving and otherIsMoving then
  41. myFactor, theirFactor = 0, 0
  42. else
  43. myFactor, theirFactor = .5, .5
  44. end
  45. self.position.x = util.lerp(self.position.x, self.position.x - dx * myFactor, .05)
  46. self.position.y = util.lerp(self.position.y, self.position.y - dy * myFactor, .05)
  47. other.position.x = util.lerp(other.position.x, other.position.x + dx * theirFactor, .05)
  48. other.position.y = util.lerp(other.position.y, other.position.y + dy * theirFactor, .05)
  49. if not selfIsMoving and self.destination then
  50. self.destination.x = self.destination.x + (self.position.x - x1)
  51. self.destination.y = self.destination.y + (self.position.y - y1)
  52. end
  53. if not otherIsMoving and other.destination then
  54. other.destination.x = other.destination.x + (other.position.x - x2)
  55. other.destination.y = other.destination.y + (other.position.y - y2)
  56. end
  57. end
  58. end
  59. function unit:isMoving()
  60. return self.state == 'move'
  61. end
  62. function unit:flipAnimation()
  63. local sign
  64. if self.target then
  65. sign = self:signTo(self.target)
  66. else
  67. sign = util.sign(self.destination.x - self.position.x)
  68. end
  69. if self.buffs and self.buffs:getFear() then
  70. sign = -sign
  71. end
  72. if sign ~= 0 then
  73. self.animation.flipped = sign < 0
  74. end
  75. end
  76. function unit:randomizeStats(stat, ...)
  77. if not stat then return end
  78. local val = self[stat]
  79. self[stat] = util.clamp(love.math.randomNormal(val, val * .1), val - val * .33, val + val * .33)
  80. return self:randomizeStats(...)
  81. end
  82. function unit:isInvincible()
  83. return self.state == 'spawn' or (self.lastHurt and self.config.hurtGrace and (lib.tick.index - self.lastHurt) * lib.tick.rate <= self.config.hurtGrace)
  84. end
  85. function unit:hurt(amount, source)
  86. if self:isInvincible() then return end
  87. self.health = self.health - amount
  88. if self.lastHurt then
  89. self.lastHurt = lib.tick.index
  90. end
  91. if source.isMinion or source.isEnemy then
  92. if not self.target and self:distanceToPoint(self.destination.x, self.destination.y) == 0 then
  93. self.target = source
  94. end
  95. end
  96. if self.health <= 0 then
  97. self:die()
  98. end
  99. end
  100. function unit:heal(amount, source)
  101. self.health = math.min(self.health + amount, self.config.maxHealth)
  102. end
  103. function unit:die()
  104. self:dropShruju()
  105. if not self.dead then
  106. self.dead = true
  107. self.animation:set('death')
  108. end
  109. end
  110. function unit:remove()
  111. self.dead = true
  112. return lib.entity.remove(self)
  113. end
  114. function unit:runIfFeared()
  115. local fear = self.buffs:getFear()
  116. if fear then
  117. local direction = self:directionTo(fear.source)
  118. self:moveInDirection(direction + math.pi, self.config.speed / 2)
  119. return true
  120. end
  121. end
  122. function unit:drawBuffs()
  123. local fear = self.buffs:getFear()
  124. if fear then
  125. g.white()
  126. local image = app.art.fear
  127. local scale = g.imageScale(image, 40 + (1 + math.cos(math.sin(lib.tick.index) / 3) / 5))
  128. g.draw(image, self.position.x, self.position.y - 80, math.cos(lib.tick.index / 3) / 6, scale, scale, 53, 83)
  129. end
  130. end
  131. function unit:isCarryingShruju(target)
  132. return util.match(app.context.objects, function(object)
  133. return util.isa(object, app.shruju.object) and object.carrier == self and (not target or object == target)
  134. end)
  135. end
  136. function unit:pickupShruju(shruju)
  137. return shruju:pickup(self)
  138. end
  139. function unit:dropShruju()
  140. local shruju = self:isCarryingShruju()
  141. if shruju then
  142. shruju.carrier = nil
  143. end
  144. end
  145. return unit