diff --git a/arena.lua b/arena.lua index 350eac5..1652c67 100644 --- a/arena.lua +++ b/arena.lua @@ -362,6 +362,10 @@ function Arena:update(dt) main_song_instance = _G[random:table{'song1', 'song2', 'song3', 'song4', 'song5'}]:play{volume = 0.5} end + if not self.paused and not self.died and not self.won then + run_time = run_time + dt + end + if self.shop_text then self.shop_text:update(dt) end if input.escape.pressed and not self.transitioning and not self.in_credits and not self.choosing_passives then @@ -381,6 +385,7 @@ function Arena:update(dt) TransitionEffect{group = main.transitions, x = gw/2, y = gh/2, color = state.dark_transitions and bg[-2] or fg[0], transition_action = function() slow_amount = 1 music_slow_amount = 1 + run_time = 0 gold = 3 passives = {} main_song_instance:stop() @@ -800,8 +805,13 @@ function Arena:draw() end end end + + if state.run_timer then + graphics.print_centered(math.round(run_time, 0), fat_font, self.x2 - 12, self.y2 + 16, 0, 0.6, 0.6, nil, nil, fg[0]) + end camera:detach() + if self.level == 20 and self.trailer then graphics.rectangle(gw/2, gh/2, 2*gw, 2*gh, nil, nil, modal_transparent) end if self.choosing_passives or self.won or self.paused or self.died then graphics.rectangle(gw/2, gh/2, 2*gw, 2*gh, nil, nil, modal_transparent) end self.ui:draw() @@ -848,6 +858,7 @@ function Arena:die() TransitionEffect{group = main.transitions, x = gw/2, y = gh/2, color = state.dark_transitions and bg[-2] or fg[0], transition_action = function() slow_amount = 1 music_slow_amount = 1 + run_time = 0 gold = 3 passives = {} main_song_instance:stop() @@ -875,7 +886,8 @@ end function Arena:endless() if self.clicked_loop then return end self.clicked_loop = true - current_new_game_plus = current_new_game_plus - 1 + if current_new_game_plus >= 5 then current_new_game_plus = 5 + else current_new_game_plus = current_new_game_plus - 1 end if current_new_game_plus < 0 then current_new_game_plus = 0 end self.loop = self.loop + 1 self:transition() diff --git a/assets/media/twitch_boxart.jpg b/assets/media/twitch_boxart.jpg new file mode 100644 index 0000000..0cf9c1a Binary files /dev/null and b/assets/media/twitch_boxart.jpg differ diff --git a/assets/media/twitch_boxart.png b/assets/media/twitch_boxart.png new file mode 100644 index 0000000..a04d1f0 Binary files /dev/null and b/assets/media/twitch_boxart.png differ diff --git a/buy_screen.lua b/buy_screen.lua index 03af0be..9d66e8a 100644 --- a/buy_screen.lua +++ b/buy_screen.lua @@ -136,6 +136,7 @@ function BuyScreen:on_enter(from, level, loop, units, passives, shop_level, shop TransitionEffect{group = main.transitions, x = gw/2, y = gh/2, color = state.dark_transitions and bg[-2] or fg[0], transition_action = function() slow_amount = 1 music_slow_amount = 1 + run_time = 0 gold = 3 passives = {} main_song_instance:stop() @@ -190,6 +191,10 @@ function BuyScreen:update(dt) main_song_instance = _G[random:table{'song1', 'song2', 'song3', 'song4', 'song5'}]:play{volume = 0.2} end + if not self.paused then + run_time = run_time + dt + end + self:update_game_object(dt*slow_amount) if not self.in_tutorial and not self.paused then @@ -543,6 +548,7 @@ function RestartButton:update(dt) TransitionEffect{group = main.transitions, x = gw/2, y = gh/2, color = state.dark_transitions and bg[-2] or fg[0], transition_action = function() slow_amount = 1 music_slow_amount = 1 + run_time = 0 gold = 3 passives = {} main_song_instance:stop() @@ -850,6 +856,36 @@ function LevelButton:update(dt) system.save_run(self.parent.level, self.parent.loop, gold, self.parent.units, self.parent.passives, self.parent.shop_level, self.parent.shop_xp, run_passive_pool, locked_state) end end + + if self.selected and input.m2.pressed then + if self.parent.shop_level <= 1 then return end + if gold < 10 then + self.spring:pull(0.2, 200, 10) + self.selected = true + error1:play{pitch = random:float(0.95, 1.05), volume = 0.5} + if not self.info_text_2 then + self.info_text_2 = InfoText{group = main.current.ui} + self.info_text_2:activate({ + {text = '[fg]not enough gold', font = pixul_font, alignment = 'center'}, + }, nil, nil, nil, nil, 16, 4, nil, 2) + self.info_text_2.x, self.info_text_2.y = gw/2, gh/2 + 30 + end + self.t:after(2, function() self.info_text_2:deactivate(); self.info_text_2.dead = true; self.info_text_2 = nil end, 'info_text_2') + else + ui_switch2:play{pitch = random:float(0.95, 1.05), volume = 0.5} + self.shop_xp = 0 + self.parent.shop_level = self.parent.shop_level - 1 + self.max_xp = (self.parent.shop_level == 1 and 3) or (self.parent.shop_level == 2 and 4) or (self.parent.shop_level == 3 and 5) or (self.parent.shop_level == 4 and 6) or (self.parent.shop_level == 5 and 0) + self.parent.shop_xp = self.shop_xp + self:create_info_text() + self.selected = true + self.spring:pull(0.2, 200, 10) + gold = gold - 10 + self.parent.shop_text:set_text{{text = '[wavy_mid, fg]shop [fg]- [fg, nudge_down]gold: [yellow, nudge_down]' .. gold, font = pixul_font, alignment = 'center'}} + self.text = Text({{text = '[bg10]' .. tostring(self.parent.shop_level), font = pixul_font, alignment = 'center'}}, global_text_tags) + system.save_run(self.parent.level, self.parent.loop, gold, self.parent.units, self.parent.passives, self.parent.shop_level, self.parent.shop_xp, run_passive_pool, locked_state) + end + end end diff --git a/engine/init.lua b/engine/init.lua index 44189cf..dba0c94 100644 --- a/engine/init.lua +++ b/engine/init.lua @@ -125,6 +125,11 @@ function engine_run(config) steam.shutdown() return a or 0 end + elseif name == "focus" then + if main.current:is(Arena) then + if not a then open_options(main.current) + else close_options(main.current) end + end elseif name == "keypressed" then input.keyboard_state[a] = true; input.last_key_pressed = a elseif name == "keyreleased" then input.keyboard_state[a] = false elseif name == "mousepressed" then input.mouse_state[input.mouse_buttons[c]] = true; input.last_key_pressed = input.mouse_buttons[c] diff --git a/engine/system.lua b/engine/system.lua index 27dd0b1..85db464 100644 --- a/engine/system.lua +++ b/engine/system.lua @@ -139,7 +139,7 @@ end function system.save_run(level, loop, gold, units, passives, shop_level, shop_xp, run_passive_pool, locked_state) local run = {level = level, loop = loop, gold = gold, units = units, passives = passives, shop_level = shop_level, shop_xp = shop_xp, run_passive_pool = run_passive_pool, locked_state = locked_state, - current_new_game_plus = current_new_game_plus} + current_new_game_plus = current_new_game_plus, run_time = run_time} local str = "return " .. table.tostring(run) love.filesystem.write("run_v4.txt", str) end diff --git a/main.lua b/main.lua index 973216c..3d9e853 100644 --- a/main.lua +++ b/main.lua @@ -774,7 +774,7 @@ function init() } character_effect_descriptions = { - ['vagrant'] = function() return '[yellow]+15%[fg] attack speed and damage per active set' end, + ['vagrant'] = function() return '[yellow]+15%[fg] attack speed and damage per active class' end, ['swordsman'] = function() return "[fg]the swordsman's damage is [yellow]doubled" end, ['wizard'] = function() return '[fg]the projectile chains [yellow]2[fg] times' end, ['magician'] = function() return '[fg]the magician becomes invulnerable for [yellow]6[fg] seconds but also cannot attack' end, @@ -834,7 +834,7 @@ function init() } character_effect_descriptions_gray = { - ['vagrant'] = function() return '[light_bg]+15% attack speed and damage per active set' end, + ['vagrant'] = function() return '[light_bg]+15% attack speed and damage per active class' end, ['swordsman'] = function() return "[light_bg]the swordsman's damage is doubled" end, ['wizard'] = function() return '[light_bg]the projectile chains 3 times' end, ['magician'] = function() return '[light_bg]the magician becomes invulnerable for 6 seconds but also cannot attack' end, @@ -1010,7 +1010,7 @@ function init() ylb1(lvl) .. ']4[light_bg]/[' .. ylb2(lvl) .. ']3[light_bg]/[' .. ylb3(lvl) .. ']2[fg] attacks' end, ['mercenary'] = function(lvl) return '[' .. ylb1(lvl) .. ']2[light_bg]/[' .. ylb2(lvl) .. ']4 [fg]- [' .. ylb1(lvl) .. ']+8%[light_bg]/[' .. ylb2(lvl) .. ']+16% [fg]chance for enemies to drop gold on death' end, - ['explorer'] = function(lvl) return '[yellow]+15%[fg] attack speed and damage per active set to allied explorers' end, + ['explorer'] = function(lvl) return '[yellow]+15%[fg] attack speed and damage per active class to allied explorers' end, } tier_to_characters = { @@ -1731,15 +1731,15 @@ function init() 'silencing_strike', 'culling_strike', 'lightning_strike', 'psycholeak', 'divine_blessing', 'hardening', 'kinetic_strike', } main:add(Arena'arena') - main:go_to('arena', 16, 0, { - {character = 'archer', level = 3}, - {character = 'barrager', level = 3}, - {character = 'corruptor', level = 3}, - {character = 'host', level = 3}, - {character = 'beastmaster', level = 3}, - {character = 'infestor', level = 3}, + main:go_to('arena', 7, 0, { + {character = 'arcanist', level = 1}, + {character = 'artificer', level = 1}, + {character = 'witch', level = 1}, + {character = 'warden', level = 3}, + {character = 'psychic', level = 1}, + {character = 'vulcanist', level = 1}, }, { - {passive = 'hive', level = 3}, + {passive = 'magnify', level = 3}, }) ]]-- @@ -1875,6 +1875,7 @@ function open_options(self) if self.restart_button then self.restart_button.dead = true; self.restart_button = nil end if self.mouse_button then self.mouse_button.dead = true; self.mouse_button = nil end if self.dark_transition_button then self.dark_transition_button.dead = true; self.dark_transition_button = nil end + if self.run_timer_button then self.run_timer_button.dead = true; self.run_timer_button = nil end if self.sfx_button then self.sfx_button.dead = true; self.sfx_button = nil end if self.music_button then self.music_button.dead = true; self.music_button = nil end if self.video_button_1 then self.video_button_1.dead = true; self.video_button_1 = nil end @@ -1904,6 +1905,7 @@ function open_options(self) TransitionEffect{group = main.transitions, x = gw/2, y = gh/2, color = state.dark_transitions and bg[-2] or fg[0], transition_action = function() slow_amount = 1 music_slow_amount = 1 + run_time = 0 gold = 3 passives = {} main_song_instance:stop() @@ -1925,20 +1927,27 @@ function open_options(self) end} end - self.mouse_button = Button{group = self.ui, x = gw/2 - 57, y = gh - 150, force_update = true, button_text = 'mouse control: ' .. tostring(state.mouse_control and 'yes' or 'no'), fg_color = 'bg10', bg_color = 'bg', + self.mouse_button = Button{group = self.ui, x = gw/2 - 113, y = gh - 150, force_update = true, button_text = 'mouse control: ' .. tostring(state.mouse_control and 'yes' or 'no'), fg_color = 'bg10', bg_color = 'bg', action = function(b) ui_switch1:play{pitch = random:float(0.95, 1.05), volume = 0.5} state.mouse_control = not state.mouse_control b:set_text('mouse control: ' .. tostring(state.mouse_control and 'yes' or 'no')) end} - self.dark_transition_button = Button{group = self.ui, x = gw/2 + 64, y = gh - 150, force_update = true, button_text = 'dark transitions: ' .. tostring(state.dark_transitions and 'yes' or 'no'), + self.dark_transition_button = Button{group = self.ui, x = gw/2 + 13, y = gh - 150, force_update = true, button_text = 'dark transitions: ' .. tostring(state.dark_transitions and 'yes' or 'no'), fg_color = 'bg10', bg_color = 'bg', action = function(b) ui_switch1:play{pitch = random:float(0.95, 1.05), volume = 0.5} state.dark_transitions = not state.dark_transitions b:set_text('dark transitions: ' .. tostring(state.dark_transitions and 'yes' or 'no')) end} + self.run_timer_button = Button{group = self.ui, x = gw/2 + 121, y = gh - 150, force_update = true, button_text = 'run timer: ' .. tostring(state.run_timer and 'yes' or 'no'), fg_color = 'bg10', bg_color = 'bg', + action = function(b) + ui_switch1:play{pitch = random:float(0.95, 1.05), volume = 0.5} + state.run_timer = not state.run_timer + b:set_text('run timer: ' .. tostring(state.run_timer and 'yes' or 'no')) + end} + self.sfx_button = Button{group = self.ui, x = gw/2 - 46, y = gh - 175, force_update = true, button_text = 'sfx volume: ' .. tostring((state.sfx_volume or 0.5)*10), fg_color = 'bg10', bg_color = 'bg', action = function(b) ui_switch2:play{pitch = random:float(0.95, 1.05), volume = 0.5} @@ -2113,6 +2122,7 @@ function close_options(self) if self.restart_button then self.restart_button.dead = true; self.restart_button = nil end if self.mouse_button then self.mouse_button.dead = true; self.mouse_button = nil end if self.dark_transition_button then self.dark_transition_button.dead = true; self.dark_transition_button = nil end + if self.run_timer_button then self.run_timer_button.dead = true; self.run_timer_button = nil end if self.sfx_button then self.sfx_button.dead = true; self.sfx_button = nil end if self.music_button then self.music_button.dead = true; self.music_button = nil end if self.video_button_1 then self.video_button_1.dead = true; self.video_button_1 = nil end diff --git a/mainmenu.lua b/mainmenu.lua index 9c2f107..8aee8b2 100644 --- a/mainmenu.lua +++ b/mainmenu.lua @@ -99,6 +99,7 @@ function MainMenu:on_enter(from) 'intimidation', 'vulnerability', 'temporal_chains', 'ceremonial_dagger', 'homing_barrage', 'critical_strike', 'noxious_strike', 'infesting_strike', 'burning_strike', 'lucky_strike', 'healing_strike', 'stunning_strike', 'silencing_strike', 'culling_strike', 'lightning_strike', 'psycholeak', 'divine_blessing', 'hardening', 'kinetic_strike', } + run_time = run.time or 0 gold = run.gold or 3 passives = run.passives or {} locked_state = run.locked_state diff --git a/objects.lua b/objects.lua index f449af4..607757c 100644 --- a/objects.lua +++ b/objects.lua @@ -246,10 +246,10 @@ function Unit:calculate_stats(first_run) end 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] + self.base_mvspd = math.min(35 + 1.5*y[x], 35 + 1.5*y[150]) if x % 25 == 0 then self.base_dmg = (12 + current_new_game_plus*2) + (1.25 + current_new_game_plus)*y[x] - self.base_mvspd = 35 + 1.1*y[x] + self.base_mvspd = math.min(35 + 1.1*y[x], 35 + 1.1*y[150]) end else local x = self.level @@ -265,7 +265,7 @@ function Unit:calculate_stats(first_run) end self.base_hp = 25 + 16.5*y[x] self.base_dmg = 4.5 + 2.5*y[x] - self.base_mvspd = 70 + 3*y[x] + self.base_mvspd = math.min(70 + 3*y[x], 70 + 3*y[150]) end else if self.boss then @@ -284,10 +284,10 @@ function Unit:calculate_stats(first_run) end 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] + self.base_mvspd = math.min(35 + 1.5*y[x], 35 + 1.5*y[150]) if x % 25 == 0 then 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] + self.base_mvspd = math.min(35 + 1.2*y[x], 35 + 1.2*y[150]) end else local x = self.level @@ -303,7 +303,7 @@ function Unit:calculate_stats(first_run) end self.base_hp = 22 + (current_new_game_plus*3) + (15 + current_new_game_plus*2.7)*y[x] self.base_dmg = (4 + current_new_game_plus*1.15) + (2 + current_new_game_plus*0.83)*y[x] - self.base_mvspd = 70 + 3*y[x] + self.base_mvspd = math.min(70 + 3*y[x], 70 + 3*y[150]) end end elseif self:is(Saboteur) then diff --git a/player.lua b/player.lua index 131e842..27d097c 100644 --- a/player.lua +++ b/player.lua @@ -2965,7 +2965,7 @@ ForceField:implement(GameObject) ForceField:implement(Physics) function ForceField:init(args) self:init_game_object(args) - self:set_as_circle(12, 'static', 'force_field') + self:set_as_circle((self.parent and self.parent.magnify and (self.parent.magnify == 1 and 14) or (self.parent.magnify == 2 and 17) or (self.parent.magnify == 3 and 20)) or 12, 'static', 'force_field') self.hfx:add('hit', 1) self.color = fg[0] diff --git a/todo b/todo index 89797c2..d394308 100644 --- a/todo +++ b/todo @@ -1,5 +1,15 @@ Weekly maintenance updates: +#2 + + * Fixed a bug where NG+5 difficulty would go down to NG+4 after looping + * Capped enemy movement speed after level 150 + * Warden's bubble is now affected by magnify + * Changed all text instances of "active set" to "active class" to avoid confusion + * Added a run timer option - note that the timer will be off for saved runs that started before the patch + * Alt tabbing now automatically pauses the game while in the arena + * Shop level can now be reduced + #1 * Fixed several blue screen crashes due to broken looping state @@ -20,6 +30,8 @@ Weekly maintenance updates: --- +30Hz + Invoker - casts attacks and spells from other units having a unit like this from the start will help ensure that attacks are behind function calls that can be accessed by another unit easily rather than mostly hidden like they are now @@ -69,6 +81,8 @@ Unit ideas - https://i.imgur.com/VNMS2YV.png Unit ideas - https://steamcommunity.com/app/915310/discussions/0/3069747783693969554/ Unit ideas - https://steamcommunity.com/app/915310/discussions/0/3046104336668792953/ Achievement ideas - https://i.imgur.com/Q7jHWB2.png, https://i.imgur.com/2l5eist.png +general ideas - https://i.imgur.com/W8EYUU1.png +room types - https://i.imgur.com/u2AY1ea.png Draft system Ban system @@ -112,6 +126,8 @@ Roguelite update: 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: + Hit: + Everything hits except DoT Damage type: Attack - physical attacks, decreased by the enemy's armor Spell - magical attacks, decreased by the enemy's magic resistance