QoL update 1/2

master
a327ex 2021-05-28 22:42:16 -03:00
parent a859f6df56
commit 129cf3ff3b
8 changed files with 284 additions and 132 deletions

View File

@ -777,6 +777,7 @@ end
function Arena:die()
if not self.died_text and not self.won then
self.died = true
system.save_run(0, gold, {}, passives, run_passive_pool_by_tiers)
self.t:tween(2, self, {main_slow_amount = 0}, math.linear, function() self.main_slow_amount = 0 end)
self.died_text = Text2{group = self.ui, x = gw/2, y = gh/2 - 32, lines = {
{text = '[wavy_mid, cbyc]you died...', font = fat_font, alignment = 'center', height_multiplier = 1.25},

View File

@ -20,6 +20,7 @@ function BuyScreen:on_exit()
self.sets_text = nil
self.items_text = nil
self.ng_text = nil
self.level_text = nil
self.characters = nil
self.sets = nil
self.cards = nil
@ -31,6 +32,8 @@ function BuyScreen:on_exit()
self.springs = nil
self.flashes = nil
self.hfx = nil
self.tutorial_button = nil
self.restart_button = nil
end
@ -54,7 +57,10 @@ function BuyScreen:on_enter(from, level, units, passives)
self.ui = Group()
self.tutorial = Group()
self:set_cards()
self.locked = locked_state and locked_state.locked
LockButton{group = self.main, x = 210, y = 18, parent = self}
self:set_cards(nil, nil, true)
self:set_party_and_sets()
self:set_items()
@ -63,10 +69,11 @@ function BuyScreen:on_enter(from, level, units, passives)
self.sets_text = Text({{text = '[wavy_mid, fg]classes', font = pixul_font, alignment = 'center'}}, global_text_tags)
self.items_text = Text({{text = '[wavy_mid, fg]items', font = pixul_font, alignment = 'center'}}, global_text_tags)
self.ng_text = Text({{text = '[fg]NG+' .. new_game_plus, font = pixul_font, alignment = 'center'}}, global_text_tags)
self.level_text = Text({{text = '[fg]Lv.' .. tostring(level == 0 and 1 or self.level+1) .. tostring((self.level+1) % 3 == 0 and ' (elite)' or ''), font = pixul_font, alignment = 'center'}}, global_text_tags)
if not self.first_screen then RerollButton{group = self.main, x = 150, y = 18, parent = self} end
GoButton{group = self.main, x = gw - 90, y = gh - 20, parent = self}
self.tutorial_button = Button{group = self.main, x = gw/2 + 142, y = 18, button_text = '?', fg_color = 'yellowm5', bg_color = 'yellow', action = function()
self.tutorial_button = Button{group = self.main, x = gw/2 + 136, y = 18, button_text = '?', fg_color = 'bg10', bg_color = 'bg', action = function()
self.in_tutorial = true
self.title_text = Text2{group = self.tutorial, x = gw/2, y = 35, lines = {{text = '[fg]WELCOME TO SNKRX!', font = fat_font, alignment = 'center'}}}
self.tutorial_text = Text2{group = self.tutorial, x = 228, y = 160, lines = {
@ -103,6 +110,30 @@ function BuyScreen:on_enter(from, level, units, passives)
end}
end}
self.restart_button = Button{group = self.ui, x = gw/2 + 154, y = 18, force_update = true, button_text = 'R', fg_color = 'bg10', bg_color = 'bg', action = function(b)
self.transitioning = true
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}
TransitionEffect{group = main.transitions, x = gw/2, y = gh/2, color = fg[0], transition_action = function()
slow_amount = 1
gold = 2
passives = {}
main_song_instance:stop()
run_passive_pool_by_tiers = {
[1] = {'wall_echo', 'wall_rider', 'centipede', 'temporal_chains', 'amplify', 'amplify_x', 'ballista', 'ballista_x', 'blunt_arrow', 'berserking', 'unwavering_stance', 'assassination', 'unleash', 'blessing',
'hex_master', 'force_push', 'spawning_pool'},
[2] = {'ouroboros_technique_r', 'ouroboros_technique_l', 'intimidation', 'vulnerability', 'resonance', 'point_blank', 'longshot', 'explosive_arrow', 'chronomancy', 'awakening', 'ultimatum', 'echo_barrage',
'reinforce', 'payback', 'whispers_of_doom', 'heavy_impact', 'immolation', 'call_of_the_void'},
[3] = {'divine_machine_arrow', 'divine_punishment', 'flying_daggers', 'crucio', 'hive', 'void_rift'},
}
max_units = 7 + new_game_plus
main:add(BuyScreen'buy_screen')
system.save_run(0, gold, {}, passives, run_passive_pool_by_tiers)
main:go_to('buy_screen', 0, {}, passives)
end, text = Text({{text = '[wavy, bg]restarting...', font = pixul_font, alignment = 'center'}}, global_text_tags)}
end}
trigger:tween(1, main_song_instance, {volume = 0.2}, math.linear)
if self.level == 1 then
@ -130,6 +161,7 @@ function BuyScreen:update(dt)
if self.party_text then self.party_text:update(dt) end
if self.items_text then self.items_text:update(dt) end
if self.ng_text then self.ng_text:update(dt) end
if self.level_text then self.level_text:update(dt) end
else
self.tutorial:update(dt*slow_amount)
end
@ -162,6 +194,7 @@ function BuyScreen:draw()
self.main:draw()
self.effects:draw()
if self.items_text then self.items_text:draw(32, 145) end
if self.level_text then self.level_text:draw(260, gh - 20) end
self.ui:draw()
if self.unit_grabbed then
@ -179,9 +212,7 @@ function BuyScreen:draw()
if self.shop_text then self.shop_text:draw(64, 20) end
if self.sets_text then self.sets_text:draw(328, 20) end
if self.party_text then self.party_text:draw(440, 20) end
if new_game_plus > 0 then
self.ng_text:draw(240, 20)
end
if new_game_plus > 0 then self.ng_text:draw(240, 20) end
if self.in_tutorial then
graphics.rectangle(gw/2, gh/2, 2*gw, 2*gh, nil, nil, modal_transparent_2)
@ -264,7 +295,7 @@ function BuyScreen:gain_gold(amount)
end
function BuyScreen:set_cards(level, dont_spawn_effect)
function BuyScreen:set_cards(level, dont_spawn_effect, first_call)
if self.cards then for i = 1, 3 do if self.cards[i] then self.cards[i]:die(dont_spawn_effect) end end end
self.cards = {}
local all_units = {}
@ -277,10 +308,16 @@ function BuyScreen:set_cards(level, dont_spawn_effect)
unit_3 = random:table(tier_to_characters[random:weighted_pick(unpack(level_to_tier_weights[level or self.level]))])
all_units = {unit_1, unit_2, unit_3}
until not table.all(all_units, function(v) return table.any(non_attacking_characters, function(u) return v == u end) end)
if first_call and self.locked then
if locked_state.cards[1] then self.cards[1] = ShopCard{group = self.main, x = 60, y = 75, w = 80, h = 90, unit = locked_state.cards[1], parent = self, i = 1} end
if locked_state.cards[2] then self.cards[2] = ShopCard{group = self.main, x = 140, y = 75, w = 80, h = 90, unit = locked_state.cards[2], parent = self, i = 2} end
if locked_state.cards[3] then self.cards[3] = ShopCard{group = self.main, x = 220, y = 75, w = 80, h = 90, unit = locked_state.cards[3], parent = self, i = 3} end
else
self.cards[1] = ShopCard{group = self.main, x = 60, y = 75, w = 80, h = 90, unit = unit_1, parent = self, i = 1}
self.cards[2] = ShopCard{group = self.main, x = 140, y = 75, w = 80, h = 90, unit = unit_2, parent = self, i = 2}
self.cards[3] = ShopCard{group = self.main, x = 220, y = 75, w = 80, h = 90, unit = unit_3, parent = self, i = 3}
end
end
function BuyScreen:set_party_and_sets()
@ -467,7 +504,7 @@ function RestartButton:update(dt)
max_units = 7 + new_game_plus
system.save_state()
main:add(BuyScreen'buy_screen')
system.save_run(0, gold, {}, passives, run_passive_pool_by_tiers)
system.save_run(0, gold, {}, passives, run_passive_pool_by_tiers, locked_state)
main:go_to('buy_screen', 0, {}, passives)
end, text = Text({{text = '[wavy, bg]restarting...', font = pixul_font, alignment = 'center'}}, global_text_tags)}
end
@ -546,6 +583,13 @@ function Button:on_mouse_exit()
end
function Button:set_text(text)
self.button_text = text
self.text:set_text{{text = '[' .. self.fg_color .. ']' .. self.button_text, font = pixul_font, alignment = 'center'}}
self.spring:pull(0.2, 200, 10)
end
GoButton = Object:extend()
@ -574,6 +618,7 @@ 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
ui_switch2:play{pitch = random:float(0.95, 1.05), volume = 0.5}
self.spring:pull(0.2, 200, 10)
self.selected = true
@ -612,6 +657,58 @@ function GoButton:on_mouse_exit()
end
LockButton = Object:extend()
LockButton:implement(GameObject)
function LockButton:init(args)
self:init_game_object(args)
self.shape = Rectangle(self.x, self.y, 32, 16)
self.interact_with_mouse = true
if self.parent.locked then self.shape.w = 44
else self.shape.w = 32 end
if self.parent.locked then self.text = Text({{text = '[fgm5]' .. tostring(self.parent.locked and 'unlock' or 'lock'), font = pixul_font, alignment = 'center'}}, global_text_tags)
else self.text = Text({{text = '[bg10]' .. tostring(self.parent.locked and 'unlock' or 'lock'), font = pixul_font, alignment = 'center'}}, global_text_tags) end
end
function LockButton:update(dt)
self:update_game_object(dt)
if self.selected and input.m1.pressed then
self.parent.locked = not self.parent.locked
if not self.parent.locked then locked_state = nil end
ui_switch2:play{pitch = random:float(0.95, 1.05), volume = 0.5}
self.selected = true
self.spring:pull(0.2, 200, 10)
self.text:set_text{{text = '[fgm5]' .. tostring(self.parent.locked and 'unlock' or 'lock'), font = pixul_font, alignment = 'center'}}
if self.parent.locked then self.shape.w = 44
else self.shape.w = 32 end
end
end
function LockButton:draw()
graphics.push(self.x, self.y, 0, self.spring.x, self.spring.y)
graphics.rectangle(self.x, self.y, self.shape.w, self.shape.h, 4, 4, (self.selected or self.parent.locked) and fg[0] or bg[1])
self.text:draw(self.x, self.y + 1)
graphics.pop()
end
function LockButton: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]' .. tostring(self.parent.locked and 'unlock' or 'lock'), font = pixul_font, alignment = 'center'}}
self.spring:pull(0.2, 200, 10)
end
function LockButton:on_mouse_exit()
if not self.parent.locked then self.text:set_text{{text = '[bg10]' .. tostring(self.parent.locked and 'unlock' or 'lock'), font = pixul_font, alignment = 'center'}} end
self.selected = false
end
RerollButton = Object:extend()
RerollButton:implement(GameObject)

View File

@ -265,7 +265,6 @@ function Seeker:update(dt)
self:calculate_stats()
self.stun_dmg_m = (self.barbarian_stunned and 2 or 1)
self.bane_dmg_m = (self.baned and 1.5 or 1)
if self.shooter then
self.t:set_every_multiplier('shooter', (1 - self.level*0.02))
@ -392,7 +391,7 @@ function Seeker:hit(damage, projectile)
if self.push_invulnerable then return end
self:show_hp()
local actual_damage = math.max(self:calculate_damage(damage)*(self.stun_dmg_m or 1)*(self.bane_dmg_m or 1), 0)
local actual_damage = math.max(self:calculate_damage(damage)*(self.stun_dmg_m or 1), 0)
if self.vulnerable then actual_damage = actual_damage*1.2 end
self.hp = self.hp - actual_damage
if self.hp > self.max_hp then self.hp = self.max_hp end
@ -473,6 +472,27 @@ function Seeker:hit(damage, projectile)
end
end)
end
if self.jester_cursed then
trigger:after(0.01, function()
_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)
for i = 1, 3 do
local t = {group = main.current.main, x = self.x + 8*math.cos(r), y = self.y + 8*math.sin(r), v = 250, r = r, color = red[0], dmg = self.jester_ref.dmg,
pierce = self.jester_lvl3 and 2 or 0, homing = self.jester_lvl3, character = self.jester_ref.character, parent = self.jester_ref}
Projectile(table.merge(t, mods or {}))
r = r + math.pi/1.5
end
end)
end
if self.bane_cursed then
trigger:after(0.01, function()
DotArea{group = main.current.effects, x = self.x, y = self.y, rs = (self.bane_ref.level == 3 and 2 or 1)*self.bane_ref.area_size_m*18, color = purple[0],
dmg = self.bane_ref.area_dmg_m*self.bane_ref.dmg*(self.bane_ref.dot_dmg_m or 1), void_rift = true, duration = 1}
end)
end
end
end
@ -528,11 +548,16 @@ function Seeker:curse(curse, duration, arg1, arg2, arg3)
self.launcher = arg2
self:push(random:float(50, 75)*self.launcher.knockback_m, random:table{0, math.pi, math.pi/2, -math.pi/2})
end, 'launcher_curse')
elseif curse == 'bard' then
self.bard_cursed = true
elseif curse == 'jester' then
self.jester_cursed = true
self.jester_lvl3 = arg1
self.jester_ref = arg2
self.t:after(duration*curse_m, function() self.jester_cursed = false end, 'jester_curse')
elseif curse == 'bane' then
self.baned = true
self.t:after(duration*curse_m, function() self.baned = false end, 'bane_curse')
self.bane_cursed = true
self.bane_lvl3 = arg1
self.bane_ref = arg2
self.t:after(duration*curse_m, function() self.bane_cursed = false end, 'bane_curse')
elseif curse == 'infestor' then
self.infested = arg1
self.infested_dmg = arg2
@ -703,11 +728,16 @@ function EnemyCritter:curse(curse, duration, arg1, arg2, arg3)
self.launcher = arg2
self:push(random:float(50, 75)*self.launcher.knockback_m, random:table{0, math.pi, math.pi/2, -math.pi/2})
end, 'launcher_curse')
elseif curse == 'bard' then
self.bard_cursed = true
elseif curse == 'jester' then
self.jester_cursed = true
self.jester_lvl3 = arg1
self.jester_ref = arg2
self.t:after(duration*curse_m, function() self.jester_cursed = false end, 'jester_curse')
elseif curse == 'bane' then
self.baned = true
self.t:after(duration*curse_m, function() self.baned = false end, 'bane_curse')
self.bane_cursed = true
self.bane_lvl3 = arg1
self.bane_ref = arg2
self.t:after(duration*curse_m, function() self.bane_cursed = false end, 'bane_curse')
elseif curse == 'infestor' then
self.infested = arg1
self.infested_dmg = arg2

View File

@ -137,8 +137,8 @@ function system.load_state()
end
function system.save_run(level, gold, units, passives, run_passive_pool_by_tiers)
local run = {level = level, gold = gold, units = units, passives = passives, run_passive_pool_by_tiers = run_passive_pool_by_tiers}
function system.save_run(level, gold, units, passives, run_passive_pool_by_tiers, locked_state)
local run = {level = level, gold = gold, units = units, passives = passives, run_passive_pool_by_tiers = run_passive_pool_by_tiers, locked_state = locked_state}
local str = "return " .. table.tostring(run)
love.filesystem.write("run.txt", str)
end

View File

@ -232,7 +232,7 @@ function init()
['corruptor'] = 'Corruptor',
['beastmaster'] = 'Beastmaster',
['launcher'] = 'Launcher',
['bard'] = 'Bard',
['jester'] = 'Jester',
['assassin'] = 'Assassin',
['host'] = 'Host',
['carver'] = 'Carver',
@ -276,7 +276,7 @@ function init()
['corruptor'] = orange[0],
['beastmaster'] = red[0],
['launcher'] = yellow[0],
['bard'] = red[0],
['jester'] = red[0],
['assassin'] = purple[0],
['host'] = orange[0],
['carver'] = green[0],
@ -320,7 +320,7 @@ function init()
['corruptor'] = 'orange',
['beastmaster'] = 'red',
['launcher'] = 'yellow',
['bard'] = 'red',
['jester'] = 'red',
['assassin'] = 'purple',
['host'] = 'orange',
['carver'] = 'green',
@ -364,7 +364,7 @@ function init()
['corruptor'] = {'ranger', 'swarmer'},
['beastmaster'] = {'rogue', 'swarmer'},
['launcher'] = {'curser', 'forcer'},
['bard'] = {'curser', 'rogue'},
['jester'] = {'curser', 'rogue'},
['assassin'] = {'rogue', 'voider'},
['host'] = {'swarmer'},
['carver'] = {'conjurer', 'healer'},
@ -408,7 +408,7 @@ function init()
['corruptor'] = '[green]Ranger, [orange]Swarmer',
['beastmaster'] = '[red]Rogue, [orange]Swarmer',
['launcher'] = '[yellow]Forcer, [purple]Curser',
['bard'] = '[purple]Curser, [red]Rogue',
['jester'] = '[purple]Curser, [red]Rogue',
['assassin'] = '[red]Rogue, [purple]Voider',
['host'] = '[orange]Swarmer',
['carver'] = '[orange]Conjurer, [green]Healer',
@ -467,19 +467,19 @@ function init()
['pyromancer'] = function(lvl) return '[fg]nearby enemies take [yellow]' .. get_character_stat('pyromancer', lvl, 'dmg') .. '[fg] damage per second' end,
['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]nearby enemies are pushed after [yellow]4[fg] seconds, taking [yellow]' .. 2*get_character_stat('launcher', lvl, 'dmg') .. '[fg] damage on wall hit' end,
['bard'] = function(lvl) return "[fg]throws a knife that deals [yellow]" .. get_character_stat('bard', lvl, 'dmg') .. "[fg] damage and inflicts enemies hit with the bard's curse" 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]5[fg] nearby enemies for [yellow]6[fg] seconds, they will explode into [yellow]3[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 for [yellow]3[fg] seconds' end,
['host'] = function(lvl) return '[fg]periodically spawn [yellow]1[fg] small critter' end,
['carver'] = function(lvl) return '[fg]carves a statue that periodically heals [yellow]1[fg] unit for [yellow]20%[fg] max HP if in range' end,
['bane'] = function(lvl) return '[fg]creates a large area that curses enemies to take [yellow]+50%[fg] damage' end,
['bane'] = function(lvl) return '[fg]curser [yellow]5[fg] nearby enemies for [yellow]6[fg] seconds, they will create small void rifts on death' end,
['psykino'] = function(lvl) return '[fg]pulls enemies together for [yellow]2[fg] seconds' end,
['barrager'] = function(lvl) return '[fg]shoots a barrage of [yellow]3[fg] arrows, each dealing [yellow]' .. get_character_stat('barrager', lvl, 'dmg') .. '[fg] damage and pushing enemies' end,
['highlander'] = function(lvl) return '[fg]deals [yellow]' .. 5*get_character_stat('highlander', lvl, 'dmg') .. '[fg] AoE damage' end,
['fairy'] = function(lvl) return '[fg]periodically heals [yellow]1[fg] unit at random and grants it [yellow]+100%[fg] attack speed for [yellow]6[fg] seconds' end,
['priest'] = function(lvl) return '[fg]heals all allies for [yellow]20%[fg] their max HP' end,
['infestor'] = function(lvl) return '[fg]curses nearby enemies for [yellow]6[fg] seconds, they will release [yellow]2[fg] critters on death' end,
['infestor'] = function(lvl) return '[fg]curses all nearby enemies for [yellow]6[fg] seconds, they will release [yellow]2[fg] critters on death' end,
['flagellant'] = function(lvl) return '[fg]deals [yellow]' .. 2*get_character_stat('flagellant', lvl, 'dmg') .. '[fg] damage to self and grants [yellow]+4%[fg] damage to all allies per cast' end,
}
@ -503,7 +503,7 @@ function init()
['chronomancer'] = '[blue]Quicken',
['spellblade'] = '[blue]Spiralism',
['psykeeper'] = '[fg]Crucio',
['engineer'] = '[orange]Upgrade',
['engineer'] = '[orange]Upgrade!!!',
['plague_doctor'] = '[purple]Black Death Steam',
['barbarian'] = '[yellow]Seism',
['juggernaut'] = '[yellow]Brutal Impact',
@ -513,7 +513,7 @@ function init()
['corruptor'] = '[orange]Corruption',
['beastmaster'] = '[red]Call of the Wild',
['launcher'] = '[orange]Kineticism',
['bard'] = "[red]The Bard's Song",
['jester'] = "[red]Pandemonium",
['assassin'] = '[purple]Toxic Delivery',
['host'] = '[orange]Invasion',
['carver'] = '[green]World Tree',
@ -547,7 +547,7 @@ function init()
['chronomancer'] = '[light_bg]Quicken',
['spellblade'] = '[light_bg]Spiralism',
['psykeeper'] = '[light_bg]Crucio',
['engineer'] = '[light_bg]Upgrade',
['engineer'] = '[light_bg]Upgrade!!!',
['plague_doctor'] = '[light_bg]Black Death Steam',
['barbarian'] = '[light_bg]Seism',
['juggernaut'] = '[light_bg]Brutal Impact',
@ -557,7 +557,7 @@ function init()
['corruptor'] = '[light_bg]Corruption',
['beastmaster'] = '[light_bg]Call of the Wild',
['launcher'] = '[light_bg]Kineticism',
['bard'] = "[light_bg]The Bard's Song",
['jester'] = "[light_bg]Pandemonium",
['assassin'] = '[light_bg]Toxic Delivery',
['host'] = '[light_bg]Invasion',
['carver'] = '[light_bg]World Tree',
@ -572,7 +572,7 @@ function init()
}
character_effect_descriptions = {
['vagrant'] = function() return '[yellow]+10%[fg] damage and [yellow]+5%[fg] attack speed per active set' end,
['vagrant'] = function() return '[yellow]+10%[fg] damage and [yellow]+10%[fg] attack speed per active set' end,
['swordsman'] = function() return "[fg]the swordsman's damage is [yellow]doubled" end,
['wizard'] = function() return '[fg]the projectile chains [yellow]3[fg] times' end,
['archer'] = function() return '[fg]the arrow ricochets off walls [yellow]3[fg] times' end,
@ -591,7 +591,7 @@ function init()
['chronomancer'] = function() return '[fg]enemies take damave over time [yellow]50%[fg] faster' end,
['spellblade'] = function() return '[fg]faster projectile speed and tighter turns' end,
['psykeeper'] = function() return '[fg]also redistributes damage taken as damage to all enemies at [yellow]double[fg] value' end,
['engineer'] = function() return '[fg]every 3rd sentry dropped upgrade all sentries with [yellow]+100%[fg] damage and attack speed' end,
['engineer'] = function() return '[fg]drops [yellow]2[fg] additional turrets and grants all turrets [yellow]+50%[fg] damage and attack speed' end,
['plague_doctor'] = function() return '[fg]nearby enemies take an additional [yellow]' .. get_character_stat('plague_doctor', 3, 'dmg') .. '[fg] damage per second' end,
['barbarian'] = function() return '[fg]stunned enemies also take [yellow]100%[fg] increased damage' end,
['juggernaut'] = function() return '[fg]enemies pushed by the juggernaut take [yellow]' .. 4*get_character_stat('juggernaut', 3, 'dmg') .. '[fg] damage if they hit a wall' end,
@ -601,11 +601,11 @@ function init()
['corruptor'] = function() return '[fg]spawn [yellow]3[fg] small critters if the corruptor hits an enemy' end,
['beastmaster'] = function() return '[fg]spawn [yellow]2[fg] small critters if the beastmaster gets hit' end,
['launcher'] = function() return '[fg]enemies launched take [yellow]300%[fg] more damage when they hit walls' end,
['bard'] = function() return '[fg]every 8th attack consume the curse to deal [yellow]' .. 4*get_character_stat('bard', 3, 'dmg') .. '[fg] damage to affected enemies' end,
['jester'] = function() return '[fg]all knives seek enemies and pierce [yellow]2[fg] times' end,
['assassin'] = function() return '[fg]poison inflicted from crits deals [yellow]8x[fg] damage' end,
['host'] = function() return '[fg][yellow]+100%[fg] critter spawn rate and spawn [yellow]2[fg] critters instead' end,
['carver'] = function() return '[fg]carves a tree that heals [yellow]twice[fg] as fast, in a bigger area, and heals [yellow]2[fg] units instead' end,
['bane'] = function() return '[fg]the area also deals [yellow]' .. get_character_stat('bane', 3, 'dmg') .. '[fg] damage per second and slows enemies by [yellow]50%[fg]' end,
['bane'] = function() return "[yellow]100%[fg] increased area for bane's void rifts" end,
['psykino'] = function() return '[fg]enemies take [yellow]' .. 4*get_character_stat('psykino', 3, 'dmg') .. '[fg] damage and are pushed away when the area expires' end,
['barrager'] = function() return '[fg]every 3rd attack the barrage shoots [yellow]15[fg] projectiles and they push harder' end,
['highlander'] = function() return '[fg]quickly repeats the attack [yellow]3[fg] times' end,
@ -616,7 +616,7 @@ function init()
}
character_effect_descriptions_gray = {
['vagrant'] = function() return '[light_bg]+10% damage and +5% attack speed per active set' end,
['vagrant'] = function() return '[light_bg]+10% damage and +10% attack speed per active set' end,
['swordsman'] = function() return "[light_bg]the swordsman's damage is doubled" end,
['wizard'] = function() return '[light_bg]the projectile chains 3 times' end,
['archer'] = function() return '[light_bg]the arrow ricochets off walls 3 times' end,
@ -635,7 +635,7 @@ function init()
['chronomancer'] = function() return '[light_bg]enemies take damave over time 50% faster' end,
['spellblade'] = function() return '[light_bg]faster projectile speed and tighter turns' end,
['psykeeper'] = function() return '[light_bg]also redistributes damage taken as damage to all enemies at double value' end,
['engineer'] = function() return '[light_bg]every 3rd sentry dropped upgrade all sentries with +100% damage and attack speed' end,
['engineer'] = function() return '[light_bg]drops 3 additional turrets and grants all turrets +100% damage and attack speed' end,
['plague_doctor'] = function() return '[light_bg]nearby enemies take an additional ' .. get_character_stat('plague_doctor', 3, 'dmg') .. ' damage per second' end,
['barbarian'] = function() return '[light_bg]stunned enemies also take 100% increased damage' end,
['juggernaut'] = function() return '[light_bg]enemies pushed by the juggernaut take ' .. 4*get_character_stat('juggernaut', 3, 'dmg') .. ' damage if they hit a wall' end,
@ -645,11 +645,11 @@ function init()
['corruptor'] = function() return '[light_bg]spawn 3 small critters if the corruptor hits an enemy' end,
['beastmaster'] = function() return '[light_bg]spawn 2 small critters if the beastmaster gets hit' end,
['launcher'] = function() return '[light_bg]enemies launched take 300% more damage when they hit walls' end,
['bard'] = function() return '[light_bg]every 8th attack consume the curse to deal ' .. 4*get_character_stat('bard', 3, 'dmg') .. ' damage to affected enemies' end,
['jester'] = function() return '[light_bg]curses 6 enemies and all knives seek enemies and pierce 2 times' end,
['assassin'] = function() return '[light_bg]poison inflicted from crits deals 8x damage' end,
['host'] = function() return '[light_bg]+100% critter spawn rate and spawn 2 critters instead' end,
['carver'] = function() return '[light_bg]carves a tree that heals twice as fast, in a bigger area, and heals 2 units instead' end,
['bane'] = function() return '[light_bg]the area also deals ' .. get_character_stat('bane', 3, 'dmg') .. ' damage per second and slows enemies by 50%' end,
['bane'] = function() return "[light_bg]100% increased area for bane's void rifts" end,
['psykino'] = function() return '[light_bg]enemies take ' .. 4*get_character_stat('psykino', 3, 'dmg') .. ' damage and are pushed away when the area expires' end,
['barrager'] = function() return '[light_bg]every 3rd attack the barrage shoots 15 projectiles and they push harder' end,
['highlander'] = function() return '[light_bg]quickly repeats the attack 3 times' end,
@ -689,7 +689,7 @@ function init()
['corruptor'] = function(lvl) return get_character_stat_string('corruptor', lvl) end,
['beastmaster'] = function(lvl) return get_character_stat_string('beastmaster', lvl) end,
['launcher'] = function(lvl) return get_character_stat_string('launcher', lvl) end,
['bard'] = function(lvl) return get_character_stat_string('bard', lvl) end,
['jester'] = function(lvl) return get_character_stat_string('jester', lvl) end,
['assassin'] = function(lvl) return get_character_stat_string('assassin', lvl) end,
['host'] = function(lvl) return get_character_stat_string('host', lvl) end,
['carver'] = function(lvl) return get_character_stat_string('carver', lvl) end,
@ -743,7 +743,7 @@ function init()
tier_to_characters = {
[1] = {'vagrant', 'swordsman', 'wizard', 'archer', 'scout', 'cleric'},
[2] = {'saboteur', 'sage', 'squire', 'dual_gunner', 'hunter', 'chronomancer', 'barbarian', 'cryomancer', 'beastmaster', 'launcher', 'bard', 'carver'},
[2] = {'saboteur', 'sage', 'squire', 'dual_gunner', 'hunter', 'chronomancer', 'barbarian', 'cryomancer', 'beastmaster', 'launcher', 'jester', 'carver'},
[3] = {'outlaw', 'elementor', 'stormweaver', 'spellblade', 'psykeeper', 'engineer', 'juggernaut', 'pyromancer', 'corruptor', 'assassin', 'bane', 'barrager', 'infestor', 'flagellant'},
[4] = {'priest', 'highlander', 'psykino', 'lich', 'host', 'fairy', 'blade', 'plague_doctor', 'cannoneer'},
}
@ -780,7 +780,7 @@ function init()
['corruptor'] = 3,
['beastmaster'] = 2,
['launcher'] = 2,
['bard'] = 2,
['jester'] = 2,
['assassin'] = 3,
['host'] = 4,
['carver'] = 2,
@ -957,7 +957,7 @@ function init()
['assassination'] = '[fg]crits from rogues deal [yellow]8x[fg] damage but normal attacks deal [yellow]half[fg] damage',
['magnify'] = '[yellow]+25%[fg] area size',
['echo_barrage'] = '[yellow]20%[fg] chance to create [yellow]3[fg] secondary AoEs on AoE hit',
['unleash'] = '[yellow]+2%[fg] area size and damage per second',
['unleash'] = '[yellow]+1%[fg] area size and damage per second',
['reinforce'] = '[yellow]+10%[fg] damage, defense and attack speed to all allies with at least one enchanter',
['payback'] = '[yellow]+5%[fg] damage to all allies whenever an enchanter is hit',
['blessing'] = '[yellow]+20%[fg] healing effectiveness',
@ -1167,6 +1167,7 @@ function init()
}
gold = run.gold or 2
passives = run.passives or {}
locked_state = run.locked_state
steam.userStats.requestCurrentStats()
new_game_plus = state.new_game_plus or 0
if not state.new_game_plus then state.new_game_plus = new_game_plus end
@ -1183,8 +1184,9 @@ function init()
--[[
main:add(Arena'arena')
main:go_to('arena', 20, {
{character = 'vagrant', level = 3},
main:go_to('arena', 9, {
{character = 'dual_gunner', level = 3},
{character = 'engineer', level = 3},
{character = 'spellblade', level = 3},
{character = 'assassin', level = 3},
{character = 'scout', level = 3},
@ -1192,9 +1194,6 @@ function init()
{character = 'swordsman', level = 3},
{character = 'archer', level = 3},
}, passives)
main:add(Media'media')
main:go_to('media')
]]--
trigger:every(2, function()

View File

@ -229,19 +229,35 @@ function Unit:calculate_stats(first_run)
self.base_dmg = 10*math.pow(2, self.level-1)
self.base_mvspd = 75
elseif self:is(Seeker) then
if 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, 15, 0, 0, 18, 0, 0, 21, 0, 0, 24, 25}
local y = {0, 0, 3, 0, 0, 6, 0, 0, 9, 0, 0, 12, 0, 0, 18, 0, 0, 24, 0, 0, 32, 0, 0, 40, 48}
self.base_hp = 100 + (new_game_plus*5) + (90 + new_game_plus*10)*y[x]
self.base_dmg = (12 + new_game_plus*2) + (2 + new_game_plus)*y[x]
self.base_mvspd = 35 + 1.5*y[x]
else
local x = self.level
local y = {0, 1, 3, 3, 4, 6, 5, 6, 9, 7, 8, 12, 10, 11, 15, 12, 13, 18, 16, 17, 21, 17, 20, 24, 25}
self.base_hp = 22 + (new_game_plus*3) + (15 + new_game_plus*2.4)*y[x]
self.base_dmg = (4 + new_game_plus*1) + (2 + new_game_plus)*y[x]
self.base_hp = 28 + 18*y[x]
self.base_dmg = 5 + 3*y[x]
self.base_mvspd = 70 + 3*y[x]
end
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, 24, 0, 0, 32, 0, 0, 40, 48}
self.base_hp = 100 + (new_game_plus*5) + (90 + new_game_plus*10)*y[x]
self.base_dmg = (12 + new_game_plus*2) + (2 + new_game_plus)*y[x]
self.base_mvspd = 35 + 1.5*y[x]
else
local x = self.level
local y = {0, 1, 3, 3, 4, 6, 5, 6, 9, 7, 8, 12, 10, 11, 15, 12, 13, 18, 16, 17, 21, 17, 20, 24, 25}
self.base_hp = 22 + (new_game_plus*3) + (15 + new_game_plus*2.7)*y[x]
self.base_dmg = (4 + new_game_plus*1.25) + (2 + new_game_plus*1.2)*y[x]
self.base_mvspd = 70 + 3*y[x]
end
end
elseif self:is(Saboteur) then
self.base_hp = 100*math.pow(2, self.level-1)
self.base_dmg = 10*math.pow(2, self.level-1)

View File

@ -168,14 +168,18 @@ function Player:init(args)
self.last_heal_time = love.timer.getTime()
elseif self.character == 'engineer' then
self.turret_counter = 0
self.t:every(8, function()
SpawnEffect{group = main.current.effects, x = self.x, y = self.y, color = orange[0], action = function(x, y)
Turret{group = main.current.main, x = x, y = y, parent = self}
end}
self.turret_counter = self.turret_counter + 1
if self.turret_counter == 3 and self.level == 3 then
self.turret_counter = 0
end)
if self.level == 3 then
self.t:every(24, function()
SpawnEffect{group = main.current.effects, x = self.x - 16, y = self.y + 16, color = orange[0], action = function(x, y) Turret{group = main.current.main, x = x, y = y, parent = self} end}
SpawnEffect{group = main.current.effects, x = self.x + 16, y = self.y + 16, color = orange[0], action = function(x, y) Turret{group = main.current.main, x = x, y = y, parent = self} end}
self.t:after(0.5, function()
local turrets = main.current.main:get_objects_by_class(Turret)
buff1:play{pitch = random:float(0.95, 1.05), volume = 0.5}
for _, turret in ipairs(turrets) do
@ -183,8 +187,9 @@ function Player:init(args)
LightningLine{group = main.current.effects, src = self, dst = turret, color = orange[0]}
turret:upgrade()
end
end)
end)
end
end, nil, nil, 'spawn')
elseif self.character == 'plague_doctor' then
self.t:every(5, function()
@ -204,7 +209,7 @@ function Player:init(args)
elseif self.character == 'juggernaut' then
self.t:every(8, function()
self:attack(96, {juggernaut_push = true})
self:attack(128, {juggernaut_push = true})
end, nil, nil, 'attack')
elseif self.character == 'lich' then
@ -245,19 +250,32 @@ function Player:init(args)
end, nil, nil, 'shoot')
elseif self.character == 'launcher' then
self.t:every(8, function()
self:attack(128)
self.t:every({6, 10}, function()
buff1:play{pitch = random:float(0.9, 1.1), volume = 0.5}
local enemies = main.current.main:get_objects_by_classes(main.current.enemies)
for _, enemy in ipairs(enemies) do
if self:distance_to_object(enemy) < 128 then
local resonance_dmg = 0
if self.resonance then resonance_dmg = (self.level == 3 and 6*self.dmg*0.05*#enemies or 2*self.dmg*0.05*#enemies) end
enemy:curse('launcher', 4*(self.hex_duration_m or 1), (self.level == 3 and 6*self.dmg or 2*self.dmg) + resonance_dmg, self)
HitCircle{group = main.current.effects, x = self.x, y = self.y, rs = 6, color = yellow[0], duration = 0.1}
LightningLine{group = main.current.effects, src = self, dst = enemy, color = yellow[0]}
end
end
end, nil, nil, 'attack')
elseif self.character == 'bard' then
self.bard_counter = 0
self.attack_sensor = Circle(self.x, self.y, 64)
self.t:cooldown(2, function() local enemies = self:get_objects_in_shape(self.attack_sensor, main.current.enemies); return enemies and #enemies > 0 end, function()
local closest_enemy = self:get_closest_object_in_shape(self.attack_sensor, main.current.enemies)
if closest_enemy then
self:shoot(self:angle_to_object(closest_enemy))
elseif self.character == 'jester' then
self.t:every({6, 10}, function()
buff1:play{pitch = random:float(0.9, 1.1), volume = 0.5}
local enemies = table.first(table.shuffle(main.current.main:get_objects_by_classes(main.current.enemies)), 5)
for _, enemy in ipairs(enemies) do
if self:distance_to_object(enemy) < 128 then
enemy:curse('jester', 6*(self.hex_duration_m or 1), self.level == 3, self)
HitCircle{group = main.current.effects, x = self.x, y = self.y, rs = 6, color = red[0], duration = 0.1}
LightningLine{group = main.current.effects, src = self, dst = enemy, color = red[0]}
end
end, nil, nil, 'shoot')
end
end, nil, nil, 'attack')
elseif self.character == 'assassin' then
self.attack_sensor = Circle(self.x, self.y, 64)
@ -289,10 +307,17 @@ function Player:init(args)
end, nil, nil, 'spawn')
elseif self.character == 'bane' then
self.t:every(12, function()
self.dot_area = DotArea{group = main.current.effects, x = self.x, y = self.y, rs = self.area_size_m*128, color = self.color, dmg = self.area_dmg_m*(self.level == 3 and self.dmg or 0),
character = self.character, level = self.level, parent = self, duration = 8}
end, nil, nil, 'spawn')
self.t:every({6, 10}, function()
buff1:play{pitch = random:float(0.9, 1.1), volume = 0.5}
local enemies = table.first(table.shuffle(main.current.main:get_objects_by_classes(main.current.enemies)), 5)
for _, enemy in ipairs(enemies) do
if self:distance_to_object(enemy) < 128 then
enemy:curse('bane', 6*(self.hex_duration_m or 1), self.level == 3, self)
HitCircle{group = main.current.effects, x = self.x, y = self.y, rs = 6, color = purple[0], duration = 0.1}
LightningLine{group = main.current.effects, src = self, dst = enemy, color = purple[0]}
end
end
end, nil, nil, 'attack')
elseif self.character == 'psykino' then
self.t:every(4, function()
@ -381,7 +406,8 @@ function Player:init(args)
end, nil, nil, 'heal')
elseif self.character == 'infestor' then
self.t:every(8, function()
self.t:every({6, 10}, function()
buff1:play{pitch = random:float(0.9, 1.1), volume = 0.5}
local enemies = main.current.main:get_objects_by_classes(main.current.enemies)
for _, enemy in ipairs(enemies) do
if self:distance_to_object(enemy) < 128 then
@ -540,8 +566,8 @@ function Player:init(args)
self.unleash_area_dmg_m = 1
self.unleash_area_size_m = 1
self.t:every(1, function()
self.unleash_area_dmg_m = self.unleash_area_dmg_m + 0.02
self.unleash_area_size_m = self.unleash_area_size_m + 0.02
self.unleash_area_dmg_m = self.unleash_area_dmg_m + 0.01
self.unleash_area_size_m = self.unleash_area_size_m + 0.01
if self.dot_area then
self.dot_area:scale(self.unleash_area_size_m)
end
@ -632,7 +658,7 @@ function Player:update(dt)
if class_levels.swarmer >= 1 then number_of_active_sets = number_of_active_sets + 1 end
if class_levels.voider >= 1 then number_of_active_sets = number_of_active_sets + 1 end
self.vagrant_dmg_m = 1 + 0.1*number_of_active_sets
self.vagrant_aspd_m = 1 + 0.05*number_of_active_sets
self.vagrant_aspd_m = 1 + 0.1*number_of_active_sets
end
if self.character == 'swordsman' and self.level == 3 then
@ -1125,22 +1151,6 @@ function Player:shoot(r, mods)
Projectile(table.merge(t, mods or {}))
end
if self.character == 'bard' then
self.bard_counter = self.bard_counter + 1
if self.bard_counter == 8 and self.level == 3 then
self.bard_counter = 0
bard2:play{pitch = random:float(0.95, 1.05), volume = 0.5}
self.t:after(3, function()
bard1:play{pitch = random:float(0.95, 1.05), volume = 0.5}
local enemies = main.current.main:get_objects_by_classes(main.current.enemies)
for _, enemy in ipairs(enemies) do
enemy:hit(4*self.dmg)
hit2:play{pitch = random:float(0.95, 1.05), volume = 0.2}
end
end)
end
end
if self.character == 'vagrant' then
shoot1:play{pitch = random:float(0.95, 1.05), volume = 0.2}
elseif self.character == 'dual_gunner' then
@ -1150,7 +1160,7 @@ function Player:shoot(r, mods)
archer1:play{pitch = random:float(0.95, 1.05), volume = 0.35}
elseif self.character == 'wizard' or self.character == 'lich' then
wizard1:play{pitch = random:float(0.95, 1.05), volume = 0.15}
elseif self.character == 'scout' or self.character == 'outlaw' or self.character == 'blade' or self.character == 'spellblade' or self.character == 'bard' or self.character == 'assassin' or self.character == 'beastmaster' then
elseif self.character == 'scout' or self.character == 'outlaw' or self.character == 'blade' or self.character == 'spellblade' or self.character == 'jester' or self.character == 'assassin' or self.character == 'beastmaster' then
_G[random:table{'scout1', 'scout2'}]:play{pitch = random:float(0.95, 1.05), volume = 0.35}
if self.character == 'spellblade' then
wizard1:play{pitch = random:float(0.95, 1.05), volume = 0.15}
@ -1431,7 +1441,7 @@ function Projectile:on_collision_enter(other, contact)
self.ricochet = self.ricochet - 1
end
_G[random:table{'arrow_hit_wall1', 'arrow_hit_wall2'}]:play{pitch = random:float(0.9, 1.1), volume = 0.2}
elseif self.character == 'scout' or self.character == 'outlaw' or self.character == 'blade' or self.character == 'spellblade' or self.character == 'bard' or self.character == 'beastmaster' then
elseif self.character == 'scout' or self.character == 'outlaw' or self.character == 'blade' or self.character == 'spellblade' or self.character == 'jester' or self.character == 'beastmaster' then
self:die(x, y, r, 0)
knife_hit_wall1:play{pitch = random:float(0.9, 1.1), volume = 0.2}
local r = Unit.bounce(self, nx, ny)
@ -1496,7 +1506,7 @@ function Projectile:on_trigger_enter(other, contact)
end
if self.character == 'archer' or self.character == 'scout' or self.character == 'outlaw' or self.character == 'blade' or self.character == 'hunter' or self.character == 'spellblade' or self.character == 'engineer' or
self.character == 'bard' or self.character == 'assassin' or self.character == 'barrager' or self.character == 'beastmaster' then
self.character == 'jester' or self.character == 'assassin' or self.character == 'barrager' or self.character == 'beastmaster' then
hit2:play{pitch = random:float(0.95, 1.05), volume = 0.35}
if self.character == 'spellblade' then
magic_area1:play{pitch = random:float(0.95, 1.05), volume = 0.15}
@ -1531,10 +1541,6 @@ function Projectile:on_trigger_enter(other, contact)
end)
end
if self.character == 'bard' then
other:curse('bard')
end
if self.character == 'assassin' then
other:apply_dot((self.crit and 4*self.dmg or self.dmg/2)*(self.dot_dmg_m or 1)*(main.current.chronomancer_dot or 1), 3)
end
@ -1767,6 +1773,7 @@ function DotArea:init(args)
end
end, nil, nil, 'dot')
--[[
elseif self.character == 'bane' then
if self.level == 3 then
self.t:every(0.5, function()
@ -1776,7 +1783,7 @@ function DotArea:init(args)
buff1:play{pitch = random:float(0.8, 1.2), volume = 0.1}
end
for _, enemy in ipairs(enemies) do
enemy:curse('bane', 0.5*(self.hex_duration_m or 1))
enemy:curse('bane', 0.5*(self.hex_duration_m or 1), self.level == 3, self)
if self.level == 3 then
enemy:slow(0.5, 0.5)
enemy:hit((self.dot_dmg_m or 1)*self.dmg/2)
@ -1787,6 +1794,7 @@ function DotArea:init(args)
end
end, nil, nil, 'dot')
end
]]--
elseif self.void_rift then
self.t:every(0.2, function()
@ -2008,7 +2016,7 @@ function Turret:init(args)
self.attack_sensor = Circle(self.x, self.y, 256)
turret_deploy:play{pitch = 1.2, volume = 0.2}
self.t:every({3.5, 4.5}, function()
self.t:every({2.75, 3.5}, function()
self.t:every({0.1, 0.2}, function()
self.hfx:use('hit', 0.25, 200, 10)
HitCircle{group = main.current.effects, x = self.x + 0.8*self.shape.w*math.cos(self.r), y = self.y + 0.8*self.shape.w*math.sin(self.r), rs = 6}
@ -2053,8 +2061,8 @@ end
function Turret:upgrade()
self.upgrade_dmg_m = self.upgrade_dmg_m + 1
self.upgrade_aspd_m = self.upgrade_aspd_m + 1
self.upgrade_dmg_m = self.upgrade_dmg_m + 0.5
self.upgrade_aspd_m = self.upgrade_aspd_m + 0.5
for i = 1, 6 do HitParticle{group = main.current.effects, x = self.x, y = self.y, r = random:float(0, 2*math.pi), color = self.color} end
HitCircle{group = main.current.effects, x = self.x, y = self.y}:scale_down()
end
@ -2165,7 +2173,7 @@ function Saboteur:init(args)
_G[random:table{'saboteur1', 'saboteur2', 'saboteur3'}]:play{pitch = random:float(0.8, 1.2), volume = 0.2}
self.target = random:table(self.group:get_objects_by_classes(main.current.enemies))
self.actual_dmg = get_character_stat('saboteur', self.level, 'dmg')
self.actual_dmg = 2*get_character_stat('saboteur', self.level, 'dmg')
end

19
todo
View File

@ -1,14 +1,15 @@
Curser buff - change all cursers to trigger like the infestor does and for the effects to be on-death effects
Forcer buff - change all forcers to have a bigger area or to trigger their effects via projectiles (like the hunter)
Conjurer buff - buff saboteur, buff engineer
Vagrant buff - increase "active set" bonuses
AoE nerf - Unleash 1%
* Curser buff - change all cursers to trigger like the infestor does and for the effects to be on-death effects
* Forcer buff - buff juggernaut
* Conjurer buff - buff saboteur, buff engineer
* Vagrant buff - increase "active set" bonuses
* AoE nerf - Unleash 1%, rephrase it to more clear
* Save runs
* Move units
Lock shop button
Harder difficulty on NG+0
Increase boss HP on levels 24 and 25
Show next level in shop (also if elite or not)
* Lock shop button
* Harder difficulty on NG+0
* Increase boss HP on levels 24 and 25
* Show next level in shop (also if elite or not)
* Restart run button on shop
View synergy after beating game
Option to turn off screen shake
Option to show cooldowns on snake