Loop update 3/5

master
a327ex 2021-07-03 02:08:35 -03:00
parent 95df672169
commit 894a492cc3
26 changed files with 642 additions and 187 deletions

View File

@ -330,10 +330,6 @@ function Arena:on_enter(from, level, loop, units, passives, shop_level, shop_xp,
self.t:after(1.125 + math.floor(n/4)*0.25, function() self.spawning_enemies = false end, 'spawning_enemies')
self.enemy_spawns_prevented = 0
end)
self.level = 25*(self.loop+1)
self.won = true
self:quit()
end
@ -392,7 +388,9 @@ function Arena:update(dt)
'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', 'blessing', 'haste', 'divine_barrage', 'orbitism', 'psyker_orbs', 'psychosense', 'rearm', 'taunt', 'summon_instability',
'insurance', 'dividends', 'berserking', 'unwavering_stance', 'unrelenting_stance', 'blessing', 'haste', 'divine_barrage', 'orbitism', 'psyker_orbs', 'psychosense', 'rearm', 'taunt', 'construct_instability',
'intimidation', 'vulnerability', 'temporal_chains', 'ceremonial_dagger', 'homing_barrage', 'critical_strike', 'noxious_strike', 'infesting_strike', 'burning_strike', 'lucky_strike', 'healing_strike', 'stunning_strike',
'silencing_strike', 'culling_strike', 'lightning_strike', 'psycholeak', 'divine_blessing',
}
max_units = math.clamp(7 + current_new_game_plus + self.loop, 7, 12)
main:add(BuyScreen'buy_screen')
@ -838,7 +836,9 @@ function Arena:die()
'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', 'blessing', 'haste', 'divine_barrage', 'orbitism', 'psyker_orbs', 'psychosense', 'rearm', 'taunt', 'summon_instability',
'insurance', 'dividends', 'berserking', 'unwavering_stance', 'unrelenting_stance', 'blessing', 'haste', 'divine_barrage', 'orbitism', 'psyker_orbs', 'psychosense', 'rearm', 'taunt', 'construct_instability',
'intimidation', 'vulnerability', 'temporal_chains', 'ceremonial_dagger', 'homing_barrage', 'critical_strike', 'noxious_strike', 'infesting_strike', 'burning_strike', 'lucky_strike', 'healing_strike', 'stunning_strike',
'silencing_strike', 'culling_strike', 'lightning_strike', 'psycholeak', 'divine_blessing',
}
max_units = math.clamp(7 + current_new_game_plus + self.loop, 7, 12)
main:add(BuyScreen'buy_screen')
@ -908,6 +908,8 @@ function Arena:create_credits()
open_url(b, 'https://freesound.org/people/benzix2/sounds/467951/') end}
Button{group = self.credits, x = 204, y = 160, button_text = 'lord', fg_color = 'yellowm5', bg_color = 'yellow', credits_button = true, action = function(b)
open_url(b, 'https://store.steampowered.com/developer/T_TGames') end}
Button{group = self.credits, x = 254, y = 160, button_text = 'InspectorJ', fg_color = 'yellowm5', bg_color = 'yellow', credits_button = true, action = function(b)
open_url(b, 'https://freesound.org/people/InspectorJ/sounds/458586/') end}
Text2{group = self.credits, x = 70, y = 190, lines = {{text = '[red]playtesters: ', font = pixul_font}}}
Button{group = self.credits, x = 130, y = 190, button_text = 'Jofer', fg_color = 'redm5', bg_color = 'red', credits_button = true, action = function(b)
open_url(b, 'https://twitter.com/JofersGames') end}
@ -1108,7 +1110,7 @@ function Arena:spawn_n_enemies(p, j, n, pass)
spawn1:play{pitch = random:float(0.8, 1.2), volume = 0.15}
if not pass then
check_circle:move_to(x, y)
local objects = self.main:get_objects_in_shape(check_circle, {Seeker, EnemyCritter, Critter, Player, Illusion, Volcano, Saboteur, Pet, Turret})
local objects = self.main:get_objects_in_shape(check_circle, {Seeker, EnemyCritter, Critter, Player, Sentry, Automaton, Bomb, Volcano, Saboteur, Pet, Turret})
if #objects > 0 then self.enemy_spawns_prevented = self.enemy_spawns_prevented + 1; return end
end

Binary file not shown.

After

Width:  |  Height:  |  Size: 529 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 374 B

View File

Before

Width:  |  Height:  |  Size: 376 B

After

Width:  |  Height:  |  Size: 376 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 472 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 341 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 460 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 354 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 511 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 517 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 409 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 518 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 497 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 429 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 394 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 478 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 463 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 497 B

Binary file not shown.

View File

@ -144,7 +144,9 @@ function BuyScreen:on_enter(from, level, loop, units, passives, shop_level, shop
'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', 'blessing', 'haste', 'divine_barrage', 'orbitism', 'psyker_orbs', 'psychosense', 'rearm', 'taunt', 'summon_instability',
'insurance', 'dividends', 'berserking', 'unwavering_stance', 'unrelenting_stance', 'blessing', 'haste', 'divine_barrage', 'orbitism', 'psyker_orbs', 'psychosense', 'rearm', 'taunt', 'construct_instability',
'intimidation', 'vulnerability', 'temporal_chains', 'ceremonial_dagger', 'homing_barrage', 'critical_strike', 'noxious_strike', 'infesting_strike', 'burning_strike', 'lucky_strike', 'healing_strike', 'stunning_strike',
'silencing_strike', 'culling_strike', 'lightning_strike', 'psycholeak', 'divine_blessing',
}
max_units = math.clamp(7 + current_new_game_plus + self.loop, 7, 12)
main:add(BuyScreen'buy_screen')
@ -547,7 +549,9 @@ function RestartButton:update(dt)
'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', 'blessing', 'haste', 'divine_barrage', 'orbitism', 'psyker_orbs', 'psychosense', 'rearm', 'taunt', 'summon_instability',
'insurance', 'dividends', 'berserking', 'unwavering_stance', 'unrelenting_stance', 'blessing', 'haste', 'divine_barrage', 'orbitism', 'psyker_orbs', 'psychosense', 'rearm', 'taunt', 'construct_instability',
'intimidation', 'vulnerability', 'temporal_chains', 'ceremonial_dagger', 'homing_barrage', 'critical_strike', 'noxious_strike', 'infesting_strike', 'burning_strike', 'lucky_strike', 'healing_strike', 'stunning_strike',
'silencing_strike', 'culling_strike', 'lightning_strike', 'psycholeak', 'divine_blessing',
}
system.save_state()
main:add(BuyScreen'buy_screen')

View File

@ -214,6 +214,7 @@ function Seeker:init(args)
self.hp = self.max_hp
local n = math.remap(current_new_game_plus, 0, 5, 1, 0.75)
self.t:every({3*n, 5*n}, function()
if self.silenced or self.barbarian_stunned then return end
local enemy = self:get_closest_object_in_shape(Circle(self.x, self.y, 64), main.current.enemies)
if enemy then
wizard1:play{pitch = random:float(0.95, 1.05), volume = 0.5}
@ -247,17 +248,17 @@ function Seeker:init(args)
local player = main.current.player
if player and player.intimidation and not self.boss and not self.tank then
self.buff_hp_m = 0.8
self.buff_hp_m = (player.intimidation == 1 and 0.9) or (player.intimidation == 2 and 0.8) or (player.intimidation == 3 and 0.7)
self:calculate_stats()
self.hp = self.max_hp
end
if player and player.vulnerability then
self.vulnerable = true
self.vulnerable = (player.vulnerability == 1 and 1.1) or (player.vulnerability == 2 and 1.2) or (player.vulnerability == 3 and 1.3)
end
if player and player.temporal_chains then
self.temporal_chains_mvspd_m = 0.8
self.temporal_chains_mvspd_m = (player.temporal_chains and 0.9) or (player.temporal_chains and 0.8) or (player.temporal_chains and 0.7)
end
self.usurer_count = 0
@ -348,9 +349,9 @@ end
function Seeker:draw()
graphics.push(self.x, self.y, self.r, self.hfx.hit.x, self.hfx.hit.x)
if self.boss then
graphics.rectangle(self.x, self.y, self.shape.w, self.shape.h, 4, 4, self.hfx.hit.f and fg[0] or self.color)
graphics.rectangle(self.x, self.y, self.shape.w, self.shape.h, 4, 4, self.hfx.hit.f and fg[0] or (self.silenced and bg[10]) or self.color)
else
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.rectangle(self.x, self.y, self.shape.w, self.shape.h, 3, 3, self.hfx.hit.f and fg[0] or (self.silenced and bg[10]) or self.color)
end
graphics.pop()
@ -384,7 +385,7 @@ function Seeker:on_collision_enter(other, contact)
if main.current.player.heavy_impact then
if self.being_pushed then
self:hit(self.push_force)
self:hit(self.push_force, nil, nil, true)
end
end
@ -413,8 +414,8 @@ function Seeker:on_collision_enter(other, contact)
elseif table.any(main.current.enemies, function(v) return other:is(v) end) then
if self.being_pushed and math.length(self:get_velocity()) > 60 then
other:hit(math.floor(self.push_force/4))
self:hit(math.floor(self.push_force/2))
other:hit(math.floor(self.push_force/4), nil, nil, true)
self:hit(math.floor(self.push_force/2), nil, nil, true)
other:push(math.floor(self.push_force/2), other:angle_to_object(self))
HitCircle{group = main.current.effects, x = x, y = y, rs = 6, color = fg[0], duration = 0.1}
for i = 1, 2 do HitParticle{group = main.current.effects, x = x, y = y, color = self.color} end
@ -431,13 +432,13 @@ function Seeker:on_collision_enter(other, contact)
elseif other:is(Turret) then
self.headbutting = false
_G[random:table{'player_hit1', 'player_hit2'}]:play{pitch = random:float(0.95, 1.05), volume = 0.35}
self:hit(0)
self:hit(0, nil, nil, true)
self:push(random:float(2.5, 7), other:angle_to_object(self))
end
end
function Seeker:hit(damage, projectile, dot)
function Seeker:hit(damage, projectile, dot, from_enemy)
local pyrod = self.pyrod
self.pyrod = false
if self.dead then return end
@ -445,12 +446,65 @@ function Seeker:hit(damage, projectile, dot)
if self.push_invulnerable then return end
self:show_hp()
local actual_damage = math.max(self:calculate_damage(damage)*(self.stun_dmg_m or 1), 0)
if self.vulnerable then actual_damage = actual_damage*1.2 end
local crit = 1
if main.current.player.critical_strike and not dot and not from_enemy then
if random:bool((main.current.player.critical_strike == 1 and 5) or (main.current.player.critical_strike == 2 and 10) or (main.current.player.critical_strike == 3 and 15)) then
crit = 2
camera:shake(2.5, 0.25)
rogue_crit1:play{pitch = random:float(0.95, 1.05), volume = 0.5}
rogue_crit2:play{pitch = random:float(0.95, 1.05), volume = 0.15}
for i = 1, 2 do HitParticle{group = main.current.effects, x = self.x, y = self.y, color = self.color, v = random:float(100, 400)} end
HitCircle{group = main.current.effects, x = self.x, y = self.y, rs = 12, color = fg[0], duration = 0.3}:scale_down():change_color(0.5, self.color)
end
end
if main.current.player.kinetic_strike and not dot and not from_enemy then
if random:bool((main.current.player.kinetic_strike == 1 and 10) or (main.current.player.kinetic_strike == 2 and 20) or (main.current.player.kinetic_strike == 3 and 30)) then
local units = main.current.player:get_all_units()
local cx, cy = 0, 0
for _, unit in ipairs(units) do
cx = cx + unit.x
cy = cy + unit.y
end
cx = cx/#units
cy = cy/#units
self:push(random:float(30, 60), projectile and projectile.r or self:angle_from_point(cx, cy))
end
end
if main.current.player.stunning_strike and not dot and not from_enemy then
if random:bool((main.current.player.stunning_strike == 1 and 8) or (main.current.player.stunning_strike == 2 and 16) or (main.current.player.stunning_strike == 3 and 24)) then
self:slow(0.1, 2)
self.barbarian_stunned = true
self.t:after(2, function() self.barbarian_stunned = false end)
end
end
if main.current.player.silencing_strike and not dot and not from_enemy then
if random:bool((main.current.player.silencing_strike == 1 and 8) or (main.current.player.silencing_strike == 2 and 16) or (main.current.player.silencing_strike == 3 and 24)) then
self.silenced = true
self.t:after(4, function() self.silenced = false end)
end
end
local actual_damage = math.max(self:calculate_damage(damage)*(self.stun_dmg_m or 1)*crit, 0)
if self.vulnerable then actual_damage = actual_damage*self.vulnerable end
self.hp = self.hp - actual_damage
if self.hp > self.max_hp then self.hp = self.max_hp end
main.current.damage_dealt = main.current.damage_dealt + actual_damage
if main.current.player.noxious_strike and not dot and not from_enemy then
if random:bool((main.current.player.noxious_strike == 1 and 8) or (main.current.player.noxious_strike == 2 and 16) or (main.current.player.noxious_strike == 3 and 24)) then
self:apply_dot(0.6*actual_damage*(main.current.player.dot_dmg_m or 1)*(main.current.chronomancer_dot or 1), 3)
end
end
if main.current.player.burning_strike and not dot and not from_enemy then
if random:bool(15) then
self:apply_dot(0.6*actual_damage*(main.current.player.dot_dmg_m or 1)*(main.current.chronomancer_dot or 1), 3, red[0])
end
end
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()
@ -472,6 +526,12 @@ function Seeker:hit(damage, projectile, dot)
end)
end
if self.boss and main.current.player.culling_strike then
if self.hp <= self.max_hp*((main.current.player.culling_strike == 1 and 0.1) or (main.current.player.culling_strike == 2 and 0.2) or (main.current.player.culling_strike == 3 and 0.3)) then
self.hp = 0
end
end
if self.hp <= 0 then
self.dead = true
for i = 1, random:int(4, 6) do HitParticle{group = main.current.effects, x = self.x, y = self.y, color = self.color} end
@ -578,6 +638,54 @@ function Seeker:hit(damage, projectile, dot)
dmg = self.bane_ref.area_dmg_m*self.bane_ref.dmg*(self.bane_ref.dot_dmg_m or 1), void_rift = true, duration = 1}
end)
end
if main.current.player.ceremonial_dagger and not from_enemy then
trigger:after(0.01, function()
if tostring(self.x) == tostring(0/0) or tostring(self.y) == tostring(0/0) then return end
_G[random:table{'scout1', 'scout2'}]:play{pitch = random:float(0.95, 1.05), volume = 0.35}
HitCircle{group = main.current.effects, x = self.x, y = self.y, rs = 6}
local r = random:float(0, 2*math.pi)
local t = {group = main.current.main, x = self.x + 8*math.cos(r), y = self.y + 8*math.sin(r), v = 250, r = r, color = red[0], dmg = self.max_hp,
homing = true, character = 'jester', parent = main.current.player}
Projectile(table.merge(t, mods or {}))
end)
end
if main.current.player.homing_barrage and not from_enemy then
trigger:after(0.01, function()
if random:bool((main.current.player.homing_barrage == 1 and 8) or (main.current.player.homing_barrage == 2 and 16) or (main.current.player.homing_barrage == 3 and 24)) then
local target = main.current.player:get_closest_object_in_shape(Circle(main.current.player.x, main.current.player.y, 128), main.current.enemies)
main.current.player:barrage(target and main.current.player:angle_to_object(target) or main.current.player.r, 4, nil, nil, nil, true)
end
end)
end
if main.current.player.infesting_strike and not from_enemy then
trigger:after(0.01, function()
if random:bool((main.current.player.infesting_strike == 1 and 10) or (main.current.player.infesting_strike == 2 and 20) or (main.current.player.infesting_strike == 3 and 30)) then
critter1:play{pitch = random:float(0.95, 1.05), volume = 0.5}
for i = 1, 2 do
Critter{group = main.current.main, x = self.x, y = self.y, color = orange[0], r = random:float(0, 2*math.pi), v = 5, dmg = main.current.player.dmg, parent = main.current.player}
end
end
end)
end
if main.current.player.lucky_strike and not from_enemy then
if random:bool(8) then
trigger:after(0.01, function()
Gold{group = main.current.main, x = self.x, y = self.y}
end)
end
end
if main.current.player.healing_strike and not from_enemy then
if random:bool(8) then
trigger:after(0.01, function()
HealingOrb{group = main.current.main, x = self.x, y = self.y}
end)
end
end
end
end
@ -668,13 +776,13 @@ function Seeker:curse(curse, duration, arg1, arg2, arg3)
end
function Seeker:apply_dot(dmg, duration)
function Seeker:apply_dot(dmg, duration, color)
self.t:every(0.25, function()
hit2:play{pitch = random:float(0.8, 1.2), volume = 0.2}
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
for i = 1, 1 do HitParticle{group = main.current.effects, x = self.x, y = self.y, color = color or purple[0]} end
end, math.floor(duration/0.2))
end
@ -833,7 +941,7 @@ end
function EnemyCritter: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)
other:hit(self.dmg, nil, nil, true)
end
end
@ -978,7 +1086,7 @@ function EnemyProjectile:on_trigger_enter(other, contact)
elseif other:is(Seeker) or other:is(EnemyCritter) then
if self.source == 'shooter' then
self:die(self.x, self.y, nil, random:int(2, 3))
other:hit(0.5*self.dmg)
other:hit(0.5*self.dmg, nil, nil, true)
end
end
end

192
main.lua
View File

@ -21,6 +21,8 @@ function init()
input:bind('enter', {'space', 'return', 'fleft', 'fdown', 'fright'})
local s = {tags = {sfx}}
artificer1 = Sound('458586__inspectorj__ui-mechanical-notification-01-fx.ogg', s)
explosion1 = Sound('Explosion Grenade_04.ogg', s)
mine1 = Sound('Weapon Swap 2.ogg', s)
level_up1 = Sound('Buff 4.ogg', s)
unlock1 = Sound('Unlock 3.ogg', s)
@ -213,7 +215,26 @@ function init()
psychosense = Image('psychosense')
rearm = Image('rearm')
taunt = Image('taunt')
summon_instability = Image('summon_instability')
construct_instability = Image('construct_instability')
intimidation = Image('intimidation')
vulnerability = Image('vulnerability')
temporal_chains = Image('temporal_chains')
ceremonial_dagger = Image('ceremonial_dagger')
homing_barrage = Image('homing_barrage')
critical_strike = Image('critical_strike')
noxious_strike = Image('noxious_strike')
infesting_strike = Image('infesting_strike')
kinetic_strike = Image('kinetic_strike')
burning_strike = Image('burning_strike')
lucky_strike = Image('lucky_strike')
healing_strike = Image('healing_strike')
stunning_strike = Image('stunning_strike')
silencing_strike = Image('silencing_strike')
warping_shots = Image('warping_shots')
culling_strike = Image('culling_strike')
lightning_strike = Image('lightning_strike')
psycholeak = Image('psycholeak')
divine_blessing = Image('divine_blessing')
class_colors = {
['warrior'] = yellow[0],
@ -265,12 +286,14 @@ function init()
['blade'] = 'Blade',
['elementor'] = 'Elementor',
['saboteur'] = 'Saboteur',
['bomber'] = 'Bomber',
['stormweaver'] = 'Stormweaver',
['sage'] = 'Sage',
['squire'] = 'Squire',
['cannoneer'] = 'Cannoneer',
['dual_gunner'] = 'Dual Gunner',
['hunter'] = 'Hunter',
['sentry'] = 'Sentry',
['chronomancer'] = 'Chronomancer',
['spellblade'] = 'Spellblade',
['psykeeper'] = 'Psykeeper',
@ -298,6 +321,7 @@ function init()
['flagellant'] = 'Flagellant',
['arcanist'] = 'Arcanist',
['illusionist'] = 'Illusionist',
['artificer'] = 'Artificer',
['witch'] = 'Witch',
['silencer'] = 'Silencer',
['vulcanist'] = 'Vulcanist',
@ -322,12 +346,14 @@ function init()
['blade'] = yellow[0],
['elementor'] = blue[0],
['saboteur'] = orange[0],
['bomber'] = orange[0],
['stormweaver'] = blue[0],
['sage'] = purple[0],
['squire'] = yellow[0],
['cannoneer'] = orange[0],
['dual_gunner'] = green[0],
['hunter'] = green[0],
['sentry'] = green[0],
['chronomancer'] = blue[0],
['spellblade'] = blue[0],
['psykeeper'] = fg[0],
@ -355,6 +381,7 @@ function init()
['flagellant'] = fg[0],
['arcanist'] = blue2[0],
['illusionist'] = blue2[0],
['artificer'] = blue2[0],
['witch'] = purple[0],
['silencer'] = blue2[0],
['vulcanist'] = red[0],
@ -379,12 +406,14 @@ function init()
['blade'] = 'yellow',
['elementor'] = 'blue',
['saboteur'] = 'orange',
['bomber'] = 'orange',
['stormweaver'] = 'blue',
['sage'] = 'purple',
['squire'] = 'yellow',
['cannoneer'] = 'orange',
['dual_gunner'] = 'green',
['hunter'] = 'green',
['sentry'] = 'green',
['chronomancer'] = 'blue',
['spellblade'] = 'blue',
['psykeeper'] = 'fg',
@ -412,6 +441,7 @@ function init()
['flagellant'] = 'fg',
['arcanist'] = 'blue2',
['illusionist'] = 'blue2',
['artificer'] = 'blue2',
['witch'] = 'purple',
['silencer'] = 'blue2',
['vulcanist'] = 'red',
@ -435,13 +465,15 @@ function init()
['outlaw'] = {'warrior', 'rogue'},
['blade'] = {'warrior', 'nuker'},
['elementor'] = {'mage', 'nuker'},
['saboteur'] = {'rogue', 'conjurer', 'nuker'},
-- ['saboteur'] = {'rogue', 'conjurer', 'nuker'},
['bomber'] = {'nuker', 'conjurer'},
['stormweaver'] = {'enchanter'},
['sage'] = {'nuker', 'forcer'},
['squire'] = {'warrior', 'enchanter'},
['cannoneer'] = {'ranger', 'nuker'},
['dual_gunner'] = {'ranger', 'rogue'},
['hunter'] = {'ranger', 'conjurer', 'forcer'},
-- ['hunter'] = {'ranger', 'conjurer', 'forcer'},
['sentry'] = {'ranger', 'conjurer'},
['chronomancer'] = {'mage', 'enchanter'},
['spellblade'] = {'mage', 'rogue'},
['psykeeper'] = {'healer', 'psyker'},
@ -468,7 +500,8 @@ function init()
['infestor'] = {'curser', 'swarmer'},
['flagellant'] = {'psyker', 'enchanter'},
['arcanist'] = {'sorcerer'},
['illusionist'] = {'sorcerer', 'conjurer'},
-- ['illusionist'] = {'sorcerer', 'conjurer'},
['artificer'] = {'sorcerer', 'conjurer'},
['witch'] = {'sorcerer', 'voider'},
['silencer'] = {'sorcerer', 'curser'},
['vulcanist'] = {'sorcerer', 'nuker'},
@ -492,17 +525,19 @@ function init()
['outlaw'] = '[yellow]Warrior, [red]Rogue',
['blade'] = '[yellow]Warrior, [red]Nuker',
['elementor'] = '[blue]Mage, [red]Nuker',
['saboteur'] = '[red]Rogue, [orange]Conjurer, [red]Nuker',
-- ['saboteur'] = '[red]Rogue, [orange]Conjurer, [red]Nuker',
['bomber'] = '[red]Nuker, [orange]Builder',
['stormweaver'] = '[blue]Enchanter',
['sage'] = '[red]Nuker, [yellow]Forcer',
['squire'] = '[yellow]Warrior, [blue]Enchanter',
['cannoneer'] = '[green]Ranger, [red]Nuker',
['dual_gunner'] = '[green]Ranger, [red]Rogue',
['hunter'] = '[green]Ranger, [orange]Conjurer, [yellow]Forcer',
-- ['hunter'] = '[green]Ranger, [orange]Conjurer, [yellow]Forcer',
['sentry'] = '[green]Ranger, [orange]Builder',
['chronomancer'] = '[blue]Mage, Enchanter',
['spellblade'] = '[blue]Mage, [red]Rogue',
['psykeeper'] = '[green]Healer, [fg]Psyker',
['engineer'] = '[orange]Conjurer',
['engineer'] = '[orange]Builder',
['plague_doctor'] = '[red]Nuker, [purple]Voider',
['barbarian'] = '[purple]Curser, [yellow]Warrior',
['juggernaut'] = '[yellow]Forcer, Warrior',
@ -515,7 +550,7 @@ function init()
['jester'] = '[purple]Curser, [red]Rogue',
['assassin'] = '[red]Rogue, [purple]Voider',
['host'] = '[orange]Swarmer',
['carver'] = '[orange]Conjurer, [green]Healer',
['carver'] = '[orange]Builder, [green]Healer',
['bane'] = '[purple]Curser, Voider',
['psykino'] = '[blue]Mage, [fg]Psyker, [yellow]Forcer',
['barrager'] = '[green]Ranger, [yellow]Forcer',
@ -525,7 +560,8 @@ function init()
['infestor'] = '[purple]Curser, [orange]Swarmer',
['flagellant'] = '[fg]Psyker, [blue]Enchanter',
['arcanist'] = '[blue2]Sorcerer',
['illusionist'] = '[blue2]Sorcerer, [orange]Conjurer',
-- ['illusionist'] = '[blue2]Sorcerer, [orange]Conjurer',
['artificer'] = '[blue2]Sorcerer, [orange]Builder',
['witch'] = '[blue2]Sorcerer, [purple]Voider',
['silencer'] = '[blue2]Sorcerer, [purple]Curser',
['vulcanist'] = '[blue2]Sorcerer, [red]Nuker',
@ -566,12 +602,14 @@ function init()
['blade'] = function(lvl) return '[fg]throws multiple blades that deal [yellow]' .. get_character_stat('blade', lvl, 'dmg') .. ' AoE[fg] damage' end,
['elementor'] = function(lvl) return '[fg]deals [yellow]' .. get_character_stat('elementor', lvl, 'dmg') .. ' AoE[fg] damage in a large area centered on a random target' end,
['saboteur'] = function(lvl) return '[fg]calls [yellow]2[fg] saboteurs to seek targets and deal [yellow]' .. get_character_stat('saboteur', lvl, 'dmg') .. ' AoE[fg] damage' end,
['bomber'] = function(lvl) return '[fg]plants a bomb, when it explodes it deals [yellow]' .. 2*get_character_stat('bomber', lvl, 'dmg') .. ' AoE[fg] damage' end,
['stormweaver'] = function(lvl) return '[fg]infuses projectiles with chain lightning that deals [yellow]20%[fg] damage to [yellow]2[fg] enemies' end,
['sage'] = function(lvl) return '[fg]shoots a slow projectile that draws enemies in' end,
['squire'] = function(lvl) return '[yellow]+20%[fg] damage and defense to all allies' end,
['cannoneer'] = function(lvl) return '[fg]shoots a projectile that deals [yellow]' .. 2*get_character_stat('cannoneer', lvl, 'dmg') .. ' AoE[fg] damage' end,
['dual_gunner'] = function(lvl) return '[fg]shoots two parallel projectiles, each dealing [yellow]' .. get_character_stat('dual_gunner', lvl, 'dmg') .. '[fg] damage' end,
['hunter'] = function(lvl) return '[fg]shoots an arrow that deals [yellow]' .. get_character_stat('hunter', lvl, 'dmg') .. '[fg] damage and has a [yellow]20%[fg] chance to summon a pet' end,
['sentry'] = function(lvl) return '[fg]spawns a rotating turret that shoots [yellow]4[fg] projectiles, each dealing [yellow]' .. get_character_stat('sentry', lvl, 'dmg') .. '[fg] damage' end,
['chronomancer'] = function(lvl) return '[yellow]+20%[fg] attack speed to all allies' end,
['spellblade'] = function(lvl) return '[fg]throws knives that deal [yellow]' .. get_character_stat('spellblade', lvl, 'dmg') .. '[fg] damage, pierce and spiral outwards' end,
['psykeeper'] = function(lvl) return '[fg]creates [yellow]1[fg] healing orb every time the psykeeper takes [yellow]20%[fg] of its max HP in damage' end,
@ -589,7 +627,7 @@ function init()
['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,
['host'] = function(lvl) return '[fg]periodically spawn [yellow]1[fg] small critter' end,
['carver'] = function(lvl) return '[fg]carves a statue that periodically heals [yellow]1[fg] unit for [yellow]20%[fg] max HP if in range' end,
['carver'] = function(lvl) return '[fg]carves a statue that creates [yellow]1[fg] healing orb every [yellow]6[fg] seconds' end,
['bane'] = function(lvl) return '[fg]curses [yellow]6[fg] nearby enemies for [yellow]6[fg] seconds, they will create small void rifts on death' end,
['psykino'] = function(lvl) return '[fg]pulls enemies together for [yellow]2[fg] seconds' end,
['barrager'] = function(lvl) return '[fg]shoots a barrage of [yellow]3[fg] arrows, each dealing [yellow]' .. get_character_stat('barrager', lvl, 'dmg') .. '[fg] damage and pushing enemies' end,
@ -600,6 +638,7 @@ function init()
['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,
['artificer'] = function(lvl) return '[fg]spawns an automaton that shoots a projectile that deals [yellow]' .. get_character_stat('artificer', lvl, 'dmg') .. '[fg] damage' end,
['witch'] = function(lvl) return '[fg]creates an area that ricochets 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]4[fg] times, dealing [yellow]' .. get_character_stat('vulcanist', lvl, 'dmg') .. ' AoE [fg]damage' end,
@ -619,17 +658,19 @@ function init()
['magician'] = '[blue]Ethereal Form',
['archer'] = '[green]Bounce Shot',
['scout'] = '[red]Dagger Resonance',
['cleric'] = '[green]Mass Heal ',
['cleric'] = '[green]Mass Heal',
['outlaw'] = '[red]Flying Daggers',
['blade'] = '[yellow]Blade Resonance',
['elementor'] = '[blue]Windfield',
['saboteur'] = '[orange]Demoman',
['bomber'] = '[orange]Demoman',
['stormweaver'] = '[blue]Wide Lightning',
['sage'] = '[purple]Dimension Compression',
['squire'] = '[yellow]Shiny Gear',
['cannoneer'] = '[orange]Cannon Barrage',
['dual_gunner'] = '[green]Gun Kata',
['hunter'] = '[green]Feral Pack',
['sentry'] = '[green]Sentry Barrage',
['chronomancer'] = '[blue]Quicken',
['spellblade'] = '[blue]Spiralism',
['psykeeper'] = '[fg]Crucio',
@ -657,6 +698,7 @@ function init()
['flagellant'] = '[red]Zealotry',
['arcanist'] = '[blue2]Arcane Orb',
['illusionist'] = '[blue2]Mirror Image',
['artificer'] = '[blue2]Spell Formula Efficiency',
['witch'] = '[purple]Death Pool',
['silencer'] = '[blue2]Arcane Curse',
['vulcanist'] = '[red]Lava Burst',
@ -681,12 +723,14 @@ function init()
['blade'] = '[light_bg]Blade Resonance',
['elementor'] = '[light_bg]Windfield',
['saboteur'] = '[light_bg]Demoman',
['bomber'] = '[light_bg]Demoman',
['stormweaver'] = '[light_bg]Wide Lightning',
['sage'] = '[light_bg]Dimension Compression',
['squire'] = '[light_bg]Shiny Gear',
['cannoneer'] = '[light_bg]Cannon Barrage',
['dual_gunner'] = '[light_bg]Gun Kata',
['hunter'] = '[light_bg]Feral Pack',
['sentry'] = '[light_bg]Sentry Barrage',
['chronomancer'] = '[light_bg]Quicken',
['spellblade'] = '[light_bg]Spiralism',
['psykeeper'] = '[light_bg]Crucio',
@ -714,6 +758,7 @@ function init()
['flagellant'] = '[light_bg]Zealotry',
['arcanist'] = '[light_bg]Arcane Orb',
['illusionist'] = '[light_bg]Mirror Image',
['artificer'] = '[light_bg]Spell Formula Efficiency',
['witch'] = '[light_bg]Death Pool',
['silencer'] = '[light_bg]Arcane Curse',
['vulcanist'] = '[light_bg]Lava Burst',
@ -738,12 +783,14 @@ function init()
['blade'] = function() return '[fg]deal additional [yellow]' .. math.round(get_character_stat('blade', 3, 'dmg')/3, 2) .. '[fg] damage per enemy hit' end,
['elementor'] = function() return '[fg]slows enemies by [yellow]60%[fg] for [yellow]6[fg] seconds on hit' end,
['saboteur'] = function() return '[fg]the explosion has [yellow]50%[fg] chance to crit, increasing in size and dealing [yellow]2x[fg] damage' end,
['bomber'] = function() return '[yellow]+100%[fg] bomb area and damage' end,
['stormweaver'] = function() return "[fg]chain lightning's trigger area of effect and number of units hit is [yellow]doubled" end,
['sage'] = function() return '[fg]when the projectile expires deal [yellow]' .. 3*get_character_stat('sage', 3, 'dmg') .. '[fg] damage to all enemies under its influence' end,
['squire'] = function() return '[yellow]+30%[fg] damage, attack speed, movement speed and defense to all allies' end,
['cannoneer'] = function() return '[fg]showers the hit area in [yellow]7[fg] additional cannon shots that deal [yellow]' .. get_character_stat('cannoneer', 3, 'dmg')/2 .. '[fg] AoE damage' end,
['dual_gunner'] = function() return '[fg]every 5th attack shoot in rapid succession for [yellow]2[fg] seconds' end,
['hunter'] = function() return '[fg]summons [yellow]3[fg] pets and the pets ricochet off walls once' end,
['sentry'] = function() return '[yellow]+50%[fg] sentry attack speed and the projectiles ricochet [yellow]twice[fg]' end,
['chronomancer'] = function() return '[fg]enemies take damage over time [yellow]50%[fg] faster' end,
['spellblade'] = function() return '[fg]faster projectile speed and tighter turns' end,
['psykeeper'] = function() return '[fg]deal [yellow]double[fg] the damage taken by the psykeeper to all enemies' end,
@ -760,7 +807,7 @@ function init()
['jester'] = function() return '[fg]all knives seek enemies and pierce [yellow]2[fg] times' end,
['assassin'] = function() return '[fg]poison inflicted from crits deals [yellow]8x[fg] damage' end,
['host'] = function() return '[fg][yellow]+100%[fg] critter spawn rate and spawn [yellow]2[fg] critters instead' end,
['carver'] = function() return '[fg]carves a tree that heals [yellow]twice[fg] as fast, in a bigger area, and heals [yellow]2[fg] units instead' end,
['carver'] = function() return '[fg]carves a tree that creates healing orbs [yellow]twice[fg] as fast' end,
['bane'] = function() return "[yellow]100%[fg] increased area for bane's void rifts" end,
['psykino'] = function() return '[fg]enemies take [yellow]' .. 4*get_character_stat('psykino', 3, 'dmg') .. '[fg] damage and are pushed away when the area expires' end,
['barrager'] = function() return '[fg]every 3rd attack the barrage shoots [yellow]15[fg] projectiles and they push harder' end,
@ -771,6 +818,7 @@ function init()
['flagellant'] = function() return '[yellow]2X[fg] flagellant max HP and grants [yellow]+12%[fg] damage to all allies per cast instead' end,
['arcanist'] = function() return '[yellow]+50%[fg] attack speed for the orb and [yellow]2[fg] projectiles are released per cast' end,
['illusionist'] = function() return '[yellow]doubles[fg] the number of copies created and they release [yellow]12[fg] projectiles on death' end,
['artificer'] = function() return '[fg]automatons shoot and move 50% faster and release [yellow]12[fg] projectiles on death' end,
['witch'] = function() return '[fg]the area 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 number and speed of explosions is [yellow]doubled[fg]' end,
@ -795,12 +843,14 @@ function init()
['blade'] = function() return '[light_bg]deal additional ' .. math.round(get_character_stat('blade', 3, 'dmg')/2, 2) .. ' damage per enemy hit' end,
['elementor'] = function() return '[light_bg]slows enemies by 60% for 6 seconds on hit' end,
['saboteur'] = function() return '[light_bg]the explosion has 50% chance to crit, increasing in size and dealing 2x damage' end,
['bomber'] = function() return '[light_bg]+100% bomb area and damage' end,
['stormweaver'] = function() return "[light_bg]chain lightning's trigger area of effect and number of units hit is doubled" end,
['sage'] = function() return '[light_bg]when the projectile expires deal ' .. 3*get_character_stat('sage', 3, 'dmg') .. ' damage to all enemies under its influence' end,
['squire'] = function() return '[light_bg]+30% damage, attack speed, movement speed and defense to all allies' end,
['cannoneer'] = function() return '[light_bg]showers the hit area in 7 additional cannon shots that deal ' .. get_character_stat('cannoneer', 3, 'dmg')/2 .. ' AoE damage' end,
['dual_gunner'] = function() return '[light_bg]every 5th attack shoot in rapid succession for 2 seconds' end,
['hunter'] = function() return '[light_bg]summons 3 pets and the pets ricochet off walls once' end,
['sentry'] = function() return '[light_bg]+50% attack speed and the projectiles ricochet twice' end,
['chronomancer'] = function() return '[light_bg]enemies take damage over time 50% faster' end,
['spellblade'] = function() return '[light_bg]faster projectile speed and tighter turns' end,
['psykeeper'] = function() return '[light_bg]deal double the damage taken by the psykeeper to all enemies' end,
@ -828,6 +878,7 @@ function init()
['flagellant'] = function() return '[light_bg]2X flagellant max HP and grants +12% damage to all allies per cast instead' end,
['arcanist'] = function() return '[light_bg]+50% attack speed for the orb and 2 projectiles are released per cast' end,
['illusionist'] = function() return '[light_bg]doubles the number of copies created and they release 12 projectiles on death' end,
['artificer'] = function() return '[light_bg]automatons shoot and move 50% faster and 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 number and speed of explosions is doubled' end,
@ -852,12 +903,14 @@ function init()
['blade'] = function(lvl) return get_character_stat_string('blade', lvl) end,
['elementor'] = function(lvl) return get_character_stat_string('elementor', lvl) end,
['saboteur'] = function(lvl) return get_character_stat_string('saboteur', lvl) end,
['bomber'] = function(lvl) return get_character_stat_string('bomber', lvl) end,
['stormweaver'] = function(lvl) return get_character_stat_string('stormweaver', lvl) end,
['sage'] = function(lvl) return get_character_stat_string('sage', lvl) end,
['squire'] = function(lvl) return get_character_stat_string('squire', lvl) end,
['cannoneer'] = function(lvl) return get_character_stat_string('cannoneer', lvl) end,
['dual_gunner'] = function(lvl) return get_character_stat_string('dual_gunner', lvl) end,
['hunter'] = function(lvl) return get_character_stat_string('hunter', lvl) end,
['sentry'] = function(lvl) return get_character_stat_string('sentry', lvl) end,
['chronomancer'] = function(lvl) return get_character_stat_string('chronomancer', lvl) end,
['spellblade'] = function(lvl) return get_character_stat_string('spellblade', lvl) end,
['psykeeper'] = function(lvl) return get_character_stat_string('psykeeper', lvl) end,
@ -885,6 +938,7 @@ function init()
['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,
['artificer'] = function(lvl) return get_character_stat_string('artificer', 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,
@ -943,7 +997,7 @@ function init()
['healer'] = function(lvl) return '[' .. ylb1(lvl) .. ']2[light_bg]/[' .. ylb2(lvl) .. ']4 [fg]- [' .. ylb1(lvl) .. ']+8%[light_bg]/[' .. ylb2(lvl) .. ']+16% [fg] chance for enemies to drop healing orbs on death' end,
['enchanter'] = function(lvl) return '[' .. ylb1(lvl) .. ']2[light_bg]/[' .. ylb2(lvl) .. ']4 [fg]- [' .. ylb1(lvl) .. ']+15%[light_bg]/[' .. ylb2(lvl) .. ']+25% [fg]damage to all allies' end,
['nuker'] = function(lvl) return '[' .. ylb1(lvl) .. ']3[light_bg]/[' .. ylb2(lvl) .. ']6 [fg]- [' .. ylb1(lvl) .. ']+15%[light_bg]/[' .. ylb2(lvl) .. ']+25% [fg]area damage and size to allied nukers' end,
['conjurer'] = function(lvl) return '[' .. ylb1(lvl) .. ']2[light_bg]/[' .. ylb2(lvl) .. ']4 [fg]- [' .. ylb1(lvl) .. ']+25%[light_bg]/[' .. ylb2(lvl) .. ']+50% [fg]summon damage and duration' end,
['conjurer'] = function(lvl) return '[' .. ylb1(lvl) .. ']2[light_bg]/[' .. ylb2(lvl) .. ']4 [fg]- [' .. ylb1(lvl) .. ']+25%[light_bg]/[' .. ylb2(lvl) .. ']+50% [fg]construct damage and duration' end,
['psyker'] = function(lvl) return '[fg]create a piercing, damaging orb around each psyker' end,
['curser'] = function(lvl) return '[' .. ylb1(lvl) .. ']2[light_bg]/[' .. ylb2(lvl) .. ']4 [fg]- [' .. ylb1(lvl) .. ']+1[light_bg]/[' .. ylb2(lvl) .. ']+3 [fg]max curse targets to allied cursers' end,
['forcer'] = function(lvl) return '[' .. ylb1(lvl) .. ']2[light_bg]/[' .. ylb2(lvl) .. ']4 [fg]- [' .. ylb1(lvl) .. ']+25%[light_bg]/[' .. ylb2(lvl) .. ']+50% [fg]knockback force to all allies' end,
@ -959,8 +1013,8 @@ function init()
tier_to_characters = {
[1] = {'vagrant', 'swordsman', 'magician', 'archer', 'scout', 'cleric', 'arcanist', 'merchant'},
[2] = {'wizard', 'saboteur', 'sage', 'squire', 'dual_gunner', 'hunter', 'chronomancer', 'barbarian', 'cryomancer', 'beastmaster', 'jester', 'carver', 'psychic', 'witch', 'silencer', 'outlaw', 'miner'},
[3] = {'elementor', 'stormweaver', 'spellblade', 'psykeeper', 'engineer', 'juggernaut', 'pyromancer', 'host', 'assassin', 'bane', 'barrager', 'infestor', 'flagellant', 'illusionist', 'usurer', 'gambler'},
[2] = {'wizard', 'bomber', 'sage', 'squire', 'dual_gunner', 'sentry', 'chronomancer', 'barbarian', 'cryomancer', 'beastmaster', 'jester', 'carver', 'psychic', 'witch', 'silencer', 'outlaw', 'miner'},
[3] = {'elementor', 'stormweaver', 'spellblade', 'psykeeper', 'engineer', 'juggernaut', 'pyromancer', 'host', 'assassin', 'bane', 'barrager', 'infestor', 'flagellant', 'artificer', 'usurer', 'gambler'},
[4] = {'priest', 'highlander', 'psykino', 'fairy', 'blade', 'plague_doctor', 'cannoneer', 'vulcanist', 'warden', 'corruptor', 'thief'},
}
@ -977,14 +1031,16 @@ function init()
['outlaw'] = 2,
['blade'] = 4,
['elementor'] = 3,
['saboteur'] = 2,
-- ['saboteur'] = 2,
['bomber'] = 2,
['wizard'] = 2,
['stormweaver'] = 3,
['sage'] = 2,
['squire'] = 2,
['cannoneer'] = 4,
['dual_gunner'] = 2,
['hunter'] = 2,
-- ['hunter'] = 2,
['sentry'] = 2,
['chronomancer'] = 2,
['spellblade'] = 3,
['psykeeper'] = 3,
@ -1011,7 +1067,8 @@ function init()
['infestor'] = 3,
['flagellant'] = 3,
['arcanist'] = 1,
['illusionist'] = 3,
-- ['illusionist'] = 3,
['artificer'] = 3,
['witch'] = 2,
['silencer'] = 2,
['vulcanist'] = 4,
@ -1026,7 +1083,7 @@ function init()
launches_projectiles = function(character)
local classes = {'vagrant', 'archer', 'scout', 'outlaw', 'blade', 'wizard', 'cannoneer', 'dual_gunner', 'hunter', 'spellblade', 'engineer', 'corruptor', 'beastmaster', 'jester', 'assassin', 'barrager',
'arcanist', 'illusionist', 'miner', 'thief'}
'arcanist', 'illusionist', 'artificer', 'miner', 'thief', 'sentry'}
return table.any(classes, function(v) return v == character end)
end
@ -1202,7 +1259,26 @@ function init()
['psychosense'] = 'Psychosense',
['rearm'] = 'Rearm',
['taunt'] = 'Taunt',
['summon_instability'] = 'Summon Instability',
['construct_instability'] = 'Construct Instability',
['intimidation'] = 'Intimidation',
['vulnerability'] = 'Vulnerability',
['temporal_chains'] = 'Temporal Chains',
['ceremonial_dagger'] = 'Ceremonial Dagger',
['homing_barrage'] = 'Homing Barrage',
['critical_strike'] = 'Critical Strike',
['noxious_strike'] = 'Noxious Strike',
['infesting_strike'] = 'Infesting Strike',
['kinetic_strike'] = 'Kinetic Strike',
['burning_strike'] = 'Burning Strike',
['lucky_strike'] = 'Lucky Strike',
['healing_strike'] = 'Healing Strike',
['stunning_strike'] = 'Stunning Strike',
['silencing_strike'] = 'Silencing Strike',
['warping_shots'] = 'Warping Shots',
['culling_strike'] = 'Culling Strike',
['lightning_strike'] = 'Lightning Strike',
['psycholeak'] = 'Psycholeak',
['divine_blessing'] = 'Divine Blessing',
}
passive_descriptions = {
@ -1266,9 +1342,28 @@ function init()
['orbitism'] = '[yellow]+33/66/99%[fg] orb movement speed',
['psyker_orbs'] = '[yellow]+1/2/3[fg] psyker orbs',
['psychosense'] = '[yellow]+33/66/99%[fg] orb range',
['rearm'] = '[fg]summons repeat their attacks once',
['taunt'] = '[yellow]10/20/30%[fg] chance for summons to taunt nearby enemies on attack',
['summon_instability'] = '[fg]summons explode when disappearing, dealing [yellow]100/150/200%[fg] damage',
['rearm'] = '[fg]constructs repeat their attacks once',
['taunt'] = '[yellow]10/20/30%[fg] chance for constructs to taunt nearby enemies on attack',
['construct_instability'] = '[fg]constructs explode when disappearing, dealing [yellow]100/150/200%[fg] damage',
['intimidation'] = '[fg]enemies spawn with [yellow]-10/20/30%[fg] max HP',
['vulnerability'] = '[fg]enemies take [yellow]+10/20/30%[fg] damage',
['temporal_chains'] = '[fg]enemies are [yellow]10/20/30%[fg] slower',
['ceremonial_dagger'] = '[fg]killing an enemy fires a homing dagger',
['homing_barrage'] = '[yellow]8/16/24%[fg] chance to release a homing barrage on enemy kill',
['critical_strike'] = '[yellow]5/10/15%[fg] chance for attacks to critically strike, dealing [yellow]2x[fg] damage',
['noxious_strike'] = '[yellow]8/16/24%[fg] chance for attacks to poison, dealing [yellow]20%[fg] dps for [yellow]3[fg] seconds',
['infesting_strike'] = '[yellow]10/20/30%[fg] chance for attacks to spawn [yellow]2[fg] critters on kill',
['kinetic_strike'] = '[yellow]10/20/30%[fg] chance for attacks to push enemies away with high force',
['burning_strike'] = '[yellow]15%[fg] chance for attacks to burn, dealing [yellow]20%[fg] dps for [yellow]3[fg] seconds',
['lucky_strike'] = '[yellow]8%[fg] chance for attacks to cause enemies to drop gold on death',
['healing_strike'] = '[yellow]8%[fg] chance for attacks to spawn a healing orb on kill',
['stunning_strike'] = '[yellow]8/16/24%[fg] chance for attacks to stun for [yellow]2[fg] seconds',
['silencing_strike'] = '[yellow]8/16/24%[fg] chance for attacks to silence for [yellow]2[fg] seconds on hit',
['warping_shots'] = 'projectiles ignore wall collisions and warp around the screen [yellow]1/2/3[fg] times',
['culling_strike'] = '[fg]instantly kill elites below [yellow]10/20/30%[fg] max HP',
['lightning_strike'] = '[yellow]5/10/15%[fg] chance for projectiles to create chain lightning, dealing [yellow]60/80/100%[fg] damage',
['psycholeak'] = '[fg]position [yellow]1[fg] generates [yellow]1[fg] psyker orb every [yellow]10[fg] seconds',
['divine_blessing'] = '[fg]generate [yellow]1[fg] healing orb every [yellow]8[fg] seconds',
}
local ts = function(lvl, a, b, c) return '[' .. ylb1(lvl) .. ']' .. tostring(a) .. '[light_bg]/[' .. ylb2(lvl) .. ']' .. tostring(b) .. '[light_bg]/[' .. ylb3(lvl) .. ']' .. tostring(c) .. '[fg]' end
@ -1333,9 +1428,28 @@ function init()
['orbitism'] = function(lvl) return ts(lvl, '+33%', '66%', '99%') .. ' orb movement speed' end,
['psyker_orbs'] = function(lvl) return ts(lvl, '+1', '2', '3') .. ' psyker orbs' end,
['psychosense'] = function(lvl) return ts(lvl, '+33%', '66%', '99%') .. ' orb range' end,
['rearm'] = function(lvl) return '[fg]summons repeat their attacks once' end,
['taunt'] = function(lvl) return ts(lvl, '10%', '20%', '30%') .. ' chance for summons to taunt nearby enemies on attack' end,
['summon_instability'] = function(lvl) return '[fg]summons explode when disappearing, dealing [yellow]100/150/200%[fg] damage' end,
['rearm'] = function(lvl) return '[fg]constructs repeat their attacks once' end,
['taunt'] = function(lvl) return ts(lvl, '10%', '20%', '30%') .. ' chance for constructs to taunt nearby enemies on attack' end,
['construct_instability'] = function(lvl) return '[fg]constructs explode when disappearing, dealing ' .. ts(lvl, '100', '150', '200%') .. ' damage' end,
['intimidation'] = function(lvl) return '[fg]enemies spawn with ' .. ts(lvl, '-10', '20', '30%') .. ' max HP' end,
['vulnerability'] = function(lvl) return '[fg]enemies take ' .. ts(lvl, '+10', '20', '30%').. ' damage' end,
['temporal_chains'] = function(lvl) return '[fg]enemies are ' .. ts(lvl, '10', '20', '30%').. ' slower' end,
['ceremonial_dagger'] = function(lvl) return '[fg]killing an enemy fires a homing dagger' end,
['homing_barrage'] = function(lvl) return ts(lvl, '8', '16', '24%') .. ' chance to release a homing barrage on enemy kill' end,
['critical_strike'] = function(lvl) return ts(lvl, '5', '10', '15%') .. ' chance for attacks to critically strike, dealing [yellow]2x[fg] damage' end,
['noxious_strike'] = function(lvl) return ts(lvl, '8', '16', '24%') .. ' chance for attacks to poison, dealing [yellow]20%[fg] dps for [yellow]3[fg] seconds' end,
['infesting_strike'] = function(lvl) return ts(lvl, '10', '20', '30%') .. ' chance for attacks to spawn [yellow]2[fg] critters on kill' end,
['kinetic_strike'] = function(lvl) return ts(lvl, '10', '20', '30%') .. ' chance for attacks to push enemies away with high force' end,
['burning_strike'] = function(lvl) return '[yellow]15%[fg] chance for attacks to burn, dealing [yellow]20%[fg] dps for [yellow]3[fg] seconds' end,
['lucky_strike'] = function(lvl) return '[yellow]8%[fg] chance for attacks to cause enemies to drop gold on death' end,
['healing_strike'] = function(lvl) return '[yellow]8%[fg] chance for attacks to spawn a healing orb on kill' end,
['stunning_strike'] = function(lvl) return ts(lvl, '8', '16', '24%') .. ' chance for attacks to stun for [yellow]2[fg] seconds' end,
['silencing_strike'] = function(lvl) return ts(lvl, '8', '16', '24%') .. ' chance for attacks to silence for [yellow]2[fg] seconds on hit' end,
['warping_shots'] = function(lvl) return 'projectiles ignore wall collisions and warp around the screen ' .. ts(lvl, '1', '2', '3') .. ' times' end,
['culling_strike'] = function(lvl) return '[fg]instantly kill elites below ' .. ts(lvl, '10', '20', '30%') .. ' max HP' end,
['lightning_strike'] = function(lvl) return ts(lvl, '5', '10', '15%') .. ' chance for projectiles to create chain lightning, dealing ' .. ts(lvl, '60', '80', '100%') .. ' damage' end,
['psycholeak'] = function(lvl) return '[fg]position [yellow]1[fg] generates [yellow]1[fg] psyker orb every [yellow]10[fg] seconds' end,
['divine_blessing'] = function(lvl) return '[fg]generate [yellow]1[fg] healing orb every [yellow]8[fg] seconds' end,
}
level_to_tier_weights = {
@ -1571,7 +1685,7 @@ function init()
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', 'haste', 'rearm',
'magnetism', 'insurance', 'dividends', 'haste', 'rearm', 'ceremonial_dagger', 'burning_strike', 'lucky_strike', 'healing_strike', 'psycholeak', 'divine_blessing'
}
steam.userStats.requestCurrentStats()
@ -1584,8 +1698,10 @@ function init()
main_song_instance = _G[random:table{'song1', 'song2', 'song3', 'song4', 'song5'}]:play{volume = 0.5}
main = Main()
--[[
main:add(MainMenu'mainmenu')
main:go_to('mainmenu')
]]--
--[[
main:add(BuyScreen'buy_screen')
@ -1593,33 +1709,25 @@ function init()
-- main:go_to('buy_screen', 7, run.units or {}, {'unleash'})
]]--
--[[
gold = 10
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', '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', 'blessing', 'haste', 'divine_barrage', 'orbitism', 'psyker_orbs', 'psychosense', 'rearm', 'taunt', 'summon_instability',
'insurance', 'dividends', 'berserking', 'unwavering_stance', 'unrelenting_stance', 'blessing', 'haste', 'divine_barrage', 'orbitism', 'psyker_orbs', 'psychosense', 'rearm', 'taunt', 'construct_instability',
'intimidation', 'vulnerability', 'temporal_chains', 'ceremonial_dagger', 'homing_barrage', 'critical_strike', 'noxious_strike', 'infesting_strike', 'burning_strike', 'lucky_strike', 'healing_strike', 'stunning_strike',
'silencing_strike', 'culling_strike', 'lightning_strike', 'psycholeak', 'divine_blessing',
}
main:add(Arena'arena')
main:go_to('arena', 1, {
{character = 'vagrant', level = 3},
main:go_to('arena', 2, 0, {
{character = 'carver', level = 3},
-- {character = 'carver', level = 2},
{character = 'dual_gunner', level = 3},
-- {character = 'saboteur', level = 2},
-- {character = 'hunter', level = 2},
}, {
{passive = 'summon_instability', level = 3},
{passive = 'summon_instability', level = 3},
{passive = 'summon_instability', level = 3},
{passive = 'summon_instability', level = 3},
{passive = 'summon_instability', level = 3},
{passive = 'summon_instability', level = 3},
{passive = 'summon_instability', level = 3},
{passive = 'summon_instability', level = 3},
{passive = 'rearm', level = 1},
})
]]--
--[[
main:add(Media'media')
@ -1789,7 +1897,7 @@ function open_options(self)
'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', 'blessing', 'haste', 'divine_barrage', 'orbitism', 'psyker_orbs', 'psychosense', 'rearm', 'taunt', 'summon_instability',
'insurance', 'dividends', 'berserking', 'unwavering_stance', 'unrelenting_stance', 'blessing', 'haste', 'divine_barrage', 'orbitism', 'psyker_orbs', 'psychosense', 'rearm', 'taunt', 'construct_instability',
}
max_units = 7 + current_new_game_plus
main:add(BuyScreen'buy_screen')

View File

@ -310,7 +310,7 @@ function Unit:calculate_stats(first_run)
self.base_hp = 100*math.pow(2, self.level-1)
self.base_dmg = 10*math.pow(2, self.level-1)
self.base_mvspd = 75
elseif self:is(Illusion) then
elseif self:is(Automaton) 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

View File

@ -141,14 +141,14 @@ function Player:init(args)
if self.level == 3 then
for i = 1, 4 do
local check_circle = Circle(random:float(main.current.x1 + 16, main.current.x2 - 16), random:float(main.current.y1 + 16, main.current.y2 - 16), 2)
local objects = main.current.main:get_objects_in_shape(check_circle, {Seeker, EnemyCritter, Critter, Illusion, Volcano, Saboteur, Pet, Turret})
local objects = main.current.main:get_objects_in_shape(check_circle, {Seeker, EnemyCritter, Critter, Sentry, Automaton, Bomb, Volcano, Saboteur, Pet, Turret})
while #objects > 0 do
check_circle:move_to(random:float(main.current.x1 + 16, main.current.x2 - 16), random:float(main.current.y1 + 16, main.current.y2 - 16))
objects = main.current.main:get_objects_in_shape(check_circle, {Seeker, EnemyCritter, Critter, Illusion, Volcano, Saboteur, Pet, Turret})
objects = main.current.main:get_objects_in_shape(check_circle, {Seeker, EnemyCritter, Critter, Sentry, Automaton, Bomb, Volcano, Saboteur, Pet, Turret})
end
SpawnEffect{group = main.current.effects, x = check_circle.x, y = check_circle.y, color = green[0], action = function(x, y)
local check_circle = Circle(x, y, 2)
local objects = main.current.main:get_objects_in_shape(check_circle, {Seeker, EnemyCritter, Critter, Illusion, Volcano, Saboteur, Pet, Turret})
local objects = main.current.main:get_objects_in_shape(check_circle, {Seeker, EnemyCritter, Critter, Sentry, Automaton, Bomb, Volcano, Saboteur, Pet, Turret})
if #objects == 0 then
HealingOrb{group = main.current.main, x = x, y = y}
end
@ -156,14 +156,14 @@ function Player:init(args)
end
else
local check_circle = Circle(random:float(main.current.x1 + 16, main.current.x2 - 16), random:float(main.current.y1 + 16, main.current.y2 - 16), 2)
local objects = main.current.main:get_objects_in_shape(check_circle, {Seeker, EnemyCritter, Critter, Illusion, Volcano, Saboteur, Pet, Turret})
local objects = main.current.main:get_objects_in_shape(check_circle, {Seeker, EnemyCritter, Critter, Sentry, Automaton, Bomb, Volcano, Saboteur, Pet, Turret})
while #objects > 0 do
check_circle:move_to(random:float(main.current.x1 + 16, main.current.x2 - 16), random:float(main.current.y1 + 16, main.current.y2 - 16))
objects = main.current.main:get_objects_in_shape(check_circle, {Seeker, EnemyCritter, Critter, Illusion, Volcano, Saboteur, Pet, Turret})
objects = main.current.main:get_objects_in_shape(check_circle, {Seeker, EnemyCritter, Critter, Sentry, Automaton, Bomb, Volcano, Saboteur, Pet, Turret})
end
SpawnEffect{group = main.current.effects, x = check_circle.x, y = check_circle.y, color = green[0], action = function(x, y)
local check_circle = Circle(x, y, 2)
local objects = main.current.main:get_objects_in_shape(check_circle, {Seeker, EnemyCritter, Critter, Illusion, Volcano, Saboteur, Pet, Turret})
local objects = main.current.main:get_objects_in_shape(check_circle, {Seeker, EnemyCritter, Critter, Sentry, Automaton, Bomb, Volcano, Saboteur, Pet, Turret})
if #objects == 0 then
HealingOrb{group = main.current.main, x = x, y = y}
end
@ -205,32 +205,15 @@ function Player:init(args)
end
end, nil, nil, 'shoot')
elseif self.character == 'illusionist' then
elseif self.character == 'artificer' then
self.sorcerer_count = 0
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))
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))
end)
end
end
end
end, nil, nil, 'shoot')
self.t:every(8, function()
self.t:every(0.25, function()
self.t:every(6, 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}
artificer1:play{pitch = random:float(0.95, 1.05), volume = 0.5}
local check_circle = Circle(self.x, self.y, 2)
local objects = main.current.main:get_objects_in_shape(check_circle, {Seeker, EnemyCritter, Critter, Illusion, Volcano, Saboteur, Pet, Turret})
if #objects == 0 then Illusion{group = main.current.main, x = x, y = y, parent = self, level = self.level, conjurer_buff_m = self.conjurer_buff_m or 1} end
local objects = main.current.main:get_objects_in_shape(check_circle, {Seeker, EnemyCritter, Critter, Volcano, Saboteur, Pet, Turret, Sentry, Bomb})
if #objects == 0 then Automaton{group = main.current.main, x = x, y = y, parent = self, level = self.level, conjurer_buff_m = self.conjurer_buff_m or 1} end
end}
if main.current.sorcerer_level > 0 then
self.sorcerer_count = self.sorcerer_count + 1
@ -239,15 +222,14 @@ function Player:init(args)
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)
illusion1:play{pitch = random:float(0.95, 1.05), volume = 0.5}
artificer1:play{pitch = random:float(0.95, 1.05), volume = 0.5}
local check_circle = Circle(self.x, self.y, 2)
local objects = main.current.main:get_objects_in_shape(check_circle, {Seeker, EnemyCritter, Critter, Illusion, Volcano, Saboteur, Pet, Turret})
if #objects == 0 then Illusion{group = main.current.main, x = x, y = y, parent = self, level = self.level, conjurer_buff_m = self.conjurer_buff_m or 1} end
local objects = main.current.main:get_objects_in_shape(check_circle, {Seeker, EnemyCritter, Critter, Volcano, Saboteur, Pet, Turret, Sentry, Bomb})
if #objects == 0 then Automaton{group = main.current.main, x = x, y = y, parent = self, level = self.level, conjurer_buff_m = self.conjurer_buff_m or 1} end
end}
end)
end
end
end, self.level == 3 and 2 or 1)
end)
elseif self.character == 'outlaw' then
@ -316,6 +298,13 @@ function Player:init(args)
end, 2)
end, nil, nil, 'spawn')
elseif self.character == 'bomber' then
self.t:every(8, function()
SpawnEffect{group = main.current.effects, x = self.x, y = self.y, color = self.color, action = function(x, y)
Bomb{group = main.current.main, x = x, y = y, parent = self, level = self.level, conjurer_buff_m = self.conjurer_buff_m or 1}
end}
end, nil, nil, 'spawn')
elseif self.character == 'stormweaver' then
self.t:every(8, function()
stormweaver1:play{pitch = random:float(0.95, 1.05), volume = 0.5}
@ -363,7 +352,7 @@ function Player:init(args)
x, y = x/2, y/2
main.current.t:every_immediate(0.1, function()
local check_circle = Circle(x, y, 2)
local objects = main.current.main:get_objects_in_shape(check_circle, {Player, Seeker, EnemyCritter, Critter, Illusion, Saboteur, Pet, Turret})
local objects = main.current.main:get_objects_in_shape(check_circle, {Player, Seeker, EnemyCritter, Critter, Saboteur, Pet, Turret, Sentry, Bomb})
if #objects == 0 then
Volcano{group = main.current.main, x = x, y = y, color = self.color, parent = self, rs = 24, level = self.level}
main.current.t:cancel('volcano_spawn')
@ -619,7 +608,12 @@ function Player:init(args)
elseif self.character == 'carver' then
self.t:every(16, function()
Tree{group = main.current.main, x = self.x, y = self.y, color = self.color, parent = self, rs = self.area_size_m*(self.level == 3 and 128 or 64), level = self.level}
Tree{group = main.current.main, x = self.x, y = self.y, color = self.color, parent = self, level = self.level}
end, nil, nil, 'spawn')
elseif self.character == 'sentry' then
self.t:every(7, function()
Sentry{group = main.current.main, x = self.x, y = self.y, color = self.color, parent = self, level = self.level}
end, nil, nil, 'spawn')
elseif self.character == 'bane' then
@ -697,14 +691,14 @@ function Player:init(args)
buff1:play{pitch = random:float(0.95, 1.05), volume = 0.5}
for i = 1, 2 do
local check_circle = Circle(random:float(main.current.x1 + 16, main.current.x2 - 16), random:float(main.current.y1 + 16, main.current.y2 - 16), 2)
local objects = main.current.main:get_objects_in_shape(check_circle, {Seeker, EnemyCritter, Critter, Illusion, Volcano, Saboteur, Pet, Turret})
local objects = main.current.main:get_objects_in_shape(check_circle, {Seeker, EnemyCritter, Critter, Volcano, Saboteur, Bomb, Pet, Turret, Sentry})
while #objects > 0 do
check_circle:move_to(random:float(main.current.x1 + 16, main.current.x2 - 16), random:float(main.current.y1 + 16, main.current.y2 - 16))
objects = main.current.main:get_objects_in_shape(check_circle, {Seeker, EnemyCritter, Critter, Illusion, Volcano, Saboteur, Pet, Turret})
objects = main.current.main:get_objects_in_shape(check_circle, {Seeker, EnemyCritter, Critter, Volcano, Saboteur, Pet, Turret, Sentry, Bomb})
end
SpawnEffect{group = main.current.effects, x = check_circle.x, y = check_circle.y, color = green[0], action = function(x, y)
local check_circle = Circle(x, y, 2)
local objects = main.current.main:get_objects_in_shape(check_circle, {Seeker, EnemyCritter, Critter, Illusion, Volcano, Saboteur, Pet, Turret})
local objects = main.current.main:get_objects_in_shape(check_circle, {Seeker, EnemyCritter, Critter, Volcano, Saboteur, Bomb, Pet, Turret, Sentry})
if #objects == 0 then
HealingOrb{group = main.current.main, x = x, y = y}
end
@ -720,14 +714,14 @@ function Player:init(args)
heal1:play{pitch = random:float(0.95, 1.05), volume = 0.5}
buff1:play{pitch = random:float(0.95, 1.05), volume = 0.5}
local check_circle = Circle(random:float(main.current.x1 + 16, main.current.x2 - 16), random:float(main.current.y1 + 16, main.current.y2 - 16), 2)
local objects = main.current.main:get_objects_in_shape(check_circle, {Seeker, EnemyCritter, Critter, Illusion, Volcano, Saboteur, Pet, Turret})
local objects = main.current.main:get_objects_in_shape(check_circle, {Seeker, EnemyCritter, Critter, Volcano, Saboteur, Bomb, Pet, Turret, Sentry})
while #objects > 0 do
check_circle:move_to(random:float(main.current.x1 + 16, main.current.x2 - 16), random:float(main.current.y1 + 16, main.current.y2 - 16))
objects = main.current.main:get_objects_in_shape(check_circle, {Seeker, EnemyCritter, Critter, Illusion, Volcano, Saboteur, Pet, Turret})
objects = main.current.main:get_objects_in_shape(check_circle, {Seeker, EnemyCritter, Critter, Volcano, Saboteur, Bomb, Pet, Turret, Sentry})
end
SpawnEffect{group = main.current.effects, x = check_circle.x, y = check_circle.y, color = green[0], action = function(x, y)
local check_circle = Circle(x, y, 2)
local objects = main.current.main:get_objects_in_shape(check_circle, {Seeker, EnemyCritter, Critter, Illusion, Volcano, Saboteur, Pet, Turret})
local objects = main.current.main:get_objects_in_shape(check_circle, {Seeker, EnemyCritter, Critter, Volcano, Saboteur, Pet, Turret, Sentry, Bomb})
if #objects == 0 then
HealingOrb{group = main.current.main, x = x, y = y}
end
@ -812,7 +806,7 @@ function Player:init(args)
for i = 1, 5 do
SpawnEffect{group = main.current.effects, x = x, y = y, color = green[0], action = function(x, y)
local check_circle = Circle(x, y, 2)
local objects = main.current.main:get_objects_in_shape(check_circle, {Seeker, EnemyCritter, Critter, Illusion, Volcano, Saboteur, Pet, Turret})
local objects = main.current.main:get_objects_in_shape(check_circle, {Seeker, EnemyCritter, Critter, Volcano, Saboteur, Bomb, Pet, Turret, Sentry, Automaton})
if #objects == 0 then HealingOrb{group = main.current.main, x = x, y = y} end
end}
end
@ -1090,6 +1084,24 @@ function Player:init(args)
end)
end
if self.leader and self.psycholeak then
main.current.t:every(10, function()
local unit = main.current.player
Projectile{group = main.current.main, x = unit.x + 24*math.cos(unit.r), y = unit.y + 24*math.sin(unit.r), color = fg[0], v = 200, dmg = unit.dmg, character = 'psyker', parent = unit}
end)
end
if self.leader and self.divine_blessing then
main.current.t:every(8, function()
local x, y = random:float(main.current.x1 + 16, main.current.x2 - 16), random:float(main.current.y1 + 16, main.current.y2 - 16)
SpawnEffect{group = main.current.effects, x = x, y = y, color = green[0], action = function(x, y)
local check_circle = Circle(x, y, 2)
local objects = main.current.main:get_objects_in_shape(check_circle, {Seeker, EnemyCritter, Critter, Volcano, Saboteur, Bomb, Pet, Turret, Sentry, Automaton})
if #objects == 0 then HealingOrb{group = main.current.main, x = x, y = y} end
end}
end)
end
self.mouse_control_v_buffer = {}
if main.current:is(MainMenu) then
@ -1516,14 +1528,14 @@ function Player:hit(damage, from_undead)
if self.stored_heal > (0.2*self.max_hp) then
self.stored_heal = 0
local check_circle = Circle(random:float(main.current.x1 + 16, main.current.x2 - 16), random:float(main.current.y1 + 16, main.current.y2 - 16), 2)
local objects = main.current.main:get_objects_in_shape(check_circle, {Seeker, EnemyCritter, Critter, Illusion, Volcano, Saboteur, Pet, Turret})
local objects = main.current.main:get_objects_in_shape(check_circle, {Seeker, EnemyCritter, Critter, Volcano, Saboteur, Bomb, Pet, Turret, Sentry, Automaton})
while #objects > 0 do
check_circle:move_to(random:float(main.current.x1 + 16, main.current.x2 - 16), random:float(main.current.y1 + 16, main.current.y2 - 16))
objects = main.current.main:get_objects_in_shape(check_circle, {Seeker, EnemyCritter, Critter, Illusion, Volcano, Saboteur, Pet, Turret})
objects = main.current.main:get_objects_in_shape(check_circle, {Seeker, EnemyCritter, Critter, Volcano, Saboteur, Bomb, Pet, Turret, Sentry, Automaton})
end
SpawnEffect{group = main.current.effects, x = check_circle.x, y = check_circle.y, color = green[0], action = function(x, y)
local check_circle = Circle(x, y, 2)
local objects = main.current.main:get_objects_in_shape(check_circle, {Seeker, EnemyCritter, Critter, Illusion, Volcano, Saboteur, Pet, Turret})
local objects = main.current.main:get_objects_in_shape(check_circle, {Seeker, EnemyCritter, Critter, Sentry, Volcano, Saboteur, Bomb, Pet, Turret, Automaton})
if #objects == 0 then
HealingOrb{group = main.current.main, x = x, y = y}
end
@ -1817,7 +1829,7 @@ function Player:shoot(r, mods)
Projectile(table.merge(t, mods or {}))
end
if self.character == 'vagrant' or self.character == 'illusionist' then
if self.character == 'vagrant' or self.character == 'artificer' then
shoot1:play{pitch = random:float(0.95, 1.05), volume = 0.2}
elseif self.character == 'dual_gunner' then
dual_gunner1:play{pitch = random:float(0.95, 1.05), volume = 0.3}
@ -1886,7 +1898,7 @@ function Player:dot_attack(area, mods)
end
function Player:barrage(r, n, pierce, ricochet, shoot_5)
function Player:barrage(r, n, pierce, ricochet, shoot_5, homing)
n = n or 8
for i = 1, n do
self.t:after((i-1)*0.075, function()
@ -1894,7 +1906,7 @@ function Player:barrage(r, n, pierce, ricochet, shoot_5)
else archer1:play{pitch = random:float(0.95, 1.05), volume = 0.35} end
HitCircle{group = main.current.effects, x = self.x + 0.8*self.shape.w*math.cos(r), y = self.y + 0.8*self.shape.w*math.sin(r), rs = 6}
local t = {group = main.current.main, x = self.x + 1.6*self.shape.w*math.cos(r), y = self.y + 1.6*self.shape.w*math.sin(r), v = 250, r = r + random:float(-math.pi/16, math.pi/16), color = self.color, dmg = self.dmg,
parent = self, character = 'barrage', level = self.level, pierce = pierce or 0, ricochet = ricochet or 0, shoot_5 = shoot_5}
parent = self, character = 'barrage', level = self.level, pierce = pierce or 0, ricochet = ricochet or 0, shoot_5 = shoot_5, homing = homing}
Projectile(table.merge(t, mods or {}))
end)
end
@ -1909,7 +1921,7 @@ Projectile:implement(Physics)
function Projectile:init(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', main.current.player.warping_shots and 'true_ghost' or 'projectile')
self.pierce = args.pierce or 0
self.chain = args.chain or 0
self.ricochet = args.ricochet or 0
@ -1986,7 +1998,7 @@ function Projectile:init(args)
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 = 2, character = 'arcanist_projectile',
parent = self.parent, level = self.parent.level}
local check_circle = Circle(t.x, t.y, 2)
local objects = main.current.main:get_objects_in_shape(check_circle, {Player, Seeker, EnemyCritter, Critter, Illusion, Volcano, Saboteur, Pet, Turret})
local objects = main.current.main:get_objects_in_shape(check_circle, {Player, Seeker, EnemyCritter, Critter, Sentry, Volcano, Saboteur, Bomb, Pet, Turret, Automaton})
if #objects == 0 then Projectile(table.merge(t, mods or {})) end
end
end)
@ -2162,7 +2174,7 @@ function Projectile:on_collision_enter(other, contact)
else r = 0 end
if other:is(Wall) then
if self.character == 'archer' or self.character == 'hunter' or self.character == 'barrage' or self.character == 'barrager' then
if self.character == 'archer' or self.character == 'hunter' or self.character == 'barrage' or self.character == 'barrager' or self.character == 'sentry' then
if self.ricochet <= 0 then
self:die(x, y, r, 0)
WallArrow{group = main.current.main, x = x, y = y, r = self.r, color = self.color}
@ -2183,7 +2195,7 @@ function Projectile:on_collision_enter(other, contact)
if self.character == 'spellblade' then
magic_area1:play{pitch = random:float(0.95, 1.05), volume = 0.075}
end
elseif self.character == 'illusionist_death' then
elseif self.character == 'artificer_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}
@ -2249,7 +2261,7 @@ function Projectile:on_trigger_enter(other, contact)
if self.character == 'archer' or self.character == 'scout' or self.character == 'outlaw' or self.character == 'blade' or self.character == 'hunter' or self.character == 'spellblade' or self.character == 'engineer' or
self.character == 'jester' or self.character == 'assassin' or self.character == 'barrager' or self.character == 'beastmaster' or self.character == 'witch' or self.character == 'miner' or self.character == 'thief' or
self.character == 'psyker' then
self.character == 'psyker' or self.character == 'sentry' then
hit2:play{pitch = random:float(0.95, 1.05), volume = 0.35}
if self.character == 'spellblade' or self.character == 'psyker' then
magic_area1:play{pitch = random:float(0.95, 1.05), volume = 0.15}
@ -2312,6 +2324,26 @@ function Projectile:on_trigger_enter(other, contact)
end
end
if self.parent and self.parent.lightning_strike then
if random:bool((self.parent.lightning_strike == 1 and 5) or (self.parent.lightning_strike == 2 and 10) or (self.parent.lightning_strike == 3 and 15)) then
local src = other
for j = 1, 3 do
main.current.t:after((j-1)*0.1, function()
_G[random:table{'spark1', 'spark2', 'spark3'}]:play{pitch = random:float(0.9, 1.1), volume = 0.3}
for i = 1, 3 do
table.insert(self.infused_enemies_hit, src)
local dst = src:get_random_object_in_shape(Circle(src.x, src.y, 64), main.current.enemies, self.infused_enemies_hit)
if dst then
dst:hit(0.33*((self.parent.lightning_strike == 1 and 0.6) or (self.parent.lightning_strike == 2 and 0.8) or (self.parent.lightning_strike == 3 and 1))*self.dmg*(self.distance_dmg_m or 1))
LightningLine{group = main.current.effects, src = src, dst = dst}
src = dst
end
end
end)
end
end
end
if self.crit then
camera:shake(5, 0.25)
rogue_crit1:play{pitch = random:float(0.95, 1.05), volume = 0.5}
@ -2388,7 +2420,7 @@ function Area:init(args)
elseif self.character == 'blade' then
blade_hit1:play{pitch = random:float(0.9, 1.1), volume = 0.35}
hit2:play{pitch = random:float(0.95, 1.05), volume = 0.2}
elseif self.character == 'saboteur' or self.character == 'pyromancer' then
elseif self.character == 'saboteur' or self.character == 'pyromancer' or self.character == 'bomber' then
if self.character == 'pyromancer' then pyro2:play{pitch = random:float(0.95, 1.05), volume = 0.4} end
_G[random:table{'saboteur_hit1', 'saboteur_hit2'}]:play{pitch = random:float(0.95, 1.05), volume = 0.2}
elseif self.character == 'cannoneer' then
@ -2729,7 +2761,7 @@ function Tree:init(args)
self:set_restitution(0.5)
self.hfx:add('hit', 1)
self.color = orange[0]
self.heal_sensor = Circle(self.x, self.y, args.rs)
self.heal_sensor = Circle(self.x, self.y, 48)
self.vr = 0
self.dvr = random:float(-math.pi/4, math.pi/4)
@ -2743,6 +2775,39 @@ function Tree:init(args)
self.t:tween(0.05, self, {rs = args.rs}, math.cubic_in_out, function() self.spring:pull(0.15) end)
self.t:after(0.2, function() self.color = args.color end)
self.t:every(self.parent.level == 3 and 3 or 6, function()
self.hfx:use('hit', 0.2)
HealingOrb{group = main.current.main, x = self.x, y = self.y}
if self.parent.taunt and random:bool((self.parent.taunt == 1 and 10) or (self.parent.taunt == 2 and 20) or (self.parent.taunt == 3 and 30)) then
local enemies = self:get_objects_in_shape(Circle(self.x, self.y, 96), main.current.enemies)
if #enemies > 0 then
for _, enemy in ipairs(enemies) do
enemy.taunted = self
enemy.t:after(4, function() enemy.taunted = false end, 'taunt')
end
end
end
if self.parent.rearm then
self.t:after(0.25, function()
self.hfx:use('hit', 0.2)
HealingOrb{group = main.current.main, x = self.x, y = self.y}
if self.parent.taunt and random:bool((self.parent.taunt == 1 and 10) or (self.parent.taunt == 2 and 20) or (self.parent.taunt == 3 and 30)) then
local enemies = self:get_objects_in_shape(Circle(self.x, self.y, 96), main.current.enemies)
if #enemies > 0 then
for _, enemy in ipairs(enemies) do
enemy.taunted = self
enemy.t:after(4, function() enemy.taunted = false end, 'taunt')
end
end
end
end)
end
end)
--[[
self.t:cooldown(3.33/(self.level == 3 and 2 or 1), function() return #self:get_objects_in_shape(self.heal_sensor, {Player}) > 0 end, 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
@ -2815,14 +2880,15 @@ function Tree:init(args)
end
end
end)
]]--
self.t:after(10*(self.parent.conjurer_buff_m or 1), function()
self.t:after(12*(self.parent.conjurer_buff_m or 1), function()
self.t:every_immediate(0.05, function() self.hidden = not self.hidden end, 7, function()
self.dead = true
if self.parent.summon_instability then
if self.parent.construct_instability then
camera:shake(2, 0.5)
local n = (self.parent.summon_instability == 1 and 1) or (self.parent.summon_instability == 2 and 1.5) or (self.parent.summon_instability == 3 and 2) or 1
local n = (self.parent.construct_instability == 1 and 1) or (self.parent.construct_instability == 2 and 1.5) or (self.parent.construct_instability == 3 and 2) or 1
Area{group = main.current.effects, x = self.x, y = self.y, r = self.r, w = self.parent.area_size_m*48, color = self.color, dmg = n*self.parent.dmg*self.parent.area_dmg_m, parent = self.parent}
_G[random:table{'cannoneer1', 'cannoneer2'}]:play{pitch = random:float(0.95, 1.05), volume = 0.5}
end
@ -2974,6 +3040,97 @@ end
Sentry = Object:extend()
Sentry:implement(GameObject)
Sentry:implement(Physics)
function Sentry:init(args)
self:init_game_object(args)
self:set_as_rectangle(6, 6, 'static', 'player')
self:set_restitution(0.5)
self.hfx:add('hit', 1)
self.t:after(15*(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.construct_instability then
camera:shake(2, 0.5)
local n = (self.parent.construct_instability == 1 and 1) or (self.parent.construct_instability == 2 and 1.5) or (self.parent.construct_instability == 3 and 2) or 1
Area{group = main.current.effects, x = self.x, y = self.y, r = self.r, w = self.parent.area_size_m*48, color = self.color, dmg = n*self.parent.dmg*self.parent.area_dmg_m, parent = self.parent}
_G[random:table{'cannoneer1', 'cannoneer2'}]:play{pitch = random:float(0.95, 1.05), volume = 0.5}
end
end)
self.t:every({2.75, 3.5}, function()
self.hfx:use('hit', 0.25, 200, 10)
local r = self.r
for i = 1, 4 do
archer1:play{pitch = random:float(0.95, 1.05), volume = 0.35}
HitCircle{group = main.current.effects, x = self.x + 0.8*self.shape.w*math.cos(r), y = self.y + 0.8*self.shape.w*math.sin(r), rs = 6}
local t = {group = main.current.main, x = self.x + 1.6*self.shape.w*math.cos(r), y = self.y + 1.6*self.shape.w*math.sin(r), v = 200, r = r, color = self.color,
dmg = self.parent.dmg*(self.parent.conjurer_buff_m or 1), character = 'sentry', parent = self.parent}
Projectile(table.merge(t, mods or {}))
r = r + math.pi/2
end
if self.parent.taunt and random:bool((self.parent.taunt == 1 and 10) or (self.parent.taunt == 2 and 20) or (self.parent.taunt == 3 and 30)) then
local enemies = self:get_objects_in_shape(Circle(self.x, self.y, 96), main.current.enemies)
if #enemies > 0 then
for _, enemy in ipairs(enemies) do
enemy.taunted = self
enemy.t:after(4, function() enemy.taunted = false end, 'taunt')
end
end
end
if self.parent.rearm then
self.t:after(0.25, function()
self.hfx:use('hit', 0.25, 200, 10)
local r = self.r
for i = 1, 4 do
archer1:play{pitch = random:float(0.95, 1.05), volume = 0.35}
HitCircle{group = main.current.effects, x = self.x + 0.8*self.shape.w*math.cos(r), y = self.y + 0.8*self.shape.w*math.sin(r), rs = 6}
local t = {group = main.current.main, x = self.x + 1.6*self.shape.w*math.cos(r), y = self.y + 1.6*self.shape.w*math.sin(r), v = 200, r = r, color = self.color,
dmg = self.parent.dmg*(self.parent.conjurer_buff_m or 1), character = 'sentry', parent = self.parent}
Projectile(table.merge(t, mods or {}))
r = r + math.pi/2
end
if self.parent.taunt and random:bool((self.parent.taunt == 1 and 10) or (self.parent.taunt == 2 and 20) or (self.parent.taunt == 3 and 30)) then
local enemies = self:get_objects_in_shape(Circle(self.x, self.y, 96), main.current.enemies)
if #enemies > 0 then
for _, enemy in ipairs(enemies) do
enemy.taunted = self
enemy.t:after(4, function() enemy.taunted = false end, 'taunt')
end
end
end
end)
end
end)
end
function Sentry:update(dt)
self:update_game_object(dt)
self.r = self.r + math.pi*dt
self:set_angle(self.r)
end
function Sentry:draw()
if self.hidden then return end
graphics.push(self.x, self.y, self.r, self.spring.x, self.spring.x)
graphics.rectangle(self.x, self.y, 2*self.shape.w, 4, 2, 2, self.hfx.hit.f and fg[0] or self.color)
graphics.rectangle(self.x, self.y, 4, 2*self.shape.h, 2, 2, self.hfx.hit.f and fg[0] or self.color)
graphics.pop()
end
Turret = Object:extend()
Turret:implement(GameObject)
Turret:implement(Physics)
@ -3038,9 +3195,9 @@ function Turret:init(args)
HitCircle{group = main.current.effects, x = self.x, y = self.y}:scale_down()
self.dead = true
if self.parent.summon_instability then
if self.parent.construct_instability then
camera:shake(2, 0.5)
local n = (self.parent.summon_instability == 1 and 1) or (self.parent.summon_instability == 2 and 1.5) or (self.parent.summon_instability == 3 and 2) or 1
local n = (self.parent.construct_instability == 1 and 1) or (self.parent.construct_instability == 2 and 1.5) or (self.parent.construct_instability == 3 and 2) or 1
Area{group = main.current.effects, x = self.x, y = self.y, r = self.r, w = self.parent.area_size_m*48, color = self.color, dmg = n*self.parent.dmg*self.parent.area_dmg_m, parent = self.parent}
_G[random:table{'cannoneer1', 'cannoneer2'}]:play{pitch = random:float(0.95, 1.05), volume = 0.5}
end
@ -3135,9 +3292,9 @@ function Pet:on_collision_enter(other, contact)
else
self.dead = true
if self.parent.summon_instability then
if self.parent.construct_instability then
camera:shake(2, 0.5)
local n = (self.parent.summon_instability == 1 and 1) or (self.parent.summon_instability == 2 and 1.5) or (self.parent.summon_instability == 3 and 2) or 1
local n = (self.parent.construct_instability == 1 and 1) or (self.parent.construct_instability == 2 and 1.5) or (self.parent.construct_instability == 3 and 2) or 1
Area{group = main.current.effects, x = self.x, y = self.y, r = self.r, w = self.parent.area_size_m*48, color = self.color, dmg = n*self.parent.dmg*self.parent.area_dmg_m, parent = self.parent}
_G[random:table{'cannoneer1', 'cannoneer2'}]:play{pitch = random:float(0.95, 1.05), volume = 0.5}
end
@ -3173,6 +3330,74 @@ end
Bomb = Object:extend()
Bomb:implement(GameObject)
Bomb:implement(Physics)
function Bomb:init(args)
self:init_game_object(args)
self:set_as_rectangle(8, 8, 'static', 'player')
self:set_restitution(0.5)
self.hfx:add('hit', 1)
mine1:play{pitch = random:float(0.95, 1.05), volume = 0.5}
self.color = orange[0]
self.dmg = 2*get_character_stat('bomber', self.level, 'dmg')
self.t:after(8, function() self:explode() end)
end
function Bomb:update(dt)
self:update_game_object(dt)
end
function Bomb: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
function Bomb:explode()
camera:shake(4, 0.5)
local t = {group = main.current.effects, x = self.x, y = self.y, r = self.r, w = self.parent.area_size_m*64*(self.level == 3 and 2 or 1), color = self.color,
dmg = self.parent.area_dmg_m*self.dmg*(self.parent.conjurer_buff_m or 1)*(self.level == 3 and 2 or 1), character = self.character, parent = self.parent}
Area(table.merge(t, mods or {}))
if not self.parent.construct_instability and not self.parent.rearm then self.dead = true end
_G[random:table{'cannoneer1', 'cannoneer2'}]:play{pitch = random:float(0.95, 1.05), volume = 0.5}
_G[random:table{'saboteur_hit1', 'saboteur_hit2'}]:play{pitch = random:float(0.95, 1.05), volume = 0.5}
explosion1:play{pitch = random:float(0.95, 1.05), volume = 0.5}
self.t:after(0.25, function()
if self.parent.construct_instability then
camera:shake(2, 0.5)
local n = (self.parent.construct_instability == 1 and 1) or (self.parent.construct_instability == 2 and 1.5) or (self.parent.construct_instability == 3 and 2) or 1
Area{group = main.current.effects, x = self.x, y = self.y, r = self.r + random:float(-math.pi/16, math.pi/16), w = self.parent.area_size_m*48*(self.level == 3 and 2 or 1), color = self.color,
dmg = n*self.parent.dmg*self.parent.area_dmg_m*(self.level == 3 and 2 or 1), parent = self.parent}
_G[random:table{'cannoneer1', 'cannoneer2'}]:play{pitch = random:float(0.95, 1.05), volume = 0.5}
self.dead = true
end
if self.parent.rearm then
camera:shake(2, 0.5)
local n = (self.parent.construct_instability == 1 and 1) or (self.parent.construct_instability == 2 and 1.5) or (self.parent.construct_instability == 3 and 2) or 1
Area{group = main.current.effects, x = self.x, y = self.y, r = self.r + random:float(-math.pi/16, math.pi/16), w = self.parent.area_size_m*48*(self.level == 3 and 2 or 1), color = self.color,
dmg = n*self.parent.dmg*self.parent.area_dmg_m*(self.level == 3 and 2 or 1), parent = self.parent}
_G[random:table{'cannoneer1', 'cannoneer2'}]:play{pitch = random:float(0.95, 1.05), volume = 0.5}
self.dead = true
end
end)
end
function Bomb:on_collision_enter(other, contact)
if table.any(main.current.enemies, function(v) return other:is(v) end) then
self:explode()
end
end
Saboteur = Object:extend()
Saboteur:implement(GameObject)
@ -3232,10 +3457,10 @@ function Saboteur:on_collision_enter(other, contact)
dmg = (self.crit and 2 or 1)*self.area_dmg_m*self.actual_dmg*(self.conjurer_buff_m or 1), character = self.character, parent = self.parent}
Area(table.merge(t, mods or {}))
if self.parent.summon_instability then
if self.parent.construct_instability then
self.t:after(0.25, function()
camera:shake(2, 0.5)
local n = (self.parent.summon_instability == 1 and 1) or (self.parent.summon_instability == 2 and 1.5) or (self.parent.summon_instability == 3 and 2) or 1
local n = (self.parent.construct_instability == 1 and 1) or (self.parent.construct_instability == 2 and 1.5) or (self.parent.construct_instability == 3 and 2) or 1
Area{group = main.current.effects, x = self.x, y = self.y, r = self.r, w = self.parent.area_size_m*48, color = self.color, dmg = n*self.parent.dmg*self.parent.area_dmg_m, parent = self.parent}
_G[random:table{'cannoneer1', 'cannoneer2'}]:play{pitch = random:float(0.95, 1.05), volume = 0.5}
self.dead = true
@ -3248,18 +3473,18 @@ end
Illusion = Object:extend()
Illusion:implement(GameObject)
Illusion:implement(Physics)
Illusion:implement(Unit)
function Illusion:init(args)
Automaton = Object:extend()
Automaton:implement(GameObject)
Automaton:implement(Physics)
Automaton:implement(Unit)
function Automaton: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.color = character_colors.artificer
self.character = 'artificer'
self.classes = {'sorcerer', 'conjurer'}
self:calculate_stats(true)
self:set_as_steerable(self.v, 2000, 4*math.pi, 4)
@ -3268,10 +3493,12 @@ function Illusion:init(args)
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}
turret1:play{pitch = random:float(0.95, 1.05), volume = 0.10}
turret2:play{pitch = random:float(0.95, 1.05), volume = 0.10}
wizard1:play{pitch = random:float(0.95, 1.05), volume = 0.10}
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',
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 = 'artificer',
parent = self.parent, level = self.parent.level}
Projectile(table.merge(t, mods or {}))
end
@ -3280,10 +3507,12 @@ function Illusion:init(args)
self.t:after(0.25, 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}
turret1:play{pitch = random:float(0.95, 1.05), volume = 0.10}
turret2:play{pitch = random:float(0.95, 1.05), volume = 0.10}
wizard1:play{pitch = random:float(0.95, 1.05), volume = 0.10}
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',
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 = 'artificer',
parent = self.parent, level = self.parent.level}
Projectile(table.merge(t, mods or {}))
end
@ -3301,7 +3530,7 @@ function Illusion:init(args)
end
end, nil, nil, 'shoot')
self.t:after(12*(self.parent.conjurer_buff_m or 1), function()
self.t:after(18*(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()
@ -3310,14 +3539,14 @@ function Illusion:init(args)
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',
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 = 'artificer_death',
parent = self.parent, level = self.parent.level, pierce = 1, ricochet = 1}
end
end
if self.parent.summon_instability then
if self.parent.construct_instability then
camera:shake(2, 0.5)
local n = (self.parent.summon_instability == 1 and 1) or (self.parent.summon_instability == 2 and 1.5) or (self.parent.summon_instability == 3 and 2) or 1
local n = (self.parent.construct_instability == 1 and 1) or (self.parent.construct_instability == 2 and 1.5) or (self.parent.construct_instability == 3 and 2) or 1
Area{group = main.current.effects, x = self.x, y = self.y, r = self.r, w = self.parent.area_size_m*48, color = self.color, dmg = n*self.parent.dmg*self.parent.area_dmg_m, parent = self.parent}
_G[random:table{'cannoneer1', 'cannoneer2'}]:play{pitch = random:float(0.95, 1.05), volume = 0.5}
end
@ -3325,7 +3554,7 @@ function Illusion:init(args)
end
function Illusion:update(dt)
function Automaton:update(dt)
self:update_game_object(dt)
self:calculate_stats()
@ -3346,11 +3575,12 @@ function Illusion:update(dt)
end
self.r = self:get_angle()
self.t:set_every_multiplier('shoot', self.parent.level == 3 and 0.75 or 1)
self.attack_sensor:move_to(self.x, self.y)
end
function Illusion:draw()
function Automaton: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()

53
todo
View File

@ -1,24 +1,24 @@
Loop Update
New items:
General:
Intimidation - enemies spawn with -10/20/30% max HP
Vulnerability - enemies take +10/20/30% damage
Ceremonial Dagger - killing an enemy fires a homing dagger
Homing Barrage - X/Y/Z% chance to release a homing barrage on enemy kill
Critical Strike - X/Y/Z% chance for attacks to critically strike, dealing 2x damage
Noxious Strike - X/Y/Z% chance for attacks to poison enemies, dealing 20% damage per second for 3 seconds
Infesting Strike - X/Y/Z% chance for attacks to spawn 2 critters on kill
Kinetic Strike - X/Y/Z% chance for attacks to push enemies away with high force
Burning Strike - X% chance for attacks to ignite, dealing 20% dps for 3 seconds, ignited enemies ignite nearby enemies on death
Lucky Strike - X% chance for attacks to cause enemies to drop gold on death
Healing Strike - X% chance for attacks to spawn a healing orb on kill
Stunning Strike - X/Y/Z% chance for attacks to stun for 2 seconds
Silencing Strike - X/Y/Z% chance for attacks to silence on hit
Warping Shots - X/Y/Z% chance for projectiles to ignore wall collisions and warp around the screen 1/2/3 times
Culling Strike - instantly kill elites below 10/20/30% max HP
Lightning Strike - X/Y/Z% chance for attacks to create chain lightning, dealing 60/80/100% damage and hitting 2/3/4 enemies
Psycholeak - Position X generates 1 additional orb every Y seconds
Divine Blessing - generate 1 healing orb every X seconds
* Intimidation - enemies spawn with -10/20/30% max HP
* Vulnerability - enemies take +10/20/30% damage
* Temporal Chains - enemies are 10/20/30% slower
* Ceremonial Dagger - killing an enemy fires a homing dagger
* Homing Barrage - 8/16/24% chance to release a homing barrage on enemy kill
* Critical Strike - 5/10/15% chance for attacks to critically strike, dealing 2x damage
* Noxious Strike - 8/16/24% chance for attacks to poison enemies, dealing 20% dps for 3 seconds
* Infesting Strike - 10/20/30% chance for attacks to spawn 2 critters on kill
* Kinetic Strike - 10/20/30% chance for attacks to push enemies away with high force
* Burning Strike - 15% chance for attacks to ignite, dealing 20% dps for 3 seconds
* Lucky Strike - 8% chance for attacks to cause enemies to drop gold on death
* Healing Strike - 8% chance for attacks to spawn a healing orb on kill
* Stunning Strike - 8/16/24% chance for attacks to stun for 2 seconds
* Silencing Strike - 8/16/24% chance for attacks to silence for 4 seconds on hit
* Culling Strike - instantly kill elites below 10/20/30% max HP
* Lightning Strike - 5/10/15% chance for projectiles to create chain lightning, dealing 60/80/100% damage
* Psycholeak - Position 1 generates 1 psyker orb every 10 seconds
* Divine Blessing - generate 1 healing orb every 8 seconds
Psyker
Additional orbs will prefer to be assigned to psykers, if there are no psykers then they will be assigned randomly
Orb speed scales with the unit's attack speed
@ -35,12 +35,15 @@ Loop Update
Increases amount of orbs dropped with only 1 or 2 healers
Decreases amount of orbs dropped on full build
Healing orbs are slightly attracted to the snake
Warriors
Increase sensor range
Set juggernaut and barbarian's attacks on :cooldown calls
Builders
Carver (builder, healer) - every 12 seconds, creates a tree that drops 3 healing orbs, Lv.3 effect: drops 6 healing orbs instead
Engineer (builder) - unchanged
Illusionist -> Golemancer (builder, sorcerer): spawns 1 golem every 6 seconds, it shoots 2 rocks that deal X damage each, Lv.3 effect: golem rocks stun enemies for 2 seconds
Saboteur -> Bomber (builder, nuker): plants a static bomb, when it explodes it deals 2X AoE damage, Lv.3 effect: 100% increased bomb area and damage
Hunter (builder, ranger, forcer): shoots a projectile that has 20% chance to spawn a pet, Lv.3 effect: spawns 3 pets instead
* Carver (builder, healer) - creates a tree that creates 1 healing orb every 6 seconds, Lv.3 effect: healing orbs are created twice as fast
* Engineer (builder): unchanged
* Illusionist -> Artificer (builder, sorcerer): unchanged
* Saboteur -> Bomber (builder, nuker): plants a bomb, when it explodes it deals 2X AoE damage, Lv.3 effect: +100% bomb area and damage
* Hunter -> Sentry (builder, ranger): spawns a turret that shoots projectiles, each dealing X damage, Lv.3 effect: +50% attack speed and the projectiles ricochet twice
Looping
* Change end screen
* Loop button on end screen
@ -53,14 +56,14 @@ Loop Update
Add option for mouse cursor to always be visible
Show cooldown on elite attack
Add visuals divine intervention, fairy buff
warrior attack range
barbarian jugg attack
Party + items on death screen
* Silenced units are now gray
* Items on end screen
* Items on passive screen
* Selling items
* Decreased sound effect volume for shoot 5
Bug fixes
Fix stars
* Fixed a series of crashes that happened sometimes right before changing from the arena back to the shop
* Fixed merchant not giving interest or free reroll if it died in combat
* Fixed shoot 5 and death 6 not working if a unit placed before it on the snake died