Sorcerer update 4/4

master
a327ex 2021-06-05 03:11:23 -03:00
parent 521ce0cca5
commit 2276925f34
8 changed files with 260 additions and 155 deletions

View File

@ -327,6 +327,7 @@ function Arena:update(dt)
main_song_instance = _G[random:table{'song1', 'song2', 'song3', 'song4', 'song5'}]:play{volume = 0.5} main_song_instance = _G[random:table{'song1', 'song2', 'song3', 'song4', 'song5'}]:play{volume = 0.5}
end end
if self.shop_text then self.shop_text:update(dt) end
-- print(self.enemy_spawns_prevented) -- print(self.enemy_spawns_prevented)
if input.escape.pressed and not self.transitioning and not self.in_credits and not self.choosing_passives then if input.escape.pressed and not self.transitioning and not self.in_credits and not self.choosing_passives then
@ -338,7 +339,7 @@ function Arena:update(dt)
self.paused_t2 = Text2{group = self.ui, x = gw/2, y = gh/2 - 92, lines = {{text = '[bg10]turn left turn right', font = pixul_font, alignment = 'center'}}} self.paused_t2 = Text2{group = self.ui, x = gw/2, y = gh/2 - 92, lines = {{text = '[bg10]turn left turn right', font = pixul_font, alignment = 'center'}}}
self.ng_t = Text2{group = self.ui, x = gw/2 + 63, y = gh - 50, lines = {{text = '[bg10]current: ' .. current_new_game_plus, font = pixul_font, alignment = 'center'}}} self.ng_t = Text2{group = self.ui, x = gw/2 + 63, y = gh - 50, lines = {{text = '[bg10]current: ' .. current_new_game_plus, font = pixul_font, alignment = 'center'}}}
self.resume_button = Button{group = self.ui, x = gw/2, y = gh - 200, force_update = true, button_text = 'resume game (esc)', fg_color = 'bg10', bg_color = 'bg', action = function(b) self.resume_button = Button{group = self.ui, x = gw/2, y = gh - 225, force_update = true, button_text = 'resume game (esc)', fg_color = 'bg10', bg_color = 'bg', action = function(b)
trigger:tween(0.25, _G, {slow_amount = 1}, math.linear, function() trigger:tween(0.25, _G, {slow_amount = 1}, math.linear, function()
slow_amount = 1 slow_amount = 1
self.paused = false self.paused = false
@ -357,14 +358,16 @@ function Arena:update(dt)
if self.video_button_3 then self.video_button_3.dead = true; self.video_button_3 = nil end if self.video_button_3 then self.video_button_3.dead = true; self.video_button_3 = nil end
if self.quit_button then self.quit_button.dead = true; self.quit_button = nil end if self.quit_button then self.quit_button.dead = true; self.quit_button = nil end
if self.screen_shake_button then self.screen_shake_button.dead = true; self.screen_shake_button = nil end if self.screen_shake_button then self.screen_shake_button.dead = true; self.screen_shake_button = nil end
if self.screen_movement_button then self.screen_movement_button.dead = true; self.screen_movement_button = nil end
if self.cooldown_snake_button then self.cooldown_snake_button.dead = true; self.cooldown_snake_button = nil end if self.cooldown_snake_button then self.cooldown_snake_button.dead = true; self.cooldown_snake_button = nil end
if self.arrow_snake_button then self.arrow_snake_button.dead = true; self.arrow_snake_button = nil end
if self.ng_plus_plus_button then self.ng_plus_plus_button.dead = true; self.ng_plus_plus_button = nil end if self.ng_plus_plus_button then self.ng_plus_plus_button.dead = true; self.ng_plus_plus_button = nil end
if self.ng_plus_minus_button then self.ng_plus_minus_button.dead = true; self.ng_plus_minus_button = nil end if self.ng_plus_minus_button then self.ng_plus_minus_button.dead = true; self.ng_plus_minus_button = nil end
system.save_state() system.save_state()
end, 'pause') end, 'pause')
end} end}
self.restart_button = Button{group = self.ui, x = gw/2, y = gh - 175, force_update = true, button_text = 'restart run (r)', fg_color = 'bg10', bg_color = 'bg', action = function(b) self.restart_button = Button{group = self.ui, x = gw/2, y = gh - 200, force_update = true, button_text = 'restart run (r)', fg_color = 'bg10', bg_color = 'bg', action = function(b)
self.transitioning = true self.transitioning = true
ui_transition2:play{pitch = random:float(0.95, 1.05), volume = 0.5} 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_switch2:play{pitch = random:float(0.95, 1.05), volume = 0.5}
@ -389,7 +392,7 @@ function Arena:update(dt)
end, text = Text({{text = '[wavy, bg]restarting...', font = pixul_font, alignment = 'center'}}, global_text_tags)} end, text = Text({{text = '[wavy, bg]restarting...', font = pixul_font, alignment = 'center'}}, global_text_tags)}
end} end}
self.sfx_button = Button{group = self.ui, x = gw/2, y = gh - 150, force_update = true, button_text = 'toggle sfx (n)', fg_color = 'bg10', bg_color = 'bg', action = function(b) self.sfx_button = Button{group = self.ui, x = gw/2, y = gh - 175, force_update = true, button_text = 'toggle sfx (n)', fg_color = 'bg10', bg_color = 'bg', action = function(b)
ui_switch2:play{pitch = random:float(0.95, 1.05), volume = 0.5} ui_switch2:play{pitch = random:float(0.95, 1.05), volume = 0.5}
b.spring:pull(0.2, 200, 10) b.spring:pull(0.2, 200, 10)
b.selected = true b.selected = true
@ -401,7 +404,7 @@ function Arena:update(dt)
end end
end} end}
self.music_button = Button{group = self.ui, x = gw/2, y = gh - 125, force_update = true, button_text = 'toggle music (m)', fg_color = 'bg10', bg_color = 'bg', action = function(b) self.music_button = Button{group = self.ui, x = gw/2, y = gh - 150, force_update = true, button_text = 'toggle music (m)', fg_color = 'bg10', bg_color = 'bg', action = function(b)
ui_switch2:play{pitch = random:float(0.95, 1.05), volume = 0.5} ui_switch2:play{pitch = random:float(0.95, 1.05), volume = 0.5}
b.spring:pull(0.2, 200, 10) b.spring:pull(0.2, 200, 10)
b.selected = true b.selected = true
@ -413,7 +416,7 @@ function Arena:update(dt)
end end
end} end}
self.video_button_1 = Button{group = self.ui, x = gw/2 - 86, y = gh - 100, force_update = true, button_text = 'window size-', fg_color = 'bg10', bg_color = 'bg', action = function() 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} ui_switch1:play{pitch = random:float(0.95, 1.05), volume = 0.5}
sx, sy = sx - 1, sy - 1 sx, sy = sx - 1, sy - 1
love.window.setMode(480*sx, 270*sy) love.window.setMode(480*sx, 270*sy)
@ -421,7 +424,7 @@ function Arena:update(dt)
state.fullscreen = false state.fullscreen = false
end} end}
self.video_button_2 = Button{group = self.ui, x = gw/2, y = gh - 100, force_update = true, button_text = 'window size+', fg_color = 'bg10', bg_color = 'bg', action = function() 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()
ui_switch1:play{pitch = random:float(0.95, 1.05), volume = 0.5} ui_switch1:play{pitch = random:float(0.95, 1.05), volume = 0.5}
sx, sy = sx + 1, sy + 1 sx, sy = sx + 1, sy + 1
love.window.setMode(480*sx, 270*sy) love.window.setMode(480*sx, 270*sy)
@ -429,7 +432,7 @@ function Arena:update(dt)
state.fullscreen = false state.fullscreen = false
end} end}
self.video_button_3 = Button{group = self.ui, x = gw/2 + 79, y = gh - 100, force_update = true, button_text = 'fullscreen', fg_color = 'bg10', bg_color = 'bg', action = function() self.video_button_3 = Button{group = self.ui, x = gw/2 + 79, y = gh - 125, force_update = true, button_text = 'fullscreen', fg_color = 'bg10', bg_color = 'bg', action = function()
ui_switch1:play{pitch = random:float(0.95, 1.05), volume = 0.5} ui_switch1:play{pitch = random:float(0.95, 1.05), volume = 0.5}
local _, _, flags = love.window.getMode() local _, _, flags = love.window.getMode()
local window_width, window_height = love.window.getDesktopDimensions(flags.display) local window_width, window_height = love.window.getDesktopDimensions(flags.display)
@ -439,20 +442,38 @@ function Arena:update(dt)
state.fullscreen = true state.fullscreen = true
end} end}
self.screen_shake_button = Button{group = self.ui, x = gw/2 - 57, y = gh - 75, w = 110, force_update = true, button_text = '[bg10]screen shake: ' .. tostring(state.no_screen_shake and 'no' or 'yes'), self.screen_shake_button = Button{group = self.ui, x = gw/2 - 57, y = gh - 100, w = 110, force_update = true, button_text = '[bg10]screen shake: ' .. tostring(state.no_screen_shake and 'no' or 'yes'),
fg_color = 'bg10', bg_color = 'bg', action = function(b) fg_color = 'bg10', bg_color = 'bg', action = function(b)
ui_switch1:play{pitch = random:float(0.95, 1.05), volume = 0.5} ui_switch1:play{pitch = random:float(0.95, 1.05), volume = 0.5}
state.no_screen_shake = not state.no_screen_shake state.no_screen_shake = not state.no_screen_shake
b:set_text('screen shake: ' .. tostring(state.no_screen_shake and 'no' or 'yes')) b:set_text('screen shake: ' .. tostring(state.no_screen_shake and 'no' or 'yes'))
end} end}
self.cooldown_snake_button = Button{group = self.ui, x = gw/2 + 75, y = gh - 75, w = 145, force_update = true, button_text = '[bg10]cooldowns on snake: ' .. tostring(state.cooldown_snake and 'yes' or 'no'), self.cooldown_snake_button = Button{group = self.ui, x = gw/2 + 75, y = gh - 100, w = 145, force_update = true, button_text = '[bg10]cooldowns on snake: ' .. tostring(state.cooldown_snake and 'yes' or 'no'),
fg_color = 'bg10', bg_color = 'bg', action = function(b) fg_color = 'bg10', bg_color = 'bg', action = function(b)
ui_switch1:play{pitch = random:float(0.95, 1.05), volume = 0.5} ui_switch1:play{pitch = random:float(0.95, 1.05), volume = 0.5}
state.cooldown_snake = not state.cooldown_snake state.cooldown_snake = not state.cooldown_snake
b:set_text('cooldowns on snake: ' .. tostring(state.cooldown_snake and 'yes' or 'no')) b:set_text('cooldowns on snake: ' .. tostring(state.cooldown_snake and 'yes' or 'no'))
end} end}
self.arrow_snake_button = Button{group = self.ui, x = gw/2 + 65, y = gh - 75, w = 125, force_update = true, button_text = '[bg10]arrow on snake: ' .. tostring(state.arrow_snake 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.arrow_snake = not state.arrow_snake
b:set_text('arrow on snake: ' .. tostring(state.arrow_snake and 'yes' or 'no'))
end}
self.screen_movement_button = Button{group = self.ui, x = gw/2 - 69, y = gh - 75, w = 135, force_update = true, button_text = '[bg10]screen movement: ' .. tostring(state.no_screen_movement and 'no' or 'yes'),
fg_color = 'bg10', bg_color = 'bg', action = function(b)
ui_switch1:play{pitch = random:float(0.95, 1.05), volume = 0.5}
state.no_screen_movement = not state.no_screen_movement
if state.no_screen_movement then
camera.x, camera.y = gw/2, gh/2
camera.r = 0
end
b:set_text('screen movement: ' .. tostring(state.no_screen_movement and 'no' or 'yes'))
end}
self.ng_plus_minus_button = Button{group = self.ui, x = gw/2 - 58, y = gh - 50, force_update = true, button_text = 'NG+ down', fg_color = 'bg10', bg_color = 'bg', action = function(b) self.ng_plus_minus_button = Button{group = self.ui, x = gw/2 - 58, y = gh - 50, force_update = true, button_text = 'NG+ down', fg_color = 'bg10', bg_color = 'bg', action = function(b)
ui_switch1:play{pitch = random:float(0.95, 1.05), volume = 0.5} ui_switch1:play{pitch = random:float(0.95, 1.05), volume = 0.5}
b.spring:pull(0.2, 200, 10) b.spring:pull(0.2, 200, 10)
@ -495,7 +516,9 @@ function Arena:update(dt)
if self.video_button_2 then self.video_button_2.dead = true; self.video_button_2 = nil end if self.video_button_2 then self.video_button_2.dead = true; self.video_button_2 = nil end
if self.video_button_3 then self.video_button_3.dead = true; self.video_button_3 = nil end if self.video_button_3 then self.video_button_3.dead = true; self.video_button_3 = nil end
if self.screen_shake_button then self.screen_shake_button.dead = true; self.screen_shake_button = nil end if self.screen_shake_button then self.screen_shake_button.dead = true; self.screen_shake_button = nil end
if self.screen_movement_button then self.screen_movement_button.dead = true; self.screen_movement_button = nil end
if self.cooldown_snake_button then self.cooldown_snake_button.dead = true; self.cooldown_snake_button = nil end if self.cooldown_snake_button then self.cooldown_snake_button.dead = true; self.cooldown_snake_button = nil end
if self.arrow_snake_button then self.arrow_snake_button.dead = true; self.arrow_snake_button = nil end
if self.quit_button then self.quit_button.dead = true; self.quit_button = nil end if self.quit_button then self.quit_button.dead = true; self.quit_button = nil end
if self.ng_plus_plus_button then self.ng_plus_plus_button.dead = true; self.ng_plus_plus_button = nil end if self.ng_plus_plus_button then self.ng_plus_plus_button.dead = true; self.ng_plus_plus_button = nil end
if self.ng_plus_minus_button then self.ng_plus_minus_button.dead = true; self.ng_plus_minus_button = nil end if self.ng_plus_minus_button then self.ng_plus_minus_button.dead = true; self.ng_plus_minus_button = nil end
@ -787,6 +810,26 @@ function Arena:quit()
self.arena_clear_text.dead = true self.arena_clear_text.dead = true
trigger:tween(1, _G, {slow_amount = 0}, math.linear, function() slow_amount = 0 end, 'slow_amount') 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) 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:set_passives()
RerollButton{group = main.current.ui, x = 40, y = 40, parent = self, force_update = true}
self.shop_text = Text({{text = '[wavy_mid, fg]gold: [yellow]' .. gold, font = pixul_font, alignment = 'center'}}, global_text_tags)
else
self:transition()
end
end, 'transition')
end
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
self.cards = {}
end
local card_w, card_h = 100, 100 local card_w, card_h = 100, 100
local w = 3*card_w + 2*20 local w = 3*card_w + 2*20
self.choosing_passives = true self.choosing_passives = true
@ -813,11 +856,6 @@ function Arena:quit()
if not passive_1 and not passive_2 and not passive_3 then if not passive_1 and not passive_2 and not passive_3 then
self:transition() self:transition()
end end
else
self:transition()
end
end, 'transition')
end
end end
@ -881,6 +919,8 @@ function Arena:draw()
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 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() self.ui:draw()
if self.shop_text then self.shop_text:draw(40, 20) end
if self.in_credits then graphics.rectangle(gw/2, gh/2, 2*gw, 2*gh, nil, nil, modal_transparent_2) end if self.in_credits then graphics.rectangle(gw/2, gh/2, 2*gw, 2*gh, nil, nil, modal_transparent_2) end
self.credits:draw() self.credits:draw()
end end

View File

@ -773,9 +773,14 @@ RerollButton = Object:extend()
RerollButton:implement(GameObject) RerollButton:implement(GameObject)
function RerollButton:init(args) function RerollButton:init(args)
self:init_game_object(args) self:init_game_object(args)
self.shape = Rectangle(self.x, self.y, 54, 16)
self.interact_with_mouse = true self.interact_with_mouse = true
if self.parent:is(BuyScreen) then
self.shape = Rectangle(self.x, self.y, 54, 16)
self.text = Text({{text = '[bg10]reroll: [yellow]2', font = pixul_font, alignment = 'center'}}, global_text_tags) self.text = Text({{text = '[bg10]reroll: [yellow]2', font = pixul_font, alignment = 'center'}}, global_text_tags)
elseif self.parent:is(Arena) then
self.shape = Rectangle(self.x, self.y, 60, 16)
self.text = Text({{text = '[bg10]reroll: [yellow]15', font = pixul_font, alignment = 'center'}}, global_text_tags)
end
end end
@ -783,6 +788,7 @@ function RerollButton:update(dt)
self:update_game_object(dt) self:update_game_object(dt)
if self.selected and input.m1.pressed then if self.selected and input.m1.pressed then
if self.parent:is(BuyScreen) then
if gold < 2 then if gold < 2 then
self.spring:pull(0.2, 200, 10) self.spring:pull(0.2, 200, 10)
self.selected = true self.selected = true
@ -804,6 +810,28 @@ function RerollButton:update(dt)
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.parent.shop_text:set_text{{text = '[wavy_mid, fg]shop [fg]- [fg, nudge_down]gold: [yellow, nudge_down]' .. gold, font = pixul_font, alignment = 'center'}}
system.save_run(self.parent.level == 1 and 0 or self.parent.level, gold, self.parent.units, passives, run_passive_pool_by_tiers, locked_state) system.save_run(self.parent.level == 1 and 0 or self.parent.level, gold, self.parent.units, passives, run_passive_pool_by_tiers, locked_state)
end end
elseif self.parent:is(Arena) then
if gold < 15 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 then
self.info_text = InfoText{group = main.current.ui, force_update = true}
self.info_text:activate({
{text = '[fg]not enough gold', font = pixul_font, alignment = 'center'},
}, nil, nil, nil, nil, 16, 4, nil, 2)
self.info_text.x, self.info_text.y = gw/2, gh/2 + 10
end
self.t:after(2, function() self.info_text:deactivate(); self.info_text.dead = true; self.info_text = nil end, 'info_text')
else
ui_switch2:play{pitch = random:float(0.95, 1.05), volume = 0.5}
self.parent:set_passives(true)
self.selected = true
self.spring:pull(0.2, 200, 10)
gold = gold - 15
self.parent.shop_text:set_text{{text = '[fg, nudge_down]gold: [yellow, nudge_down]' .. gold, font = pixul_font, alignment = 'center'}}
end
end
end end
end end
@ -820,13 +848,21 @@ function RerollButton:on_mouse_enter()
ui_hover1:play{pitch = random:float(1.3, 1.5), volume = 0.5} 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} pop2:play{pitch = random:float(0.95, 1.05), volume = 0.5}
self.selected = true self.selected = true
if self.parent:is(BuyScreen) then
self.text:set_text{{text = '[fgm5]reroll: 2', font = pixul_font, alignment = 'center'}} self.text:set_text{{text = '[fgm5]reroll: 2', font = pixul_font, alignment = 'center'}}
elseif self.parent:is(Arena) then
self.text:set_text{{text = '[fgm5]reroll: 15', font = pixul_font, alignment = 'center'}}
end
self.spring:pull(0.2, 200, 10) self.spring:pull(0.2, 200, 10)
end end
function RerollButton:on_mouse_exit() function RerollButton:on_mouse_exit()
if self.parent:is(BuyScreen) then
self.text:set_text{{text = '[bg10]reroll: [yellow]2', font = pixul_font, alignment = 'center'}} self.text:set_text{{text = '[bg10]reroll: [yellow]2', font = pixul_font, alignment = 'center'}}
elseif self.parent:is(Arena) then
self.text:set_text{{text = '[fgm5]reroll: [yellow]15', font = pixul_font, alignment = 'center'}}
end
self.selected = false self.selected = false
end end
@ -1106,7 +1142,6 @@ function PassiveCard:update(dt)
if self.selected and input.m1.pressed and self.arena.choosing_passives then if self.selected and input.m1.pressed and self.arena.choosing_passives then
self.arena.choosing_passives = false self.arena.choosing_passives = false
table.insert(passives, self.passive)
table.insert(self.arena.passives, self.passive) table.insert(self.arena.passives, self.passive)
self.arena:restore_passives_to_pool(self.card_i) self.arena:restore_passives_to_pool(self.card_i)
trigger:tween(0.25, _G, {slow_amount = 1}, math.linear, function() trigger:tween(0.25, _G, {slow_amount = 1}, math.linear, function()
@ -1324,7 +1359,6 @@ function CharacterIcon:init(args)
self:init_game_object(args) self:init_game_object(args)
self.shape = Rectangle(self.x, self.y, 40, 20) self.shape = Rectangle(self.x, self.y, 40, 20)
self.interact_with_mouse = true self.interact_with_mouse = true
print(character_color_strings[self.character])
self.character_text = Text({{text = '[' .. character_color_strings[self.character] .. ']' .. string.lower(character_names[self.character]), font = pixul_font, alignment = 'center'}}, global_text_tags) self.character_text = Text({{text = '[' .. character_color_strings[self.character] .. ']' .. string.lower(character_names[self.character]), font = pixul_font, alignment = 'center'}}, global_text_tags)
end end
@ -1480,7 +1514,11 @@ function ClassIcon:draw()
local next_n local next_n
if self.parent:is(ShopCard) then if self.parent:is(ShopCard) then
next_n = n+1 next_n = n+1
if k then
if next_n > k then next_n = nil end
else
if next_n > j then next_n = nil end if next_n > j then next_n = nil end
end
if table.any(self.units, function(v) return v.character == self.character end) then next_n = nil end if table.any(self.units, function(v) return v.character == self.character end) then next_n = nil end
end end

View File

@ -23,7 +23,7 @@ function Seeker:init(args)
HitCircle{group = main.current.effects, x = self.x, y = self.y, rs = 6, color = green[0], duration = 0.1} HitCircle{group = main.current.effects, x = self.x, y = self.y, rs = 6, color = green[0], duration = 0.1}
for _, enemy in ipairs(enemies) do for _, enemy in ipairs(enemies) do
LightningLine{group = main.current.effects, src = self, dst = enemy, color = green[0]} LightningLine{group = main.current.effects, src = self, dst = enemy, color = green[0]}
enemy:speed_boost(3 + self.level*0.05 + current_new_game_plus*0.2) enemy:speed_boost(3 + self.level*0.025 + current_new_game_plus*0.1)
end end
end end
end) end)
@ -85,7 +85,7 @@ function Seeker:init(args)
enemy:hit(10000) enemy:hit(10000)
critter1:play{pitch = random:float(0.95, 1.05), volume = 0.5} critter1:play{pitch = random:float(0.95, 1.05), volume = 0.5}
critter3:play{pitch = random:float(0.95, 1.05), volume = 0.6} critter3:play{pitch = random:float(0.95, 1.05), volume = 0.6}
for i = 1, random:int(4, 6) do EnemyCritter{group = main.current.main, x = enemy.x, y = enemy.y, color = purple[0], r = random:float(0, 2*math.pi), v = 5 + 0.1*enemy.level, dmg = enemy.dmg} end for i = 1, random:int(4, 6) do EnemyCritter{group = main.current.main, x = enemy.x, y = enemy.y, color = purple[0], r = random:float(0, 2*math.pi), v = 8 + 0.1*enemy.level, dmg = 2*enemy.dmg} end
end end
end) end)
@ -100,7 +100,7 @@ function Seeker:init(args)
LightningLine{group = main.current.effects, src = self, dst = enemy, color = blue[0]} LightningLine{group = main.current.effects, src = self, dst = enemy, color = blue[0]}
enemy:hit(10000) enemy:hit(10000)
shoot1:play{pitch = random:float(0.95, 1.05), volume = 0.4} shoot1:play{pitch = random:float(0.95, 1.05), volume = 0.4}
local n = 8 + current_new_game_plus*2 local n = math.floor(8 + current_new_game_plus*1.5)
for i = 1, n do EnemyProjectile{group = main.current.main, x = enemy.x, y = enemy.y, color = blue[0], r = (i-1)*math.pi/(n/2), v = 120 + 5*enemy.level, dmg = (1 + 0.1*current_new_game_plus)*enemy.dmg} end for i = 1, n do EnemyProjectile{group = main.current.main, x = enemy.x, y = enemy.y, color = blue[0], r = (i-1)*math.pi/(n/2), v = 120 + 5*enemy.level, dmg = (1 + 0.1*current_new_game_plus)*enemy.dmg} end
end end
end) end)
@ -119,7 +119,7 @@ function Seeker:init(args)
enemy:hit(10000) enemy:hit(10000)
shoot1:play{pitch = random:float(0.95, 1.05), volume = 0.4} shoot1:play{pitch = random:float(0.95, 1.05), volume = 0.4}
local n = 8 + current_new_game_plus*2 local n = 8 + current_new_game_plus*2
for i = 1, n do EnemyProjectile{group = main.current.main, x = enemy.x, y = enemy.y, color = blue[0], r = (i-1)*math.pi/(n/2), v = 150 + 5*enemy.level, dmg = (1 + 0.2*current_new_game_plus)*enemy.dmg} end for i = 1, n do EnemyProjectile{group = main.current.main, x = enemy.x, y = enemy.y, color = blue[0], r = (i-1)*math.pi/(n/2), v = 125 + 5*enemy.level, dmg = (1 + 0.2*current_new_game_plus)*enemy.dmg} end
end end
elseif attack == 'swarm' then elseif attack == 'swarm' then
local enemies = self:get_objects_in_shape(Circle(self.x, self.y, 128), {Seeker}) local enemies = self:get_objects_in_shape(Circle(self.x, self.y, 128), {Seeker})
@ -130,7 +130,7 @@ function Seeker:init(args)
enemy:hit(10000) enemy:hit(10000)
critter1:play{pitch = random:float(0.95, 1.05), volume = 0.5} critter1:play{pitch = random:float(0.95, 1.05), volume = 0.5}
critter3:play{pitch = random:float(0.95, 1.05), volume = 0.6} critter3:play{pitch = random:float(0.95, 1.05), volume = 0.6}
for i = 1, random:int(4, 6) do EnemyCritter{group = main.current.main, x = enemy.x, y = enemy.y, color = purple[0], r = random:float(0, 2*math.pi), v = 5 + 0.1*enemy.level, dmg = enemy.dmg} end for i = 1, random:int(4, 6) do EnemyCritter{group = main.current.main, x = enemy.x, y = enemy.y, color = purple[0], r = random:float(0, 2*math.pi), v = 8 + 0.1*enemy.level, dmg = 2*enemy.dmg} end
end end
elseif attack == 'force' then elseif attack == 'force' then
local enemies = self:get_objects_in_shape(Circle(self.x, self.y, 64), {Seeker}) local enemies = self:get_objects_in_shape(Circle(self.x, self.y, 64), {Seeker})
@ -150,7 +150,7 @@ function Seeker:init(args)
HitCircle{group = main.current.effects, x = self.x, y = self.y, rs = 6, color = green[0], duration = 0.1} HitCircle{group = main.current.effects, x = self.x, y = self.y, rs = 6, color = green[0], duration = 0.1}
for _, enemy in ipairs(enemies) do for _, enemy in ipairs(enemies) do
LightningLine{group = main.current.effects, src = self, dst = enemy, color = green[0]} LightningLine{group = main.current.effects, src = self, dst = enemy, color = green[0]}
enemy:speed_boost(3 + self.level*0.05 + current_new_game_plus*0.2) enemy:speed_boost(3 + self.level*0.025 + current_new_game_plus*0.1)
end end
end end
end end
@ -186,7 +186,7 @@ function Seeker:init(args)
self.color = orange[0]:clone() self.color = orange[0]:clone()
self.last_headbutt_time = 0 self.last_headbutt_time = 0
local n = math.remap(current_new_game_plus, 0, 5, 1, 0.5) local n = math.remap(current_new_game_plus, 0, 5, 1, 0.5)
self.t:every(function() return math.distance(self.x, self.y, main.current.player.x, main.current.player.y) < 64 and love.timer.getTime() - self.last_headbutt_time > 10*n end, function() self.t:every(function() return math.distance(self.x, self.y, main.current.player.x, main.current.player.y) < 76 and love.timer.getTime() - self.last_headbutt_time > 10*n end, function()
if self.silenced then return end if self.silenced then return end
if self.headbutt_charging or self.headbutting then return end if self.headbutt_charging or self.headbutting then return end
self.headbutt_charging = true self.headbutt_charging = true
@ -198,16 +198,24 @@ function Seeker:init(args)
self.last_headbutt_time = love.timer.getTime() self.last_headbutt_time = love.timer.getTime()
self:set_damping(0) self:set_damping(0)
self:apply_steering_impulse(300, self:angle_to_object(main.current.player), 0.75) self:apply_steering_impulse(300, self:angle_to_object(main.current.player), 0.75)
self.t:after(0.5, function() self.t:after(0.75, function()
self.headbutting = false self.headbutting = false
end) end)
end) end)
end) end)
elseif self.tank then elseif self.tank then
self.color = yellow[0]:clone() self.color = yellow[0]:clone()
self.buff_hp_m = 1.25 + (0.025*self.level) + (0.2*current_new_game_plus) self.buff_hp_m = 1.25 + (0.1*self.level) + (0.4*current_new_game_plus)
self:calculate_stats() self:calculate_stats()
self.hp = self.max_hp self.hp = self.max_hp
local n = math.remap(current_new_game_plus, 0, 5, 1, 0.75)
self.t:every({3*n, 5*n}, function()
local enemy = self:get_closest_object_in_shape(Circle(self.x, self.y, 128), main.current.enemies)
if enemy then
wizard1:play{pitch = random:float(0.95, 1.05), volume = 0.5}
enemy:push(random:float(40, 80), enemy:angle_to_object(main.current.player), true)
end
end)
elseif self.shooter then elseif self.shooter then
self.color = fg[0]:clone() self.color = fg[0]:clone()
local n = math.remap(current_new_game_plus, 0, 5, 1, 0.5) local n = math.remap(current_new_game_plus, 0, 5, 1, 0.5)
@ -221,7 +229,7 @@ function Seeker:init(args)
self.hfx:use('hit', 0.25, 200, 10, 0.1) self.hfx:use('hit', 0.25, 200, 10, 0.1)
local r = self.r local r = self.r
HitCircle{group = main.current.effects, x = self.x + 0.8*self.shape.w*math.cos(r), y = self.y + 0.8*self.shape.w*math.sin(r), rs = 6} HitCircle{group = main.current.effects, x = self.x + 0.8*self.shape.w*math.cos(r), y = self.y + 0.8*self.shape.w*math.sin(r), rs = 6}
EnemyProjectile{group = main.current.main, x = self.x + 1.6*self.shape.w*math.cos(r), y = self.y + 1.6*self.shape.w*math.sin(r), color = fg[0], r = r, v = 150 + 5*self.level + 8*current_new_game_plus, EnemyProjectile{group = main.current.main, x = self.x + 1.6*self.shape.w*math.cos(r), y = self.y + 1.6*self.shape.w*math.sin(r), color = fg[0], r = r, v = 140 + 5*self.level + 4*current_new_game_plus,
dmg = (current_new_game_plus*0.1 + 1)*self.dmg} dmg = (current_new_game_plus*0.1 + 1)*self.dmg}
end) end)
end end
@ -257,8 +265,8 @@ function Seeker:update(dt)
if self.headbutt_charging or self.headbutting then self.buff_def_m = 3 end if self.headbutt_charging or self.headbutting then self.buff_def_m = 3 end
if self.speed_boosting then if self.speed_boosting then
local n = math.remap(love.timer.getTime() - self.speed_boosting, 0, (3 + 0.05*self.level + current_new_game_plus*0.2), 1, 0.5) local n = math.remap(love.timer.getTime() - self.speed_boosting, 0, (3 + 0.025*self.level + current_new_game_plus*0.1), 1, 0.5)
self.speed_boosting_mvspd_m = (3 + 0.05*self.level + 0.2*current_new_game_plus)*n self.speed_boosting_mvspd_m = (3 + 0.025*self.level + 0.1*current_new_game_plus)*n
if not self.speed_booster and not self.exploder and not self.headbutter and not self.tank and not self.shooter and not self.spawner then if not self.speed_booster and not self.exploder and not self.headbutter and not self.tank and not self.shooter and not self.spawner then
self.color.r = math.remap(n, 1, 0.5, green[0].r, red[0].r) self.color.r = math.remap(n, 1, 0.5, green[0].r, red[0].r)
self.color.g = math.remap(n, 1, 0.5, green[0].g, red[0].g) self.color.g = math.remap(n, 1, 0.5, green[0].g, red[0].g)
@ -269,7 +277,7 @@ function Seeker:update(dt)
if self.slowed then self.slow_mvspd_m = self.slowed if self.slowed then self.slow_mvspd_m = self.slowed
else self.slow_mvspd_m = 1 end else self.slow_mvspd_m = 1 end
self.buff_mvspd_m = (self.speed_boosting_mvspd_m or 1)*(self.slow_mvspd_m or 1)*(self.temporal_chains_mvspd_m or 1) self.buff_mvspd_m = (self.speed_boosting_mvspd_m or 1)*(self.slow_mvspd_m or 1)*(self.temporal_chains_mvspd_m or 1)*(self.tank and 0.35 or 1)
self:calculate_stats() self:calculate_stats()
@ -368,6 +376,10 @@ function Seeker:on_collision_enter(other, contact)
end end
end end
if self.headbutter and self.headbutting then
self.headbutting = false
end
elseif table.any(main.current.enemies, function(v) return other:is(v) end) then elseif table.any(main.current.enemies, function(v) return other:is(v) end) then
if self.being_pushed and math.length(self:get_velocity()) > 60 then if self.being_pushed and math.length(self:get_velocity()) > 60 then
other:hit(math.floor(self.push_force/4)) other:hit(math.floor(self.push_force/4))
@ -382,9 +394,11 @@ function Seeker:on_collision_enter(other, contact)
HitCircle{group = main.current.effects, x = x, y = y, rs = 6, color = fg[0], duration = 0.1} HitCircle{group = main.current.effects, x = x, y = y, rs = 6, color = fg[0], duration = 0.1}
for i = 1, 2 do HitParticle{group = main.current.effects, x = x, y = y, color = self.color} end for i = 1, 2 do HitParticle{group = main.current.effects, x = x, y = y, color = self.color} end
hit2:play{pitch = random:float(0.95, 1.05), volume = 0.35} hit2:play{pitch = random:float(0.95, 1.05), volume = 0.35}
if other:is(Seeker) then self.headbutting = false end
end end
elseif other:is(Turret) then elseif other:is(Turret) then
self.headbutting = false
_G[random:table{'player_hit1', 'player_hit2'}]:play{pitch = random:float(0.95, 1.05), volume = 0.35} _G[random:table{'player_hit1', 'player_hit2'}]:play{pitch = random:float(0.95, 1.05), volume = 0.35}
self:hit(0) self:hit(0)
self:push(random:float(2.5, 7), other:angle_to_object(self)) self:push(random:float(2.5, 7), other:angle_to_object(self))
@ -443,9 +457,9 @@ function Seeker:hit(damage, projectile)
if self.silenced then return end if self.silenced then return end
shoot1:play{pitch = random:float(0.95, 1.05), volume = 0.4} shoot1:play{pitch = random:float(0.95, 1.05), volume = 0.4}
trigger:after(0.01, function() trigger:after(0.01, function()
local n = 8 + current_new_game_plus*2 local n = math.floor(8 + current_new_game_plus*1.5)
for i = 1, n do for i = 1, n do
EnemyProjectile{group = main.current.main, x = self.x, y = self.y, color = blue[0], r = (i-1)*math.pi/(n/2), v = 120 + 5*self.level, dmg = 2*self.dmg} EnemyProjectile{group = main.current.main, x = self.x, y = self.y, color = blue[0], r = (i-1)*math.pi/(n/2), v = 120 + 5*self.level, dmg = 1.5*self.dmg}
end end
end) end)
end end

View File

@ -29,7 +29,7 @@ function init()
thunder1 = Sound('399656__bajko__sfx-thunder-blast.ogg', s) thunder1 = Sound('399656__bajko__sfx-thunder-blast.ogg', s)
flagellant1 = Sound('Whipping Horse 3.ogg', s) flagellant1 = Sound('Whipping Horse 3.ogg', s)
bard2 = Sound('376532__womb-affliction__flute-trill.ogg', s) bard2 = Sound('376532__womb-affliction__flute-trill.ogg', s)
bard1 = Sound('Magical Impact 12.ogg', s) arcane2 = Sound('Magical Impact 12.ogg', s)
frost1 = Sound('Frost Bolt 20.ogg', s) frost1 = Sound('Frost Bolt 20.ogg', s)
arcane1 = Sound('Magical Impact 26.ogg', s) arcane1 = Sound('Magical Impact 26.ogg', s)
pyro1 = Sound('Fire bolt 5.ogg', s) pyro1 = Sound('Fire bolt 5.ogg', s)
@ -383,7 +383,7 @@ function init()
['elementor'] = {'mage', 'nuker'}, ['elementor'] = {'mage', 'nuker'},
['saboteur'] = {'rogue', 'conjurer', 'nuker'}, ['saboteur'] = {'rogue', 'conjurer', 'nuker'},
['stormweaver'] = {'enchanter'}, ['stormweaver'] = {'enchanter'},
['sage'] = {'forcer', 'nuker'}, ['sage'] = {'nuker', 'forcer'},
['squire'] = {'warrior', 'enchanter'}, ['squire'] = {'warrior', 'enchanter'},
['cannoneer'] = {'ranger', 'nuker'}, ['cannoneer'] = {'ranger', 'nuker'},
['dual_gunner'] = {'ranger', 'rogue'}, ['dual_gunner'] = {'ranger', 'rogue'},
@ -435,7 +435,7 @@ function init()
['elementor'] = '[blue]Mage, [red]Nuker', ['elementor'] = '[blue]Mage, [red]Nuker',
['saboteur'] = '[red]Rogue, [orange]Conjurer, [red]Nuker', ['saboteur'] = '[red]Rogue, [orange]Conjurer, [red]Nuker',
['stormweaver'] = '[blue]Enchanter', ['stormweaver'] = '[blue]Enchanter',
['sage'] = '[red]Nuker', ['sage'] = '[red]Nuker, [yellow]Forcer',
['squire'] = '[yellow]Warrior, [blue]Enchanter', ['squire'] = '[yellow]Warrior, [blue]Enchanter',
['cannoneer'] = '[green]Ranger, [red]Nuker', ['cannoneer'] = '[green]Ranger, [red]Nuker',
['dual_gunner'] = '[green]Ranger, [red]Rogue', ['dual_gunner'] = '[green]Ranger, [red]Rogue',
@ -494,7 +494,7 @@ function init()
['swordsman'] = function(lvl) return '[fg]deals [yellow]' .. get_character_stat('swordsman', lvl, 'dmg') .. '[fg] damage in an area, deals extra [yellow]' .. ['swordsman'] = function(lvl) return '[fg]deals [yellow]' .. get_character_stat('swordsman', lvl, 'dmg') .. '[fg] damage in an area, deals extra [yellow]' ..
math.round(get_character_stat('swordsman', lvl, 'dmg')*0.15, 2) .. '[fg] damage per unit hit' end, math.round(get_character_stat('swordsman', lvl, 'dmg')*0.15, 2) .. '[fg] damage per unit hit' end,
['wizard'] = function(lvl) return '[fg]shoots a projectile that deals [yellow]' .. get_character_stat('wizard', lvl, 'dmg') .. ' AoE[fg] damage' end, ['wizard'] = function(lvl) return '[fg]shoots a projectile that deals [yellow]' .. get_character_stat('wizard', lvl, 'dmg') .. ' AoE[fg] damage' end,
['magician'] = function(lvl) return '[fg]creates a small area that deals [yellow]' .. get_character_stat('magician', lvl, 'dmg') .. 'AoE[fg] damage' end, ['magician'] = function(lvl) return '[fg]creates a small area that deals [yellow]' .. get_character_stat('magician', lvl, 'dmg') .. ' AoE[fg] damage' end,
['archer'] = function(lvl) return '[fg]shoots an arrow that deals [yellow]' .. get_character_stat('archer', lvl, 'dmg') .. '[fg] damage and pierces' end, ['archer'] = function(lvl) return '[fg]shoots an arrow that deals [yellow]' .. get_character_stat('archer', lvl, 'dmg') .. '[fg] damage and pierces' end,
['scout'] = function(lvl) return '[fg]throws a knife that deals [yellow]' .. get_character_stat('scout', lvl, 'dmg') .. '[fg] damage and chains [yellow]3[fg] times' end, ['scout'] = function(lvl) return '[fg]throws a knife that deals [yellow]' .. get_character_stat('scout', lvl, 'dmg') .. '[fg] damage and chains [yellow]3[fg] times' end,
['cleric'] = function(lvl) return '[fg]heals a unit for [yellow]20%[fg] of its max hp when it drops below [yellow]50%[fg] max hp' end, ['cleric'] = function(lvl) return '[fg]heals a unit for [yellow]20%[fg] of its max hp when it drops below [yellow]50%[fg] max hp' end,
@ -538,16 +538,16 @@ function init()
['illusionist'] = function(lvl) return '[fg]launches a projectile that deals [yellow]' .. get_character_stat('illusionist', lvl, 'dmg') .. '[fg] damage and creates copies that do the same' end, ['illusionist'] = function(lvl) return '[fg]launches a projectile that deals [yellow]' .. get_character_stat('illusionist', lvl, 'dmg') .. '[fg] damage and creates copies that do the same' end,
['witch'] = function(lvl) return '[fg]creates an area that ricochets around the arena and deals [yellow]' .. get_character_stat('witch', lvl, 'dmg') .. '[fg] damage per second' end, ['witch'] = function(lvl) return '[fg]creates an area that ricochets around the arena and deals [yellow]' .. get_character_stat('witch', lvl, 'dmg') .. '[fg] damage per second' end,
['silencer'] = function(lvl) return '[fg]curses [yellow]5[fg] nearby enemies for [yellow]6[fg] seconds, preventing them from using special attacks' end, ['silencer'] = function(lvl) return '[fg]curses [yellow]5[fg] nearby enemies for [yellow]6[fg] seconds, preventing them from using special attacks' end,
['vulcanist'] = function(lvl) return '[fg]creates a volcano that explodes the nearby area [yellow]4[fg] times, dealing [yellow]' .. get_character_stat('vulcanist', lvl, 'dmg') .. 'AoE [fg]damage' end, ['vulcanist'] = function(lvl) return '[fg]creates a volcano that explodes the nearby area [yellow]4[fg] times, dealing [yellow]' .. get_character_stat('vulcanist', lvl, 'dmg') .. ' AoE [fg]damage' end,
['warden'] = function(lvl) return '[fg]creates a force field around a random unit that prevents enemies from entering' end, ['warden'] = function(lvl) return '[fg]creates a force field around a random unit that prevents enemies from entering' end,
['psychic'] = function(lvl) return '[fg]creates a small area that deals [yellow]' .. get_character_stat('psychic', lvl, 'dmg') .. 'AoE[fg] damage' end, ['psychic'] = function(lvl) return '[fg]creates a small area that deals [yellow]' .. get_character_stat('psychic', lvl, 'dmg') .. ' AoE[fg] damage' end,
} }
character_effect_names = { character_effect_names = {
['vagrant'] = '[fg]Champion', ['vagrant'] = '[fg]Champion',
['swordsman'] = '[yellow]Cleave', ['swordsman'] = '[yellow]Cleave',
['wizard'] = '[blue]Magic Missile', ['wizard'] = '[blue]Magic Missile',
['magician'] = '[blue]Teleportation', ['magician'] = '[blue]Ethereal Form',
['archer'] = '[green]Bounce Shot', ['archer'] = '[green]Bounce Shot',
['scout'] = '[red]Dagger Resonance', ['scout'] = '[red]Dagger Resonance',
['cleric'] = '[green]Mass Heal ', ['cleric'] = '[green]Mass Heal ',
@ -599,7 +599,7 @@ function init()
['vagrant'] = '[light_bg]Champion', ['vagrant'] = '[light_bg]Champion',
['swordsman'] = '[light_bg]Cleave', ['swordsman'] = '[light_bg]Cleave',
['wizard'] = '[light_bg]Magic Missile', ['wizard'] = '[light_bg]Magic Missile',
['magician'] = '[light_bg]Teleportation', ['magician'] = '[light_bg]Ethereal Form',
['archer'] = '[light_bg]Bounce Shot', ['archer'] = '[light_bg]Bounce Shot',
['scout'] = '[light_bg]Dagger Resonance', ['scout'] = '[light_bg]Dagger Resonance',
['cleric'] = '[light_bg]Mass Heal ', ['cleric'] = '[light_bg]Mass Heal ',
@ -651,7 +651,7 @@ function init()
['vagrant'] = function() return '[yellow]+10%[fg] damage and [yellow]+10%[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, ['swordsman'] = function() return "[fg]the swordsman's damage is [yellow]doubled" end,
['wizard'] = function() return '[fg]the projectile chains [yellow]2[fg] times' 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' end, ['magician'] = function() return '[fg]the magician becomes invulnerable for [yellow]6[fg] seconds but also cannot attack' end,
['archer'] = function() return '[fg]the arrow ricochets off walls [yellow]3[fg] times' end, ['archer'] = function() return '[fg]the arrow ricochets off walls [yellow]3[fg] times' end,
['scout'] = function() return '[yellow]+25%[fg] damage per chain and [yellow]+3[fg] chains' end, ['scout'] = function() return '[yellow]+25%[fg] damage per chain and [yellow]+3[fg] chains' end,
['cleric'] = function() return '[fg]heals all units' end, ['cleric'] = function() return '[fg]heals all units' end,
@ -690,20 +690,20 @@ function init()
['priest'] = function() return '[fg]picks [yellow]3[fg] units at random and grants them a buff that prevents death once' end, ['priest'] = function() return '[fg]picks [yellow]3[fg] units at random and grants them a buff that prevents death once' end,
['infestor'] = function() return '[fg][yellow]triples[fg] the number of critters released' end, ['infestor'] = function() return '[fg][yellow]triples[fg] the number of critters released' end,
['flagellant'] = function() return '[fg]deals [yellow]' .. 2*get_character_stat('flagellant', 3, 'dmg') .. '[fg] damage to all allies and grants [yellow]+12%[fg] damage to all allies per cast' end, ['flagellant'] = function() return '[fg]deals [yellow]' .. 2*get_character_stat('flagellant', 3, 'dmg') .. '[fg] damage to all allies and grants [yellow]+12%[fg] damage to all allies per cast' end,
['arcanist'] = function() return '[yellow]+100%[fg] attack speed for the orb and [yellow]2[fg] projectiles are released per cast' end, ['arcanist'] = function() return '[yellow]+50%[fg] attack speed for the orb and [yellow]2[fg] projectiles are released per cast' end,
['illusionist'] = function() return '[yellow]doubles[fg] the number of copies created and they release [yellow]12[fg] projectiles on death that pierce and ricochet once' end, ['illusionist'] = function() return '[yellow]doubles[fg] the number of copies created and they release [yellow]12[fg] projectiles on death that pierce and ricochet once' end,
['witch'] = function() return '[fg]the area periodically releases projectiles, each dealing [yellow]' .. get_character_stat('witch', 3, 'dmg') .. '[fg] damage and chaining once' end, ['witch'] = function() return '[fg]the area periodically releases projectiles, each dealing [yellow]' .. get_character_stat('witch', 3, 'dmg') .. '[fg] damage and chaining once' end,
['silencer'] = function() return '[fg]the curse also deals [yellow]' .. get_character_stat('silencer', 3, 'dmg') .. '[fg] damage per second' end, ['silencer'] = function() return '[fg]the curse also deals [yellow]' .. get_character_stat('silencer', 3, 'dmg') .. '[fg] damage per second' end,
['vulcanist'] = function() return '[fg]the number and speed of explosions is [yellow]doubled[fg]' end, ['vulcanist'] = function() return '[fg]the number and speed of explosions is [yellow]doubled[fg]' end,
['warden'] = function() return '[fg]creates the force field around [yellow]2[fg] units, and they are always the [yellow]head[fg] and [yellow]tail[fg] of the snake' end, ['warden'] = function() return '[fg]creates the force field around [yellow]2[fg] units' end,
['psychic'] = function() return '[fg]the attack can happen from any distance and deals [yellow]double[fg] damage' end, ['psychic'] = function() return '[fg]the attack can happen from any distance and repeats once' end,
} }
character_effect_descriptions_gray = { character_effect_descriptions_gray = {
['vagrant'] = function() return '[light_bg]+10% damage and +10% 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, ['swordsman'] = function() return "[light_bg]the swordsman's damage is doubled" end,
['wizard'] = function() return '[light_bg]the projectile chains 3 times' end, ['wizard'] = function() return '[light_bg]the projectile chains 3 times' end,
['magician'] = function() return '[light_bg]the magician becomes invulnerable for 6 seconds' end, ['magician'] = function() return '[light_bg]the magician becomes invulnerable for 6 seconds but also cannot attack' end,
['archer'] = function() return '[light_bg]the arrow ricochets off walls 3 times' end, ['archer'] = function() return '[light_bg]the arrow ricochets off walls 3 times' end,
['scout'] = function() return '[light_bg]+25% damage per chain and +3 chains' end, ['scout'] = function() return '[light_bg]+25% damage per chain and +3 chains' end,
['cleric'] = function() return '[light_bg]heals all units' end, ['cleric'] = function() return '[light_bg]heals all units' end,
@ -742,13 +742,13 @@ function init()
['priest'] = function() return '[light_bg]picks 3 units at random and grants them a buff that prevents death once' end, ['priest'] = function() return '[light_bg]picks 3 units at random and grants them a buff that prevents death once' end,
['infestor'] = function() return '[light_bg]triples the number of critters released' end, ['infestor'] = function() return '[light_bg]triples the number of critters released' end,
['flagellant'] = function() return '[light_bg]deals ' .. 2*get_character_stat('flagellant', 3, 'dmg') .. ' damage to all allies and grants +12% damage to all allies per cast' end, ['flagellant'] = function() return '[light_bg]deals ' .. 2*get_character_stat('flagellant', 3, 'dmg') .. ' damage to all allies and grants +12% damage to all allies per cast' end,
['arcanist'] = function() return '[light_bg]+100% attack speed for the orb and 2 projectiles are released per cast' end, ['arcanist'] = function() return '[light_bg]+50% attack speed for the orb and 2 projectiles are released per cast' end,
['illusionist'] = function() return '[light_bg]doubles the number of copies created and they release 12 projectiles on death that pierce and ricochet once' end, ['illusionist'] = function() return '[light_bg]doubles the number of copies created and they release 12 projectiles on death that pierce and ricochet once' end,
['witch'] = function() return '[light_bg]the area periodically releases projectiles, each dealing ' .. get_character_stat('witch', 3, 'dmg') .. ' damage and chaining once' end, ['witch'] = function() return '[light_bg]the area periodically releases projectiles, each dealing ' .. get_character_stat('witch', 3, 'dmg') .. ' damage and chaining once' end,
['silencer'] = function() return '[light_bg]the curse also deals ' .. get_character_stat('silencer', 3, 'dmg') .. ' damage per second' end, ['silencer'] = function() return '[light_bg]the curse also deals ' .. get_character_stat('silencer', 3, 'dmg') .. ' damage per second' end,
['vulcanist'] = function() return '[light_bg]the number and speed of explosions is doubled' end, ['vulcanist'] = function() return '[light_bg]the number and speed of explosions is doubled' end,
['warden'] = function() return '[light_bg]creates the force field around 2 units, and they are always the head and tail of the snake' end, ['warden'] = function() return '[light_bg]creates the force field around 2 units' end,
['psychic'] = function() return '[light_bg]the attack can happen from any distance and deals double damage' end, ['psychic'] = function() return '[light_bg]the attack can happen from any distance and repeats once' end,
} }
character_stats = { character_stats = {
@ -1196,12 +1196,12 @@ function init()
[4] = {3, 5}, [4] = {3, 5},
[5] = {4, 7}, [5] = {4, 7},
[6] = {6, 10}, [6] = {6, 10},
[7] = {6, 7}, [7] = {10, 14},
[8] = {7, 8}, [8] = {12, 16},
[9] = {10, 16}, [9] = {14, 18},
[10] = {10, 12}, [10] = {10, 14},
[11] = {12, 14}, [11] = {12, 16},
[12] = {18, 20}, [12] = {20, 24},
[13] = {12, 16}, [13] = {12, 16},
[14] = {14, 18}, [14] = {14, 18},
[15] = {16, 20}, [15] = {16, 20},
@ -1234,13 +1234,13 @@ function init()
[3] = {10}, [3] = {10},
[4] = {4, 4}, [4] = {4, 4},
[5] = {4, 3, 2}, [5] = {4, 3, 2},
[6] = {14}, [6] = {12},
[7] = {5, 3, 2}, [7] = {5, 3, 2},
[8] = {6, 3, 3, 3}, [8] = {6, 3, 3, 3},
[9] = {18}, [9] = {14},
[10] = {8, 4}, [10] = {8, 4},
[11] = {8, 6, 2}, [11] = {8, 6, 2},
[12] = {18}, [12] = {16},
[13] = {8, 8}, [13] = {8, 8},
[14] = {12, 6}, [14] = {12, 6},
[15] = {18}, [15] = {18},
@ -1319,14 +1319,21 @@ function init()
main:add(BuyScreen'buy_screen') main:add(BuyScreen'buy_screen')
main:go_to('buy_screen', run.level or 0, run.units or {}, passives) main:go_to('buy_screen', run.level or 0, run.units or {}, passives)
-- main:go_to('buy_screen', 2, run.units or {}, passives)
--[[ --[[
main:add(Arena'arena') main:add(Arena'arena')
main:go_to('arena', 14, { main:go_to('arena', 1, {
{character = 'magician', level = 3}, {character = 'dual_gunner', level = 1},
{character = 'scout', level = 1},
}, passives) }, passives)
]]-- ]]--
--[[
main:add(Media'media')
main:go_to('media')
]]--
trigger:every(2, function() trigger:every(2, function()
if debugging_memory then if debugging_memory then
for k, v in pairs(system.type_count()) do for k, v in pairs(system.type_count()) do
@ -1336,6 +1343,8 @@ function init()
print() print()
end end
end) end)
print(table.tostring(love.graphics.getSupported()))
end end

View File

@ -11,7 +11,11 @@ function Media:on_enter(from)
self.effects = Group() self.effects = Group()
self.ui = Group() self.ui = Group()
graphics.set_background_color(fg[0]) graphics.set_background_color(blue2[0])
Text2{group = self.ui, x = gw/2, y = gh/2, lines = {
{text = '[fg]SNKRX', font = fat_font, alignment = 'center', height_offset = -15},
{text = '[fg]sorcerer update', font = pixul_font, alignment = 'center'},
}}
end end
@ -27,10 +31,3 @@ function Media:draw()
self.effects:draw() self.effects:draw()
self.ui:draw() self.ui:draw()
end end
--[[
build your party: hire heroes, rank them up and defeat endless waves of enemies
make synergies: combine heroes of the same class to unlock unique class passives
find passive items: further enhance your party with powerful passive items
create your build: explore the possibilities and combinations to create your own unique build
]]--

View File

@ -232,7 +232,7 @@ function Unit:calculate_stats(first_run)
if current_new_game_plus == 0 then if current_new_game_plus == 0 then
if self.boss then if self.boss then
local x = self.level 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} 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}
self.base_hp = 100 + (current_new_game_plus*5) + (90 + current_new_game_plus*10)*y[x] 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_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 = 35 + 1.5*y[x]
@ -246,7 +246,7 @@ function Unit:calculate_stats(first_run)
else else
if self.boss then if self.boss then
local x = self.level 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} 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}
self.base_hp = 100 + (current_new_game_plus*5) + (90 + current_new_game_plus*10)*y[x] 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_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 = 35 + 1.5*y[x]
@ -254,7 +254,7 @@ function Unit:calculate_stats(first_run)
local x = self.level 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} 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 + (current_new_game_plus*3) + (15 + current_new_game_plus*2.7)*y[x] 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.25) + (2 + current_new_game_plus*1.2)*y[x] self.base_dmg = (4 + current_new_game_plus*1.15) + (2 + current_new_game_plus*1)*y[x]
self.base_mvspd = 70 + 3*y[x] self.base_mvspd = 70 + 3*y[x]
end end
end end

View File

@ -41,6 +41,7 @@ function Player:init(args)
elseif self.character == 'magician' then elseif self.character == 'magician' then
self.attack_sensor = Circle(self.x, self.y, 96) self.attack_sensor = Circle(self.x, self.y, 96)
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() 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()
if self.magician_invulnerable then return end
local enemy = self:get_random_object_in_shape(self.attack_sensor, main.current.enemies) local enemy = self:get_random_object_in_shape(self.attack_sensor, main.current.enemies)
if enemy then if enemy then
self:attack(32, {x = enemy.x, y = enemy.y}) self:attack(32, {x = enemy.x, y = enemy.y})
@ -91,7 +92,7 @@ function Player:init(args)
elseif self.character == 'arcanist' then elseif self.character == 'arcanist' then
self.sorcerer_count = 0 self.sorcerer_count = 0
self.attack_sensor = Circle(self.x, self.y, 128) self.attack_sensor = Circle(self.x, self.y, 128)
self.t:cooldown(3, function() local enemies = self:get_objects_in_shape(self.attack_sensor, main.current.enemies); return enemies and #enemies > 0 end, function() self.t:cooldown(4, 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) local closest_enemy = self:get_closest_object_in_shape(self.attack_sensor, main.current.enemies)
if closest_enemy then if closest_enemy then
self:shoot(self:angle_to_object(closest_enemy), {pierce = 1000, v = 40}) self:shoot(self:angle_to_object(closest_enemy), {pierce = 1000, v = 40})
@ -838,7 +839,6 @@ function Player:init(args)
local unit_1 = random:table_remove(units) local unit_1 = random:table_remove(units)
local unit_2 = random:table_remove(units) local unit_2 = random:table_remove(units)
local unit_3 = random:table_remove(units) local unit_3 = random:table_remove(units)
print(unit_1, unit_2, unit_3)
if unit_1 then unit_1.t:every(2, function() unit_1:hit(0.05*unit_1.max_hp) end) end if unit_1 then unit_1.t:every(2, function() unit_1:hit(0.05*unit_1.max_hp) end) end
if unit_2 then unit_2.t:every(2, function() unit_2:hit(0.05*unit_2.max_hp) end) end if unit_2 then unit_2.t:every(2, function() unit_2:hit(0.05*unit_2.max_hp) end) end
if unit_3 then unit_3.t:every(2, function() unit_3:hit(0.05*unit_3.max_hp) end) end if unit_3 then unit_3.t:every(2, function() unit_3:hit(0.05*unit_3.max_hp) end) end
@ -889,6 +889,7 @@ function Player:update(dt)
if class_levels.forcer >= 1 then number_of_active_sets = number_of_active_sets + 1 end if class_levels.forcer >= 1 then number_of_active_sets = number_of_active_sets + 1 end
if class_levels.swarmer >= 1 then number_of_active_sets = number_of_active_sets + 1 end 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 if class_levels.voider >= 1 then number_of_active_sets = number_of_active_sets + 1 end
if class_levels.sorcerer >= 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_dmg_m = 1 + 0.1*number_of_active_sets
self.vagrant_aspd_m = 1 + 0.1*number_of_active_sets self.vagrant_aspd_m = 1 + 0.1*number_of_active_sets
end end
@ -953,6 +954,7 @@ function Player:update(dt)
if class_levels.forcer >= 1 then number_of_active_sets = number_of_active_sets + 1 end if class_levels.forcer >= 1 then number_of_active_sets = number_of_active_sets + 1 end
if class_levels.swarmer >= 1 then number_of_active_sets = number_of_active_sets + 1 end 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 if class_levels.voider >= 1 then number_of_active_sets = number_of_active_sets + 1 end
if class_levels.sorcerer >= 1 then number_of_active_sets = number_of_active_sets + 1 end
if main.current.psyker_level == 2 then if main.current.psyker_level == 2 then
self.psyker_dmg_m = 1 + 0.2*number_of_active_sets self.psyker_dmg_m = 1 + 0.2*number_of_active_sets
self.psyker_aspd_m = 1 + 0.2*number_of_active_sets self.psyker_aspd_m = 1 + 0.2*number_of_active_sets
@ -1021,11 +1023,13 @@ function Player:update(dt)
for _, unit in ipairs(units) do for _, unit in ipairs(units) do
total_v = total_v + unit.max_v total_v = total_v + unit.max_v
end end
total_v = total_v/#units total_v = math.floor(total_v/#units)
self.total_v = total_v
self:set_velocity(total_v*math.cos(self.r), total_v*math.sin(self.r)) self:set_velocity(total_v*math.cos(self.r), total_v*math.sin(self.r))
if not main.current.won and not main.current.choosing_passives then if not main.current.won and not main.current.choosing_passives then
if not state.no_screen_movement then
local vx, vy = self:get_velocity() local vx, vy = self:get_velocity()
local hd = math.remap(math.abs(self.x - gw/2), 0, 192, 1, 0) local hd = math.remap(math.abs(self.x - gw/2), 0, 192, 1, 0)
local vd = math.remap(math.abs(self.y - gh/2), 0, 108, 1, 0) local vd = math.remap(math.abs(self.y - gh/2), 0, 108, 1, 0)
@ -1037,6 +1041,7 @@ function Player:update(dt)
elseif input.move_up.down then camera.r = math.lerp_angle_dt(0.01, dt, camera.r, -math.pi/256) elseif input.move_up.down then camera.r = math.lerp_angle_dt(0.01, dt, camera.r, -math.pi/256)
else camera.r = math.lerp_angle_dt(0.005, dt, camera.r, 0) end else camera.r = math.lerp_angle_dt(0.005, dt, camera.r, 0) end
end end
end
self:set_angle(self.r) self:set_angle(self.r)
@ -1049,7 +1054,7 @@ function Player:update(dt)
local distance_to_previous = math.distance(previous.x, previous.y, point.x, point.y) local distance_to_previous = math.distance(previous.x, previous.y, point.x, point.y)
distance_sum = distance_sum + distance_to_previous distance_sum = distance_sum + distance_to_previous
if distance_sum >= target_distance then if distance_sum >= target_distance then
p = point p = self.parent.previous_positions[i-1]
break break
end end
previous = point previous = point
@ -1079,6 +1084,12 @@ function Player:draw()
else else
graphics.rectangle(self.x, self.y, self.shape.w, self.shape.h, 3, 3, (self.hfx.hit.f or self.hfx.shoot.f) and fg[0] or self.color) graphics.rectangle(self.x, self.y, self.shape.w, self.shape.h, 3, 3, (self.hfx.hit.f or self.hfx.shoot.f) and fg[0] or self.color)
end end
if self.leader and state.arrow_snake then
local x, y = self.x + 0.9*self.shape.w, self.y
graphics.line(x + 3, y, x, y - 3, character_colors[self.character], 1)
graphics.line(x + 3, y, x, y + 3, character_colors[self.character], 1)
end
end end
graphics.pop() graphics.pop()
end end
@ -1148,7 +1159,7 @@ function Player:hit(damage)
self.hfx:use('hit', 0.25, 200, 10) self.hfx:use('hit', 0.25, 200, 10)
self:show_hp() self:show_hp()
local actual_damage = self:calculate_damage(damage) local actual_damage = math.max(self:calculate_damage(damage), 0)
self.hp = self.hp - actual_damage self.hp = self.hp - actual_damage
_G[random:table{'player_hit1', 'player_hit2'}]:play{pitch = random:float(0.95, 1.05), volume = 0.5} _G[random:table{'player_hit1', 'player_hit2'}]:play{pitch = random:float(0.95, 1.05), volume = 0.5}
camera:shake(4, 0.5) camera:shake(4, 0.5)
@ -1221,6 +1232,7 @@ function Player:hit(damage)
self.hp = 1 self.hp = 1
end end
else else
self.dead = true
if self.leader then self:recalculate_followers() if self.leader then self:recalculate_followers()
else self.parent:recalculate_followers() end else self.parent:recalculate_followers() end
end end
@ -1546,6 +1558,7 @@ function Projectile:init(args)
self.t:every(self.parent.level == 3 and 0.54 or 0.8, function() self.t:every(self.parent.level == 3 and 0.54 or 0.8, function()
local enemies = table.head(self:get_objects_in_shape(Circle(self.x, self.y, 128), main.current.enemies), self.level == 3 and 2 or 1) local enemies = table.head(self:get_objects_in_shape(Circle(self.x, self.y, 128), main.current.enemies), self.level == 3 and 2 or 1)
for _, enemy in ipairs(enemies) do for _, enemy in ipairs(enemies) do
arcane2:play{pitch = random:float(0.7, 1.3), volume = 0.15}
self.hfx:use('hit', 0.5) self.hfx:use('hit', 0.5)
local r = self:angle_to_object(enemy) local r = self:angle_to_object(enemy)
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 = self.parent.color, dmg = self.parent.dmg, pierce = 1000, character = 'arcanist_projectile', 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 = self.parent.color, dmg = self.parent.dmg, pierce = 1000, character = 'arcanist_projectile',

46
todo
View File

@ -1,23 +1,3 @@
Sorcerer update
Options
Option to have an arrow at the head of the snake - https://imgur.com/a/poXVsoN
Option to turn off camera movement
Options menu from buy screen
Volume slider
Fix fullscreen with different resolutions that don't scale properly
QoL
Item reroll for 15 gold
Fix highlight colors and highlight reserve
Rename tutorial to guide or manual
Add visuals for defensive ouroboros
Unlock automatically on shop enter
Balance
Buff tanks, maybe add a simple forcer ability to them
Buff 24/25 HP again
Buff headbutter (+ trigger range)
Bugs
Fix leader (snake head) distance being off by 1 or 2 pixels on certain snake speeds
Sorcerer update patch notes Sorcerer update patch notes
Summary: This update adds 1 new class with 8 new units, implements more QoL features, further balance changes and lots of bug fixes. Summary: This update adds 1 new class with 8 new units, implements more QoL features, further balance changes and lots of bug fixes.
Added sorcerer class: sorcerers repeat their attacks once every 4/3/2 attacks Added sorcerer class: sorcerers repeat their attacks once every 4/3/2 attacks
@ -56,15 +36,21 @@ Summary: This update adds 1 new class with 8 new units, implements more QoL feat
Fixed a crash whenever the Fairy would try to select a unit that just died this or last frame Fixed a crash whenever the Fairy would try to select a unit that just died this or last frame
Fixed a crash that would happen when trying to restart the game through the menu on the passive selection screen Fixed a crash that would happen when trying to restart the game through the menu on the passive selection screen
Fixed a bug where you could die and win at the same time Fixed a bug where you could die and win at the same time
Fixed a crash triggered by infested enemies on death rarely Fixed a crash triggered rarely by infested enemies on death
Fixed a bug where quitting on level 1 would automatically jump to level 2 Fixed a bug where quitting on level 1 would automatically jump to level 2
Fixed a bug where lock state would carry over after a run death/win/restart Fixed a bug where the shop's lock state would carry over after a run death/win/restart
Fixed a bug where items from a previous restarted run would remain after the 1st round Fixed a bug where items from a previous restarted run would remain after the 1st round of a new run
Fixed a bug where the leader's (snake head) distance was off by 1 or 2 pixels on certain snake speeds
Added item reroll for 15 gold
Buffed tank (yellow enemy) and added a special attack
Buffed boss HP for levels 18, 24 and 25 (again)
Added an option to have an arrow at the head of the snake
Added an option to turn off camera movement
Slightly decreased enemy damage across all difficulties
--- ---
Future ideas: Future updates:
Chaos related classes Chaos related classes
Invoker - shoots a projectile with random properties, Lv.3 effect - ??? Invoker - shoots a projectile with random properties, Lv.3 effect - ???
Trappers: Trappers:
@ -83,7 +69,15 @@ Hide cursor during waves
Mouse follow control? Mouse follow control?
Visuals for divine intervertion, fairy buff Visuals for divine intervertion, fairy buff
Fix enemies still spawning after arena clear (this happens with the extra enemy spawns that were blocked earlier) Fix enemies still spawning after arena clear (this happens with the extra enemy spawns that were blocked earlier)
Fix highlight colors and highlight reserve
Add visuals for defensive ouroboros
Options menu from buy screen
Volume slider
Fix fullscreen with different resolutions that don't scale properly
Roguelite update: Roguelite update:
Slay the Spire-like node selection map (copy code from SHOOTRX repo as this is already implemented there) Hades-like no map system, this is more elegant and requires way less work than something like Isaac or StS
Units die permanently when they die Units die permanently when they die
Units can have items attached to them like in Underlords
Unit item ideas:
This unit's projectiles pierce/chain/fork/seek/split/stun/etc