From 4de2640da1cb6f48ce66804beb2a05eca0477523 Mon Sep 17 00:00:00 2001 From: a327ex Date: Mon, 14 Jun 2021 01:59:16 -0300 Subject: [PATCH] Item update 1/5 --- arena.lua | 68 +++++++++++++++++-------- buy_screen.lua | 97 ++++++++++++++++++++++++++++-------- enemies.lua | 1 + main.lua | 22 +++++---- objects.lua | 8 +-- player.lua | 13 ++++- todo | 132 ++++++++++++++++++++++--------------------------- 7 files changed, 212 insertions(+), 129 deletions(-) diff --git a/arena.lua b/arena.lua index 98065fd..8c867b7 100644 --- a/arena.lua +++ b/arena.lua @@ -7,7 +7,7 @@ function Arena:init(name) end -function Arena:on_enter(from, level, units, passives, shop_level, shop_xp) +function Arena:on_enter(from, level, units, passives, shop_level, shop_xp, lock) self.hfx:add('condition1', 1) self.hfx:add('condition2', 1) self.level = level or 1 @@ -15,6 +15,9 @@ function Arena:on_enter(from, level, units, passives, shop_level, shop_xp) self.passives = passives self.shop_level = shop_level or 1 self.shop_xp = shop_xp or 0 + self.lock = lock + + self.starting_units = table.copy(units) if not state.mouse_control then input:set_mouse_visible(false) @@ -448,11 +451,13 @@ function Arena:update(dt) end} self.video_button_1 = Button{group = self.ui, x = gw/2 - 86, y = gh - 125, force_update = true, button_text = 'window size-', fg_color = 'bg10', bg_color = 'bg', action = function() - ui_switch1:play{pitch = random:float(0.95, 1.05), volume = 0.5} - sx, sy = sx - 1, sy - 1 - love.window.setMode(480*sx, 270*sy) - state.sx, state.sy = sx, sy - state.fullscreen = false + if sx > 1 and sy > 1 then + ui_switch1:play{pitch = random:float(0.95, 1.05), volume = 0.5} + sx, sy = sx - 1, sy - 1 + love.window.setMode(480*sx, 270*sy) + state.sx, state.sy = sx, sy + state.fullscreen = false + end end} self.video_button_2 = Button{group = self.ui, x = gw/2, y = gh - 125, force_update = true, button_text = 'window size+', fg_color = 'bg10', bg_color = 'bg', action = function() @@ -617,18 +622,38 @@ function Arena:quit() input:set_mouse_visible(true) self.won = true locked_state = nil + + if current_new_game_plus == new_game_plus then + new_game_plus = new_game_plus + 1 + state.new_game_plus = new_game_plus + end + current_new_game_plus = current_new_game_plus + 1 + state.current_new_game_plus = current_new_game_plus + max_units = 7 + current_new_game_plus + system.save_run() trigger:tween(1, _G, {slow_amount = 0}, math.linear, function() slow_amount = 0 end, 'slow_amount') trigger:tween(4, camera, {x = gw/2, y = gh/2, r = 0}, math.linear, function() camera.x, camera.y, camera.r = gw/2, gh/2, 0 end) - self.win_text = Text2{group = self.ui, x = gw/2, y = gh/2 - 66, force_update = true, lines = {{text = '[wavy_mid, cbyc2]congratulations!', font = fat_font, alignment = 'center'}}} + self.win_text = Text2{group = self.ui, x = gw/2 + 40, y = gh/2 - 66, force_update = true, lines = {{text = '[wavy_mid, cbyc2]congratulations!', font = fat_font, alignment = 'center'}}} trigger:after(2.5, function() self.build_text = Text2{group = self.ui, x = 40, y = 20, force_update = true, lines = {{text = "[wavy_mid, fg]your build", font = pixul_font, alignment = 'center'}}} for i, unit in ipairs(self.units) do - CharacterPart{group = self.ui, x = 40, y = 40 + (i-1)*19, character = unit.character, level = unit.level, force_update = true, cant_click = true, parent = self} + CharacterPart{group = self.ui, x = 20, y = 40 + (i-1)*19, character = unit.character, level = unit.level, force_update = true, cant_click = true, parent = self} + Text2{group = self.ui, x = 20 + 14 + pixul_font:get_text_width(unit.character)/2, y = 40 + (i-1)*19, force_update = true, lines = { + {text = '[' .. character_color_strings[unit.character] .. ']' .. unit.character, font = pixul_font, alignment = 'left'} + }} end - if current_new_game_plus == 5 then - self.win_text2 = Text2{group = self.ui, x = gw/2, y = gh/2 + 30, force_update = true, lines = { + if current_new_game_plus == 6 then + if current_new_game_plus == new_game_plus then + new_game_plus = 5 + state.new_game_plus = new_game_plus + end + current_new_game_plus = 5 + state.current_new_game_plus = current_new_game_plus + max_units = 12 + + self.win_text2 = Text2{group = self.ui, x = gw/2 + 40, y = gh/2 + 30, force_update = true, lines = { {text = "[fg]now you've really beaten the game!", font = pixul_font, alignment = 'center', height_multiplier = 1.24}, {text = "[fg]thanks a lot for playing it and completing it entirely!", font = pixul_font, alignment = 'center', height_multiplier = 1.24}, {text = "[fg]this game was inspired by:", font = pixul_font, alignment = 'center', height_multiplier = 4}, @@ -636,7 +661,7 @@ function Arena:quit() {text = "[fg]and to get more games like this in the future:", font = pixul_font, alignment = 'center', height_multiplier = 4}, {text = "[wavy_mid, yellow]thanks for playing!", font = pixul_font, alignment = 'center'}, }} - SteamFollowButton{group = self.ui, x = gw/2, y = gh/2 + 78, force_update = true} + SteamFollowButton{group = self.ui, x = gw/2 + 40, y = gh/2 + 78, force_update = true} Button{group = self.ui, x = gw - 40, y = gh - 44, force_update = true, button_text = 'credits', fg_color = 'bg10', bg_color = 'bg', action = function() self:create_credits() end} Button{group = self.ui, x = gw - 32, y = gh - 20, force_update = true, button_text = 'quit', fg_color = 'bg10', bg_color = 'bg', action = function() love.event.quit() end} local open_url = function(b, url) @@ -646,10 +671,10 @@ function Arena:quit() ui_switch1:play{pitch = random:float(0.95, 1.05), volume = 0.5} system.open_url(url) end - Button{group = self.ui, x = gw/2 - 50, y = gh/2 + 12, force_update = true, button_text = 'nimble quest', fg_color = 'bluem5', bg_color = 'blue', action = function(b) open_url(b, 'https://store.steampowered.com/app/259780/Nimble_Quest/') end} - Button{group = self.ui, x = gw/2 + 50, y = gh/2 + 12, force_update = true, button_text = 'dota underlords', fg_color = 'bluem5', bg_color = 'blue', action = function(b) open_url(b, 'https://store.steampowered.com/app/1046930/Dota_Underlords/') end} + Button{group = self.ui, x = gw/2 - 50 + 40, y = gh/2 + 12, force_update = true, button_text = 'nimble quest', fg_color = 'bluem5', bg_color = 'blue', action = function(b) open_url(b, 'https://store.steampowered.com/app/259780/Nimble_Quest/') end} + Button{group = self.ui, x = gw/2 + 50 + 40, y = gh/2 + 12, force_update = true, button_text = 'dota underlords', fg_color = 'bluem5', bg_color = 'blue', action = function(b) open_url(b, 'https://store.steampowered.com/app/1046930/Dota_Underlords/') end} else - self.win_text2 = Text2{group = self.ui, x = gw/2, y = gh/2 + 20, force_update = true, lines = { + self.win_text2 = Text2{group = self.ui, x = gw/2 + 40, y = gh/2 + 20, force_update = true, lines = { {text = "[fg]you've beaten the game!", font = pixul_font, alignment = 'center', height_multiplier = 1.24}, {text = "[fg]i made this game in 3 months as a dev challenge", font = pixul_font, alignment = 'center', height_multiplier = 1.24}, {text = "[fg]and i'm happy with how it turned out!", font = pixul_font, alignment = 'center', height_multiplier = 1.24}, @@ -657,7 +682,7 @@ function Arena:quit() {text = "[fg]i will release more games this year, so stay tuned!", font = pixul_font, alignment = 'center', height_multiplier = 1.4}, {text = "[wavy_mid, yellow]thanks for playing!", font = pixul_font, alignment = 'center'}, }} - SteamFollowButton{group = self.ui, x = gw/2, y = gh/2 + 34, force_update = true} + SteamFollowButton{group = self.ui, x = gw/2 + 40, y = gh/2 + 34, force_update = true} RestartButton{group = self.ui, x = gw - 40, y = gh - 20, force_update = true} trigger:after(8, function() self.try_ng_text = Text2{group = self.ui, x = gw - 210, y = gh - 20, force_update = true, lines = { @@ -789,9 +814,8 @@ function Arena:quit() steam.userStats.storeStats() end - local units = self.player:get_all_units() local all_units_level_2 = true - for _, unit in ipairs(units) do + for _, unit in ipairs(self.starting_units) do if unit.level ~= 2 then all_units_level_2 = false break @@ -806,7 +830,7 @@ function Arena:quit() local units = self.player:get_all_units() local all_units_level_3 = true - for _, unit in ipairs(units) do + for _, unit in ipairs(self.starting_units) do if unit.level ~= 3 then all_units_level_3 = false break @@ -843,9 +867,9 @@ end function Arena:set_passives(from_reroll) if from_reroll then self:restore_passives_to_pool(0) - self.cards[1].dead = true - self.cards[2].dead = true - self.cards[3].dead = true + if self.cards[1] then self.cards[1].dead = true end + if self.cards[2] then self.cards[2].dead = true end + if self.cards[3] then self.cards[3].dead = true end self.cards = {} end @@ -947,6 +971,7 @@ end function Arena:die() if not self.died_text and not self.won and not self.arena_clear_text then + input:set_mouse_visible(false) self.t:cancel('divine_punishment') self.died = true locked_state = nil @@ -1057,6 +1082,7 @@ end function Arena:transition() self.transitioning = true + if not self.lock then locked_state = nil end ui_transition2:play{pitch = random:float(0.95, 1.05), volume = 0.5} TransitionEffect{group = main.transitions, x = self.player.x, y = self.player.y, color = self.color, transition_action = function(t) if self.level % 2 == 0 and self.shop_level < 5 then diff --git a/buy_screen.lua b/buy_screen.lua index faae610..e6e3675 100644 --- a/buy_screen.lua +++ b/buy_screen.lua @@ -65,7 +65,7 @@ function BuyScreen:on_enter(from, level, units, passives, shop_level, shop_xp) self.locked = locked_state and locked_state.locked LockButton{group = self.main, x = 205, y = 18, parent = self} - self:set_cards(nil, nil, true) + self:set_cards(self.shop_level, nil, true) self:set_party_and_sets() self:set_items() @@ -136,6 +136,7 @@ function BuyScreen:on_enter(from, level, units, passives, shop_level, shop_xp) ui_transition2:play{pitch = random:float(0.95, 1.05), volume = 0.5} ui_switch2:play{pitch = random:float(0.95, 1.05), volume = 0.5} ui_switch1:play{pitch = random:float(0.95, 1.05), volume = 0.5} + locked_state = nil TransitionEffect{group = main.transitions, x = gw/2, y = gh/2, color = fg[0], transition_action = function() slow_amount = 1 gold = 3 @@ -507,7 +508,7 @@ function RestartButton:init(args) self:init_game_object(args) self.shape = Rectangle(self.x, self.y, pixul_font:get_text_width('restart') + 4, pixul_font.h + 4) self.interact_with_mouse = true - self.text = Text({{text = '[bg10]NG+' .. tostring(current_new_game_plus+1), font = pixul_font, alignment = 'center'}}, global_text_tags) + self.text = Text({{text = '[bg10]NG+' .. tostring(current_new_game_plus), font = pixul_font, alignment = 'center'}}, global_text_tags) end @@ -532,13 +533,6 @@ function RestartButton:update(dt) 'reinforce', 'payback', 'whispers_of_doom', 'heavy_impact', 'immolation', 'call_of_the_void'}, [3] = {'divine_machine_arrow', 'divine_punishment', 'flying_daggers', 'crucio', 'hive', 'void_rift'}, } - if current_new_game_plus == new_game_plus then - new_game_plus = new_game_plus + 1 - state.new_game_plus = new_game_plus - end - current_new_game_plus = current_new_game_plus + 1 - state.current_new_game_plus = current_new_game_plus - max_units = 7 + current_new_game_plus system.save_state() main:add(BuyScreen'buy_screen') system.save_run() @@ -561,14 +555,14 @@ function RestartButton:on_mouse_enter() ui_hover1:play{pitch = random:float(1.3, 1.5), volume = 0.5} pop2:play{pitch = random:float(0.95, 1.05), volume = 0.5} self.selected = true - self.text:set_text{{text = '[fgm5]NG+' .. tostring(current_new_game_plus+1), font = pixul_font, alignment = 'center'}} + self.text:set_text{{text = '[fgm5]NG+' .. tostring(current_new_game_plus), font = pixul_font, alignment = 'center'}} self.spring:pull(0.2, 200, 10) end function RestartButton:on_mouse_exit() if main.current.in_credits then return end - self.text:set_text{{text = '[bg10]NG+' .. tostring(current_new_game_plus+1), font = pixul_font, alignment = 'center'}} + self.text:set_text{{text = '[bg10]NG+' .. tostring(current_new_game_plus), font = pixul_font, alignment = 'center'}} self.selected = false end @@ -681,16 +675,17 @@ function GoButton:update(dt) self.t:after(2, function() self.info_text:deactivate(); self.info_text.dead = true; self.info_text = nil end, 'info_text') else - if self.parent.locked then locked_state = {locked = true, cards = {self.parent.cards[1] and self.parent.cards[1].unit, self.parent.cards[2] and self.parent.cards[2].unit, self.parent.cards[3] and self.parent.cards[3].unit}} end + locked_state = {locked = self.parent.locked, cards = {self.parent.cards[1] and self.parent.cards[1].unit, self.parent.cards[2] and self.parent.cards[2].unit, self.parent.cards[3] and self.parent.cards[3].unit}} ui_switch2:play{pitch = random:float(0.95, 1.05), volume = 0.5} self.spring:pull(0.2, 200, 10) self.selected = true ui_switch1:play{pitch = random:float(0.95, 1.05), volume = 0.5} ui_transition1:play{pitch = random:float(0.95, 1.05), volume = 0.5} self.transitioning = true + system.save_run(self.parent.level, gold, self.parent.units, passives, self.parent.shop_level, self.parent.shop_xp, run_passive_pool_by_tiers, locked_state) TransitionEffect{group = main.transitions, x = self.x, y = self.y, color = character_colors[random:table(self.parent.units).character], transition_action = function() main:add(Arena'arena') - main:go_to('arena', ((self.parent.first_screen and 1) or (self.parent.level + 1)), self.parent.units, self.parent.passives, self.parent.shop_level, self.parent.shop_xp) + main:go_to('arena', ((self.parent.first_screen and 1) or (self.parent.level + 1)), self.parent.units, self.parent.passives, self.parent.shop_level, self.parent.shop_xp, self.parent.locked) end, text = Text({{text = '[wavy, bg]level ' .. ((self.parent.first_screen and 1) or (self.parent.level + 1)) .. '/25', font = pixul_font, alignment = 'center'}}, global_text_tags)} end end @@ -922,7 +917,7 @@ function RerollButton:init(args) self.free_reroll = true self.text = Text({{text = '[bg10]reroll: [yellow]0', font = pixul_font, alignment = 'center'}}, global_text_tags) else - self.text = Text({{text = '[bg10]reroll: [yellow]15', font = pixul_font, alignment = 'center'}}, global_text_tags) + self.text = Text({{text = '[bg10]reroll: [yellow]10', font = pixul_font, alignment = 'center'}}, global_text_tags) end end end @@ -955,7 +950,7 @@ function RerollButton:update(dt) system.save_run(self.parent.level, gold, self.parent.units, passives, self.parent.shop_level, self.parent.shop_xp, run_passive_pool_by_tiers, locked_state) end elseif self.parent:is(Arena) then - if gold < 15 and not self.free_reroll then + if gold < 10 and not self.free_reroll then self.spring:pull(0.2, 200, 10) self.selected = true error1:play{pitch = random:float(0.95, 1.05), volume = 0.5} @@ -972,10 +967,10 @@ function RerollButton:update(dt) self.parent:set_passives(true) self.selected = true self.spring:pull(0.2, 200, 10) - if not self.free_reroll then gold = gold - 15 end + if not self.free_reroll then gold = gold - 10 end self.parent.shop_text:set_text{{text = '[fg, nudge_down]gold: [yellow, nudge_down]' .. gold, font = pixul_font, alignment = 'center'}} self.free_reroll = false - self.text = Text({{text = '[bg10]reroll: [yellow]15', font = pixul_font, alignment = 'center'}}, global_text_tags) + self.text = Text({{text = '[bg10]reroll: [yellow]10', font = pixul_font, alignment = 'center'}}, global_text_tags) end end end @@ -1004,7 +999,7 @@ function RerollButton:on_mouse_enter() if self.free_reroll then self.text:set_text{{text = '[fgm5]reroll: 0', font = pixul_font, alignment = 'center'}} else - self.text:set_text{{text = '[fgm5]reroll: 15', font = pixul_font, alignment = 'center'}} + self.text:set_text{{text = '[fgm5]reroll: 10', font = pixul_font, alignment = 'center'}} end end self.spring:pull(0.2, 200, 10) @@ -1018,7 +1013,7 @@ function RerollButton:on_mouse_exit() if self.free_reroll then self.text:set_text{{text = '[fgm5]reroll: [yellow]0', font = pixul_font, alignment = 'center'}} else - self.text:set_text{{text = '[fgm5]reroll: [yellow]15', font = pixul_font, alignment = 'center'}} + self.text:set_text{{text = '[fgm5]reroll: [yellow]10', font = pixul_font, alignment = 'center'}} end end self.selected = false @@ -1155,11 +1150,13 @@ function CharacterPart:update(dt) table.remove(self.parent.units, self.i) self:die() self.parent:set_party_and_sets() + self.parent:refresh_cards() else self.parent.parent:gain_gold(self:get_sale_price()) self.parent.parent.units[self.i].reserve[self.level] = self.parent.parent.units[self.i].reserve[self.level] - 1 self:die() self.parent.parent:set_party_and_sets() + self.parent.parent:refresh_cards() end end @@ -1195,8 +1192,8 @@ function CharacterPart:on_mouse_enter() self.spring:pull(0.2, 200, 10) self.info_text = InfoText{group = main.current.ui, force_update = self.force_update} self.info_text:activate({ - {text = '[' .. character_color_strings[self.character] .. ']' .. self.character:capitalize() .. '[fg] - [yellow]Lv.' .. self.level .. '[fg] - sells for [yellow]' .. self:get_sale_price(), - font = pixul_font, alignment = 'center', height_multiplier = 1.25}, + {text = '[' .. character_color_strings[self.character] .. ']' .. self.character:capitalize() .. '[fg] - [yellow]Lv.' .. self.level .. '[fg], tier [yellow]' .. character_tiers[self.character] .. '[fg] - sells for [yellow]' .. + self:get_sale_price(), font = pixul_font, alignment = 'center', height_multiplier = 1.25}, {text = '[fg]Classes: ' .. character_class_strings[self.character], font = pixul_font, alignment = 'center', height_multiplier = 1.25}, {text = character_descriptions[self.character](self.level), font = pixul_font, alignment = 'center', height_multiplier = 2}, {text = '[' .. (self.level == 3 and 'yellow' or 'light_bg') .. ']Lv.3 [' .. (self.level == 3 and 'fg' or 'light_bg') .. ']Effect - ' .. @@ -1408,6 +1405,15 @@ function ItemCard:die() end +function BuyScreen:refresh_cards() + for i = 1, 3 do + if self.cards[i] then + self.cards[i]:refresh() + end + end +end + + ShopCard = Object:extend() ShopCard:implement(GameObject) @@ -1425,6 +1431,24 @@ function ShopCard:init(args) end self.cost = character_tiers[self.unit] self.spring:pull(0.2, 200, 10) + self:refresh() +end + + +function ShopCard:refresh() + self.owned = table.any(self.parent.units, function(v) return v.character == self.unit end) + if self.owned then + self.owned_n = 0 + for _, unit in ipairs(self.parent.units) do + if unit.character == self.unit then + self.owned_n = self.owned_n + ((unit.level == 1 and 1) or (unit.level == 2 and 3) or (unit.level == 3 and 9)) + if unit.reserve then + self.owned_n = self.owned_n + (unit.reserve[2] or 0)*3 + self.owned_n = self.owned_n + (unit.reserve[1] or 0) + end + end + end + end end @@ -1437,6 +1461,7 @@ function ShopCard:update(dt) _G[random:table{'coins1', 'coins2', 'coins3'}]:play{pitch = random:float(0.95, 1.05), volume = 0.5} self:die() self.parent.cards[self.i] = nil + self.parent:refresh_cards() system.save_run(self.parent.level == 1 and 0 or self.parent.level, gold, self.parent.units, passives, self.parent.shop_level, self.parent.shop_xp, run_passive_pool_by_tiers, locked_state) else error1:play{pitch = random:float(0.95, 1.05), volume = 0.5} @@ -1475,6 +1500,36 @@ function ShopCard:draw() if self.selected then graphics.rectangle(self.x, self.y, self.w, self.h, 6, 6, bg[-1]) end + if self.owned then + local x, y = self.x + self.w/5, self.y - self.h/2 + 12 + if self.owned_n == 1 then + graphics.rectangle(x, y, 2, 2, nil, nil, character_colors[self.unit]) + elseif self.owned_n == 2 then + graphics.rectangle(x, y, 2, 2, nil, nil, character_colors[self.unit]) + graphics.rectangle(x + 4, y, 2, 2, nil, nil, character_colors[self.unit]) + elseif self.owned_n == 3 then + graphics.rectangle(x, y, 4, 4, nil, nil, character_colors[self.unit]) + elseif self.owned_n == 4 then + graphics.rectangle(x, y, 4, 4, nil, nil, character_colors[self.unit]) + graphics.rectangle(x + 5, y, 2, 2, nil, nil, character_colors[self.unit]) + elseif self.owned_n == 5 then + graphics.rectangle(x, y, 4, 4, nil, nil, character_colors[self.unit]) + graphics.rectangle(x + 5, y, 2, 2, nil, nil, character_colors[self.unit]) + graphics.rectangle(x + 9, y, 2, 2, nil, nil, character_colors[self.unit]) + elseif self.owned_n == 6 then + graphics.rectangle(x, y, 4, 4, nil, nil, character_colors[self.unit]) + graphics.rectangle(x + 6, y, 4, 4, nil, nil, character_colors[self.unit]) + elseif self.owned_n == 7 then + graphics.rectangle(x, y, 4, 4, nil, nil, character_colors[self.unit]) + graphics.rectangle(x + 6, y, 4, 4, nil, nil, character_colors[self.unit]) + graphics.rectangle(x + 11, y, 2, 2, nil, nil, character_colors[self.unit]) + elseif self.owned_n == 8 then + graphics.rectangle(x, y, 4, 4, nil, nil, character_colors[self.unit]) + graphics.rectangle(x + 6, y, 4, 4, nil, nil, character_colors[self.unit]) + graphics.rectangle(x + 11, y, 2, 2, nil, nil, character_colors[self.unit]) + graphics.rectangle(x + 15, y, 2, 2, nil, nil, character_colors[self.unit]) + end + end graphics.pop() end diff --git a/enemies.lua b/enemies.lua index 1e15ca8..cd9c19c 100644 --- a/enemies.lua +++ b/enemies.lua @@ -517,6 +517,7 @@ function Seeker:hit(damage, projectile) if self.jester_cursed then trigger:after(0.01, function() + if tostring(self.x) == tostring(0/0) or tostring(self.y) == tostring(0/0) then return end _G[random:table{'scout1', 'scout2'}]:play{pitch = random:float(0.95, 1.05), volume = 0.35} HitCircle{group = main.current.effects, x = self.x, y = self.y, rs = 6} local r = random:float(0, 2*math.pi) diff --git a/main.lua b/main.lua index 8768dce..7c2cb2c 100644 --- a/main.lua +++ b/main.lua @@ -554,7 +554,7 @@ function init() ['corruptor'] = function(lvl) return '[fg]spawn [yellow]3[fg] small critters if the corruptor kills an enemy' end, ['beastmaster'] = function(lvl) return '[fg]spawn [yellow]2[fg] small critters if the beastmaster crits' end, ['launcher'] = function(lvl) return '[fg]all nearby enemies are pushed after [yellow]4[fg] seconds, taking [yellow]' .. 2*get_character_stat('launcher', lvl, 'dmg') .. '[fg] damage on wall hit' end, - ['jester'] = function(lvl) return "[fg]curses [yellow]6[fg] nearby enemies for [yellow]6[fg] seconds, they will explode into [yellow]3[fg] knives on death" end, + ['jester'] = function(lvl) return "[fg]curses [yellow]6[fg] nearby enemies for [yellow]6[fg] seconds, they will explode into [yellow]4[fg] knives on death" end, ['assassin'] = function(lvl) return '[fg]throws a piercing knife that deals [yellow]' .. get_character_stat('assassin', lvl, 'dmg') .. '[fg] damage + [yellow]' .. get_character_stat('assassin', lvl, 'dmg')/2 .. '[fg] damage per second' end, ['host'] = function(lvl) return '[fg]periodically spawn [yellow]1[fg] small critter' end, @@ -1465,14 +1465,18 @@ function init() --[[ main:add(Arena'arena') - main:go_to('arena', 21, { - {character = 'plague_doctor', level = 2}, + main:go_to('arena', 25, { {character = 'swordsman', level = 3}, - {character = 'barbarian', level = 2}, - {character = 'outlaw', level = 2}, - {character = 'warden', level = 2}, - {character = 'juggernaut', level = 2}, - {character = 'blade', level = 2}, + {character = 'plague_doctor', level = 3}, + {character = 'pyromancer', level = 3}, + {character = 'witch', level = 3}, + {character = 'arcanist', level = 3}, + {character = 'usurer', level = 3}, + {character = 'warden', level = 3}, + {character = 'silencer', level = 3}, + {character = 'vulcanist', level = 3}, + {character = 'bane', level = 3}, + {character = 'illusionist', level = 3}, }, passives) ]]-- @@ -1565,7 +1569,7 @@ function update(dt) end --[[ - if input.f12.pressed then + if input.f11.pressed then steam.userStats.resetAllStats(true) steam.userStats.storeStats() end diff --git a/objects.lua b/objects.lua index b3a1b58..3fe99ee 100644 --- a/objects.lua +++ b/objects.lua @@ -232,13 +232,13 @@ function Unit:calculate_stats(first_run) if current_new_game_plus == 0 then if self.boss then local x = self.level - local y = {0, 0, 3, 0, 0, 6, 0, 0, 9, 0, 0, 12, 0, 0, 18, 0, 0, 40, 0, 0, 32, 0, 0, 64, 96} + local y = {0, 0, 3, 0, 0, 6, 0, 0, 9, 0, 0, 12, 0, 0, 18, 0, 0, 40, 0, 0, 32, 0, 0, 64, 90} self.base_hp = 100 + (current_new_game_plus*5) + (90 + current_new_game_plus*10)*y[x] self.base_dmg = (12 + current_new_game_plus*2) + (2 + current_new_game_plus)*y[x] self.base_mvspd = 35 + 1.5*y[x] if x == 25 then self.base_dmg = (12 + current_new_game_plus*2) + (1.25 + current_new_game_plus)*y[x] - self.base_mvspd = 35 + 1.2*y[x] + self.base_mvspd = 35 + 1.1*y[x] end else local x = self.level @@ -250,12 +250,12 @@ function Unit:calculate_stats(first_run) else if self.boss then local x = self.level - local y = {0, 0, 3, 0, 0, 6, 0, 0, 9, 0, 0, 12, 0, 0, 18, 0, 0, 40, 0, 0, 32, 0, 0, 64, 96} + local y = {0, 0, 3, 0, 0, 6, 0, 0, 9, 0, 0, 12, 0, 0, 18, 0, 0, 40, 0, 0, 32, 0, 0, 64, 84} self.base_hp = 100 + (current_new_game_plus*5) + (90 + current_new_game_plus*10)*y[x] self.base_dmg = (12 + current_new_game_plus*2) + (2 + current_new_game_plus)*y[x] self.base_mvspd = 35 + 1.5*y[x] if x == 25 then - self.base_dmg = (12 + current_new_game_plus*2) + (2 + 0.5*current_new_game_plus)*y[x] + self.base_dmg = (12 + current_new_game_plus*2) + (1.75 + 0.5*current_new_game_plus)*y[x] self.base_mvspd = 35 + 1.2*y[x] end else diff --git a/player.lua b/player.lua index 7d7573a..e412649 100644 --- a/player.lua +++ b/player.lua @@ -1125,12 +1125,13 @@ function Player:update(dt) if input.move_right.pressed and not self.move_left_pressed then self.move_right_pressed = love.timer.getTime() end if input.move_left.released then self.move_left_pressed = nil end if input.move_right.released then self.move_right_pressed = nil end - if input.move_left.down then self.r = self.r - 1.66*math.pi*dt end - if input.move_right.down then self.r = self.r + 1.66*math.pi*dt end if state.mouse_control then local v = Vector(math.cos(self.r), math.sin(self.r)):perpendicular():dot(Vector(math.cos(self:angle_to_mouse()), math.sin(self:angle_to_mouse()))) self.r = self.r + math.sign(v)*1.66*math.pi*dt + else + if input.move_left.down then self.r = self.r - 1.66*math.pi*dt end + if input.move_right.down then self.r = self.r + 1.66*math.pi*dt end end local total_v = 0 @@ -2521,6 +2522,7 @@ Volcano:implement(Physics) function Volcano:init(args) self:init_game_object(args) if not self.group.world then self.dead = true; return end + if tostring(self.x) == tostring(0/0) or tostring(self.y) == tostring(0/0) then self.dead = true; return end self:set_as_rectangle(9, 9, 'static', 'player') self:set_restitution(0.5) self.hfx:add('hit', 1) @@ -2562,6 +2564,7 @@ end function Volcano:draw() if self.hidden then return end + if not self.hfx.hit then return end graphics.push(self.x, self.y, math.pi/4, self.spring.x, self.spring.x) graphics.rectangle(self.x, self.y, 1.5*self.shape.w, 4, 2, 2, self.hfx.hit.f and fg[0] or self.color) @@ -2670,6 +2673,7 @@ end function Pet:draw() + if not self.hfx.hit then return end graphics.push(self.x, self.y, self.r, self.hfx.hit.x, self.hfx.hit.x) graphics.rectangle(self.x, self.y, self.shape.w, self.shape.h, 3, 3, self.hfx.hit.f and fg[0] or self.color) graphics.pop() @@ -2876,6 +2880,7 @@ Gold:implement(GameObject) Gold:implement(Physics) function Gold:init(args) self:init_game_object(args) + if tostring(self.x) == tostring(0/0) or tostring(self.y) == tostring(0/0) then self.dead = true; return end self:set_as_rectangle(3, 3, 'dynamic', 'ghost') self:set_restitution(0.5) local r = random:float(0, 2*math.pi) @@ -2897,6 +2902,7 @@ end function Gold:draw() + if not self.hfx.hit then return end graphics.push(self.x, self.y, self.r, self.hfx.hit.x, self.hfx.hit.x) graphics.rectangle(self.x, self.y, self.shape.w, self.shape.h, 1, 1, self.hfx.hit.f and fg[0] or self.color) graphics.pop() @@ -2920,6 +2926,7 @@ function Gold:on_trigger_enter(other, contact) if th then if th.level == 3 then trigger:after(0.01, function() + if not main.current.main.world then return end _G[random:table{'scout1', 'scout2'}]:play{pitch = random:float(0.95, 1.05), volume = 0.35} HitCircle{group = main.current.effects, x = self.x, y = self.y, rs = 6} local r = random:float(0, 2*math.pi) @@ -2931,6 +2938,7 @@ function Gold:on_trigger_enter(other, contact) end) else trigger:after(0.01, function() + if not main.current.main.world then return end _G[random:table{'scout1', 'scout2'}]:play{pitch = random:float(0.95, 1.05), volume = 0.35} HitCircle{group = main.current.effects, x = self.x, y = self.y, rs = 6} local r = random:float(0, 2*math.pi) @@ -3004,6 +3012,7 @@ end function Critter:draw() + if not self.hfx.hit then return end graphics.push(self.x, self.y, self.r, self.hfx.hit.x, self.hfx.hit.x) graphics.rectangle(self.x, self.y, self.shape.w, self.shape.h, 2, 2, self.hfx.hit.f and fg[0] or self.color) graphics.pop() diff --git a/todo b/todo index 53ab18d..5f430d1 100644 --- a/todo +++ b/todo @@ -1,89 +1,69 @@ -Shop Update - New units - * Mercenaries (2/4) - +10/20% chance for enemies to drop gold on death - * Miner (tier 1 mercenary) - picking up gold releases 4 homing projectiles that deal X damage, Lv.3 - release 8 homing projectiles instead and they pierce twice - * Merchant (tier 2 mercenary) - gain +1 interest for every 10 gold, Lv.3 - your first item reroll is always free - * Usurer (tier 3 curser, mercenary, voider) - curses 3 nearby enemies indefinitely with debt, dealing X damage over time, Lv.3 - if the same enemy is cursed 3 times it takes 50X damage - * Gambler (tier 3 mercenary, sorcerer) - deal 2X damage to a single random enemy where X is how much gold you have, Lv.3 effect - 60%/40%/20% chance to cast the attack 2/3/4 times - * Thief (tier 4 mercenary, rogue) - throws a knife that deals 2X damage and chains 5 times, Lv.3 - if the knife crits it deals 10X damage, chains 10 times and grants 1 gold - - Shop changes - * Shop level up - * Shop level visual - * Shop level up button - * Level up logic - * Level probabilities - * Hook probabilities into reroll - - Item changes - * Sorcerer items - - Unit changes - * Launcher - removed - * Jester - changed to 4 projectiles - - Misc additions and changes - * Added sorcerer achievement - * Added mercenary achievement - - QoL - * Removed restart button from end screen - * First item reroll is now free - * Gold is given right after the round ends in the arena rather than on the shop for easier item rerolls - * Added mouse follow control mode - * The cursor is now invisible during waves, unless mouse follow mode is enabled - * Added visuals for tank attack - - Balance - * Decreased level 25 boss movement speed - * Change headbutters so they're less likely to kill multiple units at once - - Bug fixes - * Fixed a crash when too many illusions would be spawned in short succession - * Fixed a bug where enemy critters would sometimes be unkillable - * Fixed a rare crash involving broken enemy critter state - * Fixed text for the illusionist's Lv.3 effect going outside the screen - * Fixed a crash when hovering over the Lich in the shop due to playing with a save before the sorcerer update - * Fixed a crash when warden's force field would spawn on top of enemies - * Fixed a crash when a volcano would spawn on top of enemies - * Fixed a crash when a pet would spawn on top of enemies - * Fixed a bug where the maximum number of units would be wrong on certain conditions - * Fixed a crash when clicking too fast after unpausing the game - * Fixed unit highlight when hovering over classes - * Fixed Lv.3 plague doctor moving all his created areas with him - * Fixed a bug where quitting on level 2 would go back to level 1 - -Sacrifice Update +Item Update New mechanics Sacrifice units to level items up + Items New items - https://steamcommunity.com/app/915310/discussions/0/3106901028662504698/ + Position X has +Y% attack speed + Position X has +Y% damage + Position X is 1 tier higher + Position X is also class Y + When a unit dies it explodes, launching enemies away + When a unit dies it explodes, releasing piercing projectiles Reworked items Items shouldn't just be more powerful versions of other items Items should have drawbacks Items that apply to a position on the snake (a good middle step between applying them to individual units like in Underlords) + Item discussions + https://steamcommunity.com/app/915310/discussions/0/3106901028662504698/ + https://steamcommunity.com/app/915310/discussions/0/3106901665846294204/ + https://steamcommunity.com/app/915310/discussions/0/4658391921151238820/ + https://i.imgur.com/cfIyMBL.png + Balance + * Decreased shop reroll cost to 10 + Decrease mercenary gold drop chance to 8/16% (from 10/20%) QoL - Current items visible on item selection screen - Endless mode + Current snake/items visible on item selection screen Volume slider Add visuals for defensive ouroboros, divine intervention, fairy buff Options menu from buy screen + https://i.imgur.com/JJUddT3.png + Improve volcano to not look like spawn marker - https://i.imgur.com/HuS15HG.png + Add option for mouse cursor to always be visible + Add main menu + Soundtrack button + Discord button + Arena run button + Options button + Quit button + * Added tier text to characters on the shop screen + * Added shop unit highlights/markers to make it easier to tell when you already own something + * Added unit names to the "your build" section of the end game screen + Bug fixes - Fix fullscreen with different resolutions that don't scale properly + Fix fullscreen with different resolutions that don't scale properly - https://steamcommunity.com/app/915310/discussions/0/3106901665841020282/ Fix enemies still spawning after arena clear (this happens with the extra enemy spawns that were blocked earlier) - https://i.imgur.com/ieVqYNI.png - https://i.imgur.com/3JCeFuZ.png + * Fixed a bug where passives would sometimes disappear from a run? + * Fixed a bug where clicking "window size-" too many times could bug out the game + * Fixed a bug where the shop would be rerolled after quitting in the arena + * Fixed multiple bugs related to locking the shop and quitting + * Fixed a bug where units could be duplicated in the shop by locking and quitting the game at certain steps + * Fixed a bug where quitting on level 2 would go back to level 1 shop (again) + * Fixed a bug where mouse cursor wasn't visible on death + * Fixed a bug where NG+ level wasn't increased when the game is beaten but only when the NG+ button was clicked + * Fixed a bug where level 2 and level 3 achievements (win with only level 2/3 units) were not triggering correctly sometimes + * Fixed a bug where shop level wasn't respected on the shop's first roll + * Fixed multiple crashes that would happen when picking up gold + * Fixed a crash when the jester's curse would trigger + * Fixed a crash when gold would be picked up with a Miner in the party + * Fixed a crash involving broken state for Pets, Critters or Volcanos + * Fixed a crash that happened after rerolling items too many times -Melee Update - New Units - Guardians - https://i.imgur.com/Ynu5Cdw.png - Assists (2/4) - - Ringmaster (tier 4 assist, nuker) - +15% to all stats to adjacent units, Lv.3 effect - create a cross that deals AoE damage 5 times for 10 seconds - Absorber (tier 2 assist, warrior) - absorbs 50% damage from adjacent units, Lv.3 effect - absorbs 75% damage from adjacent units and gives the absorber +25% defense - Pardoner (tier 3 assist, mercenary) - - Oracle (tier 1 assist) - +10% dodge chance to adjacent units, Lv.3 effect - +20% dodge chance to adjacent units - Seraph (tier 2 assist, healer) - periodically chooses 1 random unit and gives it +100% defense for 6 seconds, Lv.3 - choose 2 units instead +Endless Update + Endless mode + Units die permanently when they die + Slower scaling with less individually threatening units + Max snake size goes up every 10 levels --- @@ -95,6 +75,13 @@ Trappers: Brawlers: units focused on crashing on enemies https://i.imgur.com/5YubukS.png - unit idea Conjurer unit that creates an unit that actively protects you from enemy projectiles +Guardians - https://i.imgur.com/Ynu5Cdw.png +Assists (2/4) - + Ringmaster (tier 4 assist, nuker) - +15% to all stats to adjacent units, Lv.3 effect - create a cross that deals AoE damage 5 times for 10 seconds + Absorber (tier 2 assist, warrior) - absorbs 50% damage from adjacent units, Lv.3 effect - absorbs 75% damage from adjacent units and gives the absorber +25% defense + Pardoner (tier 3 assist, mercenary) - + Oracle (tier 1 assist) - +10% dodge chance to adjacent units, Lv.3 effect - +20% dodge chance to adjacent units + Seraph (tier 2 assist, healer) - periodically chooses 1 random unit and gives it +100% defense for 6 seconds, Lv.3 - choose 2 units instead Passive that makes critters and summons block enemy projectiles Hexblaster? - curser that consumes curses to deal damage Bench? - https://i.imgur.com/B1gNVKk.png @@ -120,13 +107,14 @@ Roguelite update: Tavern (heal units) Challenge + reward Go through the labyrinth without hitting any walls + Go through the traps without getting hit Units die permanently when they die (dead units can be stored in bench to be revived later) Units can have items attached to them like in Underlords Unit item ideas: This unit's projectiles pierce/chain/fork/seek/split/stun/etc This unit is a [class] New stat system: - All stats are values from 1 to 10 (can be lower than 1 or higher than due to debuffs/buffs only) that represent consistent internal values between all units + All stats are values from 1 to 10 (can be lower than 1 or higher than 10 due to debuffs/buffs only) that represent consistent internal values between all units i.e. 3 attack speed means the same internal attack rate value (like say 6 seconds) for the entire game In general it's better if units don't have hidden internal multipliers on these stats, although sometimes that may be inevitable Damage: