diff --git a/arena.lua b/arena.lua index fde73b6..2b74564 100644 --- a/arena.lua +++ b/arena.lua @@ -398,7 +398,7 @@ function Arena:update(dt) 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', 'shoot_5', 'death_6', 'lasting_7', - 'defensive_stance', 'offensive_stance', 'kinetic_bomb', 'porcupine_technique', 'last_stand', 'seeping', 'deceleration', 'annihilation', 'malediction', 'pandemic', 'whispers_of_doom', + 'defensive_stance', 'offensive_stance', 'kinetic_bomb', 'porcupine_technique', 'last_stand', 'seeping', 'deceleration', 'annihilation', 'malediction', 'hextouch', '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', 'insurance', 'dividends', 'berserking', 'unwavering_stance', 'unrelenting_stance' @@ -586,7 +586,7 @@ function Arena:update(dt) 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', 'shoot_5', 'death_6', 'lasting_7', - 'defensive_stance', 'offensive_stance', 'kinetic_bomb', 'porcupine_technique', 'last_stand', 'seeping', 'deceleration', 'annihilation', 'malediction', 'pandemic', 'whispers_of_doom', + 'defensive_stance', 'offensive_stance', 'kinetic_bomb', 'porcupine_technique', 'last_stand', 'seeping', 'deceleration', 'annihilation', 'malediction', 'hextouch', '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', 'insurance', 'dividends', 'berserking', 'unwavering_stance', 'unrelenting_stance' @@ -999,7 +999,7 @@ function Arena:die() 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', 'shoot_5', 'death_6', 'lasting_7', - 'defensive_stance', 'offensive_stance', 'kinetic_bomb', 'porcupine_technique', 'last_stand', 'seeping', 'deceleration', 'annihilation', 'malediction', 'pandemic', 'whispers_of_doom', + 'defensive_stance', 'offensive_stance', 'kinetic_bomb', 'porcupine_technique', 'last_stand', 'seeping', 'deceleration', 'annihilation', 'malediction', 'hextouch', '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', 'insurance', 'dividends', 'berserking', 'unwavering_stance', 'unrelenting_stance' diff --git a/assets/images/pandemic.png b/assets/images/hextouch.png similarity index 100% rename from assets/images/pandemic.png rename to assets/images/hextouch.png diff --git a/buy_screen.lua b/buy_screen.lua index 1770152..3a40652 100644 --- a/buy_screen.lua +++ b/buy_screen.lua @@ -144,7 +144,7 @@ function BuyScreen:on_enter(from, level, units, passives, shop_level, shop_xp) 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', 'shoot_5', 'death_6', 'lasting_7', - 'defensive_stance', 'offensive_stance', 'kinetic_bomb', 'porcupine_technique', 'last_stand', 'seeping', 'deceleration', 'annihilation', 'malediction', 'pandemic', 'whispers_of_doom', + 'defensive_stance', 'offensive_stance', 'kinetic_bomb', 'porcupine_technique', 'last_stand', 'seeping', 'deceleration', 'annihilation', 'malediction', 'hextouch', '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', 'insurance', 'dividends', 'berserking', 'unwavering_stance', 'unrelenting_stance' @@ -531,7 +531,7 @@ function RestartButton:update(dt) 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', 'shoot_5', 'death_6', 'lasting_7', - 'defensive_stance', 'offensive_stance', 'kinetic_bomb', 'porcupine_technique', 'last_stand', 'seeping', 'deceleration', 'annihilation', 'malediction', 'pandemic', 'whispers_of_doom', + 'defensive_stance', 'offensive_stance', 'kinetic_bomb', 'porcupine_technique', 'last_stand', 'seeping', 'deceleration', 'annihilation', 'malediction', 'hextouch', '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', 'insurance', 'dividends', 'berserking', 'unwavering_stance', 'unrelenting_stance' @@ -1361,13 +1361,14 @@ function ItemCard:init(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) + self.unlevellable = table.any(unlevellable_items, function(v) return v == self.passive end) end function ItemCard:update(dt) self:update_game_object(dt) - if self.selected and input.m1.pressed then + if self.selected and input.m1.pressed and not self.unlevellable then if self.level >= 3 then return end if gold < 5 then self.spring:pull(0.2, 200, 10) @@ -1418,14 +1419,16 @@ 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]) + if not self.unlevellable then + 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 end graphics.pop() end @@ -1437,7 +1440,7 @@ function ItemCard:create_info_text() self.info_text.dead = true end self.info_text = nil - if self.level == 3 then + if self.level == 3 or self.unlevellable 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', diff --git a/enemies.lua b/enemies.lua index cd9c19c..2be1706 100644 --- a/enemies.lua +++ b/enemies.lua @@ -259,6 +259,7 @@ function Seeker:init(args) end self.usurer_count = 0 + self.curses = {} end @@ -283,7 +284,8 @@ function Seeker:update(dt) if self.slowed then self.slow_mvspd_m = self.slowed 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.tank and 0.35 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.deceleration_mvspd_m or 1) + self.buff_def_m = (self.seeping_def_m or 1) self:calculate_stats() @@ -382,6 +384,23 @@ function Seeker:on_collision_enter(other, contact) end end + if main.current.player.tremor then + if self.being_pushed then + camera:shake(2, 0.5) + earth1:play{pitch = random:float(0.95, 1.05), volume = 0.5} + Area{group = main.current.effects, x = self.x, y = self.y, r = self.r, w = 0.75*self.push_force*(main.current.player.area_size_m or 1), color = yellow[0], dmg = self.push_force/2, parent = main.current.player} + end + end + + if main.current.player.fracture then + trigger:after(0.01, function() + earth2:play{pitch = random:float(0.95, 1.05), volume = 0.5} + for i = 1, 6 do + Projectile{group = main.current.main, x = self.x, y = self.y, color = red[0], r = (i-1)*math.pi/3, v = 200, dmg = 30, parent = main.current.player, pierce = 1} + end + end) + end + if self.headbutter and self.headbutting then self.headbutting = false end @@ -412,7 +431,7 @@ function Seeker:on_collision_enter(other, contact) end -function Seeker:hit(damage, projectile) +function Seeker:hit(damage, projectile, dot) local pyrod = self.pyrod self.pyrod = false if self.dead then return end @@ -426,6 +445,18 @@ function Seeker:hit(damage, projectile) if self.hp > self.max_hp then self.hp = self.max_hp end main.current.damage_dealt = main.current.damage_dealt + actual_damage + if dot then + self.seeping_def_m = (main.current.player.seeping == 1 and 0.85) or (main.current.player.seeping == 2 and 0.75) or (main.current.player.seeping == 3 and 0.65) or 1 + self.t:after(1, function() + self.seeping_def_m = 1 + end, 'seeping') + + self.deceleration_mvspd_m = (main.current.player.deceleration == 1 and 0.85) or (main.current.player.deceleration == 2 and 0.75) or (main.current.player.deceleration == 3 and 0.65) or 1 + self.t:after(1, function() + self.deceleration_mvspd_m = 1 + end, 'deceleration') + end + if projectile and projectile.spawn_critters_on_hit then critter1:play{pitch = random:float(0.95, 1.05), volume = 0.5} trigger:after(0.01, function() @@ -576,14 +607,20 @@ function Seeker:curse(curse, duration, arg1, arg2, arg3) if main.current.player.whispers_of_doom then if not self.doom then self.doom = 0 end self.doom = self.doom + 1 - if self.doom == 4 then + if self.doom == ((main.current.player.whispers_of_doom == 1 and 4) or (main.current.player.whispers_of_doom == 2 and 3) or (main.current.player.whispers_of_doom == 3 and 2)) then self.doom = 0 - self:hit(200) + self:hit((main.current.player.whispers_of_doom == 1 and 100) or (main.current.player.whispers_of_doom == 2 and 150) or (main.current.player.whispers_of_doom == 3 and 200)) buff1:play{pitch = random:float(0.95, 1.05), volume = 0.5} ui_switch1:play{pitch = random:float(0.95, 1.05), volume = 0.5} end end + if main.current.player.hextouch then + local p = main.current.player + local dmg = (p.hextouch == 1 and 10) or (p.hextouch == 2 and 15) or (p.hextouch == 3 and 20) + self:apply_dot(dmg*(p.dot_dmg_m or 1)*(main.current.chronomancer_dot or 1), 3) + end + buff1:play{pitch = random:float(0.65, 0.75), volume = 0.25} if curse == 'launcher' then self.t:after(duration*curse_m, function() @@ -627,7 +664,7 @@ end function Seeker:apply_dot(dmg, duration) self.t:every(0.25, function() hit2:play{pitch = random:float(0.8, 1.2), volume = 0.2} - self:hit(dmg/4) + self:hit(dmg/4, nil, true) HitCircle{group = main.current.effects, x = self.x, y = self.y, rs = 6, color = fg[0], duration = 0.1} for i = 1, 1 do HitParticle{group = main.current.effects, x = self.x, y = self.y, color = self.color} end for i = 1, 1 do HitParticle{group = main.current.effects, x = self.x, y = self.y, color = purple[0]} end @@ -887,5 +924,11 @@ function EnemyProjectile:on_trigger_enter(other, contact) if other:is(Player) then self:die(self.x, self.y, nil, random:int(2, 3)) other:hit(self.dmg) + + elseif other:is(Critter) then + if main.current.player.meat_shield then + self:die(self.x, self.y, nil, random:int(2, 3)) + other:hit(1000) + end end end diff --git a/main.lua b/main.lua index 5baeb53..96bd7bc 100644 --- a/main.lua +++ b/main.lua @@ -167,7 +167,7 @@ function init() deceleration = Image('deceleration') annihilation = Image('annihilation') malediction = Image('malediction') - pandemic = Image('pandemic') + hextouch = Image('hextouch') whispers_of_doom = Image('whispers_of_doom') tremor = Image('tremor') heavy_impact = Image('heavy_impact') @@ -1136,7 +1136,7 @@ function init() ['deceleration'] = 'Deceleration', ['annihilation'] = 'Annihilation', ['malediction'] = 'Malediction', - ['pandemic'] = 'Pandemic', + ['hextouch'] = 'Hextouch', ['whispers_of_doom'] = 'Whispers of Doom', ['tremor'] = 'Tremor', ['heavy_impact'] = 'Heavy Impact', @@ -1189,15 +1189,15 @@ function init() ['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%', + ['seeping'] = '[fg]enemies taking DoT damage have [yellow]-15/25/35%[fg] defense', + ['deceleration'] = '[fg]enemies taking DoT damage have [yellow]-15/25/35%[fg] movement speed', ['annihilation'] = '[fg]when a voider dies deal its DoT damage to all enemies for [yellow]3[fg] seconds', - ['malediction'] = '[fg]cursed enemies take [yellow]+15/25/35%[fg] damage and deal [yellow]-15/25/35%[fg] damage', - ['pandemic'] = '[fg]when a cursed enemy dies its curse spreads', + ['malediction'] = '[yellow]+1/3/5[fg] max curse targets to all allied cursers', + ['hextouch'] = '[fg]enemies take [yellow]10/15/20[fg] damage per second for [yellow]3[fg] seconds when cursed', ['whispers_of_doom'] = '[fg]curses apply doom, deal [yellow]100/150/200[fg] damage every [yellow]4/3/2[fg] doom instances', - ['tremor'] = '[fg]when enemies die from hitting walls they create an area based on the knockback force', + ['tremor'] = '[fg]when enemies hit walls they create an area based on the knockback force', ['heavy_impact'] = '[fg]when enemies hit walls they take damage based on the knockback force', - ['fracture'] = '[fg]when enemies die from hitting walls they explode into projectiles', + ['fracture'] = '[fg]when enemies hit walls they explode into projectiles', ['meat_shield'] = '[fg]critters [yellow]block[fg] enemy projectiles', ['hive'] = '[fg]critters have [yellow]+1/2/3[fg] HP', ['baneling_burst'] = '[fg]critters die immediately on contact but also deal [yellow]50/100/150[fg] AoE damage', @@ -1218,7 +1218,7 @@ function init() ['enchanted'] = '[yellow]+10/20/30%[fg] attack speed to a random unit with at least two enchanters', ['freezing_field'] = '[fg]creates an area that slows enemies by [yellow]50%[fg] for [yellow]2[fg] seconds on sorcerer spell repeat', ['burning_field'] = '[fg]creates an area that deals [yellow]30[fg] dps for [yellow]2[fg] seconds on sorcerer spell repeat', - ['gravity_field'] = '[fg]creates an area that pulls enemies in for [yellow]2[fg] seconds on sorcerer spell repeat', + ['gravity_field'] = '[fg]creates an area that pulls enemies in for [yellow]1[fg] seconds on sorcerer spell repeat', ['magnetism'] = '[fg]gold coins are attracted to the snake', ['insurance'] = "[fg]heroes have [yellow]4[fg] times the chance of mercenary's bonus to drop [yellow]2[fg] gold on death", ['dividends'] = '[fg]mercenaries deal [yellow]+X%[fg] damage, where X is how much gold you have', @@ -1247,15 +1247,15 @@ function init() ['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, + ['seeping'] = function(lvl) return '[fg]enemies taking DoT damage have ' .. ts(lvl, '-15%', '25%', '35%') .. ' defense' end, + ['deceleration'] = function(lvl) return '[fg]enemies taking DoT damage have ' .. ts(lvl, '-15%', '25%', '35%') .. ' movement speed' end, ['annihilation'] = function(lvl) return '[fg]when a voider dies deal its DoT damage to all enemies for [yellow]3[fg] seconds' end, - ['malediction'] = function(lvl) return '[fg]cursed enemies take ' .. ts(lvl, '+15%', '25%', '35%') .. ' damage and deal ' .. ts(lvl, '-15%', '25%', '35%') .. ' damage' end, - ['pandemic'] = function(lvl) return '[fg]when a cursed enemy dies its curse spreads' end, + ['malediction'] = function(lvl) return ts(lvl, '+1', '3', '5') .. ' max curse targets to all allied cursers' end, + ['hextouch'] = function(lvl) return '[fg]enemies take ' .. ts(lvl, '10', '15', '20') .. 'damage per second for [yellow]3[fg] seconds when cursed' end, ['whispers_of_doom'] = function(lvl) return '[fg]curses apply doom, deal ' .. ts(lvl, '100', '150', '200') .. ' every ' .. ts(lvl, '4', '3', '2') .. ' doom instances' end, - ['tremor'] = function(lvl) return '[fg]when enemies die from hitting walls they create an area based to the knockback force' end, + ['tremor'] = function(lvl) return '[fg]when enemies hit walls they create an area based to the knockback force' end, ['heavy_impact'] = function(lvl) return '[fg]when enemies hit walls they take damage based on the knockback force' end, - ['fracture'] = function(lvl) return '[fg]when enemies die from hitting walls they explode into projectiles' end, + ['fracture'] = function(lvl) return '[fg]when enemies hit walls they explode into projectiles' end, ['meat_shield'] = function(lvl) return '[fg]critters [yellow]block[fg] enemy projectiles' end, ['hive'] = function(lvl) return '[fg]critters have ' .. ts(lvl, '+1', '2', '3') .. ' HP' end, ['baneling_burst'] = function(lvl) return '[fg]critters die immediately on contact but also deal ' .. ts(lvl, '50', '100', '150') .. ' AoE damage' end, @@ -1276,7 +1276,7 @@ function init() ['enchanted'] = function(lvl) return ts(lvl, '+10%', '20%', '30%') .. ' attack speed to a random unit with at least two enchanters' end, ['freezing_field'] = function(lvl) return '[fg]creates an area that slows enemies by [yellow]50%[fg] for [yellow]2[fg] seconds on sorcerer spell repeat' end, ['burning_field'] = function(lvl) return '[fg]creates an area that deals [yellow]30[fg] dps for [yellow]2[fg] seconds on sorcerer spell repeat' end, - ['gravity_field'] = function(lvl) return '[fg]creates an area that pulls enemies in for [yellow]2[fg] seconds on sorcerer spell repeat' end, + ['gravity_field'] = function(lvl) return '[fg]creates an area that pulls enemies in for [yellow]1[fg] seconds on sorcerer spell repeat' end, ['magnetism'] = function(lvl) return '[fg]gold coins are attracted to the snake' end, ['insurance'] = function(lvl) return "[fg]heroes have [yellow]4[fg] times the chance of mercenary's bonus to drop [yellow]2[fg] gold on death" end, ['dividends'] = function(lvl) return '[fg]mercenaries deal [yellow]+X%[fg] damage, where X is how much gold you have' end, @@ -1467,10 +1467,16 @@ function init() end end + unlevellable_items = { + 'speed_3', 'damage_4', 'shoot_5', 'death_6', 'lasting_7', 'kinetic_bomb', 'porcupine_technique', 'last_stand', 'annihilation', + 'tremor', 'heavy_impact', 'fracture', 'meat_shield', 'divine_punishment', 'unleash', 'freezing_field', 'burning_field', 'gravity_field', + 'magnetism', 'insurance', 'dividends' + } + 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', 'shoot_5', 'death_6', 'lasting_7', - 'defensive_stance', 'offensive_stance', 'kinetic_bomb', 'porcupine_technique', 'last_stand', 'seeping', 'deceleration', 'annihilation', 'malediction', 'pandemic', 'whispers_of_doom', + 'defensive_stance', 'offensive_stance', 'kinetic_bomb', 'porcupine_technique', 'last_stand', 'seeping', 'deceleration', 'annihilation', 'malediction', 'hextouch', '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', 'insurance', 'dividends', 'berserking', 'unwavering_stance', 'unrelenting_stance' @@ -1498,15 +1504,12 @@ function init() --[[ main:add(Arena'arena') - main:go_to('arena', 13, { - {character = 'witch', level = 1}, - {character = 'stormweaver', level = 1}, - {character = 'chronomancer', level = 1}, - {character = 'miner', level = 1}, + main:go_to('arena', 3, { {character = 'merchant', level = 1}, - {character = 'sage', level = 1}, - {character = 'magician', level = 1}, - }, {{passive = 'last_stand', level = 1}}) + {character = 'miner', level = 1}, + {character = 'thief', level = 1}, + {character = 'gambler', level = 1}, + }, passives) ]]-- --[[ diff --git a/player.lua b/player.lua index aa8dc76..258f531 100644 --- a/player.lua +++ b/player.lua @@ -64,6 +64,7 @@ function Player:init(args) if main.current.sorcerer_level > 0 then self.sorcerer_count = self.sorcerer_count + 1 if self.sorcerer_count >= ((main.current.sorcerer_level == 3 and 2) or (main.current.sorcerer_level == 2 and 3) or (main.current.sorcerer_level == 1 and 4)) then + self:sorcerer_repeat() self.sorcerer_count = 0 self.t:after(0.25, function() local enemy = table.shuffle(main.current.main:get_objects_by_classes(main.current.enemies))[1] @@ -161,6 +162,7 @@ function Player:init(args) if main.current.sorcerer_level > 0 then self.sorcerer_count = self.sorcerer_count + 1 if self.sorcerer_count >= ((main.current.sorcerer_level == 3 and 2) or (main.current.sorcerer_level == 2 and 3) or (main.current.sorcerer_level == 1 and 4)) then + self:sorcerer_repeat() self.sorcerer_count = 0 self.t:after(0.25, function() self:shoot(self:angle_to_object(closest_enemy), {pierce = 1000, v = 40}) @@ -180,6 +182,7 @@ function Player:init(args) if main.current.sorcerer_level > 0 then self.sorcerer_count = self.sorcerer_count + 1 if self.sorcerer_count >= ((main.current.sorcerer_level == 3 and 2) or (main.current.sorcerer_level == 2 and 3) or (main.current.sorcerer_level == 1 and 4)) then + self:sorcerer_repeat() self.sorcerer_count = 0 self.t:after(0.25, function() self:shoot(self:angle_to_object(closest_enemy)) @@ -199,6 +202,7 @@ function Player:init(args) if main.current.sorcerer_level > 0 then self.sorcerer_count = self.sorcerer_count + 1 if self.sorcerer_count >= ((main.current.sorcerer_level == 3 and 2) or (main.current.sorcerer_level == 2 and 3) or (main.current.sorcerer_level == 1 and 4)) then + self:sorcerer_repeat() self.sorcerer_count = 0 self.t:after(0.25, function() SpawnEffect{group = main.current.effects, x = self.x, y = self.y, color = self.color, action = function(x, y) @@ -262,6 +266,7 @@ function Player:init(args) self.sorcerer_count = self.sorcerer_count + 1 if self.sorcerer_count >= ((main.current.sorcerer_level == 3 and 2) or (main.current.sorcerer_level == 2 and 3) or (main.current.sorcerer_level == 1 and 4)) then self.sorcerer_count = 0 + self:sorcerer_repeat() self.t:after(0.1, function() strike() end) @@ -337,6 +342,7 @@ function Player:init(args) self.sorcerer_count = self.sorcerer_count + 1 if self.sorcerer_count >= ((main.current.sorcerer_level == 3 and 2) or (main.current.sorcerer_level == 2 and 3) or (main.current.sorcerer_level == 1 and 4)) then self.sorcerer_count = 0 + self:sorcerer_repeat() self.t:after(0.5, function() volcano() end) @@ -421,6 +427,7 @@ function Player:init(args) self.sorcerer_count = self.sorcerer_count + 1 if self.sorcerer_count >= ((main.current.sorcerer_level == 3 and 2) or (main.current.sorcerer_level == 2 and 3) or (main.current.sorcerer_level == 1 and 4)) then self.sorcerer_count = 0 + self:sorcerer_repeat() self.t:after(0.25, function() self:dot_attack(42, {duration = random:float(12, 16)}) end) @@ -496,7 +503,7 @@ function Player:init(args) self.wide_attack_sensor = Circle(self.x, self.y, 128) self.t:cooldown(6, function() local enemies = self:get_objects_in_shape(self.attack_sensor, main.current.enemies); return enemies and #enemies > 0 end, function() buff1:play{pitch = random:float(0.9, 1.1), volume = 0.5} - local enemies = table.first(table.shuffle(self:get_objects_in_shape(self.wide_attack_sensor, main.current.enemies)), 6) + local enemies = table.first2(table.shuffle(self:get_objects_in_shape(self.wide_attack_sensor, main.current.enemies)), 6 + ((self.malediction == 1 and 1) or (self.malediction == 2 and 3) or (self.malediction == 3 and 5) or 0)) for _, enemy in ipairs(enemies) do if self:distance_to_object(enemy) < 128 then enemy:curse('jester', 6*(self.hex_duration_m or 1), self.level == 3, self) @@ -511,7 +518,7 @@ function Player:init(args) self.wide_attack_sensor = Circle(self.x, self.y, 128) self.t:cooldown(6, function() local enemies = self:get_objects_in_shape(self.attack_sensor, main.current.enemies); return enemies and #enemies > 0 end, function() buff1:play{pitch = random:float(0.9, 1.1), volume = 0.5} - local enemies = table.first2(table.shuffle(self:get_objects_in_shape(self.wide_attack_sensor, main.current.enemies)), 3) + local enemies = table.first2(table.shuffle(self:get_objects_in_shape(self.wide_attack_sensor, main.current.enemies)), 3 + ((self.malediction == 1 and 1) or (self.malediction == 2 and 3) or (self.malediction == 3 and 5) or 0)) for _, enemy in ipairs(enemies) do enemy:curse('usurer', 10000, self.level == 3, self) enemy:apply_dot(self.dmg*(self.dot_dmg_m or 1)*(main.current.chronomancer_dot or 1), 10000) @@ -527,7 +534,7 @@ function Player:init(args) self.t:cooldown(6, function() local enemies = self:get_objects_in_shape(self.attack_sensor, main.current.enemies); return enemies and #enemies > 0 end, function() local curse = function() buff1:play{pitch = random:float(0.9, 1.1), volume = 0.5} - local enemies = table.first(table.shuffle(self:get_objects_in_shape(self.wide_attack_sensor, main.current.enemies)), 6) + local enemies = table.first2(table.shuffle(self:get_objects_in_shape(self.wide_attack_sensor, main.current.enemies)), 6 + ((self.malediction == 1 and 1) or (self.malediction == 2 and 3) or (self.malediction == 3 and 5) or 0)) for _, enemy in ipairs(enemies) do enemy:curse('silencer', 6*(self.hex_duration_m or 1), self.level == 3, self) if self.level == 3 then @@ -546,6 +553,7 @@ function Player:init(args) self.sorcerer_count = self.sorcerer_count + 1 if self.sorcerer_count >= ((main.current.sorcerer_level == 3 and 2) or (main.current.sorcerer_level == 2 and 3) or (main.current.sorcerer_level == 1 and 4)) then self.sorcerer_count = 0 + self:sorcerer_repeat() self.t:after(0.5, function() curse() end) @@ -587,7 +595,7 @@ function Player:init(args) self.wide_attack_sensor = Circle(self.x, self.y, 128) self.t:cooldown(6, function() local enemies = self:get_objects_in_shape(self.attack_sensor, main.current.enemies); return enemies and #enemies > 0 end, function() buff1:play{pitch = random:float(0.9, 1.1), volume = 0.5} - local enemies = table.first(table.shuffle(self:get_objects_in_shape(self.wide_attack_sensor, main.current.enemies)), 6) + local enemies = table.first2(table.shuffle(self:get_objects_in_shape(self.wide_attack_sensor, main.current.enemies)), 6 + ((self.malediction == 1 and 1) or (self.malediction == 2 and 3) or (self.malediction == 3 and 5) or 0)) for _, enemy in ipairs(enemies) do enemy:curse('bane', 6*(self.hex_duration_m or 1), self.level == 3, self) HitCircle{group = main.current.effects, x = self.x, y = self.y, rs = 6, color = purple[0], duration = 0.1} @@ -719,6 +727,7 @@ function Player:init(args) self.sorcerer_count = self.sorcerer_count + 1 if self.sorcerer_count >= ((main.current.sorcerer_level == 3 and 2) or (main.current.sorcerer_level == 2 and 3) or (main.current.sorcerer_level == 1 and 4)) then self.sorcerer_count = 0 + self:sorcerer_repeat() self.t:after(0.5, function() ward() end) @@ -750,7 +759,7 @@ function Player:init(args) self.wide_attack_sensor = Circle(self.x, self.y, 128) self.t:cooldown(6, function() local enemies = self:get_objects_in_shape(self.attack_sensor, main.current.enemies); return enemies and #enemies > 0 end, function() buff1:play{pitch = random:float(0.9, 1.1), volume = 0.5} - local enemies = table.first(table.shuffle(self:get_objects_in_shape(self.wide_attack_sensor, main.current.enemies)), 8) + local enemies = table.first2(table.shuffle(self:get_objects_in_shape(self.wide_attack_sensor, main.current.enemies)), 8 + ((self.malediction == 1 and 1) or (self.malediction == 2 and 3) or (self.malediction == 3 and 5) or 0)) for _, enemy in ipairs(enemies) do enemy:curse('infestor', 6*(self.hex_duration_m or 1), (self.level == 3 and 6 or 2), self.dmg, self) HitCircle{group = main.current.effects, x = self.x, y = self.y, rs = 6, color = orange[0], duration = 0.1} @@ -837,8 +846,8 @@ function Player:init(args) end if self.chronomancy then - if table.any(self.classes, function(v) return v == 'mage' end) or table.any(self.classes, function(v) return v == 'sorcerer' end) then - self.chronomancy_aspd_m = 1.25 + if table.any(self.classes, function(v) return v == 'mage' end) then + self.chronomancy_aspd_m = (self.chronomancy == 1 and 1.15) or (self.chronomancy == 2 and 1.25) or (self.chronomancy == 3 and 1.35) end end @@ -847,14 +856,14 @@ function Player:init(args) local units = self:get_all_units() local mages = {} for _, unit in ipairs(units) do - if table.any(unit.classes, function(v) return v == 'mage' end) or table.any(unit.classes, function(v) return v == 'sorcerer' end) then + if table.any(unit.classes, function(v) return v == 'mage' end) then table.insert(mages, unit) end end local mage = random:table(mages) if mage then - mage.awakening_aspd_m = 2 - mage.awakening_dmg_m = 2 + mage.awakening_aspd_m = (self.awakening == 1 and 1.5) or (self.awakening == 2 and 1.75) or (self.awakening == 3 and 2) + mage.awakening_dmg_m = (self.awakening == 1 and 1.5) or (self.awakening == 2 and 1.75) or (self.awakening == 3 and 2) end end) end @@ -864,14 +873,14 @@ function Player:init(args) local units = self:get_all_units() local mages = {} for _, unit in ipairs(units) do - if table.any(unit.classes, function(v) return v == 'mage' end) or table.any(unit.classes, function(v) return v == 'sorcerer' end) then + if table.any(unit.classes, function(v) return v == 'mage' end) then table.insert(mages, unit) end end local leader = main.current.player:get_leader() local enemies = main.current.main:get_objects_by_classes(main.current.enemies) if #enemies > 0 then - thunder1:play{volume = 0.4} + thunder1:play{volume = 0.3} camera:shake(4, 0.5) end for _, enemy in ipairs(enemies) do @@ -885,15 +894,15 @@ function Player:init(args) if self.unwavering_stance and table.any(self.classes, function(v) return v == 'warrior' end) then self.unwavering_stance_def_m = 1 self.t:every(5, function() - self.unwavering_stance_def_m = self.unwavering_stance_def_m + 0.05 + self.unwavering_stance_def_m = self.unwavering_stance_def_m + ((self.unwavering_stance == 1 and 0.04) or (self.unwavering_stance == 2 and 0.08) or (self.unwavering_stance == 3 and 0.12)) end) end if self.magnify then - self.magnify_area_size_m = 1.25 + self.magnify_area_size_m = (self.magnify == 1 and 1.2) or (self.magnify == 2 and 1.35) or (self.magnify == 3 and 1.5) end - if self.unleash then + if self.unleash and table.any(self.classes, function(v) return v == 'nuker' end) then self.unleash_area_dmg_m = 1 self.unleash_area_size_m = 1 self.t:every(1, function() @@ -916,13 +925,32 @@ function Player:init(args) end end if any_enchanter then - self.reinforce_dmg_m = 1.1 - self.reinforce_def_m = 1.1 - self.reinforce_aspd_m = 1.1 + local v = (self.reinforce == 1 and 1.1) or (self.reinforce == 2 and 1.2) or (self.reinforce == 3 and 1.3) or 1 + self.reinforce_dmg_m = v + self.reinforce_def_m = v + self.reinforce_aspd_m = v end end) end + if self.enchanted then + main.current.t:after(0.1, function() + local units = self:get_all_units() + local enchanter_amount = 0 + for _, unit in ipairs(units) do + if table.any(unit.classes, function(v) return v == 'enchanter' end) then + enchanter_amount = enchanter_amount + 1 + end + end + + if enchanter_amount >= 2 then + local unit = random:table(units) + unit.enchanted_aspd_m = (self.enchanted == 1 and 1.1) or (self.enchanted == 2 and 1.2) or (self.enchanted == 3 and 1.3) + end + + end) + end + if self.payback then self.payback_dmg_m = 1 end @@ -931,6 +959,10 @@ function Player:init(args) self.hex_duration_m = 1.25 end + if self.unrelenting_stance then + self.unrelenting_stance_def_m = 1 + end + if self.leader and self.immolation then main.current.t:after(0.1, function() local units = self:get_all_units() @@ -1128,7 +1160,7 @@ function Player:update(dt) end if self.berserking and table.any(self.classes, function(v) return v == 'warrior' end) then - self.berserking_aspd_m = math.remap(self.hp/self.max_hp, 0, 1, 1.5, 1) + self.berserking_aspd_m = math.remap(self.hp/self.max_hp, 0, 1, (self.berserking == 1 and 1.5) or (self.berserking == 2 and 1.75) or (self.berserking == 3 and 2), 1) end if self.speed_3 and self.follower_index == 2 then @@ -1164,10 +1196,14 @@ function Player:update(dt) self.last_stand_mvspd_m = 1.2 end + if self.dividends and table.any(self.classes, function(v) return v == 'mercenary' end) then + self.dividends_dmg_m = (1 + gold/100) + 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.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_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.enchanted_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.dividends_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.unrelenting_stance_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) @@ -1350,7 +1386,19 @@ function Player:hit(damage, from_undead) main.current.damage_taken = main.current.damage_taken + actual_damage if self.payback and table.any(self.classes, function(v) return v == 'enchanter' end) then - self.payback_dmg_m = self.payback_dmg_m + 0.05 + local units = self:get_all_units() + for _, unit in ipairs(units) do + if not unit.payback_dmg_m then unit.payback_dmg_m = 1 end + unit.payback_dmg_m = unit.payback_dmg_m + ((self.payback == 1 and 0.02) or (self.payback == 2 and 0.05) or (self.payback == 3 and 0.08) or 0) + end + end + + if self.unrelenting_stance and table.any(self.classes, function(v) return v == 'warrior' end) then + local units = self:get_all_units() + for _, unit in ipairs(units) do + if not unit.unrelenting_stance_def_m then unit.unrelenting_stance_def_m = 1 end + unit.unrelenting_stance_def_m = unit.unrelenting_stance_def_m + ((self.unrelenting_stance == 1 and 0.02) or (self.unrelenting_stance == 2 and 0.05) or (self.unrelenting_stance == 3 and 0.08) or 0) + end end if self.character == 'beastmaster' and self.level == 3 then @@ -1452,12 +1500,57 @@ function Player:hit(damage, from_undead) end) end + if self.annihilation and table.any(self.classes, function(v) return v == 'voider' end) then + local enemies = self.group:get_objects_by_classes({Seeker, EnemyCritter}) + for _, enemy in ipairs(enemies) do + enemy:apply_dot(self.dmg*(self.dot_dmg_m or 1)*(main.current.chronomancer_dot or 1), 3) + end + end + + if self.insurance then + if random:bool(4*((main.current.mercenary_level == 2 and 20) or (main.current.mercenary_level == 1 and 10) or 0)) then + main.current.t:after(0.01, function() + Gold{group = main.current.main, x = self.x, y = self.y} + Gold{group = main.current.main, x = self.x, y = self.y} + end) + end + end + if self.dot_area then self.dot_area.dead = true; self.dot_area = nil end end end end +function Player:sorcerer_repeat() + local enemies = self.group:get_objects_by_classes(main.current.enemies) + if not enemies then return end + local enemy = random:table(enemies) + if enemy then + if self.gravity_field then + ForceArea{group = main.current.effects, x = enemy.x, y = enemy.y, rs = self.area_size_m*24, color = fg[0], character = 'gravity_field', parent = self} + end + end + + local enemy = random:table(enemies) + if enemy then + if self.burning_field then + fire1:play{pitch = random:float(0.9, 1.1), volume = 0.5} + DotArea{group = main.current.effects, x = enemy.x, y = enemy.y, rs = self.area_size_m*24, color = red[0], dmg = 30*self.area_dmg_m*(self.dot_dmg_m or 1), duration = 2, character = 'burning_field'} + end + end + + local enemy = random:table(enemies) + if enemy then + if self.freezing_field then + frost1:play{pitch = random:float(0.8, 1.2), volume = 0.3} + elementor1:play{pitch = random:float(0.9, 1.1), volume = 0.3} + Area{group = main.current.effects, x = enemy.x, y = enemy.y, w = self.area_size_m*36, color = fg[0], character = 'freezing_field', parent = self} + end + end +end + + function Player:heal(amount) local hp = self.hp @@ -1542,7 +1635,7 @@ function Player:shoot(r, mods) local dmg_m = 1 local crit = false - if self.chance_to_crit and random:bool(self.chance_to_crit) then dmg_m = (self.assassination and 8 or 4); crit = true end + if self.chance_to_crit and random:bool(self.chance_to_crit) then dmg_m = ((self.assassination == 1 and 8) or (self.assassination == 2 and 10) or (self.assassination == 3 and 12) or 4); crit = true end if self.assassination and table.any(self.classes, function(v) return v == 'rogue' end) then if not crit then dmg_m = 0.5 @@ -1805,9 +1898,9 @@ function Projectile:init(args) end if self.parent.divine_machine_arrow and table.any(self.parent.classes, function(v) return v == 'ranger' end) then - if random:bool(40) then + if random:bool((self.parent.divine_machine_arrow == 1 and 10) or (self.parent.divine_machine_arrow == 2 and 20) or (self.parent.divine_machine_arrow == 3 and 30)) then self.homing = true - self.pierce = 4 + self.pierce = self.parent.divine_machine_arrow or 0 end end @@ -1823,13 +1916,13 @@ function Projectile:init(args) self.distance_dmg_m = 1 if self.parent.blunt_arrow and table.any(self.parent.classes, function(v) return v == 'ranger' end) then - if random:bool(20) then + if random:bool((self.parent.blunt_arrow == 1 and 10) or (self.parent.blunt_arrow == 2 and 20) or (self.parent.blunt_arrow == 3 and 30)) then self.knockback = 10 end end if self.parent.flying_daggers and table.any(self.parent.classes, function(v) return v == 'rogue' end) then - self.chain = self.chain + 2 + self.chain = self.chain + ((self.parent.flying_daggers == 1 and 2) or (self.parent.flying_daggers == 2 and 3) or (self.parent.flying_daggers == 3 and 4)) end end @@ -2022,7 +2115,7 @@ function Projectile:on_trigger_enter(other, contact) self.dmg = self.dmg*1.25 end if self.parent.ultimatum then - self.dmg = self.dmg*1.25 + self.dmg = self.dmg*((self.parent.ultimatum == 1 and 1.1) or (self.parent.ultimatum == 2 and 1.2) or (self.parent.ultimatum == 3 and 1.3)) end end end @@ -2109,10 +2202,11 @@ function Projectile:on_trigger_enter(other, contact) end if self.parent.explosive_arrow and table.any(self.parent.classes, function(v) return v == 'ranger' end) then - if random:bool(30) then + if random:bool((self.parent.explosive_arrow == 1 and 10) or (self.parent.explosive_arrow == 2 and 20) or (self.parent.explosive_arrow == 3 and 30)) then _G[random:table{'cannoneer1', 'cannoneer2'}]:play{pitch = random:float(0.95, 1.05), volume = 0.5} Area{group = main.current.effects, x = self.x, y = self.y, r = self.r + random:float(0, 2*math.pi), w = self.parent.area_size_m*32, color = self.color, - dmg = 0.2*self.parent.area_dmg_m*self.dmg, character = self.character, level = self.level, parent = self, void_rift = self.parent.void_rift, echo_barrage = self.parent.echo_barrage} + dmg = ((self.parent.explosive_arrow == 1 and 0.1) or (self.parent.explosive_arrow == 2 and 0.2) or (self.parent.explosive_arrow == 3 and 0.3))*self.parent.area_dmg_m*self.dmg, character = self.character, + level = self.level, parent = self, void_rift = self.parent.void_rift, echo_barrage = self.parent.echo_barrage} end end @@ -2154,6 +2248,8 @@ function Area:init(args) 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) + elseif self.character == 'freezing_field' then + enemy:slow(0.5, 2) else if self.parent.resonance then resonance_dmg = self.dmg*resonance_m*#enemies end enemy:hit(self.dmg + resonance_dmg, self) @@ -2197,12 +2293,12 @@ function Area:init(args) end end if p.echo_barrage and not self.echo_barrage_area then - if random:bool(20) then + if random:bool((p.echo_barrage == 1 and 10) or (p.echo_barrage == 2 and 20) or (p.echo_barrage == 3 and 30)) then p.t:every(0.3, function() _G[random:table{'cannoneer1', 'cannoneer2'}]:play{pitch = random:float(0.95, 1.05), volume = 0.5} Area{group = main.current.effects, x = self.x + random:float(-32, 32), y = self.y + random:float(-32, 32), r = self.r + random:float(0, 2*math.pi), w = p.area_size_m*48, color = p.color, dmg = 0.5*p.area_dmg_m*self.dmg, character = self.character, level = p.level, parent = p, echo_barrage_area = true} - end, 3) + end, p.echo_barrage) end end else @@ -2213,12 +2309,12 @@ function Area:init(args) end end if self.parent.echo_barrage and not self.echo_barrage_area then - if random:bool(20) then + if random:bool((self.parent.echo_barrage == 1 and 10) or (self.parent.echo_barrage == 2 and 20) or (self.parent.echo_barrage == 3 and 30)) then self.parent.t:every(0.3, function() _G[random:table{'cannoneer1', 'cannoneer2'}]:play{pitch = random:float(0.95, 1.05), volume = 0.5} Area{group = main.current.effects, x = self.x + random:float(-32, 32), y = self.y + random:float(-32, 32), r = self.r + random:float(0, 2*math.pi), w = self.parent.area_size_m*48, color = self.parent.color, dmg = 0.5*self.parent.area_dmg_m*self.dmg, character = self.character, level = self.parent.level, parent = self.parent, echo_barrage_area = true} - end, 3) + end, self.parent.echo_barrage) end end end @@ -2267,7 +2363,7 @@ function DotArea:init(args) self.shape = Circle(self.x, self.y, self.rs) self.closest_sensor = Circle(self.x, self.y, 128) - if self.character == 'plague_doctor' or self.character == 'pyromancer' or self.character == 'witch' then + if self.character == 'plague_doctor' or self.character == 'pyromancer' or self.character == 'witch' or self.character == 'burning_field' then self.t:every(0.2, function() local enemies = main.current.main:get_objects_in_shape(self.shape, main.current.enemies) if #enemies > 0 then self.spring:pull(0.05, 200, 10) end @@ -2279,7 +2375,7 @@ function DotArea:init(args) enemy.pyrod = self end end - enemy:hit((self.dot_dmg_m or 1)*self.dmg/5, self) + enemy:hit((self.dot_dmg_m or 1)*self.dmg/5, self, true) HitCircle{group = main.current.effects, x = enemy.x, y = enemy.y, rs = 6, color = fg[0], duration = 0.1} for i = 1, 1 do HitParticle{group = main.current.effects, x = enemy.x, y = enemy.y, color = self.color} end for i = 1, 1 do HitParticle{group = main.current.effects, x = enemy.x, y = enemy.y, color = enemy.color} end @@ -2297,7 +2393,7 @@ function DotArea:init(args) if self.level == 3 then enemy:slow(0.4, 4) end - enemy:hit((self.dot_dmg_m or 1)*2*self.dmg, self) + enemy:hit((self.dot_dmg_m or 1)*2*self.dmg, self, true) HitCircle{group = main.current.effects, x = enemy.x, y = enemy.y, rs = 6, color = fg[0], duration = 0.1} for i = 1, 1 do HitParticle{group = main.current.effects, x = enemy.x, y = enemy.y, color = self.color} end for i = 1, 1 do HitParticle{group = main.current.effects, x = enemy.x, y = enemy.y, color = enemy.color} end @@ -2333,7 +2429,7 @@ function DotArea:init(args) if #enemies > 0 then self.spring:pull(0.05, 200, 10) end for _, enemy in ipairs(enemies) do hit2:play{pitch = random:float(0.8, 1.2), volume = 0.2} - enemy:hit((self.dot_dmg_m or 1)*self.dmg/5, self) + enemy:hit((self.dot_dmg_m or 1)*self.dmg/5, self, true) HitCircle{group = main.current.effects, x = enemy.x, y = enemy.y, rs = 6, color = fg[0], duration = 0.1} for i = 1, 1 do HitParticle{group = main.current.effects, x = enemy.x, y = enemy.y, color = self.color} end for i = 1, 1 do HitParticle{group = main.current.effects, x = enemy.x, y = enemy.y, color = enemy.color} end @@ -2463,6 +2559,21 @@ function ForceArea:init(args) end end end) + + elseif self.character == 'gravity_field' then + elementor1:play{pitch = random:float(0.9, 1.1), volume = 0.4} + self.t:tween(1, self, {dvr = 0}, math.linear) + + self.t:during(1, function() + local enemies = main.current.main:get_objects_in_shape(self.shape, main.current.enemies) + local t = self.t:get_during_elapsed_time('gravity_field') + for _, enemy in ipairs(enemies) do + enemy:apply_steering_force(400*(1-t), enemy:angle_to_point(self.x, self.y)) + end + end, nil, 'gravity_field') + self.t:after(1 - 0.35, function() + self.t:every_immediate(0.05, function() self.hidden = not self.hidden end, 7, function() self.dead = true end) + end) end end @@ -2992,13 +3103,32 @@ function Gold:init(args) self:set_angular_damping(5) self.color = yellow2[0] self.hfx:add('hit', 1) + self.cant_be_picked_up = true + self.t:after(0.5, function() self.cant_be_picked_up = false end) gold1:play{pitch = random:float(0.95, 1.05), volume = 0.5} + self.magnet_sensor = Circle(self.x, self.y, 32) end function Gold:update(dt) self:update_game_object(dt) self.r = self:get_angle() + + if main.current.player.magnetism then + local players = self:get_objects_in_shape(self.magnet_sensor, {Player}) + if players and #players > 0 then + local x, y = 0, 0 + for _, p in ipairs(players) do + x = x + p.x + y = y + p.y + end + x = x/#players + y = y/#players + local r = self:angle_to_point(x, y) + self:apply_force(20*math.cos(r), 20*math.sin(r)) + end + end + self.magnet_sensor:move_to(self.x, self.y) end @@ -3010,6 +3140,8 @@ function Gold:draw() end function Gold:on_trigger_enter(other, contact) + if self.cant_be_picked_up then return end + if other:is(Player) then main.current.gold_picked_up = main.current.gold_picked_up + 1 self.dead = true @@ -3077,7 +3209,7 @@ function Critter:init(args) self.t:after(0.5, function() self.invulnerable = false end) self.dmg = args.dmg or self.parent.dmg - self.hp = 1 + ((main.current.swarmer_level == 2 and 3) or (main.current.swarmer_level == 1 and 1) or 0) + (self.parent.spawning_pool and 1 or 0) + (self.parent.hive and 2 or 0) + self.hp = 1 + ((main.current.swarmer_level == 2 and 3) or (main.current.swarmer_level == 1 and 1) or 0) + (self.parent.hive or 0) end @@ -3125,7 +3257,11 @@ function Critter:hit(damage) self.hfx:use('hit', 0.25, 200, 10) self.hp = self.hp - 1 -- self:show_hp() - if self.hp <= 0 then self:die() end + if main.current.player.baneling_burst then + self:die() + else + if self.hp <= 0 then self:die() end + end end @@ -3150,6 +3286,12 @@ function Critter:die(x, y, r, n) self.dead = true _G[random:table{'enemy_die1', 'enemy_die2'}]:play{pitch = random:float(0.9, 1.1), volume = 0.5} critter2:play{pitch = random:float(0.95, 1.05), volume = 0.2} + + if main.current.player.baneling_burst then + camera:shake(2, 0.5) + Area{group = main.current.effects, x = self.x, y = self.y, r = self.r, w = self.parent.area_size_m*24, color = self.color, dmg = self.parent.dmg*self.parent.area_dmg_m*0.25, parent = self.parent} + _G[random:table{'cannoneer1', 'cannoneer2'}]:play{pitch = random:float(0.95, 1.05), volume = 0.5} + end end diff --git a/todo b/todo index 7137b88..4708c52 100644 --- a/todo +++ b/todo @@ -25,54 +25,53 @@ Item Update * 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 + * Seeping - enemies taking DoT damage take +15/20/25% damage from all sources for 6 seconds + * Deceleration - enemies damaged by voiders have their movement speed reduced by 15/20/25% + * 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 + * Malediction - +1/3/5 max curse targets to all allied cursers + * Hextouch - enemies take 10/15/20 damage per second for 3 seconds when cursed + * Whispers of Doom - curses apply doom, when 4/3/2 doom instances are reached they deal 100/150/200 Forcer: - vt Tremor - when enemies die from hitting walls they create a damaging area with size according to the knockback force - vt Heavy Impact - when enemies hit walls they take damage according to the knockback force - vt Fracture - when enemies die from hitting walls explode into projectiles + * Tremor - when enemies die from hitting walls they create a damaging area with size according to the knockback force + * Heavy Impact - when enemies hit walls they take damage according to the knockback force + * Fracture - when enemies die from hitting walls explode into projectiles Swarmer: - vt Meat Shield - critters block enemy projectiles - vt Hive - critters have +1/2/3 HP - vt Baneling Burst - critters die immediately on contact but also deal AoE damage + * Meat Shield - critters block enemy projectiles + * Hive - critters have +1/2/3 HP + * Baneling Burst - critters die immediately on contact but also deal AoE damage Ranger: - vt Blunt Arrow - arrows fired by rangers have a 10/20/30% chance to knockback - vt Explosive Arrow - arrows fired by rangers have a 10/20/30% chance to explode, dealing 10/20/30% AoE damage - vt Divine Machine Arrow - arrows fired by rangers have a 10/20/30% chance to seek enemies and pierce 1/2/3 times + * Blunt Arrow - arrows fired by rangers have a 10/20/30% chance to knockback + * Explosive Arrow - arrows fired by rangers have a 10/20/30% chance to explode, dealing 10/20/30% AoE damage + * Divine Machine Arrow - arrows fired by rangers have a 10/20/30% chance to seek enemies and pierce 1/2/3 times Mage: - vt Chronomancy - mages cast their spells 15/25/35% faster - vt Awakening - +50/75/100% attack speed and damage to 1 mage every round for that round - vt Divine Punishment - repeatedly deal damage to all enemies based on how many mages you have + * Chronomancy - mages cast their spells 15/25/35% faster + * Awakening - +50/75/100% attack speed and damage to 1 mage every round for that round + * Divine Punishment - repeatedly deal damage to all enemies based on how many mages you have Rogue: - vt Assassination - crits from rogues deal 8/10/12x damage but normal attacks deal half damage - vt Flying Daggers - all projectiles thrown by rogues chain +2/3/4 times - vt Ultimatum - projectiles that chain gain +10/20/30% damage with each chain + * Assassination - crits from rogues deal 8/10/12x damage but normal attacks deal half damage + * Flying Daggers - all projectiles thrown by rogues chain +2/3/4 times + * Ultimatum - projectiles that chain gain +10/20/30% damage with each chain Nuker: - vt Magnify - +20/35/50% area size - vt Echo Barrage - 10/20/30% chance to create 1/2/3 secondary AoEs on AoE hit - vt Unleash - all nukers gain +1% area size and damage every second + * Magnify - +20/35/50% area size + * Echo Barrage - 10/20/30% chance to create 1/2/3 secondary AoEs on AoE hit + * Unleash - all nukers gain +1% area size and damage every second Enchanter: - vt Reinforce - +10/20/30% damage, defense and attack speed to all allies with at leats one enchanter - vt Payback - +2/5/8% damage to all allies whenever an enchanter is hit - vt Enchanted - when enemies die they have a 10/20/30% release X homing projectiles, where X is how many enchanters you have + * Reinforce - +10/20/30% damage, defense and attack speed to all allies with at leats one enchanter + * Payback - +2/5/8% damage to all allies whenever an enchanter is hit + * Enchanted - when enemies die they have a 10/20/30% release X homing projectiles, where X is how many enchanters you have Sorcerer: - vt Freezing Field - Create an area that slows enemies by 50% for 2 seconds whenever a sorcerer repeats a spell - vt Burning Field - Create an area that deals 30 damage per second for 2 seconds whenever a sorcerer repeats a spell - vt Gravity Field - Create an area that sucks enemies in for 2 seconds whenever a sorcerer repeats a spell + * Freezing Field - Create an area that slows enemies by 50% for 2 seconds whenever a sorcerer repeats a spell + * Burning Field - Create an area that deals 30 damage per second for 2 seconds whenever a sorcerer repeats a spell + * Gravity Field - Create an area that sucks enemies in for 2 seconds whenever a sorcerer repeats a spell Mercenary: - vt Magnetism - Gold coins are attracted to the snake - vt Insurance - Dying heroes have 4 times the chance of mercenary's bonus to drop 2 gold on death - vt Dividends - Mercenaries deal +X% extra damage, where X is how much gold you have + * Magnetism - Gold coins are attracted to the snake + * Insurance - Dying heroes have 4 times the chance of mercenary's bonus to drop 2 gold on death + * Dividends - Mercenaries deal +X% extra damage, where X is how much gold you have Warrior: - vt Berserking - all warriors have up to +50/75/100% attack speed based on missing HP - vt Unwavering Stance - all warriors gain +4/8/12% defense every 5 seconds - vt Unrelenting Stance - +2/5/8% defense to all allies whenever a warrior is hit + * Berserking - all warriors have up to +50/75/100% attack speed based on missing HP + * Unwavering Stance - all warriors gain +4/8/12% defense every 5 seconds + * Unrelenting Stance - +2/5/8% defense to all allies whenever a warrior is hit Removed items * Wall Echo * Wall Rider @@ -115,13 +114,13 @@ Item Update https://i.imgur.com/1CwmFYX.png https://i.imgur.com/qrExpq5.png https://i.imgur.com/DNHUST1.png + Fixed multiple bugs related to locking the shop and quitting * 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 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)