diff --git a/arena.lua b/arena.lua index 77a58ed..fde73b6 100644 --- a/arena.lua +++ b/arena.lua @@ -397,7 +397,7 @@ function Arena:update(dt) passives = {} main_song_instance:stop() run_passive_pool = { - 'centipede', 'ouroboros_technique_r', 'ouroboros_technique_l', 'amplify', 'resonance', 'ballista', 'call_of_the_void', 'crucio', 'speed_3', 'damage_4', 'level_5', 'death_6', 'lasting_7', + 'centipede', 'ouroboros_technique_r', 'ouroboros_technique_l', 'amplify', 'resonance', 'ballista', 'call_of_the_void', 'crucio', 'speed_3', 'damage_4', 'shoot_5', 'death_6', 'lasting_7', 'defensive_stance', 'offensive_stance', 'kinetic_bomb', 'porcupine_technique', 'last_stand', 'seeping', 'deceleration', 'annihilation', 'malediction', 'pandemic', 'whispers_of_doom', 'tremor', 'heavy_impact', 'fracture', 'meat_shield', 'hive', 'baneling_burst', 'blunt_arrow', 'explosive_arrow', 'divine_machine_arrow', 'chronomancy', 'awakening', 'divine_punishment', 'assassination', 'flying_daggers', 'ultimatum', 'magnify', 'echo_barrage', 'unleash', 'reinforce', 'payback', 'enchanted', 'freezing_field', 'burning_field', 'gravity_field', 'magnetism', @@ -585,7 +585,7 @@ function Arena:update(dt) passives = {} main_song_instance:stop() run_passive_pool = { - 'centipede', 'ouroboros_technique_r', 'ouroboros_technique_l', 'amplify', 'resonance', 'ballista', 'call_of_the_void', 'crucio', 'speed_3', 'damage_4', 'level_5', 'death_6', 'lasting_7', + 'centipede', 'ouroboros_technique_r', 'ouroboros_technique_l', 'amplify', 'resonance', 'ballista', 'call_of_the_void', 'crucio', 'speed_3', 'damage_4', 'shoot_5', 'death_6', 'lasting_7', 'defensive_stance', 'offensive_stance', 'kinetic_bomb', 'porcupine_technique', 'last_stand', 'seeping', 'deceleration', 'annihilation', 'malediction', 'pandemic', 'whispers_of_doom', 'tremor', 'heavy_impact', 'fracture', 'meat_shield', 'hive', 'baneling_burst', 'blunt_arrow', 'explosive_arrow', 'divine_machine_arrow', 'chronomancy', 'awakening', 'divine_punishment', 'assassination', 'flying_daggers', 'ultimatum', 'magnify', 'echo_barrage', 'unleash', 'reinforce', 'payback', 'enchanted', 'freezing_field', 'burning_field', 'gravity_field', 'magnetism', @@ -998,7 +998,7 @@ function Arena:die() passives = {} main_song_instance:stop() run_passive_pool = { - 'centipede', 'ouroboros_technique_r', 'ouroboros_technique_l', 'amplify', 'resonance', 'ballista', 'call_of_the_void', 'crucio', 'speed_3', 'damage_4', 'level_5', 'death_6', 'lasting_7', + 'centipede', 'ouroboros_technique_r', 'ouroboros_technique_l', 'amplify', 'resonance', 'ballista', 'call_of_the_void', 'crucio', 'speed_3', 'damage_4', 'shoot_5', 'death_6', 'lasting_7', 'defensive_stance', 'offensive_stance', 'kinetic_bomb', 'porcupine_technique', 'last_stand', 'seeping', 'deceleration', 'annihilation', 'malediction', 'pandemic', 'whispers_of_doom', 'tremor', 'heavy_impact', 'fracture', 'meat_shield', 'hive', 'baneling_burst', 'blunt_arrow', 'explosive_arrow', 'divine_machine_arrow', 'chronomancy', 'awakening', 'divine_punishment', 'assassination', 'flying_daggers', 'ultimatum', 'magnify', 'echo_barrage', 'unleash', 'reinforce', 'payback', 'enchanted', 'freezing_field', 'burning_field', 'gravity_field', 'magnetism', diff --git a/assets/images/level_5.png b/assets/images/shoot_5.png similarity index 100% rename from assets/images/level_5.png rename to assets/images/shoot_5.png diff --git a/assets/sounds/Buff 4.ogg b/assets/sounds/Buff 4.ogg new file mode 100644 index 0000000..e4cb25b Binary files /dev/null and b/assets/sounds/Buff 4.ogg differ diff --git a/assets/sounds/Unlock 3.ogg b/assets/sounds/Unlock 3.ogg new file mode 100644 index 0000000..66534d0 Binary files /dev/null and b/assets/sounds/Unlock 3.ogg differ diff --git a/buy_screen.lua b/buy_screen.lua index 55b0ea6..1770152 100644 --- a/buy_screen.lua +++ b/buy_screen.lua @@ -143,7 +143,7 @@ function BuyScreen:on_enter(from, level, units, passives, shop_level, shop_xp) passives = {} main_song_instance:stop() run_passive_pool = { - 'centipede', 'ouroboros_technique_r', 'ouroboros_technique_l', 'amplify', 'resonance', 'ballista', 'call_of_the_void', 'crucio', 'speed_3', 'damage_4', 'level_5', 'death_6', 'lasting_7', + 'centipede', 'ouroboros_technique_r', 'ouroboros_technique_l', 'amplify', 'resonance', 'ballista', 'call_of_the_void', 'crucio', 'speed_3', 'damage_4', 'shoot_5', 'death_6', 'lasting_7', 'defensive_stance', 'offensive_stance', 'kinetic_bomb', 'porcupine_technique', 'last_stand', 'seeping', 'deceleration', 'annihilation', 'malediction', 'pandemic', 'whispers_of_doom', 'tremor', 'heavy_impact', 'fracture', 'meat_shield', 'hive', 'baneling_burst', 'blunt_arrow', 'explosive_arrow', 'divine_machine_arrow', 'chronomancy', 'awakening', 'divine_punishment', 'assassination', 'flying_daggers', 'ultimatum', 'magnify', 'echo_barrage', 'unleash', 'reinforce', 'payback', 'enchanted', 'freezing_field', 'burning_field', 'gravity_field', 'magnetism', @@ -384,7 +384,7 @@ function BuyScreen:set_items() local y = 182 for k, passive in ipairs(self.passives) do local i, j = math.index_to_coordinates(k, 4) - table.insert(self.items, ItemCard{group = self.main, x = 45 + (i-1)*60, y = y + (j-1)*50, w = 40, h = 50, passive = passive.passive , level = passive.level, xp = passive.xp}) + table.insert(self.items, ItemCard{group = self.main, x = 45 + (i-1)*60, y = y + (j-1)*50, w = 40, h = 50, passive = passive.passive , level = passive.level, xp = passive.xp, parent = self}) end end @@ -530,7 +530,7 @@ function RestartButton:update(dt) passives = {} main_song_instance:stop() run_passive_pool = { - 'centipede', 'ouroboros_technique_r', 'ouroboros_technique_l', 'amplify', 'resonance', 'ballista', 'call_of_the_void', 'crucio', 'speed_3', 'damage_4', 'level_5', 'death_6', 'lasting_7', + 'centipede', 'ouroboros_technique_r', 'ouroboros_technique_l', 'amplify', 'resonance', 'ballista', 'call_of_the_void', 'crucio', 'speed_3', 'damage_4', 'shoot_5', 'death_6', 'lasting_7', 'defensive_stance', 'offensive_stance', 'kinetic_bomb', 'porcupine_technique', 'last_stand', 'seeping', 'deceleration', 'annihilation', 'malediction', 'pandemic', 'whispers_of_doom', 'tremor', 'heavy_impact', 'fracture', 'meat_shield', 'hive', 'baneling_burst', 'blunt_arrow', 'explosive_arrow', 'divine_machine_arrow', 'chronomancy', 'awakening', 'divine_punishment', 'assassination', 'flying_daggers', 'ultimatum', 'magnify', 'echo_barrage', 'unleash', 'reinforce', 'payback', 'enchanted', 'freezing_field', 'burning_field', 'gravity_field', 'magnetism', @@ -853,7 +853,7 @@ function LevelButton:create_info_text() local t41, t42 = get_shop_odds(self.parent.shop_level, 4), get_shop_odds(self.parent.shop_level+1, 4) self.info_text = InfoText{group = main.current.ui} self.info_text:activate({ - {text = '[yellow]Lv.' .. self.parent.shop_level .. '[fg] shop, XP: [yellow]' .. self.shop_xp .. '/' .. self.max_xp, font = pixul_font, alignment = 'center', height_multiplier = 1.5}, + {text = '[yellow]Lv.' .. self.parent.shop_level .. '[fg] shop, XP: [yellow]' .. self.shop_xp .. '/' .. self.max_xp .. '[fg], +1 XP cost: [yellow]5', font = pixul_font, alignment = 'center', height_multiplier = 1.5}, {text = '[bg10]chances of units appearing on the shop', font = pixul_font, alignment = 'center', height_multiplier = 1.25}, {text = '[yellow]current shop level [fgm10]next shop level', font = pixul_font, alignment = 'left', height_multiplier = 1.25}, {text = '[fg]tier 1: ' .. t11 .. '%' .. tostring(t11 < 10 and ' ' or '') .. ' [fgm8]tier 1: ' .. t12 .. '%', font = pixul_font, alignment = 'left', height_multiplier = 1.25}, @@ -869,7 +869,7 @@ function LevelButton:create_info_text() local t41 = get_shop_odds(self.parent.shop_level, 4) self.info_text = InfoText{group = main.current.ui} self.info_text:activate({ - {text = '[yellow]Lv.' .. self.parent.shop_level .. '[fg] shop, XP: [yellow]' .. self.shop_xp .. '/' .. self.max_xp, font = pixul_font, alignment = 'center', height_multiplier = 1.5}, + {text = '[yellow]Lv.' .. self.parent.shop_level .. '[fg] shop', font = pixul_font, alignment = 'center', height_multiplier = 1.5}, {text = '[bg10]chances of units appearing on the shop', font = pixul_font, alignment = 'center', height_multiplier = 1.25}, {text = '[yellow]current shop level', font = pixul_font, alignment = 'left', height_multiplier = 1.25}, {text = '[fg]tier 1: ' .. t11 .. '%', font = pixul_font, alignment = 'left', height_multiplier = 1.25}, @@ -1360,11 +1360,55 @@ function ItemCard:init(args) self:init_game_object(args) self.shape = Rectangle(self.x, self.y, self.w, self.h) self.interact_with_mouse = true + self.max_xp = (self.level == 0 and 0) or (self.level == 1 and 2) or (self.level == 2 and 3) or (self.level == 3 and 0) end function ItemCard:update(dt) self:update_game_object(dt) + + if self.selected and input.m1.pressed then + if self.level >= 3 then return end + if gold < 5 then + self.spring:pull(0.2, 200, 10) + self.selected = true + error1:play{pitch = random:float(0.95, 1.05), volume = 0.5} + if not self.info_text_2 then + self.info_text_2 = InfoText{group = main.current.ui} + self.info_text_2:activate({ + {text = '[fg]not enough gold', font = pixul_font, alignment = 'center'}, + }, nil, nil, nil, nil, 16, 4, nil, 2) + self.info_text_2.x, self.info_text_2.y = gw/2, gh/2 + 30 + end + self.t:after(2, function() self.info_text_2:deactivate(); self.info_text_2.dead = true; self.info_text_2 = nil end, 'info_text_2') + else + ui_switch2:play{pitch = random:float(0.95, 1.05), volume = 0.5} + self.xp = self.xp + 1 + if self.xp >= self.max_xp then + self.xp = 0 + self.level = self.level + 1 + self.max_xp = (self.level == 0 and 0) or (self.level == 1 and 2) or (self.level == 2 and 3) or (self.level == 3 and 0) + if self.level == 2 then spawn_mark1:play{pitch = 1, volume = 0.6} end + if self.level == 3 then + spawn_mark1:play{pitch = 1.2, volume = 0.6} + level_up1:play{pitch = 1, volume = 0.5} + end + end + self:create_info_text() + self.selected = true + self.spring:pull(0.2, 200, 10) + gold = gold - 5 + for _, passive in ipairs(passives) do + if passive.passive == self.passive then + passive.level = self.level + passive.xp = self.xp + end + end + self.parent.shop_text:set_text{{text = '[wavy_mid, fg]shop [fg]- [fg, nudge_down]gold: [yellow, nudge_down]' .. gold, font = pixul_font, alignment = 'center'}} + self.text = Text({{text = '[bg10]' .. tostring(self.parent.shop_level), font = pixul_font, alignment = 'center'}}, global_text_tags) + system.save_run(self.parent.level, gold, self.parent.units, passives, self.parent.shop_level, self.parent.shop_xp, run_passive_pool, locked_state) + end + end end @@ -1374,20 +1418,50 @@ function ItemCard:draw() graphics.rectangle(self.x, self.y, self.w, self.h, 6, 6, bg[-1]) end _G[self.passive]:draw(self.x, self.y, 0, 0.8, 0.7, 0, 0, fg[0]) + local x, y = self.x + self.w/2.5, self.y - self.h/2.5 + if self.level == 1 then + graphics.rectangle(x - 2, y, 2, 2, nil, nil, self.xp >= 1 and fg[0] or bg[5]) + graphics.rectangle(x + 2, y, 2, 2, nil, nil, bg[5]) + elseif self.level == 2 then + graphics.rectangle(x - 4, y, 2, 2, nil, nil, self.xp >= 1 and fg[0] or bg[5]) + graphics.rectangle(x, y, 2, 2, nil, nil, self.xp >= 2 and fg[0] or bg[5]) + graphics.rectangle(x + 4, y, 2, 2, nil, nil, bg[5]) + end graphics.pop() end +function ItemCard:create_info_text() + if self.info_text then + self.info_text:deactivate() + self.info_text.dead = true + end + self.info_text = nil + if self.level == 3 then + self.info_text = InfoText{group = main.current.ui, force_update = true} + self.info_text:activate({ + {text = '[fg]' .. passive_names[self.passive] .. ', [yellow]Lv.' .. self.level, font = pixul_font, alignment = 'center', + height_multiplier = 1.25}, + {text = passive_descriptions_level[self.passive](self.level), font = pixul_font, alignment = 'center', height_multiplier = 1.25}, + }, nil, nil, nil, nil, 16, 4, nil, 2) + self.info_text.x, self.info_text.y = gw/2, gh/2 + 10 + else + self.info_text = InfoText{group = main.current.ui, force_update = true} + self.info_text:activate({ + {text = '[fg]' .. passive_names[self.passive] .. ', [yellow]Lv.' .. self.level .. '[fg], XP: [yellow]' .. self.xp .. '/' .. self.max_xp .. '[fg], +1 XP cost: [yellow]5', font = pixul_font, alignment = 'center', + height_multiplier = 1.25}, + {text = passive_descriptions_level[self.passive](self.level), font = pixul_font, alignment = 'center', height_multiplier = 1.25}, + }, nil, nil, nil, nil, 16, 4, nil, 2) + self.info_text.x, self.info_text.y = gw/2, gh/2 + 10 + end +end + + function ItemCard:on_mouse_enter() self.selected = true ui_hover1:play{pitch = random:float(1.3, 1.5), volume = 0.5} self.spring:pull(0.2, 200, 10) - self.info_text = InfoText{group = main.current.ui, force_update = true} - self.info_text:activate({ - {text = '[fg]' .. passive_names[self.passive], font = pixul_font, alignment = 'center', height_multiplier = 1.25}, - {text = passive_descriptions_level[self.passive](self.level), font = pixul_font, alignment = 'center', height_multiplier = 1.25}, - }, nil, nil, nil, nil, 16, 4, nil, 2) - self.info_text.x, self.info_text.y = gw/2, gh/2 + 10 + self:create_info_text() end diff --git a/main.lua b/main.lua index 054c0e5..5baeb53 100644 --- a/main.lua +++ b/main.lua @@ -18,6 +18,8 @@ function init() input:bind('enter', {'space', 'return', 'fleft', 'fdown', 'fright'}) local s = {tags = {sfx}} + level_up1 = Sound('Buff 4.ogg', s) + unlock1 = Sound('Unlock 3.ogg', s) gambler1 = Sound('Collect 5.ogg', s) usurer1 = Sound('Shadow Punch 2.ogg', s) gold1 = Sound('Collect 5.ogg', s) @@ -153,7 +155,7 @@ function init() crucio = Image('crucio') speed_3 = Image('speed_3') damage_4 = Image('damage_4') - level_5 = Image('level_5') + shoot_5 = Image('shoot_5') death_6 = Image('death_6') lasting_7 = Image('lasting_7') defensive_stance = Image('defensive_stance') @@ -588,7 +590,7 @@ function init() ['psychic'] = function(lvl) return '[fg]creates a small area that deals [yellow]' .. get_character_stat('psychic', lvl, 'dmg') .. ' AoE[fg] damage' end, ['miner'] = function(lvl) return '[fg]picking up gold releases [yellow]4[fg] homing projectiles that each deal [yellow]' .. get_character_stat('miner', lvl, 'dmg') .. ' [fg]damage' end, ['merchant'] = function(lvl) return '[fg]gain [yellow]+1[fg] interest for every [yellow]10[fg] gold' end, - ['usurer'] = function(lvl) return '[fg]curses [yellow]3[fg] nearby enemies indefinitely with [yellow]debt[fg], dealing [yellow]' .. get_character_stat('usurer', lvl, 'dmg') .. '[fg] damage per second' end, + ['usurer'] = function(lvl) return '[fg]curses [yellow]3[fg] nearby enemies indefinitely with debt, dealing [yellow]' .. get_character_stat('usurer', lvl, 'dmg') .. '[fg] damage per second' end, ['gambler'] = function(lvl) return '[fg]deal [yellow]2X[fg] damage to a single random enemy where X is how much gold you have' end, ['thief'] = function(lvl) return '[fg]throws a knife that deals [yellow]' .. 2*get_character_stat('thief', lvl, 'dmg') .. '[fg] damage and chains [yellow]5[fg] times' end, } @@ -725,7 +727,7 @@ function init() ['cannoneer'] = function() return '[fg]showers the hit area in [yellow]7[fg] additional cannon shots that deal [yellow]' .. get_character_stat('cannoneer', 3, 'dmg')/2 .. '[fg] AoE damage' end, ['dual_gunner'] = function() return '[fg]every 5th attack shoot in rapid succession for [yellow]2[fg] seconds' end, ['hunter'] = function() return '[fg]summons [yellow]3[fg] pets and the pets ricochet off walls once' end, - ['chronomancer'] = function() return '[fg]enemies take damave over time [yellow]50%[fg] faster' end, + ['chronomancer'] = function() return '[fg]enemies take damage 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]drops [yellow]2[fg] additional turrets and grants all turrets [yellow]+50%[fg] damage and attack speed' end, @@ -782,7 +784,7 @@ function init() ['cannoneer'] = function() return '[light_bg]showers the hit area in 7 additional cannon shots that deal ' .. get_character_stat('cannoneer', 3, 'dmg')/2 .. ' AoE damage' end, ['dual_gunner'] = function() return '[light_bg]every 5th attack shoot in rapid succession for 2 seconds' end, ['hunter'] = function() return '[light_bg]summons 3 pets and the pets ricochet off walls once' end, - ['chronomancer'] = function() return '[light_bg]enemies take damave over time 50% faster' end, + ['chronomancer'] = function() return '[light_bg]enemies take damage 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]drops 2 additional turrets and grants all turrets +50% damage and attack speed' end, @@ -901,13 +903,13 @@ function init() } local ylb1 = function(lvl) - if lvl == 3 then return 'fg' - elseif lvl == 2 then return 'fg' + if lvl == 3 then return 'light_bg' + elseif lvl == 2 then return 'light_bg' elseif lvl == 1 then return 'yellow' else return 'light_bg' end end local ylb2 = function(lvl) - if lvl == 3 then return 'fg' + if lvl == 3 then return 'light_bg' elseif lvl == 2 then return 'yellow' else return 'light_bg' end end @@ -928,7 +930,7 @@ function init() ['curser'] = function(lvl) return '[' .. ylb1(lvl) .. ']2[light_bg]/[' .. ylb2(lvl) .. ']4 [fg]- [' .. ylb1(lvl) .. ']+25%[light_bg]/[' .. ylb2(lvl) .. ']+50% [fg]curse duration' end, ['forcer'] = function(lvl) return '[' .. ylb1(lvl) .. ']2[light_bg]/[' .. ylb2(lvl) .. ']4 [fg]- [' .. ylb1(lvl) .. ']+25%[light_bg]/[' .. ylb2(lvl) .. ']+50% [fg]knockback force to all allies' end, ['swarmer'] = function(lvl) return '[' .. ylb1(lvl) .. ']2[light_bg]/[' .. ylb2(lvl) .. ']4 [fg]- [' .. ylb1(lvl) .. ']+1[light_bg]/[' .. ylb2(lvl) .. ']+3 [fg]hits to critters' end, - ['voider'] = function(lvl) return '[' .. ylb1(lvl) .. ']2[light_bg]/[' .. ylb2(lvl) .. ']4 [fg]- [' .. ylb1(lvl) .. ']+15%[light_bg]/[' .. ylb2(lvl) .. ']+25% [fg]damage over time to allied voiders' end, + ['voider'] = function(lvl) return '[' .. ylb1(lvl) .. ']2[light_bg]/[' .. ylb2(lvl) .. ']4 [fg]- [' .. ylb1(lvl) .. ']+20%[light_bg]/[' .. ylb2(lvl) .. ']+40% [fg]damage over time to allied voiders' end, ['sorcerer'] = function(lvl) return '[' .. ylb1(lvl) .. ']2[light_bg]/[' .. ylb2(lvl) .. ']4[light_bg]/[' .. ylb3(lvl) .. ']6 [fg]- sorcerers repeat their attacks once every [' .. ylb1(lvl) .. ']4[light_bg]/[' .. ylb2(lvl) .. ']3[light_bg]/[' .. ylb3(lvl) .. ']2[fg] attacks' @@ -1003,6 +1005,12 @@ function init() ['thief'] = 4, } + launches_projectiles = function(character) + local classes = {'vagrant', 'archer', 'scout', 'outlaw', 'blade', 'wizard', 'cannoneer', 'dual_gunner', 'hunter', 'spellblade', 'engineer', 'corruptor', 'beastmaster', 'jester', 'assassin', 'barrager', + 'arcanist', 'illusionist', 'miner', 'thief'} + return table.any(classes, function(v) return v == character end) + end + get_number_of_units_per_class = function(units) local rangers = 0 local warriors = 0 @@ -1116,7 +1124,7 @@ function init() ['crucio'] = 'Crucio', ['speed_3'] = 'Speed 3', ['damage_4'] = 'Damage 4', - ['level_5'] = 'Level 5', + ['shoot_5'] = 'Shoot 5', ['death_6'] = 'Death 6', ['lasting_7'] = 'Lasting 7', ['defensive_stance'] = 'Defensive Stance', @@ -1173,13 +1181,13 @@ function init() ['crucio'] = '[fg]taking damage also shares that across all enemies at [yellow]20/30/40%[fg] its value', ['speed_3'] = '[fg]position [yellow]3[fg] has [yellow]+50%[fg] attack speed', ['damage_4'] = '[fg]position [yellow]4[fg] has [yellow]+30%[fg] damage', - ['level_5'] = '[fg]position [yellow]5[fg] is [yellow]1[fg] level higher', + ['shoot_5'] = '[fg]position [yellow]5[fg] shoots [yellow]3[fg] projectiles per second', ['death_6'] = '[fg]position [yellow]6[fg] takes [yellow]10%[fg] of its health as damage every [yellow]3[fg] seconds', ['lasting_7'] = '[fg]position [yellow]7[fg] will stay alive for [yellow]10[fg] seconds after dying', ['defensive_stance'] = '[fg]first and last positions have [yellow]+10/20/30%[fg] defense', ['offensive_stance'] = '[fg]first and last positions have [yellow]+10/20/30%[fg] damage', - ['kinetic_bomb'] = '[fg]when a unit dies it explodes, launching enemies away', - ['porcupine_technique'] = '[fg]when a unit dies it explodes, releasing piercing projectiles', + ['kinetic_bomb'] = '[fg]when an ally dies it explodes, launching enemies away', + ['porcupine_technique'] = '[fg]when an ally dies it explodes, releasing piercing and ricocheting projectiles', ['last_stand'] = '[fg]the last unit alive is fully healed and receives a [yellow]+20%[fg] bonus to all stats', ['seeping'] = '[fg]enemies taking DoT damage take [yellow]+15/20/25%[fg] damage for [yellow]6[fg] seconds', ['deceleration'] = '[fg]enemies damaged by voiders have their movement speed reduced by [yellow]15/20/25%', @@ -1231,13 +1239,13 @@ function init() ['crucio'] = function(lvl) return '[fg]taking damage also shares that across all enemies at ' .. ts(lvl, '20%', '30%', '40%') .. ' its value' end, ['speed_3'] = function(lvl) return '[fg]position [yellow]3[fg] has [yellow]+50%[fg] attack speed' end, ['damage_4'] = function(lvl) return '[fg]position [yellow]4[fg] has [yellow]+30%[fg] damage' end, - ['level_5'] = function(lvl) return '[fg]position [yellow]5[fg] is [yellow]1[fg] level higher' end, + ['shoot_5'] = function(lvl) return '[fg]position [yellow]5[fg] shoots [yellow]3[fg] projectiles per second' end, ['death_6'] = function(lvl) return '[fg]position [yellow]6[fg] takes [yellow]10%[fg] of its health as damage every [yellow]3[fg] seconds' end, ['lasting_7'] = function(lvl) return '[fg]position [yellow]7[fg] will stay alive for [yellow]10[fg] seconds after dying' end, ['defensive_stance'] = function(lvl) return '[fg]first and last positions have ' .. ts(lvl, '+10%', '20%', '30%') .. ' defense' end, ['offensive_stance'] = function(lvl) return '[fg]first and last positions have ' .. ts(lvl, '+10%', '20%', '30%') .. ' damage' end, - ['kinetic_bomb'] = function(lvl) return '[fg]when a unit dies it explodes, launching enemies away' end, - ['porcupine_technique'] = function(lvl) return '[fg]when a unit dies it explodes, releasing piercing projectiles' end, + ['kinetic_bomb'] = function(lvl) return '[fg]when an ally dies it explodes, launching enemies away' end, + ['porcupine_technique'] = function(lvl) return '[fg]when an ally dies it explodes, releasing piercing projectiles' end, ['last_stand'] = function(lvl) return '[fg]the last unit alive is fully healed and receives a [yellow]+20%[fg] bonus to all stats' end, ['seeping'] = function(lvl) return '[fg]enemies taking DoT damage take ' .. ts(lvl, '+15%', '20%', '25%') .. ' damage for [yellow]6[fg] seconds' end, ['deceleration'] = function(lvl) return '[fg]enemies damaged by voiders have their movement speed reduced by ' .. ts(lvl, '15%', '20%', '25%') end, @@ -1461,7 +1469,7 @@ function init() local run = system.load_run() run_passive_pool = run.run_passive_pool or { - 'centipede', 'ouroboros_technique_r', 'ouroboros_technique_l', 'amplify', 'resonance', 'ballista', 'call_of_the_void', 'crucio', 'speed_3', 'damage_4', 'level_5', 'death_6', 'lasting_7', + 'centipede', 'ouroboros_technique_r', 'ouroboros_technique_l', 'amplify', 'resonance', 'ballista', 'call_of_the_void', 'crucio', 'speed_3', 'damage_4', 'shoot_5', 'death_6', 'lasting_7', 'defensive_stance', 'offensive_stance', 'kinetic_bomb', 'porcupine_technique', 'last_stand', 'seeping', 'deceleration', 'annihilation', 'malediction', 'pandemic', 'whispers_of_doom', 'tremor', 'heavy_impact', 'fracture', 'meat_shield', 'hive', 'baneling_burst', 'blunt_arrow', 'explosive_arrow', 'divine_machine_arrow', 'chronomancy', 'awakening', 'divine_punishment', 'assassination', 'flying_daggers', 'ultimatum', 'magnify', 'echo_barrage', 'unleash', 'reinforce', 'payback', 'enchanted', 'freezing_field', 'burning_field', 'gravity_field', 'magnetism', @@ -1490,19 +1498,15 @@ function init() --[[ main:add(Arena'arena') - main:go_to('arena', 25, { - {character = 'swordsman', level = 3}, - {character = 'scout', level = 3}, - {character = 'thief', level = 3}, - {character = 'blade', level = 3}, - {character = 'dual_gunner', level = 3}, - {character = 'saboteur', level = 3}, - {character = 'spellblade', level = 3}, - {character = 'jester', level = 3}, - {character = 'assassin', level = 3}, - {character = 'barrager', level = 3}, - {character = 'cannoneer', level = 3}, - }, passives) + main:go_to('arena', 13, { + {character = 'witch', level = 1}, + {character = 'stormweaver', level = 1}, + {character = 'chronomancer', level = 1}, + {character = 'miner', level = 1}, + {character = 'merchant', level = 1}, + {character = 'sage', level = 1}, + {character = 'magician', level = 1}, + }, {{passive = 'last_stand', level = 1}}) ]]-- --[[ diff --git a/player.lua b/player.lua index 8dce4f8..aa8dc76 100644 --- a/player.lua +++ b/player.lua @@ -805,8 +805,8 @@ function Player:init(args) if self.ouroboros_technique_r then self.t:after(0.01, function() - self.t:every(0.4 - (0.02*#self:get_all_units()), function() - if self.move_right_pressed and love.timer.getTime() - self.move_right_pressed > 1 then + self.t:every((self.ouroboros_technique_r == 1 and 0.5) or (self.ouroboros_technique_r == 2 and 0.33) or (self.ouroboros_technique_r == 3 and 0.25), function() + if self.leader and (state.mouse_control and table.all(self.mouse_control_v_buffer, function(v) return v >= 0.5 end)) or (self.move_right_pressed and love.timer.getTime() - self.move_right_pressed > 1) then local target = self:get_closest_object_in_shape(Circle(self.x, self.y, 96), main.current.enemies) if target then local units = self:get_all_units() @@ -829,20 +829,11 @@ function Player:init(args) end) end - if self.centipede then self.centipede_mvspd_m = 1.2 end - if self.amplify then self.amplify_area_dmg_m = 1.25 end - if self.amplify_x then self.amplify_x_area_dmg_m = 1.5 end + if self.centipede then self.centipede_mvspd_m = (self.centipede == 1 and 1.1) or (self.centipede == 2 and 1.2) or (self.centipede == 3 and 1.3) end + if self.amplify then self.amplify_area_dmg_m = (self.amplify == 1 and 1.2) or (self.amplify == 2 and 1.35) or (self.amplify == 3 and 1.5) end - if self.ballista then - if table.any(self.classes, function(v) return v == 'ranger' end) or table.any(self.classes, function(v) return v == 'rogue' end) then - self.ballista_dmg_m = 1.25 - end - end - - if self.ballista_x then - if table.any(self.classes, function(v) return v == 'ranger' end) or table.any(self.classes, function(v) return v == 'rogue' end) then - self.ballista_x_dmg_m = 1.5 - end + if self.ballista and launches_projectiles(self.character) then + self.ballista_dmg_m = (self.ballista == 1 and 1.2) or (self.ballista == 2 and 1.35) or (self.ballista == 3 and 1.5) end if self.chronomancy then @@ -956,6 +947,39 @@ function Player:init(args) end end) end + + if self.leader and self.shoot_5 then + main.current.t:after(0.1, function() + self.t:every(0.33, function() + local units = self:get_all_units() + local unit = units[5] + if unit then + local target = unit:get_closest_object_in_shape(Circle(unit.x, unit.y, 96), main.current.enemies) + if target then + unit:barrage(unit:angle_to_object(target), 1) + else + unit:barrage(random:float(0, 2*math.pi), 1) + end + end + end) + end) + end + + if self.leader and self.death_6 then + main.current.t:after(0.1, function() + self.t:every(3, function() + flagellant1:play{pitch = random:float(0.95, 1.05), volume = 0.4} + local units = self:get_all_units() + local unit = units[6] + if unit then + hit2:play{pitch = random:float(0.95, 1.05), volume = 0.4} + unit:hit(0.1*unit.max_hp) + end + end) + end) + end + + self.mouse_control_v_buffer = {} end @@ -1084,17 +1108,17 @@ function Player:update(dt) if self.force_push then self.knockback_m = self.knockback_m*1.25 end if table.any(self.classes, function(v) return v == 'voider' end) then - if main.current.voider_level == 2 then self.dot_dmg_m = 1.25 - elseif main.current.voider_level == 1 then self.dot_dmg_m = 1.15 + if main.current.voider_level == 2 then self.dot_dmg_m = 1.4 + elseif main.current.voider_level == 1 then self.dot_dmg_m = 1.2 else self.dot_dmg_m = 1 end end - if self.call_of_the_void then self.dot_dmg_m = (self.dot_dmg_m or 1)*1.25 end + if self.call_of_the_void then self.dot_dmg_m = (self.dot_dmg_m or 1)*((self.call_of_the_void == 1 and 1.3) or (self.call_of_the_void == 2 and 1.6) or (self.call_of_the_void == 3 and 1.9) or 1) end if self.ouroboros_technique_l and self.leader then local units = self:get_all_units() - if self.move_left_pressed and love.timer.getTime() - self.move_left_pressed > 1 then + if (state.mouse_control and table.all(self.mouse_control_v_buffer, function(v) return v <= -0.5 end)) or (self.move_left_pressed and love.timer.getTime() - self.move_left_pressed > 1) then for _, unit in ipairs(units) do - unit.ouroboros_def_m = 1.25 + unit.ouroboros_def_m = (self.ouroboros_technique_l == 1 and 1.15) or (self.ouroboros_technique_l == 2 and 1.25) or (self.ouroboros_technique_l == 3 and 1.35) end else for _, unit in ipairs(units) do @@ -1107,13 +1131,46 @@ function Player:update(dt) self.berserking_aspd_m = math.remap(self.hp/self.max_hp, 0, 1, 1.5, 1) end + if self.speed_3 and self.follower_index == 2 then + self.speed_3_aspd_m = 1.5 + end + + if self.damage_4 and self.follower_index == 3 then + self.damage_4_dmg_m = 1.3 + end + + if self.defensive_stance and self.leader then + self.defensive_stance_def_m = (self.defensive_stance == 1 and 1.1) or (self.defensive_stance == 2 and 1.2) or (self.defensive_stance == 3 and 1.3) + end + + if self.defensive_stance and not self.leader and self.follower_index == #self.parent.followers then + self.defensive_stance_def_m = (self.defensive_stance == 1 and 1.1) or (self.defensive_stance == 2 and 1.2) or (self.defensive_stance == 3 and 1.3) + end + + if self.offensive_stance and self.leader then + self.offensive_stance_dmg_m = (self.offensive_stance == 1 and 1.1) or (self.offensive_stance == 2 and 1.2) or (self.offensive_stance == 3 and 1.3) + end + + if self.offensive_stance and not self.leader and self.follower_index == #self.parent.followers then + self.offensive_stance_dmg_m = (self.offensive_stance == 1 and 1.1) or (self.offensive_stance == 2 and 1.2) or (self.offensive_stance == 3 and 1.3) + end + + if self.leader and #self.followers == 0 and self.last_stand then + self.last_stand_dmg_m = 1.2 + self.last_stand_def_m = 1.2 + self.last_stand_aspd_m = 1.2 + self.last_stand_area_size_m = 1.2 + self.last_stand_area_dmg_m = 1.2 + self.last_stand_mvspd_m = 1.2 + end + self.buff_def_a = (self.warrior_def_a or 0) - self.buff_aspd_m = (self.chronomancer_aspd_m or 1)*(self.vagrant_aspd_m or 1)*(self.outlaw_aspd_m or 1)*(self.fairy_aspd_m or 1)*(self.psyker_aspd_m or 1)*(self.chronomancy_aspd_m or 1)*(self.awakening_aspd_m or 1)*(self.berserking_aspd_m or 1)*(self.reinforce_aspd_m or 1)*(self.squire_aspd_m or 1) - self.buff_dmg_m = (self.squire_dmg_m or 1)*(self.vagrant_dmg_m or 1)*(self.enchanter_dmg_m or 1)*(self.swordsman_dmg_m or 1)*(self.flagellant_dmg_m or 1)*(self.psyker_dmg_m or 1)*(self.ballista_dmg_m or 1)*(self.ballista_x_dmg_m or 1)*(self.awakening_dmg_m or 1)*(self.reinforce_dmg_m or 1)*(self.payback_dmg_m or 1)*(self.immolation_dmg_m or 1) - self.buff_def_m = (self.squire_def_m or 1)*(self.ouroboros_def_m or 1)*(self.unwavering_stance_def_m or 1)*(self.reinforce_def_m or 1) - self.buff_area_size_m = (self.nuker_area_size_m or 1)*(self.magnify_area_size_m or 1)*(self.unleash_area_size_m or 1) - self.buff_area_dmg_m = (self.nuker_area_dmg_m or 1)*(self.amplify_area_dmg_m or 1)*(self.amplify_x_area_dmg_m or 1)*(self.unleash_area_dmg_m or 1) - self.buff_mvspd_m = (self.wall_rider_mvspd_m or 1)*(self.centipede_mvspd_m or 1)*(self.squire_mvspd_m or 1) + self.buff_aspd_m = (self.chronomancer_aspd_m or 1)*(self.vagrant_aspd_m or 1)*(self.outlaw_aspd_m or 1)*(self.fairy_aspd_m or 1)*(self.psyker_aspd_m or 1)*(self.chronomancy_aspd_m or 1)*(self.awakening_aspd_m or 1)*(self.berserking_aspd_m or 1)*(self.reinforce_aspd_m or 1)*(self.squire_aspd_m or 1)*(self.speed_3_aspd_m or 1)*(self.last_stand_aspd_m or 1) + self.buff_dmg_m = (self.squire_dmg_m or 1)*(self.vagrant_dmg_m or 1)*(self.enchanter_dmg_m or 1)*(self.swordsman_dmg_m or 1)*(self.flagellant_dmg_m or 1)*(self.psyker_dmg_m or 1)*(self.ballista_dmg_m or 1)*(self.awakening_dmg_m or 1)*(self.reinforce_dmg_m or 1)*(self.payback_dmg_m or 1)*(self.immolation_dmg_m or 1)*(self.damage_4_dmg_m or 1)*(self.offensive_stance_dmg_m or 1)*(self.last_stand_dmg_m or 1) + self.buff_def_m = (self.squire_def_m or 1)*(self.ouroboros_def_m or 1)*(self.unwavering_stance_def_m or 1)*(self.reinforce_def_m or 1)*(self.defensive_stance_def_m or 1)*(self.last_stand_def_m or 1) + self.buff_area_size_m = (self.nuker_area_size_m or 1)*(self.magnify_area_size_m or 1)*(self.unleash_area_size_m or 1)*(self.last_stand_area_size_m or 1) + self.buff_area_dmg_m = (self.nuker_area_dmg_m or 1)*(self.amplify_area_dmg_m or 1)*(self.unleash_area_dmg_m or 1)*(self.last_stand_area_dmg_m or 1) + self.buff_mvspd_m = (self.wall_rider_mvspd_m or 1)*(self.centipede_mvspd_m or 1)*(self.squire_mvspd_m or 1)*(self.last_stand_mvspd_m or 1) self:calculate_stats() if self.attack_sensor then self.attack_sensor:move_to(self.x, self.y) end @@ -1129,8 +1186,10 @@ function Player:update(dt) if input.move_right.released then self.move_right_pressed = nil end if state.mouse_control then - local v = Vector(math.cos(self.r), math.sin(self.r)):perpendicular():dot(Vector(math.cos(self:angle_to_mouse()), math.sin(self:angle_to_mouse()))) - self.r = self.r + math.sign(v)*1.66*math.pi*dt + self.mouse_control_v = Vector(math.cos(self.r), math.sin(self.r)):perpendicular():dot(Vector(math.cos(self:angle_to_mouse()), math.sin(self:angle_to_mouse()))) + self.r = self.r + math.sign(self.mouse_control_v)*1.66*math.pi*dt + table.insert(self.mouse_control_v_buffer, 1, self.mouse_control_v) + if #self.mouse_control_v_buffer > 64 then self.mouse_control_v_buffer[65] = nil end else if input.move_left.down then self.r = self.r - 1.66*math.pi*dt end if input.move_right.down then self.r = self.r + 1.66*math.pi*dt end @@ -1199,6 +1258,8 @@ function Player:draw() if self.visual_shape == 'rectangle' then if self.magician_invulnerable then graphics.rectangle(self.x, self.y, self.shape.w, self.shape.h, 3, 3, blue_transparent) + elseif self.undead then + graphics.rectangle(self.x, self.y, self.shape.w, self.shape.h, 3, 3, self.color, 1) 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) end @@ -1208,6 +1269,10 @@ function Player:draw() 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 + + if self.ouroboros_def_m and self.ouroboros_def_m > 1 then + graphics.rectangle(self.x, self.y, 1.25*self.shape.w, 1.25*self.shape.h, 3, 3, yellow_transparent) + end end graphics.pop() end @@ -1271,9 +1336,10 @@ function Player:on_collision_enter(other, contact) end -function Player:hit(damage) +function Player:hit(damage, from_undead) if self.dead then return end if self.magician_invulnerable then return end + if self.undead and not from_undead then return end self.hfx:use('hit', 0.25, 200, 10) self:show_hp() @@ -1299,7 +1365,7 @@ function Player:hit(damage) if self.crucio then local enemies = main.current.main:get_objects_by_classes(main.current.enemies) for _, enemy in ipairs(enemies) do - enemy:hit(0.25*actual_damage) + enemy:hit(((self.crucio == 1 and 0.2) or (self.crucio == 2 and 0.3) or (self.crucio == 3 and 0.4))*actual_damage) HitCircle{group = main.current.effects, x = self.x, y = self.y, rs = 6, color = fg[0], duration = 0.1} end end @@ -1338,6 +1404,10 @@ function Player:hit(damage) HitCircle{group = main.current.effects, x = self.x, y = self.y, rs = 12}:scale_down(0.3):change_color(0.5, self.color) self.divined = false + elseif self.lasting_7 and self.follower_index == 6 and not self.undead then + self.undead = true + self.t:after(10, function() self:hit(0, true) end) + else hit4:play{pitch = random:float(0.95, 1.05), volume = 0.5} slow(0.25, 1) @@ -1353,6 +1423,33 @@ function Player:hit(damage) self.dead = true if self.leader then self:recalculate_followers() else self.parent:recalculate_followers() end + if #main.current.player.followers == 0 and main.current.player.last_stand then + heal1:play{pitch = random:float(0.95, 1.05), volume = 0.5} + buff1:play{pitch == random:float(0.9, 1.1), volume = 0.5} + main.current.player:heal(10000) + end + end + + if self.kinetic_bomb then + elementor1:play{pitch = random:float(0.9, 1.1), volume = 0.5} + local enemies = self:get_objects_in_shape(Circle(self.x, self.y, 96), main.current.enemies) + for _, enemy in ipairs(enemies) do + enemy:push(random:float(30, 50)*(self.knockback_m or 1), self:angle_to_object(enemy)) + end + end + + if self.porcupine_technique then + main.current.t:after(0.01, function() + local r = 0 + for i = 1, 8 do + archer1:play{pitch = random:float(0.95, 1.05), volume = 0.35} + 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} + local t = {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), v = 250, r = r, color = self.color, dmg = self.dmg, + parent = self, character = 'barrage', level = self.level, pierce = 1000, ricochet = 2} + Projectile(table.merge(t, mods or {})) + r = r + math.pi/4 + end + end) end if self.dot_area then self.dot_area.dead = true; self.dot_area = nil end @@ -1416,6 +1513,7 @@ function Player:recalculate_followers() follower.parent = new_leader follower.follower_index = i end + else for i = #self.followers, 1, -1 do if self.followers[i].dead then @@ -1590,7 +1688,7 @@ function Player:dot_attack(area, mods) mods = mods or {} camera:shake(2, 0.5) self.hfx:use('shoot', 0.25) - local t = {group = main.current.effects, x = mods.x or self.x, y = mods.y or self.y, r = self.r, rs = self.area_size_m*(area or 64), color = self.color, dmg = self.area_dmg_m*self.dmg, + local t = {group = main.current.effects, x = mods.x or self.x, y = mods.y or self.y, r = self.r, rs = self.area_size_m*(area or 64), color = self.color, dmg = self.area_dmg_m*self.dmg*(self.dot_dmg_m or 1), character = self.character, level = self.level, parent = self} DotArea(table.merge(t, mods)) @@ -1598,14 +1696,14 @@ function Player:dot_attack(area, mods) end -function Player:barrage(r, n) +function Player:barrage(r, n, pierce) n = n or 8 for i = 1, n do self.t:after((i-1)*0.075, function() archer1:play{pitch = random:float(0.95, 1.05), volume = 0.35} 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} local t = {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), v = 250, r = r + random:float(-math.pi/16, math.pi/16), color = self.color, dmg = self.dmg, - parent = self, character = 'barrage', level = self.level} + parent = self, character = 'barrage', level = self.level, pierce = pierce or 0} Projectile(table.merge(t, mods or {})) end) end @@ -2037,26 +2135,27 @@ function Area:init(args) local enemies = main.current.main:get_objects_in_shape(self.shape, main.current.enemies) for _, enemy in ipairs(enemies) do local resonance_dmg = 0 + local resonance_m = (self.parent.resonance == 1 and 0.03) or (self.parent.resonance == 2 and 0.05) or (self.parent.resonance == 3 and 0.07) or 0 if self.character == 'elementor' then - if self.parent.resonance then resonance_dmg = 2*self.dmg*0.05*#enemies end + if self.parent.resonance then resonance_dmg = 2*self.dmg*resonance_m*#enemies end enemy:hit(2*self.dmg + resonance_dmg, self) if self.level == 3 then enemy:slow(0.4, 6) end elseif self.character == 'swordsman' then - if self.parent.resonance then resonance_dmg = (self.dmg + self.dmg*0.15*#enemies)*0.05*#enemies end + if self.parent.resonance then resonance_dmg = (self.dmg + self.dmg*0.15*#enemies)*resonance_m*#enemies end enemy:hit(self.dmg + self.dmg*0.15*#enemies + resonance_dmg, self) elseif self.character == 'blade' and self.level == 3 then - if self.parent.resonance then resonance_dmg = (self.dmg + self.dmg*0.33*#enemies)*0.05*#enemies end + if self.parent.resonance then resonance_dmg = (self.dmg + self.dmg*0.33*#enemies)*resonance_m*#enemies end enemy:hit(self.dmg + self.dmg*0.33*#enemies + resonance_dmg, self) elseif self.character == 'highlander' then - if self.parent.resonance then resonance_dmg = 6*self.dmg*0.05*#enemies end + if self.parent.resonance then resonance_dmg = 6*self.dmg*resonance_m*#enemies end enemy:hit(6*self.dmg + resonance_dmg, self) elseif self.character == 'launcher' then if self.parent.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.parent) else - if self.parent.resonance then resonance_dmg = self.dmg*0.05*#enemies end + if self.parent.resonance then resonance_dmg = self.dmg*resonance_m*#enemies end enemy:hit(self.dmg + resonance_dmg, self) end HitCircle{group = main.current.effects, x = enemy.x, y = enemy.y, rs = 6, color = fg[0], duration = 0.1} diff --git a/todo b/todo index 5959f7d..7137b88 100644 --- a/todo +++ b/todo @@ -1,33 +1,35 @@ Item Update New mechanics - Spend gold to level items up + * Spend gold to level items up + Block levelling for unlevellable items Items General: - vt Centipede - +10/20/30% movement speed - vt Ouroboros Technique R - rotating around yourself to the right releases 2/3/4 projectiles per second - vt Ouroboros Technique L - rotating around yourself to the left grants +15/25/35% defense to all units - vt Amplify - +20/35/50% AoE damage - vt Resonance - all AoE attacks deal +3/5/7% damage per unit hit - vt Ballista - +20/35/50% projectile damage - vt Call of the Void - +30/60/90% DoT damage - vt Crucio - taking damage also shares that damage across all enemies at 20/30/40% its value + * Centipede - +10/20/30% movement speed + * Ouroboros Technique R - rotating around yourself to the right releases 2/3/4 projectiles per second + * Ouroboros Technique L - rotating around yourself to the left grants +15/25/35% defense to all units + * Amplify - +20/35/50% AoE damage + * Resonance - all AoE attacks deal +3/5/7% damage per unit hit + * Ballista - +20/35/50% projectile damage + * Call of the Void - +30/60/90% DoT damage + * Crucio - taking damage also shares that damage across all enemies at 20/30/40% its value Position: - vt Speed 3 - position 3 has +20/35/50% attack speed - vt Damage 4 - position 4 has +20/30/40% damage - vt Level 5 - position 5 is 1 level higher - vt Death 6 - position 6 takes 15% of its health as damage every 3 seconds - vt Lasting 7 - position 7 will stay alive for 10 seconds after dying - vt Defense Stance - first and last positions have +10/20/30% defense - vt Offensive Stance - first and last positions have +10/20/30% damage + * Speed 3 - position 3 has +50% attack speed + * Damage 4 - position 4 has +30% damage + * Level 5 - position 5 is 1 level higher + * Death 6 - position 6 takes 15% of its health as damage every 3 seconds + * Lasting 7 - position 7 will stay alive for 10 seconds after dying + * Defense Stance - first and last positions have +10/20/30% defense + * Offensive Stance - first and last positions have +10/20/30% damage Death: - vt Kinetic Bomb - when a unit dies it explodes, launching enemies away - vt Porcupine Technique - when a unit dies it explodes, releasing piercing projectiles - vt Last Stand - the last unit alive is fully healed and receives a +20/30/40% bonus to all stats + * Kinetic Bomb - when a unit dies it explodes, launching enemies away + * Porcupine Technique - when a unit dies it explodes, releasing piercing projectiles + * Last Stand - the last unit alive is fully healed and receives a +20% bonus to all stats Voider: vt Seeping - enemies taking DoT damage take +15/20/25% damage from all sources for 6 seconds vt Deceleration - enemies damaged by voiders have their movement speed reduced by 15/20/25% vt Annihilation - when a voider dies deal its DoT damage to all enemies for 3 seconds Curser: + change one of these for +1/3/5 curse targets vt Malediction - cursed enemies take 15/25/35% more damage and deal 15/25/35% less damage vt Pandemic - when a cursed enemy dies its curse spreads vt Whispers of Doom - curses apply doom, when 4/3/2 doom instances are reached they deal 100/150/200 @@ -78,12 +80,12 @@ Item Update * Longshot * Hex Master * Force Push - * Call of the Void * Spawning Pool * Void Rift * Blessing * Immolation Balance + * Changed voider DoT damage bonus to 20/40% (from 15/25%) * Decreased shop reroll cost to 10 Decrease mercenary gold drop chance to 8/16% (from 10/20%) QoL @@ -92,6 +94,7 @@ Item Update Add visuals for defensive ouroboros, divine intervention, fairy buff Options menu from buy screen https://i.imgur.com/JJUddT3.png + https://i.imgur.com/R3YgUqk.png Improve volcano to not look like spawn marker - https://i.imgur.com/HuS15HG.png Add option for mouse cursor to always be visible Add main menu @@ -110,14 +113,16 @@ Item Update Kill enemies that go outside play area - https://i.imgur.com/QPgZbve.png https://i.imgur.com/lCdPFZx.png https://i.imgur.com/1CwmFYX.png - Ouroboros doesn't work with mouse controls + https://i.imgur.com/qrExpq5.png + https://i.imgur.com/DNHUST1.png + * Fixed ouroboros technique passives not working with mouse controls * Fixed mouse not showing up on death * Fixed NG+5 achievement not triggering (this happened only between last Sunday night and Monday morning due to a small update I pushed breaking it) * Fixed a bug where the nuker class was not counting towards Vagrant's and Psyker's active set bonus * Fixed a bug where passives would sometimes disappear from a run? * Fixed a bug where clicking "window size-" too many times could bug out the game - * Fixed a bug where the shop would be rerolled after quitting in the arena * Fixed multiple bugs related to locking the shop and quitting + * Fixed a bug where the shop would be rerolled after quitting in the arena * Fixed a bug where units could be duplicated in the shop by locking and quitting the game at certain steps * Fixed a bug where quitting on level 2 would go back to level 1 shop (again) * Fixed a bug where mouse cursor wasn't visible on death @@ -172,7 +177,9 @@ https://steamcommunity.com/app/915310/discussions/0/4658391921156325745/ - math Roguelite update: Technical improvements: Spawn tech: spawn every entity in a grid, before spawning check to see if grid position is clear, this will prevent any issues due to entities spawning inside one another - Battle stats: DPS, damage taken, etc + Battle stats: DPS, damage taken, etc (check Underlords) + Tag system: similar to PoE + Keyword explanations: similar to StS or Artifact, simply create additional text windows for keywords and what they mean Key rebinding (for non-QWERTY keyboards) StS-like map with nodes, node types: Arena @@ -193,12 +200,16 @@ Roguelite update: This unit's projectiles pierce/chain/fork/seek/split/stun/etc This unit is a [class] New stat system: - All stats are values from 1 to 10 (can be lower than 1 or higher than 10 due to debuffs/buffs only) that represent consistent internal values between all units + All stats are values from 1 to 10 (can be lower than 1 or higher than 10 due to debuffs/buffs) that represent consistent internal values between all units i.e. 3 attack speed means the same internal attack rate value (like say 6 seconds) for the entire game In general it's better if units don't have hidden internal multipliers on these stats, although sometimes that may be inevitable Damage: - Crash - damage dealt when bumping into enemies - Projectile - damage dealt by projectiles - AoE - damage dealt in an area - DoT - damage dealt over time + Damage type: + Attack - physical attacks, decreased by the enemy's armor + Spell - magical attacks, decreased by the enemy's magic resistance + Attack type: + Crash - damage dealt when bumping into enemies + Projectile - damage dealt by projectiles + AoE - damage dealt in an area + DoT - damage dealt over time