hudunits.lua 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547
  1. HudUnits = class()
  2. local g = love.graphics
  3. function HudUnits:init()
  4. self.geometry = setmetatable({}, {__index = function(t, k)
  5. return rawset(t, k, self.geometryFunctions[k]())[k]
  6. end})
  7. self.geometryFunctions = {
  8. units = function()
  9. local u, v = ctx.hud.u, ctx.hud.v
  10. local atlas = data.atlas.hud
  11. local upgradeFactor, t = ctx.hud.upgrades:getFactor()
  12. local inc = u * (.2 + (.075 * upgradeFactor))
  13. local xx = .5 * u - (inc * (self.count - 1) / 2)
  14. local p = ctx.player
  15. local w, h = data.atlas.hud:getDimensions('minion')
  16. local res = {scale = scale, imageScale = is}
  17. for i = 1, self.count do
  18. local deck = p.deck[i]
  19. local yy = v * .005
  20. res[i] = {x = xx, y = yy}
  21. local selectFactor = math.lerp(self.prevSelectFactor[i], self.selectFactor[i], ls.accum / ls.tickrate)
  22. local scale = 1 + .6 * upgradeFactor + .1 * selectFactor
  23. local is = (.2 * scale * v) / h
  24. res[i].selectFactor = selectFactor
  25. res[i].scale = scale
  26. res[i].imageScale = is
  27. -- Background
  28. res[i].bg = {xx, yy, 0, is, is, w / 2, 0}
  29. -- Title bar
  30. local w, h = atlas:getDimensions('title')
  31. local tx = xx - (w / 2) * is
  32. local ty = yy + (10 * is)
  33. local xsc = is * (1 - (deck.cooldown / deck.maxCooldown))
  34. res[i].title = {xx, ty, 0, is, is, w / 2, 0}
  35. -- Runes
  36. do
  37. local runes = {}
  38. local ct = 3
  39. local size = v * .04 * scale
  40. local inc = size + .01 * v * scale
  41. local xx = xx - (inc * (ct - 1) / 2)
  42. local yy = yy + .174 * v * scale
  43. for j = 1, 3 do
  44. local rune = p.deck[i].runes and p.deck[i].runes[j]
  45. if rune then
  46. runes[j] = {}
  47. -- Stone
  48. local w, h = atlas:getDimensions('runeBg' .. rune.background:capitalize())
  49. local scale = size / h
  50. runes[j].bg = {'runeBg' .. rune.background:capitalize(), xx, yy, 0, scale, scale, w / 2, h / 2}
  51. -- Rune
  52. local w, h = atlas:getDimensions('rune' .. rune.image)
  53. local scale = (size * .5) / h
  54. runes[j].rune = {'rune' .. rune.image, xx, yy, 0, scale, scale, w / 2, h / 2}
  55. end
  56. xx = xx + inc
  57. end
  58. res[i].runes = runes
  59. end
  60. xx = xx + inc
  61. end
  62. return res
  63. end,
  64. upgrades = function()
  65. local u, v = ctx.hud.u, ctx.hud.v
  66. local p = ctx.player
  67. local upgradeFactor, t = ctx.hud.upgrades:getFactor()
  68. local inc = .1 * upgradeFactor * v
  69. local yy = (.15 * upgradeFactor) * v + (.35 * v)
  70. local size = math.max(.08 * v * upgradeFactor, .01)
  71. local units = self.geometry.units
  72. local res = {}
  73. for i = 1, self.count do
  74. res[i] = {}
  75. local xx = units[i].x
  76. for j, upgrade in ipairs(data.unit[p.deck[i].code].upgrades) do
  77. local line = {}
  78. if upgrade.connectedTo then
  79. table.each(upgrade.connectedTo, function(other, k)
  80. local connection = data.unit[p.deck[i].code].upgrades[other]
  81. line[k] = {xx + (inc * upgrade.x), yy + (.1 * v * upgrade.y), xx + (inc * connection.x), yy + (.1 * v * connection.y)}
  82. end)
  83. end
  84. table.insert(res[i], {xx + (inc * upgrade.x) - size / 2, yy + (.1 * v * upgrade.y) - size / 2, size, size, line})
  85. end
  86. end
  87. return res
  88. end,
  89. attributes = function()
  90. local u, v = ctx.hud.u, ctx.hud.v
  91. local upgradeFactor, t = ctx.hud.upgrades:getFactor()
  92. local inc = .1 * upgradeFactor * v
  93. local yy = (.15 * upgradeFactor) * v + (.25 * v)
  94. local size = math.max(.08 * v * upgradeFactor, .01)
  95. local units = self.geometry.units
  96. local res = {}
  97. for i = 1, self.count do
  98. res[i] = {}
  99. local x = units[i].x - (inc * (4 - 1) / 2)
  100. for j = 1, 4 do
  101. table.insert(res[i], {x - size / 2, yy - size / 2, size, size})
  102. x = x + inc
  103. end
  104. end
  105. return res
  106. end
  107. }
  108. self:ready()
  109. end
  110. function HudUnits:update()
  111. local p = ctx.player
  112. for i = 1, #self.selectFactor do
  113. self.prevSelectFactor[i] = self.selectFactor[i]
  114. self.selectFactor[i] = math.lerp(self.selectFactor[i], p.deck[i].selected and 1 or (p.summonSelect == i and .5 or 0), math.min(8 * ls.tickrate, 1))
  115. if self.selectFactor[i] > .01 and self.selectFactor[i] < .99 then table.clear(self.geometry) end
  116. end
  117. for i = 1, #self.cooldownPop do
  118. self.prevCooldownPop[i] = self.cooldownPop[i]
  119. self.cooldownPop[i] = math.lerp(self.cooldownPop[i], 0, math.min(12 * ls.tickrate, 1))
  120. end
  121. local _, t = ctx.hud.upgrades:getFactor()
  122. if t ~= 0 and t ~= 1 then ctx:mousemoved(love.mouse.getPosition()) end
  123. end
  124. function HudUnits:draw()
  125. if ctx.ded or not ctx.tutorial:shouldShowHudUnits() then return end
  126. local u, v = ctx.hud.u, ctx.hud.v
  127. local upgradeFactor, t = ctx.hud.upgrades:getFactor()
  128. if t > 0 and t < 1 then table.clear(self.geometry) end
  129. self:drawBackground()
  130. self:drawForeground()
  131. end
  132. function HudUnits:drawBackground()
  133. local u, v = ctx.hud.u, ctx.hud.v
  134. local ct = self.count
  135. local p = ctx.player
  136. local upgradeFactor, t = ctx.hud.upgrades:getFactor()
  137. local upgradeAlphaFactor = (t / ctx.hud.upgrades.maxTime) ^ 3
  138. local units = self.geometry.units
  139. self.spriteBatch:bind()
  140. if upgradeFactor > 0 then
  141. g.setColor(0, 0, 0, 80 * math.clamp(upgradeFactor, 0, 1))
  142. g.rectangle('fill', 0, 0, u, v)
  143. end
  144. for i = 1, #units do
  145. local unit = units[i]
  146. local xx, yy = unit.x, unit.y
  147. local deck = p.deck[i]
  148. local selectFactor = math.lerp(self.prevSelectFactor[i], self.selectFactor[i], ls.accum / ls.tickrate)
  149. local alpha = .45 + selectFactor * .35
  150. -- Backdrop
  151. g.setColor(255, 255, 255, 255 * alpha)
  152. self:batch('bg' .. i, 'minion', unpack(unit.bg))
  153. -- Cooldown
  154. g.setColor(255, 255, 255, 80 * alpha)
  155. local x, y, a, sx, sy, ox, oy = unpack(unit.title)
  156. self:batch('title' .. i, 'title', x, y, a, sx, sy, ox, oy)
  157. g.setColor(255, 255, 255, 255 * alpha)
  158. local cd = 1 - (deck.cooldown / deck.maxCooldown)
  159. self:batch('titleBar' .. i, 'title', x - ox * sx, y, a, sx * cd, sy)
  160. -- Runes
  161. for j = 1, 3 do
  162. if unit.runes[j] then
  163. g.setColor(255, 255, 255)
  164. self:batch('runeBg' .. i .. j, unpack(unit.runes[j].bg))
  165. g.setColor(config.runes.colors[deck.runes[j].color])
  166. self:batch('rune' .. i .. j, unpack(unit.runes[j].rune))
  167. end
  168. end
  169. end
  170. if t > 0 then
  171. -- Attributes
  172. local attributes = self.geometry.attributes
  173. for i = 1, #attributes do
  174. for j = 1, #attributes[i] do
  175. local x, y, w, h = unpack(attributes[i][j])
  176. local attribute = config.attributes.list[j]
  177. local scale = w / data.atlas.hud:getDimensions('frame')
  178. local val = (p.juju >= 30 + 20 * data.unit[p.deck[i].code].attributes[attribute]) and 255 or 150
  179. x, y = math.round(x), math.round(y)
  180. -- Frame
  181. g.setColor(val, val, val, 255 * upgradeAlphaFactor)
  182. self:batch('attributeFrame' .. i .. j, 'frame', x, y, 0, scale, scale)
  183. local image = data.media.graphics.hud.icons[attribute]
  184. if image then
  185. local scale = math.min((w - (v * .03)) / image:getWidth(), (h - (v * .03)) / image:getHeight())
  186. local code = 'attributeIcon' .. i .. j
  187. g.setColor(255, 255, 255, 255 * upgradeAlphaFactor)
  188. self:batch(code, attribute, x + w / 2, y + h / 2, 0, scale, scale, image:getWidth() / 2, image:getHeight() / 2)
  189. end
  190. end
  191. end
  192. -- Upgrade Connectors
  193. local upgrades = self.geometry.upgrades
  194. g.setLineWidth(2)
  195. for i = 1, #upgrades do
  196. for j = 1, #upgrades[i] do
  197. local who, what = p.deck[i].code, data.unit[p.deck[i].code].upgrades[j].code
  198. local _, _, _, _, line = unpack(upgrades[i][j])
  199. local upgrade = data.unit[who].upgrades[what]
  200. if line and #line > 0 then
  201. table.each(line, function(points, k)
  202. local other = data.unit[p.deck[i].code].upgrades[upgrade.connectedTo[k]]
  203. if other.level >= upgrade.prerequisites[other.code] then
  204. g.setColor(0, ctx.options.colorblind and 0 or 200, ctx.options.colorblind and 200 or 0, 200 * upgradeAlphaFactor ^ 2)
  205. else
  206. g.setColor(200, 0, 0, 200 * upgradeAlphaFactor ^ 2)
  207. end
  208. local xscale = .005 * v
  209. local yscale = math.distance(unpack(points)) / 20
  210. local angle = math.direction(unpack(points)) - math.pi / 2
  211. self:batch('upgradeConector' .. i .. j .. k, 'healthbarBar', points[1], points[2], angle, xscale, yscale, .5, 0)
  212. end)
  213. end
  214. end
  215. end
  216. g.setLineWidth(1)
  217. -- Upgrades
  218. local upgrades = self.geometry.upgrades
  219. for i = 1, #upgrades do
  220. for j = 1, #upgrades[i] do
  221. local who, what = p.deck[i].code, data.unit[p.deck[i].code].upgrades[j].code
  222. local x, y, w, h = unpack(upgrades[i][j])
  223. local scale = w / data.atlas.hud:getDimensions('frame')
  224. local upgrade = data.unit[who].upgrades[what]
  225. local val = (upgrade.level > 0 or ctx.upgrades.canBuy(who, what)) and 255 or 150
  226. x, y = math.round(x), math.round(y)
  227. -- Frame
  228. g.setColor(val, val, val, 255 * upgradeAlphaFactor)
  229. self:batch('upgradeFrame' .. i .. j, 'frame', x, y, 0, scale, scale)
  230. -- Icon
  231. local qw, qh = data.atlas.hud:getDimensions(what)
  232. if qw and qh then
  233. local val = (upgrade.level > 0 or ctx.upgrades.canBuy(who, what)) and 255 or 200
  234. local scale = math.min((w - (v * .02)) / qw, (h - (v * .02)) / qh)
  235. local x, y = x + w / 2, y + h / 2
  236. local ox, oy = qw / 2, qh / 2
  237. local code = 'upgradeIcon' .. i .. j
  238. g.setColor(val, val, val, 255 * upgradeAlphaFactor)
  239. self:batch(code, what, x, y, 0, scale, scale, ox, oy)
  240. end
  241. end
  242. end
  243. end
  244. self.spriteBatch:unbind()
  245. g.draw(self.spriteBatch)
  246. end
  247. function HudUnits:drawForeground()
  248. local u, v = ctx.hud.u, ctx.hud.v
  249. local mx, my = ctx.view:frameMouseX(), ctx.view:frameMouseY()
  250. local p = ctx.player
  251. local atlas = data.atlas.hud
  252. local upgradeFactor, t = ctx.hud.upgrades:getFactor()
  253. local upgradeAlphaFactor = (t / ctx.hud.upgrades.maxTime) ^ 3
  254. local units = self.geometry.units
  255. for i = 1, #units do
  256. local unit = units[i]
  257. local xx, yy = unit.x, unit.y
  258. local deck = p.deck[i]
  259. local alpha = .45 + unit.selectFactor * .35
  260. local scale = unit.scale
  261. local imageScale = unit.imageScale
  262. -- Cooldown
  263. local cooldownPop = math.lerp(self.prevCooldownPop[i], self.cooldownPop[i], ls.accum / ls.tickrate)
  264. g.setBlendMode('additive')
  265. g.setColor(255, 255, 255, 200 * cooldownPop)
  266. g.draw(atlas.texture, atlas.quads.title, unpack(unit.title))
  267. g.setBlendMode('alpha')
  268. -- Animation
  269. g.setCanvas(self.canvas)
  270. self.canvas:clear(0, 0, 0, 0)
  271. g.pop()
  272. self.animations[i]:draw(100, 100)
  273. ctx.view:guiPush()
  274. g.setCanvas()
  275. g.setColor(255, 255, 255)
  276. g.draw(self.canvas, xx, yy + .1 * scale * v, 0, imageScale, imageScale, 100, 100)
  277. -- Text
  278. local x, y, a, sx, sy, ox, oy = unpack(unit.title)
  279. local w, h = data.atlas.hud:getDimensions('title')
  280. local unit = data.unit[p.deck[i].code]
  281. local font = g.setFont('mesmerize', math.round(.02 * scale * v))
  282. local str = unit.name
  283. local unitCount = #ctx.units:filter(function(u) return u.player end)
  284. local cost = unit.cost * (unitCount == 0 and 0 or 1)
  285. if math.inside(mx, my, x - ox * sx, y, w * sx, h * sy) then
  286. str = string.format('%.2f', p.deck[i].cooldown)
  287. end
  288. g.setColor(255, 255, 255)
  289. g.printShadow(str, math.round(xx), math.round(yy + (.025 * v * scale)), true)
  290. g.printShadow(cost, xx - (.091 * v * scale), yy + (.0975 * v * scale), true, {0, 100, 0, 200})
  291. local count = table.count(ctx.units:filter(function(u) return u.class.code == p.deck[i].code end))
  292. g.printShadow(count, xx + (.087 * v * scale), yy + (.1 * v * scale), true, {0, 100, 0, 200})
  293. end
  294. if t > 0 then
  295. -- Attribute text
  296. local attributes = self.geometry.attributes
  297. for i = 1, #attributes do
  298. for j = 1, #attributes[i] do
  299. local x, y, w, h = unpack(attributes[i][j])
  300. local attribute = config.attributes.list[j]
  301. local level = data.unit[p.deck[i].code].attributes[attribute]
  302. g.setFont('mesmerize', math.round(.0175 * v))
  303. g.setColor(200, 200, 200, 255 * upgradeAlphaFactor)
  304. g.printShadow(level, x + .01 * v, y + .005 * v)
  305. end
  306. end
  307. -- Upgrade outlines
  308. local upgrades = self.geometry.upgrades
  309. for i = 1, #upgrades do
  310. for j = 1, #upgrades[i] do
  311. local x, y, w, h = unpack(upgrades[i][j])
  312. local who, what = p.deck[i].code, data.unit[p.deck[i].code].upgrades[j].code
  313. local upgrade = data.unit[who].upgrades[what]
  314. -- Upgrade progress test
  315. local ct = upgrade.maxLevel
  316. local size = .008 * v
  317. local inc = w / ct
  318. local yy = (y + h / 2) - (inc * (ct - 1) / 2)
  319. for i = 1, ct do
  320. g.setColor(upgrade.level >= i and {100, 255, 100, 255 * upgradeAlphaFactor} or {255, 255, 255, 100 * upgradeAlphaFactor})
  321. local image = data.media.graphics.particles.softCircle
  322. local scale = size / image:getWidth()
  323. g.draw(image, x + .005 * v, yy, 0, scale, scale, image:getWidth() / 2, image:getHeight() / 2)
  324. yy = yy + inc
  325. end
  326. end
  327. end
  328. end
  329. end
  330. function HudUnits:mousereleased(mx, my, b)
  331. if ctx.ded then return end
  332. if not ctx.tutorial:shouldShowHudUnits() or not ctx.tutorial:shouldPurchaseUpgrade() then return end
  333. if b ~= 'l' then return end
  334. local p = ctx.player
  335. -- Upgrade click
  336. local upgrades = self.geometry.upgrades
  337. for i = 1, #upgrades do
  338. for j = 1, #upgrades[i] do
  339. local who, what = p.deck[i].code, data.unit[p.deck[i].code].upgrades[j].code
  340. local x, y, w, h = unpack(upgrades[i][j])
  341. if math.inside(mx, my, x, y, w, h) then
  342. local upgrade = data.unit[who].upgrades[what]
  343. local nextLevel = upgrade.level + 1
  344. if ctx.upgrades.canBuy(who, what) and p:spend(upgrade.costs[nextLevel]) then
  345. ctx.upgrades.unlock(who, what)
  346. ctx.sound:play('upgrade')
  347. ctx.particles:emit('upgrade', mx, my, 10)
  348. else
  349. ctx.sound:play('misclick')
  350. end
  351. end
  352. end
  353. end
  354. -- Attribute click
  355. local attributes = self.geometry.attributes
  356. for i = 1, #attributes do
  357. for j = 1, #attributes[i] do
  358. local who, what = p.deck[i].code, config.attributes.list[j]
  359. local x, y, w, h = unpack(attributes[i][j])
  360. if math.inside(mx, my, x, y, w, h) then
  361. if ctx.upgrades.canBuyAttribute(who, what) and p:spend(data.unit[who].attributeCosts[what]) then
  362. ctx.upgrades.unlockAttribute(who, what)
  363. ctx.sound:play('upgrade')
  364. ctx.particles:emit('upgrade', mx, my, 10)
  365. else
  366. ctx.sound:play('misclick')
  367. end
  368. end
  369. end
  370. end
  371. end
  372. function HudUnits:mousemoved(mx, my)
  373. if not ctx.tutorial:shouldShowHudUnits() then return end
  374. local p = ctx.player
  375. -- Attribute tooltips
  376. local attributes = self.geometry.attributes
  377. for i = 1, #attributes do
  378. for j = 1, #attributes[i] do
  379. local attribute = config.attributes.list[j]
  380. local x, y, w, h = unpack(attributes[i][j])
  381. if math.inside(mx, my, x, y, w, h) then
  382. ctx.hud.tooltip:setAttributeTooltip(attribute, p.deck[i].code)
  383. return
  384. end
  385. end
  386. end
  387. -- Upgrade tooltips
  388. local upgrades = self.geometry.upgrades
  389. for i = 1, #upgrades do
  390. for j = 1, #upgrades[i] do
  391. local who, what = p.deck[i].code, data.unit[p.deck[i].code].upgrades[j].code
  392. local x, y, w, h = unpack(upgrades[i][j])
  393. if math.inside(mx, my, x, y, w, h) then
  394. ctx.hud.tooltip:setUpgradeTooltip(who, what)
  395. return
  396. end
  397. end
  398. end
  399. -- Rune/Unit tooltips
  400. local units = self.geometry.units
  401. for i = 1, #units do
  402. local unit = units[i]
  403. local size = .09 * ctx.hud.v * unit.scale
  404. if math.inside(mx, my, unit.x - size / 2, unit.y + .096 * unit.scale * ctx.hud.v - size / 2, size, size) then
  405. ctx.hud.tooltip:setUnitTooltip(p.deck[i].code)
  406. end
  407. for j = 1, 3 do
  408. local rune = unit.runes[j]
  409. local w, h = data.atlas.hud:getDimensions('runeBgNormal')
  410. if rune then
  411. local x, y, w, h = rune.bg[2], rune.bg[3], rune.bg[5] * w, rune.bg[6] * h
  412. if math.inside(mx, my, x - w / 2, y - h / 2, w, h) then
  413. ctx.hud.tooltip:setRuneTooltip(p.deck[i].runes[j])
  414. return
  415. end
  416. end
  417. end
  418. end
  419. end
  420. function HudUnits:ready()
  421. local p = ctx.player
  422. self.count = #p.deck
  423. self.selectFactor = {}
  424. self.prevSelectFactor = {}
  425. self.cooldownPop = {}
  426. self.prevCooldownPop = {}
  427. self.animations = {}
  428. self.canvas = g.newCanvas(200, 200)
  429. self.spriteBatch = g.newSpriteBatch(data.atlas.hud.texture, 512, 'stream')
  430. self.spriteBatchMap = {}
  431. local animationScaleFactors = {
  432. bruju = 1.5,
  433. thuju = .85,
  434. xuju = .85,
  435. kuju = .85
  436. }
  437. local animationOffsets = {
  438. bruju = -12,
  439. thuju = -16,
  440. xuju = -24,
  441. kuju = -16
  442. }
  443. for i = 1, self.count do
  444. self.selectFactor[i] = 0
  445. self.prevSelectFactor[i] = self.selectFactor[i]
  446. self.cooldownPop[i] = 0
  447. self.prevCooldownPop[i] = self.cooldownPop[i]
  448. local code = p.deck[i].code
  449. local animation = data.animation[code]
  450. local scale = animation.scale * animationScaleFactors[code]
  451. local offsety = animation.offsety + animationOffsets[code]
  452. self.animations[i] = data.animation[p.deck[i].code]({scale = scale, offsety = offsety})
  453. self.animations[i]:on('complete', function()
  454. self.animations[i]:set('idle', {force = true})
  455. end)
  456. end
  457. end
  458. function HudUnits:batch(code, quad, ...)
  459. local id = self.spriteBatchMap[code]
  460. self.spriteBatch:setColor(love.graphics.getColor())
  461. if id then
  462. self.spriteBatch:set(id, data.atlas.hud.quads[quad], ...)
  463. else
  464. self.spriteBatchMap[code] = self.spriteBatch:add(data.atlas.hud.quads[quad], ...)
  465. end
  466. end