mobile.lua 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176
  1. local controllers = require 'app/controllers'
  2. local cry = require 'app/cry'
  3. local sleep = require 'app/sleep'
  4. local play = require 'app/play'
  5. local vec3 = require('cpml').vec3
  6. local mat4 = require('cpml').mat4
  7. local mobile = {}
  8. function mobile:init()
  9. self.isEntered = false
  10. self.position = { 0, 11.8, 0 }
  11. self.size = 1
  12. self.rotateSpeed = .5
  13. self.angle = 3 * math.pi / 180
  14. self.model = lovr.graphics.newModel('art/mobile.obj')
  15. self.model:setMaterial(lovr.graphics.newMaterial('art/mobile_DIFF.png'))
  16. self.toys = {
  17. submarine = {
  18. scale = .5,
  19. position = { 0, 0, 0 },
  20. wonPosition = { -.75, .6, 2.2 },
  21. stringLength = 10,
  22. isEntered = false,
  23. angle = 0,
  24. angleNudge = -.59,
  25. model = lovr.graphics.newModel('art/mobile_submarine.obj'),
  26. color = { 200, 0, 0 },
  27. target = cry
  28. },
  29. plane = {
  30. scale = .5,
  31. position = { 0, self.position[2], 0 },
  32. wonPosition = { 0, .6, 2.2 },
  33. stringLength = 9.5,
  34. isEntered = false,
  35. angle = 0,
  36. angleNudge = .15,
  37. model = lovr.graphics.newModel('art/mobile_plane.obj'),
  38. color = { 200, 200, 0 },
  39. target = play
  40. },
  41. balloon = {
  42. scale = .5,
  43. position = { 0, self.position[2], 0 },
  44. wonPosition = { .75, .6, 2.2 },
  45. stringLength = 9.8,
  46. isEntered = false,
  47. angle = 0,
  48. angleNudge = .25,
  49. model = lovr.graphics.newModel('art/mobile_balloon.obj'),
  50. color = { 0, 200, 200 },
  51. target = sleep
  52. }
  53. }
  54. self.numToys = _.count(self.toys)
  55. self.toySize = .38 -- hitbox, in meters
  56. self.toyTranslateZ = .6
  57. self.toyRotate = 2 * math.pi / self.numToys
  58. _.each(self.toys, function(toy)
  59. toy.model:setMaterial(lovr.graphics.newMaterial('art/mobile_DIFF.png'))
  60. end)
  61. self.transitionFactor = 0
  62. end
  63. function mobile:update(dt)
  64. self.angle = self.angle + dt * self.rotateSpeed
  65. self:handleToyInput(dt)
  66. local toyRotate = self.toyRotate
  67. _.each(self.toys, function(toy)
  68. if toy.target.won then return end
  69. -- Translate to parent, rotate according to mobile angle
  70. local m = mat4.identity()
  71. m:translate(m, vec3(unpack(self.position)))
  72. m:rotate(m, self.angle, vec3(0, 1, 0))
  73. -- Rotate so they are spread out evenly
  74. m:rotate(m, toyRotate + toy.angleNudge, vec3(0, 1, 0))
  75. -- Move down based on string length, shoot outwards a bit
  76. m:translate(m, vec3(0, -toy.stringLength, self.toyTranslateZ))
  77. toy.position = { m[13], m[14], m[15] }
  78. toyRotate = toyRotate + self.toyRotate
  79. end)
  80. end
  81. function mobile:draw()
  82. local x, y, z = unpack(self.position)
  83. lovr.graphics.setColor(1, 1, 1)
  84. self.model:draw(x, y, z, self.size * .01, self.angle, 0, 1, 0)
  85. self:drawToys()
  86. end
  87. function mobile:drawToys()
  88. _.each(self.toys, function(toy)
  89. local x, y, z = unpack(toy.target.won and toy.wonPosition or toy.position)
  90. lovr.graphics.setColor(unpack(toy.color))
  91. lovr.graphics.setColor(1, 1, 1)
  92. toy.model:draw(x, y, z, toy.scale * .01, toy.angle, 0, 1, 0)
  93. end)
  94. drawTransition(self.transitionFactor)
  95. end
  96. function mobile.controllerInRange(basePos, size)
  97. local controller = controllers.list[1]
  98. if controller then
  99. local pos = vec3(controller:getPosition())
  100. local center = vec3(unpack(basePos))
  101. local distance = pos:dist(center)
  102. local radius = size
  103. return distance < radius and true or false
  104. end
  105. end
  106. function mobile:handleToyInput(dt)
  107. local controller = controllers.list[1]
  108. if not controller then return end
  109. local trigger = controller:getAxis('trigger')
  110. local activeToy = nil
  111. _.each(self.toys, function(toy)
  112. -- You can't interact with a toy if you've beaten its level
  113. if toy.target.won then
  114. toy.angle = toy.angle + dt
  115. return
  116. end
  117. local wasEntered = toy.isEntered
  118. toy.isEntered = self.controllerInRange(toy.position, self.toySize)
  119. if toy.isEntered then
  120. toy.scale = _.lerp(toy.scale, .625, 8 * dt)
  121. toy.angle = toy.angle + dt
  122. activeToy = toy
  123. else
  124. toy.scale = _.lerp(toy.scale, .5, 8 * dt)
  125. toy.angle = toy.angle + dt / 2
  126. end
  127. if wasEntered ~= toy.isEntered then
  128. controller:vibrate(.0035)
  129. end
  130. end)
  131. if activeToy and trigger > .9 then
  132. self.transitionFactor = math.min(self.transitionFactor + dt, 1)
  133. if self.transitionFactor > 0 then
  134. controller:vibrate(self.transitionFactor^2 * .0035)
  135. end
  136. if self.transitionFactor >= 1 then
  137. setState(activeToy.target)
  138. end
  139. else
  140. self.transitionFactor = math.max(self.transitionFactor - dt, 0)
  141. end
  142. self.isEntered = activeToy ~= nil
  143. self.rotateSpeed = _.lerp(self.rotateSpeed, self.isEntered and 0 or .5, 4 * dt)
  144. end
  145. return mobile