Sorcerer update 1/4

master
a327ex 2021-06-02 02:43:03 -03:00
parent aa598914ac
commit cea71538b2
10 changed files with 394 additions and 61 deletions

View File

@ -250,6 +250,7 @@ function Arena:on_enter(from, level, units, passives)
self.healer_level = class_levels.healer self.healer_level = class_levels.healer
self.psyker_level = class_levels.psyker self.psyker_level = class_levels.psyker
self.conjurer_level = class_levels.conjurer self.conjurer_level = class_levels.conjurer
self.sorcerer_level = class_levels.sorcerer
self.t:every(0.375, function() self.t:every(0.375, function()
local p = random:table(star_positions) local p = random:table(star_positions)
@ -732,6 +733,13 @@ function Arena:quit()
steam.userStats.storeStats() steam.userStats.storeStats()
end end
if self.sorcerer_level >= 3 then
state.achievement_sorcerers_win = true
system.save_state()
steam.userStats.setAchievement('SORCERERS_WIN')
steam.userStats.storeStats()
end
local units = self.player:get_all_units() local units = self.player:get_all_units()
local all_units_level_2 = true local all_units_level_2 = true
for _, unit in ipairs(units) do for _, unit in ipairs(units) do

Binary file not shown.

After

Width:  |  Height:  |  Size: 336 B

Binary file not shown.

Binary file not shown.

View File

@ -180,7 +180,8 @@ function Seeker:init(args)
elseif self.headbutter then elseif self.headbutter then
self.color = orange[0]:clone() self.color = orange[0]:clone()
self.last_headbutt_time = 0 self.last_headbutt_time = 0
self.t:every(function() return math.distance(self.x, self.y, main.current.player.x, main.current.player.y) < 64 and love.timer.getTime() - self.last_headbutt_time > 10 end, function() local n = math.remap(current_new_game_plus, 0, 5, 1, 0.5)
self.t:every(function() return math.distance(self.x, self.y, main.current.player.x, main.current.player.y) < 64 and love.timer.getTime() - self.last_headbutt_time > 10*n end, function()
if self.headbutt_charging or self.headbutting then return end if self.headbutt_charging or self.headbutting then return end
self.headbutt_charging = true self.headbutt_charging = true
self.t:tween(2, self.color, {r = fg[0].r, b = fg[0].b, g = fg[0].g}, math.cubic_in_out, function() self.t:tween(2, self.color, {r = fg[0].r, b = fg[0].b, g = fg[0].g}, math.cubic_in_out, function()
@ -191,19 +192,20 @@ function Seeker:init(args)
self.last_headbutt_time = love.timer.getTime() self.last_headbutt_time = love.timer.getTime()
self:set_damping(0) self:set_damping(0)
self:apply_steering_impulse(300, self:angle_to_object(main.current.player), 0.75) self:apply_steering_impulse(300, self:angle_to_object(main.current.player), 0.75)
self.t:after(0.75, function() self.t:after(0.5, function()
self.headbutting = false self.headbutting = false
end) end)
end) end)
end) end)
elseif self.tank then elseif self.tank then
self.color = yellow[0]:clone() self.color = yellow[0]:clone()
self.buff_hp_m = 1.25 + (0.025*self.level) self.buff_hp_m = 1.25 + (0.025*self.level) + (0.2*current_new_game_plus)
self:calculate_stats() self:calculate_stats()
self.hp = self.max_hp self.hp = self.max_hp
elseif self.shooter then elseif self.shooter then
self.color = fg[0]:clone() self.color = fg[0]:clone()
self.t:after({2, 4}, function() local n = math.remap(current_new_game_plus, 0, 5, 1, 0.5)
self.t:after({2*n, 4*n}, function()
self.shooting = true self.shooting = true
self.t:every({3, 5}, function() self.t:every({3, 5}, function()
for i = 1, 3 do for i = 1, 3 do
@ -434,7 +436,7 @@ function Seeker:hit(damage, projectile)
trigger:after(0.01, function() trigger:after(0.01, function()
local n = 8 + current_new_game_plus*2 local n = 8 + current_new_game_plus*2
for i = 1, n do for i = 1, n do
EnemyProjectile{group = main.current.main, x = self.x, y = self.y, color = blue[0], r = (i-1)*math.pi/(n/2), v = 150 + 5*self.level, dmg = 2*self.dmg} EnemyProjectile{group = main.current.main, x = self.x, y = self.y, color = blue[0], r = (i-1)*math.pi/(n/2), v = 120 + 5*self.level, dmg = 2*self.dmg}
end end
end) end)
end end
@ -442,8 +444,8 @@ function Seeker:hit(damage, projectile)
if self.spawner then if self.spawner then
critter1:play{pitch = random:float(0.95, 1.05), volume = 0.35} critter1:play{pitch = random:float(0.95, 1.05), volume = 0.35}
trigger:after(0.01, function() trigger:after(0.01, function()
for i = 1, random:int(3, 6) do for i = 1, random:int(5, 8) do
EnemyCritter{group = main.current.main, x = self.x, y = self.y, color = purple[0], r = random:float(0, 2*math.pi), v = 5 + 0.1*self.level, dmg = self.dmg, projectile = projectile} EnemyCritter{group = main.current.main, x = self.x, y = self.y, color = purple[0], r = random:float(0, 2*math.pi), v = 10 + 0.1*self.level, dmg = 2*self.dmg, projectile = projectile}
end end
end) end)
end end

132
main.lua
View File

@ -18,11 +18,13 @@ function init()
input:bind('enter', {'space', 'return', 'fleft', 'fdown', 'fright'}) input:bind('enter', {'space', 'return', 'fleft', 'fdown', 'fright'})
local s = {tags = {sfx}} local s = {tags = {sfx}}
illusion1 = Sound('Buff 5.ogg', s)
thunder1 = Sound('399656__bajko__sfx-thunder-blast.ogg', s) thunder1 = Sound('399656__bajko__sfx-thunder-blast.ogg', s)
flagellant1 = Sound('Whipping Horse 3.ogg', s) flagellant1 = Sound('Whipping Horse 3.ogg', s)
bard2 = Sound('376532__womb-affliction__flute-trill.ogg', s) bard2 = Sound('376532__womb-affliction__flute-trill.ogg', s)
bard1 = Sound('Magical Impact 12.ogg', s) bard1 = Sound('Magical Impact 12.ogg', s)
frost1 = Sound('Frost Bolt 20.ogg', s) frost1 = Sound('Frost Bolt 20.ogg', s)
arcane1 = Sound('Magical Impact 26.ogg', s)
pyro1 = Sound('Fire bolt 5.ogg', s) pyro1 = Sound('Fire bolt 5.ogg', s)
pyro2 = Sound('Explosion Fireworks_01.ogg', s) pyro2 = Sound('Explosion Fireworks_01.ogg', s)
dot1 = Sound('Magical Swoosh 18.ogg', s) dot1 = Sound('Magical Swoosh 18.ogg', s)
@ -125,6 +127,7 @@ function init()
forcer = Image('forcer') forcer = Image('forcer')
swarmer = Image('swarmer') swarmer = Image('swarmer')
voider = Image('voider') voider = Image('voider')
sorcerer = Image('sorcerer')
ouroboros_technique_r = Image('ouroboros_technique_r') ouroboros_technique_r = Image('ouroboros_technique_r')
ouroboros_technique_l = Image('ouroboros_technique_l') ouroboros_technique_l = Image('ouroboros_technique_l')
wall_echo = Image('wall_echo') wall_echo = Image('wall_echo')
@ -184,6 +187,7 @@ function init()
['forcer'] = yellow[0], ['forcer'] = yellow[0],
['swarmer'] = orange[0], ['swarmer'] = orange[0],
['voider'] = purple[0], ['voider'] = purple[0],
['sorcerer'] = blue2[0],
} }
class_color_strings = { class_color_strings = {
@ -200,6 +204,7 @@ function init()
['forcer'] = 'yellow', ['forcer'] = 'yellow',
['swarmer'] = 'orange', ['swarmer'] = 'orange',
['voider'] = 'purple', ['voider'] = 'purple',
['sorcerer'] = 'blue2',
} }
character_names = { character_names = {
@ -244,6 +249,13 @@ function init()
['priest'] = 'Priest', ['priest'] = 'Priest',
['infestor'] = 'Infestor', ['infestor'] = 'Infestor',
['flagellant'] = 'Flagellant', ['flagellant'] = 'Flagellant',
['arcanist'] = 'Arcanist',
['illusionist'] = 'Illusionist',
['witch'] = 'Witch',
['silencer'] = 'Silencer',
['vulcanist'] = 'Vulcanist',
['warden'] = 'Warden',
['psychic'] = 'Psychic',
} }
character_colors = { character_colors = {
@ -288,6 +300,13 @@ function init()
['priest'] = green[0], ['priest'] = green[0],
['infestor'] = orange[0], ['infestor'] = orange[0],
['flagellant'] = fg[0], ['flagellant'] = fg[0],
['arcanist'] = blue2[0],
['illusionist'] = blue2[0],
['witch'] = purple[0],
['silencer'] = blue2[0],
['vulcanist'] = red[0],
['warden'] = yellow[0],
['psychic'] = fg[0],
} }
character_color_strings = { character_color_strings = {
@ -332,6 +351,13 @@ function init()
['priest'] = 'green', ['priest'] = 'green',
['infestor'] = 'orange', ['infestor'] = 'orange',
['flagellant'] = 'fg', ['flagellant'] = 'fg',
['arcanist'] = 'blue2',
['illusionist'] = 'blue2',
['witch'] = 'purple',
['silencer'] = 'blue2',
['vulcanist'] = 'red',
['warden'] = 'yellow',
['psychic'] = 'fg',
} }
character_classes = { character_classes = {
@ -376,6 +402,13 @@ function init()
['priest'] = {'healer'}, ['priest'] = {'healer'},
['infestor'] = {'curser', 'swarmer'}, ['infestor'] = {'curser', 'swarmer'},
['flagellant'] = {'psyker', 'enchanter'}, ['flagellant'] = {'psyker', 'enchanter'},
['arcanist'] = {'sorcerer'},
['illusionist'] = {'sorcerer', 'conjurer'},
['witch'] = {'sorcerer', 'voider'},
['silencer'] = {'sorcerer', 'curser'},
['vulcanist'] = {'sorcerer', 'nuker'},
['warden'] = {'sorcerer', 'forcer'},
['psychic'] = {'sorcerer', 'psyker'},
} }
character_class_strings = { character_class_strings = {
@ -420,6 +453,13 @@ function init()
['priest'] = '[green]Healer', ['priest'] = '[green]Healer',
['infestor'] = '[purple]Curser, [orange]Swarmer', ['infestor'] = '[purple]Curser, [orange]Swarmer',
['flagellant'] = '[fg]Psyker, [blue]Enchanter', ['flagellant'] = '[fg]Psyker, [blue]Enchanter',
['arcanist'] = '[blue2]Sorcerer',
['illusionist'] = '[blue2]Sorcerer, [orange]Conjurer',
['witch'] = '[blue2]Sorcerer, [purple]Voider',
['silencer'] = '[blue2]Sorcerer, [purple]Curser',
['vulcanist'] = '[blue2]Sorcerer, [red]Nuker',
['warden'] = '[blue2]Sorcerer, [yellow]Forcer',
['psychic'] = '[blue2]Sorcerer, [fg]Psyker',
} }
get_character_stat_string = function(character, level) get_character_stat_string = function(character, level)
@ -468,7 +508,7 @@ function init()
['corruptor'] = function(lvl) return '[fg]spawn [yellow]3[fg] small critters if the corruptor kills an enemy' end, ['corruptor'] = function(lvl) return '[fg]spawn [yellow]3[fg] small critters if the corruptor kills an enemy' end,
['beastmaster'] = function(lvl) return '[fg]spawn [yellow]2[fg] small critters if the beastmaster crits' end, ['beastmaster'] = function(lvl) return '[fg]spawn [yellow]2[fg] small critters if the beastmaster crits' end,
['launcher'] = function(lvl) return '[fg]all nearby enemies are pushed after [yellow]4[fg] seconds, taking [yellow]' .. 2*get_character_stat('launcher', lvl, 'dmg') .. '[fg] damage on wall hit' end, ['launcher'] = function(lvl) return '[fg]all nearby enemies are pushed after [yellow]4[fg] seconds, taking [yellow]' .. 2*get_character_stat('launcher', lvl, 'dmg') .. '[fg] damage on wall hit' end,
['jester'] = function(lvl) return "[fg]curses [yellow]5[fg] nearby enemies for [yellow]6[fg] seconds, they will explode into [yellow]3[fg] knives on death" end, ['jester'] = function(lvl) return "[fg]curses [yellow]4[fg] nearby enemies for [yellow]6[fg] seconds, they will explode into [yellow]3[fg] knives on death" end,
['assassin'] = function(lvl) return '[fg]throws a piercing knife that deals [yellow]' .. get_character_stat('assassin', lvl, 'dmg') .. '[fg] damage + [yellow]' .. ['assassin'] = function(lvl) return '[fg]throws a piercing knife that deals [yellow]' .. get_character_stat('assassin', lvl, 'dmg') .. '[fg] damage + [yellow]' ..
get_character_stat('assassin', lvl, 'dmg')/2 .. '[fg] damage per second' end, get_character_stat('assassin', lvl, 'dmg')/2 .. '[fg] damage per second' end,
['host'] = function(lvl) return '[fg]periodically spawn [yellow]1[fg] small critter' end, ['host'] = function(lvl) return '[fg]periodically spawn [yellow]1[fg] small critter' end,
@ -481,6 +521,13 @@ function init()
['priest'] = function(lvl) return '[fg]heals all allies for [yellow]20%[fg] their max HP' end, ['priest'] = function(lvl) return '[fg]heals all allies for [yellow]20%[fg] their max HP' end,
['infestor'] = function(lvl) return '[fg]curses all nearby enemies for [yellow]6[fg] seconds, they will release [yellow]2[fg] critters on death' end, ['infestor'] = function(lvl) return '[fg]curses all nearby enemies for [yellow]6[fg] seconds, they will release [yellow]2[fg] critters on death' end,
['flagellant'] = function(lvl) return '[fg]deals [yellow]' .. 2*get_character_stat('flagellant', lvl, 'dmg') .. '[fg] damage to self and grants [yellow]+4%[fg] damage to all allies per cast' end, ['flagellant'] = function(lvl) return '[fg]deals [yellow]' .. 2*get_character_stat('flagellant', lvl, 'dmg') .. '[fg] damage to self and grants [yellow]+4%[fg] damage to all allies per cast' end,
['arcanist'] = function(lvl) return '[fg]launches a slow moving orb that launches projectiles, each dealing [yellow]' .. get_character_stat('arcanist', lvl, 'dmg') .. '[fg] damage' end,
['illusionist'] = function(lvl) return '[fg]launches a projectile that deals [yellow]' .. get_character_stat('illusionist', lvl, 'dmg') .. '[fg] damage and creates copies that do the same' end,
['witch'] = function(lvl) return '[fg]creates an area that ricochets around the arena and deals [yellow]' .. get_character_stat('witch', lvl, 'dmg') .. '[fg] damage per second' end,
['silencer'] = function(lvl) return '[fg]curses [yellow]5[fg] nearby enemies for [yellow]6[fg] seconds, preventing them from using special attacks' end,
['vulcanist'] = function(lvl) return '[fg]creates a volcano that explodes the nearby area [yellow]5[fg] times, dealing [yellow]' .. get_character_stat('vulcanist', lvl, 'dmg') .. 'AoE [fg]damage' end,
['warden'] = function(lvl) return '[fg]creates a force field around a random unit that prevents enemies from entering' end,
['psychic'] = function(lvl) return '[fg]creates a small area that deals [yellow]' .. get_character_stat('psychic', lvl, 'dmg') .. '[fg] damage' end,
} }
character_effect_names = { character_effect_names = {
@ -525,6 +572,13 @@ function init()
['priest'] = '[green]Divine Intervention', ['priest'] = '[green]Divine Intervention',
['infestor'] = '[orange]Infestation', ['infestor'] = '[orange]Infestation',
['flagellant'] = '[red]Zealotry', ['flagellant'] = '[red]Zealotry',
['arcanist'] = '[blue2]Arcane Orb',
['illusionist'] = '[blue2]Mirror Image',
['witch'] = '[purple]Death Pool',
['silencer'] = '[blue2]Arcane Curse',
['vulcanist'] = '[red]Lava Burst',
['warden'] = '[yellow]Magnetic Field',
['psychic'] = '[fg]Mental Strike'
} }
character_effect_names_gray = { character_effect_names_gray = {
@ -569,6 +623,13 @@ function init()
['priest'] = '[light_bg]Divine Intervention', ['priest'] = '[light_bg]Divine Intervention',
['infestor'] = '[light_bg]Infestation', ['infestor'] = '[light_bg]Infestation',
['flagellant'] = '[light_bg]Zealotry', ['flagellant'] = '[light_bg]Zealotry',
['arcanist'] = '[light_bg]Arcane Orb',
['illusionist'] = '[light_bg]Mirror Image',
['witch'] = '[light_bg]Death Pool',
['silencer'] = '[light_bg]Arcane Curse',
['vulcanist'] = '[light_bg]Lava Burst',
['warden'] = '[light_bg]Magnetic Field',
['psychic'] = '[light_bg]Mental Strike'
} }
character_effect_descriptions = { character_effect_descriptions = {
@ -613,6 +674,13 @@ function init()
['priest'] = function() return '[fg]picks [yellow]3[fg] units at random and grants them a buff that prevents death once' end, ['priest'] = function() return '[fg]picks [yellow]3[fg] units at random and grants them a buff that prevents death once' end,
['infestor'] = function() return '[fg][yellow]triples[fg] the number of critters released' end, ['infestor'] = function() return '[fg][yellow]triples[fg] the number of critters released' end,
['flagellant'] = function() return '[fg]deals [yellow]' .. 2*get_character_stat('flagellant', 3, 'dmg') .. '[fg] damage to all allies and grants [yellow]+12%[fg] damage to all allies per cast' end, ['flagellant'] = function() return '[fg]deals [yellow]' .. 2*get_character_stat('flagellant', 3, 'dmg') .. '[fg] damage to all allies and grants [yellow]+12%[fg] damage to all allies per cast' end,
['arcanist'] = function() return '[yellow]100%[fg] increased attack speed for the orb and [yellow]2[fg] projectiles are released per cast' end,
['illusionist'] = function() return '[yellow]doubles[fg] the number of copies created and they release [yellow]12[fg] projectiles on death that pierce and ricochet once' end,
['witch'] = function() return '[fg]the area periodically releases projectiles, each dealing [yellow]' .. get_character_stat('witch', 3, 'dmg') .. '[fg] damage and chaining once' end,
['silencer'] = function() return '[fg]the curse also deals [yellow]' .. get_character_stat('silencer', 3, 'dmg') .. '[fg] damage per second' end,
['vulcanist'] = function() return '[fg]the volcano spawn also deals [yellow]' .. get_character_stat('vulcanist', 3, 'dmg') .. 'AoE [fg] damage and [yellow]doubles[fg] the number of explosions' end,
['warden'] = function() return '[fg]creates the force field around [yellow]2[fg] additional random units' end,
['psychic'] = function() return '[fg]the attack can happen from any distance and deals [yellow]double[fg] damage' end,
} }
character_effect_descriptions_gray = { character_effect_descriptions_gray = {
@ -657,6 +725,13 @@ function init()
['priest'] = function() return '[light_bg]picks 3 units at random and grants them a buff that prevents death once' end, ['priest'] = function() return '[light_bg]picks 3 units at random and grants them a buff that prevents death once' end,
['infestor'] = function() return '[light_bg]triples the number of critters released' end, ['infestor'] = function() return '[light_bg]triples the number of critters released' end,
['flagellant'] = function() return '[light_bg]deals ' .. 2*get_character_stat('flagellant', 3, 'dmg') .. ' damage to all allies and grants +12% damage to all allies per cast' end, ['flagellant'] = function() return '[light_bg]deals ' .. 2*get_character_stat('flagellant', 3, 'dmg') .. ' damage to all allies and grants +12% damage to all allies per cast' end,
['arcanist'] = function() return '[light_bg]100% increased attack speed for the orb and 2 projectiles are released per cast' end,
['illusionist'] = function() return '[light_bg]doubles the number of copies created and they release 12 projectiles on death' end,
['witch'] = function() return '[light_bg]the area periodically releases projectiles, each dealing ' .. get_character_stat('witch', 3, 'dmg') .. ' damage and chaining once' end,
['silencer'] = function() return '[light_bg]the curse also deals ' .. get_character_stat('silencer', 3, 'dmg') .. ' damage per second' end,
['vulcanist'] = function() return '[light_bg]the volcano spawn also deals ' .. get_character_stat('vulcanist', 3, 'dmg') .. 'AoE damage and doubles the number of explosions' end,
['warden'] = function() return '[light_bg]creates the force field around 2 additional random units' end,
['psychic'] = function() return '[light_bg]the attack can happen from any distance and deals double damage' end,
} }
character_stats = { character_stats = {
@ -701,6 +776,13 @@ function init()
['priest'] = function(lvl) return get_character_stat_string('priest', lvl) end, ['priest'] = function(lvl) return get_character_stat_string('priest', lvl) end,
['infestor'] = function(lvl) return get_character_stat_string('infestor', lvl) end, ['infestor'] = function(lvl) return get_character_stat_string('infestor', lvl) end,
['flagellant'] = function(lvl) return get_character_stat_string('flagellant', lvl) end, ['flagellant'] = function(lvl) return get_character_stat_string('flagellant', lvl) end,
['arcanist'] = function(lvl) return get_character_stat_string('arcanist', lvl) end,
['illusionist'] = function(lvl) return get_character_stat_string('illusionist', lvl) end,
['witch'] = function(lvl) return get_character_stat_string('witch', lvl) end,
['silencer'] = function(lvl) return get_character_stat_string('silencer', lvl) end,
['vulcanist'] = function(lvl) return get_character_stat_string('vulcanist', lvl) end,
['warden'] = function(lvl) return get_character_stat_string('warden', lvl) end,
['psychic'] = function(lvl) return get_character_stat_string('psychic', lvl) end,
} }
class_stat_multipliers = { class_stat_multipliers = {
@ -717,14 +799,28 @@ function init()
['forcer'] = {hp = 1.25, dmg = 1.1, aspd = 0.9, area_dmg = 0.75, area_size = 0.75, def = 1.2, mvspd = 1}, ['forcer'] = {hp = 1.25, dmg = 1.1, aspd = 0.9, area_dmg = 0.75, area_size = 0.75, def = 1.2, mvspd = 1},
['swarmer'] = {hp = 1.2, dmg = 1, aspd = 1.25, area_dmg = 1, area_size = 1, def = 0.75, mvspd = 0.5}, ['swarmer'] = {hp = 1.2, dmg = 1, aspd = 1.25, area_dmg = 1, area_size = 1, def = 0.75, mvspd = 0.5},
['voider'] = {hp = 0.75, dmg = 1.3, aspd = 1, area_dmg = 0.8, area_size = 0.75, def = 0.6, mvspd = 0.8}, ['voider'] = {hp = 0.75, dmg = 1.3, aspd = 1, area_dmg = 0.8, area_size = 0.75, def = 0.6, mvspd = 0.8},
['sorcerer'] = {hp = 0.8, dmg = 1.3, aspd = 1, area_dmg = 1.2, area_size = 1, def = 0.8, mvspd = 1},
['seeker'] = {hp = 0.5, dmg = 1, aspd = 1, area_dmg = 1, area_size = 1, def = 1, mvspd = 0.3}, ['seeker'] = {hp = 0.5, dmg = 1, aspd = 1, area_dmg = 1, area_size = 1, def = 1, mvspd = 0.3},
['mini_boss'] = {hp = 1, dmg = 1, aspd = 1, area_dmg = 1, area_size = 1, def = 1, mvspd = 0.3}, ['mini_boss'] = {hp = 1, dmg = 1, aspd = 1, area_dmg = 1, area_size = 1, def = 1, mvspd = 0.3},
['enemy_critter'] = {hp = 1, dmg = 1, aspd = 1, area_dmg = 1, area_size = 1, def = 1, mvspd = 0.5}, ['enemy_critter'] = {hp = 1, dmg = 1, aspd = 1, area_dmg = 1, area_size = 1, def = 1, mvspd = 0.5},
['saboteur'] = {hp = 1, dmg = 1, aspd = 1, area_dmg = 1, area_size = 1, def = 1, mvspd = 1.4}, ['saboteur'] = {hp = 1, dmg = 1, aspd = 1, area_dmg = 1, area_size = 1, def = 1, mvspd = 1.4},
} }
local ylb1 = function(lvl) return lvl >= 2 and 'fg' or (lvl >= 1 and 'yellow' or 'light_bg') end local ylb1 = function(lvl)
local ylb2 = function(lvl) return (lvl >= 2 and 'yellow' or 'light_bg') end if lvl == 3 then return 'fg'
elseif lvl == 2 then return 'fg'
elseif lvl == 1 then return 'yellow'
else return 'light_bg' end
end
local ylb2 = function(lvl)
if lvl == 3 then return 'fg'
elseif lvl == 2 then return 'yellow'
else return 'light_bg' end
end
local ylb3 = function(lvl)
if lvl == 3 then return 'yellow'
else return 'light_bg' end
end
class_descriptions = { class_descriptions = {
['ranger'] = function(lvl) return '[' .. ylb1(lvl) .. ']3[' .. ylb2(lvl) .. ']/6 [fg]- [' .. ylb1(lvl) .. ']8%[' .. ylb2(lvl) .. ']/16% [fg]chance to release a barrage on attack to allied rangers' end, ['ranger'] = function(lvl) return '[' .. ylb1(lvl) .. ']3[' .. ylb2(lvl) .. ']/6 [fg]- [' .. ylb1(lvl) .. ']8%[' .. ylb2(lvl) .. ']/16% [fg]chance to release a barrage on attack to allied rangers' end,
['warrior'] = function(lvl) return '[' .. ylb1(lvl) .. ']3[' .. ylb2(lvl) .. ']/6 [fg]- [' .. ylb1(lvl) .. ']+25[' .. ylb2(lvl) .. ']/+50 [fg]defense to allied warriors' end, ['warrior'] = function(lvl) return '[' .. ylb1(lvl) .. ']3[' .. ylb2(lvl) .. ']/6 [fg]- [' .. ylb1(lvl) .. ']+25[' .. ylb2(lvl) .. ']/+50 [fg]defense to allied warriors' end,
@ -739,6 +835,9 @@ function init()
['forcer'] = function(lvl) return '[' .. ylb1(lvl) .. ']2[' .. ylb2(lvl) .. ']/4 [fg]- [' .. ylb1(lvl) .. ']+25%[' .. ylb2(lvl) .. ']/+50% [fg]knockback force to all allies' end, ['forcer'] = function(lvl) return '[' .. ylb1(lvl) .. ']2[' .. ylb2(lvl) .. ']/4 [fg]- [' .. ylb1(lvl) .. ']+25%[' .. ylb2(lvl) .. ']/+50% [fg]knockback force to all allies' end,
['swarmer'] = function(lvl) return '[' .. ylb1(lvl) .. ']2[' .. ylb2(lvl) .. ']/4 [fg]- [' .. ylb1(lvl) .. ']+1[' .. ylb2(lvl) .. ']/+3 [fg]hits to critters' end, ['swarmer'] = function(lvl) return '[' .. ylb1(lvl) .. ']2[' .. ylb2(lvl) .. ']/4 [fg]- [' .. ylb1(lvl) .. ']+1[' .. ylb2(lvl) .. ']/+3 [fg]hits to critters' end,
['voider'] = function(lvl) return '[' .. ylb1(lvl) .. ']2[' .. ylb2(lvl) .. ']/4 [fg]- [' .. ylb1(lvl) .. ']+15%[' .. ylb2(lvl) .. ']/+25% [fg]damage over time to allied voiders' end, ['voider'] = function(lvl) return '[' .. ylb1(lvl) .. ']2[' .. ylb2(lvl) .. ']/4 [fg]- [' .. ylb1(lvl) .. ']+15%[' .. ylb2(lvl) .. ']/+25% [fg]damage over time to allied voiders' end,
['sorcerer'] = function(lvl)
return '[' .. ybl1(lvl) .. ']2[' .. ylb2(lvl) .. ']/4[' .. ylb3(lvl) .. '/6 [fg]- sorcerers repeat their attacks once every [' .. ylb1(lvl) .. ']4/[' .. ylb2(lvl) .. ']3/[' .. ylb3(lvl) .. ']2[fg] attacks'
end,
} }
tier_to_characters = { tier_to_characters = {
@ -809,6 +908,7 @@ function init()
local forcers = 0 local forcers = 0
local swarmers = 0 local swarmers = 0
local voiders = 0 local voiders = 0
local sorcerers = 0
for _, unit in ipairs(units) do for _, unit in ipairs(units) do
for _, unit_class in ipairs(character_classes[unit.character]) do for _, unit_class in ipairs(character_classes[unit.character]) do
if unit_class == 'ranger' then rangers = rangers + 1 end if unit_class == 'ranger' then rangers = rangers + 1 end
@ -824,10 +924,11 @@ function init()
if unit_class == 'forcer' then forcers = forcers + 1 end if unit_class == 'forcer' then forcers = forcers + 1 end
if unit_class == 'swarmer' then swarmers = swarmers + 1 end if unit_class == 'swarmer' then swarmers = swarmers + 1 end
if unit_class == 'voider' then voiders = voiders + 1 end if unit_class == 'voider' then voiders = voiders + 1 end
if unit_class == 'sorcerer' then sorcerers = sorcerers + 1 end
end end
end end
return {ranger = rangers, warrior = warriors, healer = healers, mage = mages, nuker = nukers, conjurer = conjurers, rogue = rogues, return {ranger = rangers, warrior = warriors, healer = healers, mage = mages, nuker = nukers, conjurer = conjurers, rogue = rogues,
enchanter = enchanters, psyker = psykers, curser = cursers, forcer = forcers, swarmer = swarmers, voider = voiders} enchanter = enchanters, psyker = psykers, curser = cursers, forcer = forcers, swarmer = swarmers, voider = voiders, sorcerer = sorcerers}
end end
get_class_levels = function(units) get_class_levels = function(units)
@ -841,6 +942,11 @@ function init()
if number_of_units >= 4 then return 2 if number_of_units >= 4 then return 2
elseif number_of_units >= 2 then return 1 elseif number_of_units >= 2 then return 1
else return 0 end else return 0 end
elseif class == 'sorcerer' then
if number_of_units >= 6 then return 3
elseif number_of_units >= 4 then return 2
elseif number_of_units >= 2 then return 1
else return 0 end
end end
end end
return { return {
@ -857,6 +963,7 @@ function init()
forcer = units_to_class_level(units_per_class.forcer, 'forcer'), forcer = units_to_class_level(units_per_class.forcer, 'forcer'),
swarmer = units_to_class_level(units_per_class.swarmer, 'swarmer'), swarmer = units_to_class_level(units_per_class.swarmer, 'swarmer'),
voider = units_to_class_level(units_per_class.voider, 'voider'), voider = units_to_class_level(units_per_class.voider, 'voider'),
sorcerer = units_to_class_level(units_per_class.sorcerer, 'sorcerer'),
} }
end end
@ -1181,20 +1288,17 @@ function init()
if run.level and run.level > 0 then if run.level and run.level > 0 then
main_song_instance = _G[random:table{'song1', 'song2', 'song3', 'song4', 'song5'}]:play{volume = 0.5} main_song_instance = _G[random:table{'song1', 'song2', 'song3', 'song4', 'song5'}]:play{volume = 0.5}
end end
main_song_instance = _G[random:table{'song1', 'song2', 'song3', 'song4', 'song5'}]:play{volume = 0.5}
--[[
main:add(BuyScreen'buy_screen') main:add(BuyScreen'buy_screen')
main:go_to('buy_screen', run.level or 0, run.units or {}, passives) main:go_to('buy_screen', run.level or 0, run.units or {}, passives)
--[[
main:add(Arena'arena')
main:go_to('arena', 23, {
{character = 'wizard', level = 1},
{character = 'spellblade', level = 1},
{character = 'chronomancer', level = 1},
{character = 'lich', level = 1},
{character = 'psykino', level = 1},
}, passives)
]]-- ]]--
main:add(Arena'arena')
main:go_to('arena', 16, {
{character = 'witch', level = 3},
}, passives)
trigger:every(2, function() trigger:every(2, function()
if debugging_memory then if debugging_memory then

View File

@ -262,6 +262,10 @@ function Unit:calculate_stats(first_run)
self.base_hp = 100*math.pow(2, self.level-1) self.base_hp = 100*math.pow(2, self.level-1)
self.base_dmg = 10*math.pow(2, self.level-1) self.base_dmg = 10*math.pow(2, self.level-1)
self.base_mvspd = 75 self.base_mvspd = 75
elseif self:is(Illusion) then
self.base_hp = 100*math.pow(2, self.level-1)
self.base_dmg = 10*math.pow(2, self.level-1)
self.base_mvspd = 15
elseif self:is(EnemyCritter) or self:is(Critter) then elseif self:is(EnemyCritter) or self:is(Critter) then
local x = self.level local x = self.level
local y = {0, 1, 3, 3, 4, 6, 5, 6, 9, 7, 8, 12, 10, 11, 15, 12, 13, 18, 16, 17, 21, 17, 20, 24, 25} local y = {0, 1, 3, 3, 4, 6, 5, 6, 9, 7, 8, 12, 10, 11, 15, 12, 13, 18, 16, 17, 21, 17, 20, 24, 25}

View File

@ -73,6 +73,32 @@ function Player:init(args)
end end
end, nil, nil, 'heal') end, nil, nil, 'heal')
elseif self.character == 'arcanist' then
self.attack_sensor = Circle(self.x, self.y, 128)
self.t:cooldown(3, function() local enemies = self:get_objects_in_shape(self.attack_sensor, main.current.enemies); return enemies and #enemies > 0 end, function()
local closest_enemy = self:get_closest_object_in_shape(self.attack_sensor, main.current.enemies)
if closest_enemy then
self:shoot(self:angle_to_object(closest_enemy), {pierce = 1000, v = 40})
end
end, nil, nil, 'shoot')
elseif self.character == 'illusionist' then
self.attack_sensor = Circle(self.x, self.y, 96)
self.t:cooldown(2, function() local enemies = self:get_objects_in_shape(self.attack_sensor, main.current.enemies); return enemies and #enemies > 0 end, function()
local closest_enemy = self:get_closest_object_in_shape(self.attack_sensor, main.current.enemies)
if closest_enemy then
self:shoot(self:angle_to_object(closest_enemy))
end
end, nil, nil, 'shoot')
self.t:every(8, function()
self.t:every(0.25, function()
SpawnEffect{group = main.current.effects, x = self.x, y = self.y, color = self.color, action = function(x, y)
illusion1:play{pitch = random:float(0.95, 1.05), volume = 0.5}
Illusion{group = main.current.main, x = x, y = y, parent = self, level = self.level, conjurer_buff_m = self.conjurer_buff_m or 1, crit = (self.level == 3) and random:bool(50)}
end}
end, self.level == 3 and 2 or 1)
end)
elseif self.character == 'outlaw' then elseif self.character == 'outlaw' then
self.attack_sensor = Circle(self.x, self.y, 96) self.attack_sensor = Circle(self.x, self.y, 96)
self.t:cooldown(3, function() local enemies = self:get_objects_in_shape(self.attack_sensor, main.current.enemies); return enemies and #enemies > 0 end, function() self.t:cooldown(3, function() local enemies = self:get_objects_in_shape(self.attack_sensor, main.current.enemies); return enemies and #enemies > 0 end, function()
@ -100,7 +126,7 @@ function Player:init(args)
elseif self.character == 'saboteur' then elseif self.character == 'saboteur' then
self.t:every(8, function() self.t:every(8, function()
self.t:every(0.25, function() self.t:every(0.25, function()
SpawnEffect{group = main.current.effects, x = self.x, y = self.y, action = function(x, y) SpawnEffect{group = main.current.effects, x = self.x, y = self.y, color = self.color, action = function(x, y)
Saboteur{group = main.current.main, x = x, y = y, parent = self, level = self.level, conjurer_buff_m = self.conjurer_buff_m or 1, crit = (self.level == 3) and random:bool(50)} Saboteur{group = main.current.main, x = x, y = y, parent = self, level = self.level, conjurer_buff_m = self.conjurer_buff_m or 1, crit = (self.level == 3) and random:bool(50)}
end} end}
end, 2) end, 2)
@ -202,6 +228,11 @@ function Player:init(args)
end) end)
end end
elseif self.character == 'witch' then
self.t:every(6, function()
self:dot_attack(24, {duration = random:float(12, 16), homing = true})
end, nil, nil, 'attack')
elseif self.character == 'barbarian' then elseif self.character == 'barbarian' then
self.t:every(8, function() self.t:every(8, function()
self:attack(96, {stun = 4}) self:attack(96, {stun = 4})
@ -267,7 +298,7 @@ function Player:init(args)
elseif self.character == 'jester' then elseif self.character == 'jester' then
self.t:every({6, 10}, function() self.t:every({6, 10}, function()
buff1:play{pitch = random:float(0.9, 1.1), volume = 0.5} buff1:play{pitch = random:float(0.9, 1.1), volume = 0.5}
local enemies = table.first(table.shuffle(main.current.main:get_objects_by_classes(main.current.enemies)), 5) local enemies = table.first(table.shuffle(main.current.main:get_objects_by_classes(main.current.enemies)), self.level == 3 and 8 or 4)
for _, enemy in ipairs(enemies) do for _, enemy in ipairs(enemies) do
if self:distance_to_object(enemy) < 128 then if self:distance_to_object(enemy) < 128 then
enemy:curse('jester', 6*(self.hex_duration_m or 1), self.level == 3, self) enemy:curse('jester', 6*(self.hex_duration_m or 1), self.level == 3, self)
@ -1158,7 +1189,7 @@ function Player:shoot(r, mods)
dual_gunner2:play{pitch = random:float(0.95, 1.05), volume = 0.3} dual_gunner2:play{pitch = random:float(0.95, 1.05), volume = 0.3}
elseif self.character == 'archer' or self.character == 'hunter' or self.character == 'barrager' or self.character == 'corruptor' then elseif self.character == 'archer' or self.character == 'hunter' or self.character == 'barrager' or self.character == 'corruptor' then
archer1:play{pitch = random:float(0.95, 1.05), volume = 0.35} archer1:play{pitch = random:float(0.95, 1.05), volume = 0.35}
elseif self.character == 'wizard' or self.character == 'lich' then elseif self.character == 'wizard' or self.character == 'lich' or self.character == 'arcanist' or self.character == 'illusionist' then
wizard1:play{pitch = random:float(0.95, 1.05), volume = 0.15} wizard1:play{pitch = random:float(0.95, 1.05), volume = 0.15}
elseif self.character == 'scout' or self.character == 'outlaw' or self.character == 'blade' or self.character == 'spellblade' or self.character == 'jester' or self.character == 'assassin' or self.character == 'beastmaster' then elseif self.character == 'scout' or self.character == 'outlaw' or self.character == 'blade' or self.character == 'spellblade' or self.character == 'jester' or self.character == 'assassin' or self.character == 'beastmaster' then
_G[random:table{'scout1', 'scout2'}]:play{pitch = random:float(0.95, 1.05), volume = 0.35} _G[random:table{'scout1', 'scout2'}]:play{pitch = random:float(0.95, 1.05), volume = 0.35}
@ -1173,6 +1204,10 @@ function Player:shoot(r, mods)
frost1:play{pitch = random:float(0.95, 1.05), volume = 0.3} frost1:play{pitch = random:float(0.95, 1.05), volume = 0.3}
end end
if self.character == 'arcanist' then
arcane1:play{pitch = random:float(0.95, 1.05), volume = 0.3}
end
if self.chance_to_barrage and random:bool(self.chance_to_barrage) then if self.chance_to_barrage and random:bool(self.chance_to_barrage) then
self:barrage(r, 3) self:barrage(r, 3)
end end
@ -1205,7 +1240,8 @@ function Player:dot_attack(area, mods)
mods = mods or {} mods = mods or {}
camera:shake(2, 0.5) camera:shake(2, 0.5)
self.hfx:use('shoot', 0.25) 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, character = self.character, level = self.level} 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,
character = self.character, level = self.level, parent = self}
DotArea(table.merge(t, mods)) DotArea(table.merge(t, mods))
dot1:play{pitch = random:float(0.9, 1.1), volume = 0.5} dot1:play{pitch = random:float(0.9, 1.1), volume = 0.5}
@ -1233,6 +1269,7 @@ Projectile:implement(GameObject)
Projectile:implement(Physics) Projectile:implement(Physics)
function Projectile:init(args) function Projectile:init(args)
self:init_game_object(args) self:init_game_object(args)
self.hfx:add('hit', 1)
self:set_as_rectangle(10, 4, 'dynamic', 'projectile') self:set_as_rectangle(10, 4, 'dynamic', 'projectile')
self.pierce = args.pierce or 0 self.pierce = args.pierce or 0
self.chain = args.chain or 0 self.chain = args.chain or 0
@ -1291,6 +1328,23 @@ function Projectile:init(args)
self.t:every(0.08, function() self.t:every(0.08, function()
HitParticle{group = main.current.effects, x = self.x, y = self.y, color = self.color} HitParticle{group = main.current.effects, x = self.x, y = self.y, color = self.color}
end) end)
elseif self.character == 'arcanist' then
self.dmg = 0.2*self.dmg
self.t:every(0.08, function() HitParticle{group = main.current.effects, x = self.x, y = self.y, color = self.color, r = self.r + math.pi + random:float(-math.pi/6, math.pi/6), v = random:float(10, 25), parent = self} end)
self.t:every(self.parent.level == 3 and 0.54 or 0.8, function()
local enemies = table.head(self:get_objects_in_shape(Circle(self.x, self.y, 128), main.current.enemies), self.level == 3 and 2 or 1)
for _, enemy in ipairs(enemies) do
self.hfx:use('hit', 0.5)
local r = self:angle_to_object(enemy)
local t = {group = main.current.main, x = self.x + 8*math.cos(r), y = self.y + 8*math.sin(r), v = 250, r = r, color = self.parent.color, dmg = self.parent.dmg, pierce = 1000, character = 'arcanist_projectile',
parent = self.parent, level = self.parent.level}
Projectile(table.merge(t, mods or {}))
end
end)
elseif self.character == 'witch' and self.level == 3 then
self.chain = 1
end end
if self.parent.divine_machine_arrow and table.any(self.parent.classes, function(v) return v == 'ranger' end) then if self.parent.divine_machine_arrow and table.any(self.parent.classes, function(v) return v == 'ranger' end) then
@ -1381,7 +1435,12 @@ function Projectile:draw()
elseif self.character == 'lich' then elseif self.character == 'lich' then
graphics.push(self.x, self.y, self.r, self.spring.x, self.spring.x) graphics.push(self.x, self.y, self.r, self.spring.x, self.spring.x)
graphics.circle(self.x, self.y, 4 + random:float(-1, 1), self.color) graphics.circle(self.x, self.y, 3 + random:float(-1, 1), self.color)
graphics.pop()
elseif self.character == 'arcanist' then
graphics.push(self.x, self.y, self.r, self.hfx.hit.x, self.hfx.hit.x)
graphics.circle(self.x, self.y, 4, self.hfx.hit.f and fg[0] or self.color)
graphics.pop() graphics.pop()
else else
@ -1451,7 +1510,16 @@ function Projectile:on_collision_enter(other, contact)
if self.character == 'spellblade' then if self.character == 'spellblade' then
magic_area1:play{pitch = random:float(0.95, 1.05), volume = 0.075} magic_area1:play{pitch = random:float(0.95, 1.05), volume = 0.075}
end end
elseif self.character == 'wizard' or self.character == 'lich' then elseif self.character == 'illusionist_death' then
if self.ricochet <= 0 then
self:die(x, y, r, random:int(2, 3))
magic_area1:play{pitch = random:float(0.95, 1.05), volume = 0.075}
else
local r = Unit.bounce(self, nx, ny)
self.r = r
self.ricochet = self.ricochet - 1
end
elseif self.character == 'wizard' or self.character == 'lich' or self.character == 'arcanist' or self.character == 'arcanist_projectile' or self.character == 'illusionist' or self.character == 'witch' then
self:die(x, y, r, random:int(2, 3)) self:die(x, y, r, random:int(2, 3))
magic_area1:play{pitch = random:float(0.95, 1.05), volume = 0.075} magic_area1:play{pitch = random:float(0.95, 1.05), volume = 0.075}
elseif self.character == 'cannoneer' then elseif self.character == 'cannoneer' then
@ -1511,8 +1579,10 @@ function Projectile:on_trigger_enter(other, contact)
if self.character == 'spellblade' then if self.character == 'spellblade' then
magic_area1:play{pitch = random:float(0.95, 1.05), volume = 0.15} magic_area1:play{pitch = random:float(0.95, 1.05), volume = 0.15}
end end
elseif self.character == 'wizard' or self.character == 'lich' then elseif self.character == 'wizard' or self.character == 'lich' or self.character == 'arcanist' or self.character == 'illusionist' or self.character == 'illusionist_death' or self.character == 'witch' then
magic_area1:play{pitch = random:float(0.95, 1.05), volume = 0.15} magic_area1:play{pitch = random:float(0.95, 1.05), volume = 0.15}
elseif self.character == 'arcanist_projectile' then
magic_area1:play{pitch = random:float(0.95, 1.05), volume = 0.075}
else else
hit3:play{pitch = random:float(0.95, 1.05), volume = 0.35} hit3:play{pitch = random:float(0.95, 1.05), volume = 0.35}
end end
@ -1732,11 +1802,13 @@ end
DotArea = Object:extend() DotArea = Object:extend()
DotArea:implement(GameObject) DotArea:implement(GameObject)
DotArea:implement(Physics)
function DotArea:init(args) function DotArea:init(args)
self:init_game_object(args) self:init_game_object(args)
self.shape = Circle(self.x, self.y, self.rs) 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' then if self.character == 'plague_doctor' or self.character == 'pyromancer' or self.character == 'witch' then
self.t:every(0.2, function() self.t:every(0.2, function()
local enemies = main.current.main:get_objects_in_shape(self.shape, main.current.enemies) 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 if #enemies > 0 then self.spring:pull(0.05, 200, 10) end
@ -1810,6 +1882,23 @@ function DotArea:init(args)
end, nil, nil, 'dot') end, nil, nil, 'dot')
end end
if self.character == 'witch' then
self.v = random:float(40, 80)
self.r = random:table{math.pi/4, 3*math.pi/4, -math.pi/4, -3*math.pi/4}
if self.level == 3 then
self.t:every(1, function()
local enemies = main.current.main:get_objects_in_shape(self.closest_sensor, main.current.enemies)
if enemies and #enemies > 0 then
wizard1:play{pitch = random:float(0.95, 1.05), volume = 0.05}
local r = self:angle_to_object(enemies[1])
HitCircle{group = main.current.effects, x = self.x, y = self.y, rs = 6}
local t = {group = main.current.main, x = self.x, y = self.y, v = 250, r = r, color = self.parent.color, dmg = self.parent.dmg, character = 'witch', parent = self.parent, level = self.parent.level}
Projectile(table.merge(t, mods or {}))
end
end)
end
end
self.color = fg[0] self.color = fg[0]
self.color_transparent = Color(args.color.r, args.color.g, args.color.b, 0.08) self.color_transparent = Color(args.color.r, args.color.g, args.color.b, 0.08)
self.rs = 0 self.rs = 0
@ -1842,6 +1931,17 @@ function DotArea:update(dt)
self.shape:move_to(self.x, self.y) self.shape:move_to(self.x, self.y)
end end
end end
if self.character == 'witch' then
self.x, self.y = self.x + self.v*math.cos(self.r)*dt, self.y + self.v*math.sin(self.r)*dt
if self.x >= main.current.x2 - self.shape.rs/2 or self.x <= main.current.x1 + self.shape.rs/2 then
self.r = math.pi - self.r
end
if self.y >= main.current.y2 - self.shape.rs/2 or self.y <= main.current.y1 + self.shape.rs/2 then
self.r = 2*math.pi - self.r
end
self.shape:move_to(self.x, self.y)
end
end end
@ -2217,6 +2317,85 @@ end
Illusion = Object:extend()
Illusion:implement(GameObject)
Illusion:implement(Physics)
Illusion:implement(Unit)
function Illusion:init(args)
self:init_game_object(args)
self:init_unit()
self:set_as_rectangle(8, 8, 'dynamic', 'player')
self:set_restitution(0.5)
self.color = character_colors.illusionist
self.character = 'illusionist'
self.classes = {'sorcerer', 'conjurer'}
self:calculate_stats(true)
self:set_as_steerable(self.v, 2000, 4*math.pi, 4)
self.attack_sensor = Circle(self.x, self.y, 96)
self.t:cooldown(2, function() local enemies = self:get_objects_in_shape(self.attack_sensor, main.current.enemies); return enemies and #enemies > 0 end, function()
local closest_enemy = self:get_closest_object_in_shape(self.attack_sensor, main.current.enemies)
if closest_enemy then
wizard1:play{pitch = random:float(0.95, 1.05), volume = 0.05}
local r = self:angle_to_object(closest_enemy)
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.parent.color, dmg = self.parent.dmg, character = 'illusionist',
parent = self.parent, level = self.parent.level}
Projectile(table.merge(t, mods or {}))
end
end, nil, nil, 'shoot')
self.t:after(12*(self.parent.conjurer_buff_m or 1), function()
local n = n or random:int(3, 4)
for i = 1, n do HitParticle{group = main.current.effects, x = self.x, y = self.y, r = random:float(0, 2*math.pi), color = self.color} end
HitCircle{group = main.current.effects, x = self.x, y = self.y}:scale_down()
self.dead = true
if self.parent.level == 3 then
shoot1:play{pitch = random:float(0.95, 1.05), volume = 0.2}
for i = 1, 12 do
Projectile{group = main.current.main, x = self.x, y = self.y, color = self.color, r = (i-1)*math.pi/6, v = 200, dmg = self.parent.dmg, character = 'illusionist_death',
parent = self.parent, level = self.parent.level, pierce = 1, ricochet = 1}
end
end
end)
end
function Illusion:update(dt)
self:update_game_object(dt)
self:calculate_stats()
if not self.target then self.target = random:table(self.group:get_objects_by_classes(main.current.enemies)) end
if self.target and self.target.dead then self.target = random:table(self.group:get_objects_by_classes(main.current.enemies)) end
if not self.seek_f then return end
if not self.target then
self:seek_point(gw/2, gh/2)
self:wander(50, 200, 50)
self:rotate_towards_velocity(1)
self:steering_separate(32, {Illusion, Seeker, Player})
else
self:seek_point(self.target.x, self.target.y)
self:wander(50, 200, 50)
self:rotate_towards_velocity(1)
self:steering_separate(32, {Illusion, Seeker, Player})
end
self.r = self:get_angle()
self.attack_sensor:move_to(self.x, self.y)
end
function Illusion:draw()
graphics.push(self.x, self.y, self.r, self.hfx.hit.x, self.hfx.hit.x)
graphics.rectangle(self.x, self.y, self.shape.w, self.shape.h, 3, 3, self.hfx.hit.f and fg[0] or self.color)
graphics.pop()
end
Critter = Object:extend() Critter = Object:extend()
Critter:implement(GameObject) Critter:implement(GameObject)

View File

@ -12,6 +12,7 @@ function shared_init()
green = ColorRamp(Color'#8bbf40', 0.025), green = ColorRamp(Color'#8bbf40', 0.025),
red = ColorRamp(Color'#e91d39', 0.025), red = ColorRamp(Color'#e91d39', 0.025),
purple = ColorRamp(Color'#8e559e', 0.025), purple = ColorRamp(Color'#8e559e', 0.025),
blue2 = ColorRamp(Color'#4778ba', 0.025),
} }
for name, color in pairs(colors) do for name, color in pairs(colors) do
_G[name] = color _G[name] = color
@ -765,7 +766,11 @@ end
function HitParticle:draw() function HitParticle:draw()
graphics.push(self.x, self.y, self.r) graphics.push(self.x, self.y, self.r)
graphics.rectangle(self.x, self.y, self.w, self.h, 2, 2, self.color) if self.parent and not self.parent.dead then
graphics.rectangle(self.x, self.y, self.w, self.h, 2, 2, self.parent.hfx.hit.f and fg[0] or self.color)
else
graphics.rectangle(self.x, self.y, self.w, self.h, 2, 2, self.color)
end
graphics.pop() graphics.pop()
end end

93
todo
View File

@ -1,37 +1,68 @@
Hide cursor during waves Sorcerer update
Mouse follow control? Options
Item reroll for 15 gold Option to have an arrow at the head of the snake - https://imgur.com/a/poXVsoN
Option to have an arrow at the head of the snake - https://imgur.com/a/poXVsoN Option to turn off camera movement
Option to turn off camera movement Options menu from buy screen
Headbutter damage should falloff faster after it strikes Volume slider
https://i.imgur.com/QN0Ntq2.png Fix fullscreen with different resolutions that don't scale properly
https://i.imgur.com/YfhqDYr.png QoL
https://i.imgur.com/ps4OA7o.png Item reroll for 15 gold
Remove level 3 units from rotation Fix highlight colors and highlight reserve
Fix highlight colors and highlight reserve Change cursers to trigger only near enemies
Fix bug where quitting on level 1 jumps to level 2 Rename tutorial to guide or manual
Fix lock bug after death/win - https://i.imgur.com/iUyOtLk.png Add visuals for defensive ouroboros
Options menu from buy screen Unlock automatically on shop enter
Fix fullscreen with different resolutions that don't scale properly Bug fixes
Volume slider Headbutter damage should falloff faster after it strikes
https://i.imgur.com/QN0Ntq2.png
https://i.imgur.com/YfhqDYr.png
https://i.imgur.com/ps4OA7o.png
https://i.imgur.com/j1LS3zt.png
https://i.imgur.com/XXJn4uW.png
Fix bug where quitting on level 1 jumps to level 2
Fix lock bug after death/win - https://i.imgur.com/iUyOtLk.png
Fix enemies still spawning after arena clear (this happens with the extra enemy spawns that were blocked earlier)
Fix death + win at the same time bug
Balance
Buff tanks, maybe add a simple forcer ability to them
Buff 24/25 HP again
Buff headbutter (+ trigger range)
New
Sorcerer = sorcerers repeat their attacks once every 4/3/2 attacks
* Arcanist (tier 1 sorcerer) - launches a slow piercing orb that launches other piercing projectiles, Lv.3 effect - 50% increased attack speed for the orb and 2 projectiles are released per cast
* Sorcerer + Conjurer = Illusionist - launches a projectile that deals X damage and creates copies that do the same, Lv.3 effect - doubles the number of copies created and they release additional projectiles on death
* Sorcerer + Voider = Witch - creates an area that ricochets around the arena and deals X damage over time, Lv.3 effect - the area periodically releases projectiles that chains once
Sorcerer + Curser = Silencer - curses 5 nearby enemies for 6 seconds, preventing them from using special attacks, Lv.3 effect - the curse also deals X damage over time
Sorcerer + Nuker = Vulcanist - creates a volcano that explodes the nearby area 5 times, Lv.3 effect - the volcano spawn also deals damage and doubles the number of explosions
Sorcerer + Forcer = Warden - creates a force field around a random unit that prevents enemies from entering, Lv.3 effect - creates the force field around 2 other random units
Sorcerer + Psyker = Psychic - creates a small area that deals X damage, Lv.3 effect - the attack can happen from any distance and repeats twice
Sorcerer = sorcerers repeat their attacks once every 3/2 attacks Sorcerer update patch notes
Sorcerer + Conjurer = Illusionist - launches a projectile that deals X damage and creates copies that do the same, Lv.3 effect - doubles the number of copies created and they release additional projectiles on death Decreased projectile speed for the exploder (blue enemy)
Sorcerer + Voider = Witch - creates an area that deals X damage over time and seeks nearby enemies, Lv.3 effect - the area periodically releases homing projectiles that pierce 2 times Changed shooters (white enemy) to scale with NG+ difficulty
Sorcerer + Curser = Linker - links 3 enemies together, they share damage taken, Lv.3 effect - link 6 enemies instead and damage shared is doubled Increased damage and movement speed for enemy swarmers (small purple enemies)
Sorcerer + Nuker = Vulcanist - creates a volcano that explodes the nearby area 5 times, Lv.3 effect - the volcano spawn also deals damage and doubles the number of explosions Added tooltips for "restart run" and "tutorial" buttons on the shop
Sorcerer + Forcer = Warden - creates a force field around a random unit that prevents enemies from entering, Lv.3 effect - creates the force field around 2 other random units Fixed text that was going outside the screen for the Priest and the Assassin
Fixed bug where Divine Punishment would continue triggering after death
Removed cooldown visuals on snake for units that don't have cooldowns (Squire, Chronomancer and Psykeeper)
Slightly decreased base game's difficulty
Fixed NG+ difficulty only being able to be lowered, now you can lower or increase it (up to your maximum acquired value)
Patch Notes: ---
Decreased projectile speed for the exploder (blue enemy)
Added tooltips for "restart run" and "tutorial" buttons on the shop
Fixed text that was going outside the screen for the Priest and the Assassin
Fixed bug where Divine Punishment would continue triggering after death
Removed cooldown visuals on snake for units that don't have cooldowns (Squire, Chronomancer and Psykeeper)
Slightly decreased base game's difficulty
Fixed NG+ difficulty only being able to be lowered, now you can lower or increase it (up to your maximum acquired value)
Future ideas: Future ideas:
Chaos related class = Invoker - shoots a projectile with random properties, Lv.3 effect - ??? Chaos related classes
Invoker - shoots a projectile with random properties, Lv.3 effect - ???
Trappers:
Triangler - drops a trap and the 3rd trap will trigger the area, dealing X AoE damage 2-3 times
Bench? - https://i.imgur.com/B1gNVKk.png Bench? - https://i.imgur.com/B1gNVKk.png
Balance option for when there are more sets - https://i.imgur.com/JMynwbL.png Balance option for when there are more sets - https://i.imgur.com/JMynwbL.png
Negative effect: colliding with yourself kills one of your units
Go through this later https://i.imgur.com/4t7NA32.png <- lots of good improvements
Remove level 3 units from rotation
Hide cursor during waves
Mouse follow control?
Roguelite update:
Slay the Spire-like node selection map (copy code from SHOOTRX repo as this is already implemented there)
Units die permanently when they die