Shop update 3/5

master
a327ex 2021-06-10 14:39:40 -03:00
parent f808b0452f
commit 8f2caf7109
17 changed files with 444 additions and 128 deletions

View File

@ -24,7 +24,7 @@ function Arena:on_enter(from, level, units, passives)
steam.friends.setRichPresence('text', 'Arena - Level ' .. self.level) steam.friends.setRichPresence('text', 'Arena - Level ' .. self.level)
self.floor = Group() self.floor = Group()
self.main = Group():set_as_physics_world(32, 0, 0, {'player', 'enemy', 'projectile', 'enemy_projectile', 'force_field'}) self.main = Group():set_as_physics_world(32, 0, 0, {'player', 'enemy', 'projectile', 'enemy_projectile', 'force_field', 'ghost'})
self.post_main = Group() self.post_main = Group()
self.effects = Group() self.effects = Group()
self.ui = Group() self.ui = Group()
@ -39,10 +39,19 @@ function Arena:on_enter(from, level, units, passives)
self.main:disable_collision_between('enemy_projectile', 'enemy_projectile') self.main:disable_collision_between('enemy_projectile', 'enemy_projectile')
self.main:disable_collision_between('player', 'force_field') self.main:disable_collision_between('player', 'force_field')
self.main:disable_collision_between('projectile', 'force_field') self.main:disable_collision_between('projectile', 'force_field')
self.main:disable_collision_between('ghost', 'player')
self.main:disable_collision_between('ghost', 'projectile')
self.main:disable_collision_between('ghost', 'enemy')
self.main:disable_collision_between('ghost', 'enemy_projectile')
self.main:disable_collision_between('ghost', 'ghost')
self.main:disable_collision_between('ghost', 'force_field')
self.main:enable_trigger_between('projectile', 'enemy') self.main:enable_trigger_between('projectile', 'enemy')
self.main:enable_trigger_between('enemy_projectile', 'player') self.main:enable_trigger_between('enemy_projectile', 'player')
self.main:enable_trigger_between('player', 'enemy_projectile') self.main:enable_trigger_between('player', 'enemy_projectile')
self.main:enable_trigger_between('player', 'ghost')
self.main:enable_trigger_between('ghost', 'player')
self.gold_picked_up = 0
self.damage_dealt = 0 self.damage_dealt = 0
self.damage_taken = 0 self.damage_taken = 0
self.main_slow_amount = 1 self.main_slow_amount = 1
@ -257,6 +266,7 @@ function Arena:on_enter(from, level, units, passives)
self.psyker_level = class_levels.psyker self.psyker_level = class_levels.psyker
self.conjurer_level = class_levels.conjurer self.conjurer_level = class_levels.conjurer
self.sorcerer_level = class_levels.sorcerer self.sorcerer_level = class_levels.sorcerer
self.mercenary_level = class_levels.mercenary
self.t:every(0.375, function() self.t:every(0.375, function()
local p = random:table(star_positions) local p = random:table(star_positions)
@ -348,12 +358,9 @@ function Arena:update(dt)
trigger:tween(0.25, _G, {slow_amount = 1}, math.linear, function() trigger:tween(0.25, _G, {slow_amount = 1}, math.linear, function()
slow_amount = 1 slow_amount = 1
self.paused = false self.paused = false
self.paused_t1.dead = true if self.paused_t1 then self.paused_t1.dead = true; self.paused_t1 = nil end
self.paused_t2.dead = true if self.paused_t2 then self.paused_t2.dead = true; self.paused_t2 = nil end
self.paused_t1 = nil if self.ng_t then self.ng_t.dead = true; self.ng_t = nil end
self.paused_t2 = nil
self.ng_t.dead = true
self.ng_t = nil
if self.resume_button then self.resume_button.dead = true; self.resume_button = nil end if self.resume_button then self.resume_button.dead = true; self.resume_button = nil end
if self.restart_button then self.restart_button.dead = true; self.restart_button = nil end if self.restart_button then self.restart_button.dead = true; self.restart_button = nil end
if self.mouse_button then self.mouse_button.dead = true; self.mouse_button = nil end if self.mouse_button then self.mouse_button.dead = true; self.mouse_button = nil end
@ -527,12 +534,9 @@ function Arena:update(dt)
trigger:tween(0.25, _G, {slow_amount = 1}, math.linear, function() trigger:tween(0.25, _G, {slow_amount = 1}, math.linear, function()
slow_amount = 1 slow_amount = 1
self.paused = false self.paused = false
self.paused_t1.dead = true if self.paused_t1 then self.paused_t1.dead = true; self.paused_t1 = nil end
self.paused_t2.dead = true if self.paused_t2 then self.paused_t2.dead = true; self.paused_t2 = nil end
self.paused_t1 = nil if self.ng_t then self.ng_t.dead = true; self.ng_t = nil end
self.paused_t2 = nil
self.ng_t.dead = true
self.ng_t = nil
if self.resume_button then self.resume_button.dead = true; self.resume_button = nil end if self.resume_button then self.resume_button.dead = true; self.resume_button = nil end
if self.restart_button then self.restart_button.dead = true; self.restart_button = nil end if self.restart_button then self.restart_button.dead = true; self.restart_button = nil end
if self.mouse_button then self.mouse_button.dead = true; self.mouse_button = nil end if self.mouse_button then self.mouse_button.dead = true; self.mouse_button = nil end
@ -608,6 +612,7 @@ function Arena:quit()
self.quitting = true self.quitting = true
if self.level == 25 then if self.level == 25 then
if not self.win_text and not self.win_text2 then if not self.win_text and not self.win_text2 then
input:set_mouse_visible(true)
self.won = true self.won = true
locked_state = nil locked_state = nil
system.save_run() system.save_run()
@ -800,6 +805,13 @@ function Arena:quit()
steam.userStats.storeStats() steam.userStats.storeStats()
end end
if self.mercenary_level >= 2 then
state.achievement_mercenaries_win = true
system.save_state()
steam.userStats.setAchievement('MERCENARIES_WIN')
steam.userStats.storeStats()
end
local units = self.player:get_all_units() local units = self.player:get_all_units()
local all_units_level_2 = true local all_units_level_2 = true
for _, unit in ipairs(units) do for _, unit in ipairs(units) do
@ -836,6 +848,7 @@ function Arena:quit()
self:gain_gold() self:gain_gold()
self.t:after(3, function() self.t:after(3, function()
if self.level % 3 == 0 then if self.level % 3 == 0 then
input:set_mouse_visible(true)
self.arena_clear_text.dead = true self.arena_clear_text.dead = true
trigger:tween(1, _G, {slow_amount = 0}, math.linear, function() slow_amount = 0 end, 'slow_amount') trigger:tween(1, _G, {slow_amount = 0}, math.linear, function() slow_amount = 0 end, 'slow_amount')
trigger:tween(4, camera, {x = gw/2, y = gh/2, r = 0}, math.linear, function() camera.x, camera.y, camera.r = gw/2, gh/2, 0 end) trigger:tween(4, camera, {x = gw/2, y = gh/2, r = 0}, math.linear, function() camera.x, camera.y, camera.r = gw/2, gh/2, 0 end)
@ -1058,9 +1071,10 @@ end
function Arena:gain_gold() function Arena:gain_gold()
local merchant = self.player:get_unit'merchant'
self.gold_gained = random:int(level_to_gold_gained[self.level][1], level_to_gold_gained[self.level][2]) self.gold_gained = random:int(level_to_gold_gained[self.level][1], level_to_gold_gained[self.level][2])
self.interest = math.min(math.floor(gold/5), 5) self.interest = math.min(math.floor(gold/5), 5) + (merchant and math.floor(gold/10) or 0)
gold = gold + self.gold_gained + self.interest gold = gold + self.gold_gained + self.gold_picked_up + self.interest
end end
@ -1074,30 +1088,30 @@ function Arena:transition()
main:go_to('buy_screen', self.level, self.units, self.passives) main:go_to('buy_screen', self.level, self.units, self.passives)
t.t:after(0.1, function() t.t:after(0.1, function()
t.text:set_text({ t.text:set_text({
{text = '[nudge_down, bg]gold gained: ' .. tostring(self.gold_gained or 0), font = pixul_font, alignment = 'center', height_multiplier = 1.5}, {text = '[nudge_down, bg]gold gained: ' .. tostring(self.gold_gained or 0) .. ' + ' .. tostring(self.gold_picked_up or 0), font = pixul_font, alignment = 'center', height_multiplier = 1.5},
{text = '[wavy_lower, bg]interest: 0', font = pixul_font, alignment = 'center', height_multiplier = 1.5}, {text = '[wavy_lower, bg]interest: 0', font = pixul_font, alignment = 'center', height_multiplier = 1.5},
{text = '[wavy_lower, bg]total: 0', font = pixul_font, alignment = 'center'} {text = '[wavy_lower, bg]total: 0', font = pixul_font, alignment = 'center'}
}) })
_G[random:table{'coins1', 'coins2', 'coins3'}]:play{pitch = random:float(0.95, 1.05), volume = 0.5} _G[random:table{'coins1', 'coins2', 'coins3'}]:play{pitch = random:float(0.95, 1.05), volume = 0.5}
t.t:after(0.2, function() t.t:after(0.2, function()
t.text:set_text({ t.text:set_text({
{text = '[wavy_lower, bg]gold gained: ' .. tostring(self.gold_gained or 0), font = pixul_font, alignment = 'center', height_multiplier = 1.5}, {text = '[wavy_lower, bg]gold gained: ' .. tostring(self.gold_gained or 0) .. ' + ' .. tostring(self.gold_picked_up or 0), font = pixul_font, alignment = 'center', height_multiplier = 1.5},
{text = '[nudge_down, bg]interest: ' .. tostring(self.interest or 0), font = pixul_font, alignment = 'center', height_multiplier = 1.5}, {text = '[nudge_down, bg]interest: ' .. tostring(self.interest or 0), font = pixul_font, alignment = 'center', height_multiplier = 1.5},
{text = '[wavy_lower, bg]total: 0', font = pixul_font, alignment = 'center'} {text = '[wavy_lower, bg]total: 0', font = pixul_font, alignment = 'center'}
}) })
_G[random:table{'coins1', 'coins2', 'coins3'}]:play{pitch = random:float(0.95, 1.05), volume = 0.5} _G[random:table{'coins1', 'coins2', 'coins3'}]:play{pitch = random:float(0.95, 1.05), volume = 0.5}
t.t:after(0.2, function() t.t:after(0.2, function()
t.text:set_text({ t.text:set_text({
{text = '[wavy_lower, bg]gold gained: ' .. tostring(self.gold_gained or 0), font = pixul_font, alignment = 'center', height_multiplier = 1.5}, {text = '[wavy_lower, bg]gold gained: ' .. tostring(self.gold_gained or 0) .. ' + ' .. tostring(self.gold_picked_up or 0), font = pixul_font, alignment = 'center', height_multiplier = 1.5},
{text = '[wavy_lower, bg]interest: ' .. tostring(self.interest or 0), font = pixul_font, alignment = 'center', height_multiplier = 1.5}, {text = '[wavy_lower, bg]interest: ' .. tostring(self.interest or 0), font = pixul_font, alignment = 'center', height_multiplier = 1.5},
{text = '[nudge_down, bg]total: ' .. tostring((self.gold_gained or 0) + (self.interest or 0)), font = pixul_font, alignment = 'center'} {text = '[nudge_down, bg]total: ' .. tostring((self.gold_gained or 0) + (self.interest or 0) + (self.gold_picked_up or 0)), font = pixul_font, alignment = 'center'}
}) })
_G[random:table{'coins1', 'coins2', 'coins3'}]:play{pitch = random:float(0.95, 1.05), volume = 0.5} _G[random:table{'coins1', 'coins2', 'coins3'}]:play{pitch = random:float(0.95, 1.05), volume = 0.5}
end) end)
end) end)
end) end)
end, text = Text({ end, text = Text({
{text = '[wavy_lower, bg]gold gained: 0', font = pixul_font, alignment = 'center', height_multiplier = 1.5}, {text = '[wavy_lower, bg]gold gained: 0 + 0', font = pixul_font, alignment = 'center', height_multiplier = 1.5},
{text = '[wavy_lower, bg]interest: 0', font = pixul_font, alignment = 'center', height_multiplier = 1.5}, {text = '[wavy_lower, bg]interest: 0', font = pixul_font, alignment = 'center', height_multiplier = 1.5},
{text = '[wavy_lower, bg]total: 0', font = pixul_font, alignment = 'center'} {text = '[wavy_lower, bg]total: 0', font = pixul_font, alignment = 'center'}
}, global_text_tags)} }, global_text_tags)}

Binary file not shown.

After

Width:  |  Height:  |  Size: 434 B

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -781,7 +781,8 @@ function RerollButton:init(args)
self.text = Text({{text = '[bg10]reroll: [yellow]2', font = pixul_font, alignment = 'center'}}, global_text_tags) self.text = Text({{text = '[bg10]reroll: [yellow]2', font = pixul_font, alignment = 'center'}}, global_text_tags)
elseif self.parent:is(Arena) then elseif self.parent:is(Arena) then
self.shape = Rectangle(self.x, self.y, 60, 16) self.shape = Rectangle(self.x, self.y, 60, 16)
if self.parent.level == 3 then local merchant = self.parent.player:get_unit'merchant'
if self.parent.level == 3 or (merchant and merchant.level == 3) then
self.free_reroll = true self.free_reroll = true
self.text = Text({{text = '[bg10]reroll: [yellow]0', font = pixul_font, alignment = 'center'}}, global_text_tags) self.text = Text({{text = '[bg10]reroll: [yellow]0', font = pixul_font, alignment = 'center'}}, global_text_tags)
else else
@ -847,7 +848,11 @@ end
function RerollButton:draw() function RerollButton:draw()
graphics.push(self.x, self.y, 0, self.spring.x, self.spring.y) graphics.push(self.x, self.y, 0, self.spring.x, self.spring.y)
graphics.rectangle(self.x, self.y, self.shape.w, self.shape.h, 4, 4, self.selected and fg[0] or bg[1]) if self.parent:is(Arena) then
graphics.rectangle(self.x, self.y, self.shape.w, self.shape.h, 4, 4, self.selected and fg[0] or bg[-2])
else
graphics.rectangle(self.x, self.y, self.shape.w, self.shape.h, 4, 4, self.selected and fg[0] or bg[1])
end
self.text:draw(self.x, self.y + 1) self.text:draw(self.x, self.y + 1)
graphics.pop() graphics.pop()
end end
@ -1077,6 +1082,7 @@ end
function CharacterPart:get_sale_price() function CharacterPart:get_sale_price()
if not character_tiers[self.character] then return 0 end
local total = 0 local total = 0
total = total + ((self.level == 1 and character_tiers[self.character]) or (self.level == 2 and 2*character_tiers[self.character]) or (self.level == 3 and 6*character_tiers[self.character]) or 0) total = total + ((self.level == 1 and character_tiers[self.character]) or (self.level == 2 and 2*character_tiers[self.character]) or (self.level == 3 and 6*character_tiers[self.character]) or 0)
if self.reserve then if self.reserve then

View File

@ -253,6 +253,8 @@ function Seeker:init(args)
if player and player.temporal_chains then if player and player.temporal_chains then
self.temporal_chains_mvspd_m = 0.8 self.temporal_chains_mvspd_m = 0.8
end end
self.usurer_count = 0
end end
@ -435,6 +437,14 @@ function Seeker:hit(damage, projectile)
HitCircle{group = main.current.effects, x = self.x, y = self.y, rs = 12}:scale_down(0.3):change_color(0.5, self.color) HitCircle{group = main.current.effects, x = self.x, y = self.y, rs = 12}:scale_down(0.3):change_color(0.5, self.color)
_G[random:table{'enemy_die1', 'enemy_die2'}]:play{pitch = random:float(0.9, 1.1), volume = 0.5} _G[random:table{'enemy_die1', 'enemy_die2'}]:play{pitch = random:float(0.9, 1.1), volume = 0.5}
if main.current.mercenary_level > 0 then
if random:bool((main.current.mercenary_level == 2 and 20) or (main.current.mercenary_level == 1 and 10) or 0) then
trigger:after(0.01, function()
Gold{group = main.current.main, x = self.x, y = self.y}
end)
end
end
if self.boss then if self.boss then
slow(0.25, 1) slow(0.25, 1)
magic_die1:play{pitch = random:float(0.95, 1.05), volume = 0.5} magic_die1:play{pitch = random:float(0.95, 1.05), volume = 0.5}
@ -594,6 +604,17 @@ function Seeker:curse(curse, duration, arg1, arg2, arg3)
elseif curse == 'silencer' then elseif curse == 'silencer' then
self.silenced = true self.silenced = true
self.t:after(duration*curse_m, function() self.silenced = false end, 'silencer_curse') self.t:after(duration*curse_m, function() self.silenced = false end, 'silencer_curse')
elseif curse == 'usurer' then
if arg1 then
self.usurer_count = self.usurer_count + 1
if self.usurer_count == 3 then
usurer1:play{pitch = random:float(0.95, 1.05), volume = 1}
rogue_crit1:play{pitch = random:float(0.95, 1.05), volume = 1}
camera:shake(4, 0.4)
self.usurer_count = 0
self:hit(50*arg2.dmg)
end
end
end end
end end
@ -628,6 +649,7 @@ function EnemyCritter:init(args)
self:push(args.v, args.r) self:push(args.v, args.r)
self.invulnerable_to = args.projectile self.invulnerable_to = args.projectile
self.t:after(0.5, function() self.invulnerable_to = false end) self.t:after(0.5, function() self.invulnerable_to = false end)
self.usurer_count = 0
end end
@ -778,6 +800,14 @@ function EnemyCritter:curse(curse, duration, arg1, arg2, arg3)
elseif curse == 'silencer' then elseif curse == 'silencer' then
self.silenced = true self.silenced = true
self.t:after(duration*curse_m, function() self.silenced = false end, 'silencer_curse') self.t:after(duration*curse_m, function() self.silenced = false end, 'silencer_curse')
elseif curse == 'usurer' then
if arg1 then
self.usurer_count = self.usurer_count + 1
if self.usurer_count == 3 then
self.usurer_count = 0
self:hit(10*arg2.dmg)
end
end
end end
end end

View File

@ -418,6 +418,16 @@ function table.first(t, n)
end end
function table.first2(t, n)
if n == 1 then return {t[1]} end
local out = {}
for i = 1, (n or 1) do
table.push(out, t[i])
end
return out
end
-- Returns the last n values, same as tail -- Returns the last n values, same as tail
-- t = {4, 3, 2, 1} -- t = {4, 3, 2, 1}
-- table.last(t) -> 1 -- table.last(t) -> 1

120
main.lua
View File

@ -9,7 +9,6 @@ require 'media'
function init() function init()
print('Initializing engine...')
shared_init() shared_init()
input:bind('move_left', {'a', 'left', 'dpleft', 'm1'}) input:bind('move_left', {'a', 'left', 'dpleft', 'm1'})
@ -18,8 +17,11 @@ function init()
input:bind('move_down', {'s', 'down', 'dpdown'}) input:bind('move_down', {'s', 'down', 'dpdown'})
input:bind('enter', {'space', 'return', 'fleft', 'fdown', 'fright'}) input:bind('enter', {'space', 'return', 'fleft', 'fdown', 'fright'})
print('Loading sounds...')
local s = {tags = {sfx}} local s = {tags = {sfx}}
gambler1 = Sound('Collect 5.ogg', s)
usurer1 = Sound('Shadow Punch 2.ogg', s)
gold1 = Sound('Collect 5.ogg', s)
gold2 = Sound('Coins - Gears - Slot.ogg', s)
psychic1 = Sound('Magical Impact 13.ogg', s) psychic1 = Sound('Magical Impact 13.ogg', s)
fire1 = Sound('Fire bolt 3.ogg', s) fire1 = Sound('Fire bolt 3.ogg', s)
fire2 = Sound('Fire bolt 5.ogg', s) fire2 = Sound('Fire bolt 5.ogg', s)
@ -111,7 +113,6 @@ function init()
rogue_crit1 = Sound('Dagger Stab (Flesh) 4.ogg', s) rogue_crit1 = Sound('Dagger Stab (Flesh) 4.ogg', s)
rogue_crit2 = Sound('Sword hits another sword 6.ogg', s) rogue_crit2 = Sound('Sword hits another sword 6.ogg', s)
print('Loading songs...')
song1 = Sound('Kubbi - Ember - 01 Pathfinder.ogg', {tags = {music}}) song1 = Sound('Kubbi - Ember - 01 Pathfinder.ogg', {tags = {music}})
song2 = Sound('Kubbi - Ember - 02 Ember.ogg', {tags = {music}}) song2 = Sound('Kubbi - Ember - 02 Ember.ogg', {tags = {music}})
song3 = Sound('Kubbi - Ember - 03 Firelight.ogg', {tags = {music}}) song3 = Sound('Kubbi - Ember - 03 Firelight.ogg', {tags = {music}})
@ -119,7 +120,6 @@ function init()
song5 = Sound('Kubbi - Ember - 05 Compass.ogg', {tags = {music}}) song5 = Sound('Kubbi - Ember - 05 Compass.ogg', {tags = {music}})
death_song = Sound('Kubbi - Ember - 09 Formed by Glaciers.ogg', {tags = {music}}) death_song = Sound('Kubbi - Ember - 09 Formed by Glaciers.ogg', {tags = {music}})
print('Loading images...')
lock_image = Image('lock') lock_image = Image('lock')
speed_booster_elite = Image('speed_booster_elite') speed_booster_elite = Image('speed_booster_elite')
exploder_elite = Image('exploder_elite') exploder_elite = Image('exploder_elite')
@ -140,6 +140,7 @@ function init()
swarmer = Image('swarmer') swarmer = Image('swarmer')
voider = Image('voider') voider = Image('voider')
sorcerer = Image('sorcerer') sorcerer = Image('sorcerer')
mercenary = Image('mercenary')
ouroboros_technique_r = Image('ouroboros_technique_r') ouroboros_technique_r = Image('ouroboros_technique_r')
ouroboros_technique_l = Image('ouroboros_technique_l') ouroboros_technique_l = Image('ouroboros_technique_l')
wall_echo = Image('wall_echo') wall_echo = Image('wall_echo')
@ -185,7 +186,6 @@ function init()
star = Image('star') star = Image('star')
arrow = Image('arrow') arrow = Image('arrow')
print('Initializing game...')
class_colors = { class_colors = {
['warrior'] = yellow[0], ['warrior'] = yellow[0],
['ranger'] = green[0], ['ranger'] = green[0],
@ -201,6 +201,7 @@ function init()
['swarmer'] = orange[0], ['swarmer'] = orange[0],
['voider'] = purple[0], ['voider'] = purple[0],
['sorcerer'] = blue2[0], ['sorcerer'] = blue2[0],
['mercenary'] = yellow2[0],
} }
class_color_strings = { class_color_strings = {
@ -218,6 +219,7 @@ function init()
['swarmer'] = 'orange', ['swarmer'] = 'orange',
['voider'] = 'purple', ['voider'] = 'purple',
['sorcerer'] = 'blue2', ['sorcerer'] = 'blue2',
['mercenary'] = 'yellow2',
} }
character_names = { character_names = {
@ -270,6 +272,11 @@ function init()
['vulcanist'] = 'Vulcanist', ['vulcanist'] = 'Vulcanist',
['warden'] = 'Warden', ['warden'] = 'Warden',
['psychic'] = 'Psychic', ['psychic'] = 'Psychic',
['treasure_hunter'] = 'Treasure Hunter',
['merchant'] = 'Merchant',
['usurer'] = 'Usurer',
['gambler'] = 'Gambler',
['thief'] = 'Thief',
} }
character_colors = { character_colors = {
@ -322,6 +329,11 @@ function init()
['vulcanist'] = red[0], ['vulcanist'] = red[0],
['warden'] = yellow[0], ['warden'] = yellow[0],
['psychic'] = fg[0], ['psychic'] = fg[0],
['treasure_hunter'] = yellow2[0],
['merchant'] = yellow2[0],
['usurer'] = purple[0],
['gambler'] = yellow2[0],
['thief'] = red[0],
} }
character_color_strings = { character_color_strings = {
@ -374,6 +386,11 @@ function init()
['vulcanist'] = 'red', ['vulcanist'] = 'red',
['warden'] = 'yellow', ['warden'] = 'yellow',
['psychic'] = 'fg', ['psychic'] = 'fg',
['treasure_hunter'] = 'yellow2',
['merchant'] = 'yellow2',
['usurer'] = 'purple',
['gambler'] = 'yellow2',
['thief'] = 'red',
} }
character_classes = { character_classes = {
@ -426,6 +443,11 @@ function init()
['vulcanist'] = {'sorcerer', 'nuker'}, ['vulcanist'] = {'sorcerer', 'nuker'},
['warden'] = {'sorcerer', 'forcer'}, ['warden'] = {'sorcerer', 'forcer'},
['psychic'] = {'sorcerer', 'psyker'}, ['psychic'] = {'sorcerer', 'psyker'},
['treasure_hunter'] = {'mercenary'},
['merchant'] = {'mercenary'},
['usurer'] = {'curser', 'mercenary', 'voider'},
['gambler'] = {'mercenary', 'sorcerer'},
['thief'] = {'rogue', 'mercenary'},
} }
character_class_strings = { character_class_strings = {
@ -478,6 +500,11 @@ function init()
['vulcanist'] = '[blue2]Sorcerer, [red]Nuker', ['vulcanist'] = '[blue2]Sorcerer, [red]Nuker',
['warden'] = '[blue2]Sorcerer, [yellow]Forcer', ['warden'] = '[blue2]Sorcerer, [yellow]Forcer',
['psychic'] = '[blue2]Sorcerer, [fg]Psyker', ['psychic'] = '[blue2]Sorcerer, [fg]Psyker',
['treasure_hunter'] = '[yellow2]Mercenary',
['merchant'] = '[yellow2]Mercenary',
['usurer'] = '[purple]Curser, [yellow2]Mercenary, [purple]Curser',
['gambler'] = '[yellow2]Mercenary, [blue2]Sorcerer',
['thief'] = '[red]Rogue, [yellow2]Mercenary',
} }
get_character_stat_string = function(character, level) get_character_stat_string = function(character, level)
@ -547,6 +574,11 @@ function init()
['vulcanist'] = function(lvl) return '[fg]creates a volcano that explodes the nearby area [yellow]4[fg] times, dealing [yellow]' .. get_character_stat('vulcanist', lvl, 'dmg') .. ' AoE [fg]damage' end, ['vulcanist'] = function(lvl) return '[fg]creates a volcano that explodes the nearby area [yellow]4[fg] times, dealing [yellow]' .. get_character_stat('vulcanist', lvl, 'dmg') .. ' AoE [fg]damage' end,
['warden'] = function(lvl) return '[fg]creates a force field around a random unit that prevents enemies from entering' end, ['warden'] = function(lvl) return '[fg]creates a force field around a random unit that prevents enemies from entering' end,
['psychic'] = function(lvl) return '[fg]creates a small area that deals [yellow]' .. get_character_stat('psychic', lvl, 'dmg') .. ' AoE[fg] damage' end, ['psychic'] = function(lvl) return '[fg]creates a small area that deals [yellow]' .. get_character_stat('psychic', lvl, 'dmg') .. ' AoE[fg] damage' end,
['treasure_hunter'] = function(lvl) return '[fg]picking up gold releases [yellow]4[fg] homing projectiles that each deal [yellow]' .. get_character_stat('treasure_hunter', lvl, 'dmg') .. ' [fg]damage' end,
['merchant'] = function(lvl) return '[fg]gain [yellow]+1[fg] interest for every [yellow]10[fg] gold' end,
['usurer'] = function(lvl) return '[fg]curses [yellow]3[fg] nearby enemies indefinitely with [yellow]debt[fg], dealing [yellow]' .. get_character_stat('usurer', lvl, 'dmg') .. '[fg] damage per second' end,
['gambler'] = function(lvl) return '[fg]deal [yellow]2X[fg] damage to a single random enemy where X is how much gold you have' end,
['thief'] = function(lvl) return '[fg]throws a knife that deals [yellow]' .. 2*get_character_stat('thief', lvl, 'dmg') .. '[fg] damage and chains [yellow]5[fg] times' end,
} }
character_effect_names = { character_effect_names = {
@ -598,7 +630,12 @@ function init()
['silencer'] = '[blue2]Arcane Curse', ['silencer'] = '[blue2]Arcane Curse',
['vulcanist'] = '[red]Lava Burst', ['vulcanist'] = '[red]Lava Burst',
['warden'] = '[yellow]Magnetic Field', ['warden'] = '[yellow]Magnetic Field',
['psychic'] = '[fg]Mental Strike' ['psychic'] = '[fg]Mental Strike',
['treasure_hunter'] = '[yellow2]Golden Bolts',
['merchant'] = '[yellow2]Item Shop',
['usurer'] = '[purple]Bankruptcy',
['gambler'] = '[yellow2]Multicast',
['thief'] = '[red]Ultrakill',
} }
character_effect_names_gray = { character_effect_names_gray = {
@ -650,7 +687,12 @@ function init()
['silencer'] = '[light_bg]Arcane Curse', ['silencer'] = '[light_bg]Arcane Curse',
['vulcanist'] = '[light_bg]Lava Burst', ['vulcanist'] = '[light_bg]Lava Burst',
['warden'] = '[light_bg]Magnetic Field', ['warden'] = '[light_bg]Magnetic Field',
['psychic'] = '[light_bg]Mental Strike' ['psychic'] = '[light_bg]Mental Strike',
['treasure_hunter'] = '[light_bg]Golden Bolts',
['merchant'] = '[light_bg]Item Shop',
['usurer'] = '[light_bg]Bankruptcy',
['gambler'] = '[light_bg]Multicast',
['thief'] = '[light_bg]Ultrakill',
} }
character_effect_descriptions = { character_effect_descriptions = {
@ -703,6 +745,11 @@ function init()
['vulcanist'] = function() return '[fg]the number and speed of explosions is [yellow]doubled[fg]' end, ['vulcanist'] = function() return '[fg]the number and speed of explosions is [yellow]doubled[fg]' end,
['warden'] = function() return '[fg]creates the force field around [yellow]2[fg] units' end, ['warden'] = function() return '[fg]creates the force field around [yellow]2[fg] units' end,
['psychic'] = function() return '[fg]the attack can happen from any distance and repeats once' end, ['psychic'] = function() return '[fg]the attack can happen from any distance and repeats once' end,
['treasure_hunter'] = function() return '[fg]release [yellow]8[fg] homing projectiles instead and they pierce twice' end,
['merchant'] = function() return '[fg]your first item reroll is always free' end,
['usurer'] = function() return '[fg]if the same enemy is cursed [yellow]3[fg] times it takes [yellow]' .. 10*get_character_stat('usurer', 3, 'dmg') .. '[fg] damage' end,
['gambler'] = function() return '[yellow]60/40/20%[fg] chance to cast the attack [yellow]2/3/4[fg] times' end,
['thief'] = function() return '[fg]if the knife crits it deals [yellow]' .. 10*get_character_stat('thief', 3, 'dmg') .. '[fg] damage, chains [yellow]10[fg] times and grants [yellow]1[fg] gold' end,
} }
character_effect_descriptions_gray = { character_effect_descriptions_gray = {
@ -755,6 +802,11 @@ function init()
['vulcanist'] = function() return '[light_bg]the number and speed of explosions is doubled' end, ['vulcanist'] = function() return '[light_bg]the number and speed of explosions is doubled' end,
['warden'] = function() return '[light_bg]creates the force field around 2 units' end, ['warden'] = function() return '[light_bg]creates the force field around 2 units' end,
['psychic'] = function() return '[light_bg]the attack can happen from any distance and repeats once' end, ['psychic'] = function() return '[light_bg]the attack can happen from any distance and repeats once' end,
['treasure_hunter'] = function() return '[light_bg]release 8 homing projectiles instead and they pierce twice' end,
['merchant'] = function() return '[light_bg]your first item reroll is always free' end,
['usurer'] = function() return '[light_bg]if the same enemy is cursed 3 times it takes ' .. 10*get_character_stat('usurer', 3, 'dmg') .. ' damage' end,
['gambler'] = function() return '[light_bg]60/40/20% chance to cast the attack 2/3/4 times' end,
['thief'] = function() return '[light_bg]if the coin crits it deals ' .. 10*get_character_stat('thief', 3, 'dmg') .. ' damage, chains 10 times and grants 1 gold' end,
} }
character_stats = { character_stats = {
@ -807,6 +859,11 @@ function init()
['vulcanist'] = function(lvl) return get_character_stat_string('vulcanist', lvl) end, ['vulcanist'] = function(lvl) return get_character_stat_string('vulcanist', lvl) end,
['warden'] = function(lvl) return get_character_stat_string('warden', lvl) end, ['warden'] = function(lvl) return get_character_stat_string('warden', lvl) end,
['psychic'] = function(lvl) return get_character_stat_string('psychic', lvl) end, ['psychic'] = function(lvl) return get_character_stat_string('psychic', lvl) end,
['treasure_hunter'] = function(lvl) return get_character_stat_string('treasure_hunter', lvl) end,
['merchant'] = function(lvl) return get_character_stat_string('merchant', lvl) end,
['usurer'] = function(lvl) return get_character_stat_string('usurer', lvl) end,
['gambler'] = function(lvl) return get_character_stat_string('gambler', lvl) end,
['thief'] = function(lvl) return get_character_stat_string('thief', lvl) end,
} }
class_stat_multipliers = { class_stat_multipliers = {
@ -824,6 +881,7 @@ function init()
['swarmer'] = {hp = 1.2, dmg = 1, aspd = 1.25, area_dmg = 1, area_size = 1, def = 0.75, mvspd = 0.75}, ['swarmer'] = {hp = 1.2, dmg = 1, aspd = 1.25, area_dmg = 1, area_size = 1, def = 0.75, mvspd = 0.75},
['voider'] = {hp = 0.75, dmg = 1.3, aspd = 1, area_dmg = 0.8, area_size = 0.75, def = 0.6, mvspd = 0.8}, ['voider'] = {hp = 0.75, dmg = 1.3, aspd = 1, area_dmg = 0.8, area_size = 0.75, def = 0.6, mvspd = 0.8},
['sorcerer'] = {hp = 0.8, dmg = 1.3, aspd = 1, area_dmg = 1.2, area_size = 1, def = 0.8, mvspd = 1}, ['sorcerer'] = {hp = 0.8, dmg = 1.3, aspd = 1, area_dmg = 1.2, area_size = 1, def = 0.8, mvspd = 1},
['mercenary'] = {hp = 1, dmg = 1, aspd = 1, area_dmg = 1, area_size = 1, def = 1, mvspd = 1},
['seeker'] = {hp = 0.5, dmg = 1, aspd = 1, area_dmg = 1, area_size = 1, def = 1, mvspd = 0.3}, ['seeker'] = {hp = 0.5, dmg = 1, aspd = 1, area_dmg = 1, area_size = 1, def = 1, mvspd = 0.3},
['mini_boss'] = {hp = 1, dmg = 1, aspd = 1, area_dmg = 1, area_size = 1, def = 1, mvspd = 0.3}, ['mini_boss'] = {hp = 1, dmg = 1, aspd = 1, area_dmg = 1, area_size = 1, def = 1, mvspd = 0.3},
['enemy_critter'] = {hp = 1, dmg = 1, aspd = 1, area_dmg = 1, area_size = 1, def = 1, mvspd = 0.5}, ['enemy_critter'] = {hp = 1, dmg = 1, aspd = 1, area_dmg = 1, area_size = 1, def = 1, mvspd = 0.5},
@ -862,17 +920,18 @@ function init()
['sorcerer'] = function(lvl) ['sorcerer'] = function(lvl)
return '[' .. ylb1(lvl) .. ']2[' .. ylb2(lvl) .. ']/4[' .. ylb3(lvl) .. ']/6 [fg]- sorcerers repeat their attacks once every [' .. ylb1(lvl) .. ']4/[' .. ylb2(lvl) .. ']3/[' .. ylb3(lvl) .. ']2[fg] attacks' return '[' .. ylb1(lvl) .. ']2[' .. ylb2(lvl) .. ']/4[' .. ylb3(lvl) .. ']/6 [fg]- sorcerers repeat their attacks once every [' .. ylb1(lvl) .. ']4/[' .. ylb2(lvl) .. ']3/[' .. ylb3(lvl) .. ']2[fg] attacks'
end, end,
['mercenary'] = function(lvl) return '[' .. ylb1(lvl) .. ']2[' .. ylb2(lvl) .. ']/4 [fg]- [' .. ylb1(lvl) .. ']+10%[' .. ylb2(lvl) .. ']/+20% [fg]chance for enemies to drop gold on death' end,
} }
tier_to_characters = { tier_to_characters = {
[1] = {'vagrant', 'swordsman', 'magician', 'archer', 'scout', 'cleric', 'arcanist'}, [1] = {'vagrant', 'swordsman', 'magician', 'archer', 'scout', 'cleric', 'arcanist', 'treasure_hunter'},
[2] = {'wizard', 'saboteur', 'sage', 'squire', 'dual_gunner', 'hunter', 'chronomancer', 'barbarian', 'cryomancer', 'beastmaster', 'launcher', 'jester', 'carver', 'psychic', 'witch', 'silencer', 'outlaw'}, [2] = {'wizard', 'saboteur', 'sage', 'squire', 'dual_gunner', 'hunter', 'chronomancer', 'barbarian', 'cryomancer', 'beastmaster', 'launcher', 'jester', 'carver', 'psychic', 'witch', 'silencer', 'outlaw', 'merchant'},
[3] = {'elementor', 'stormweaver', 'spellblade', 'psykeeper', 'engineer', 'juggernaut', 'pyromancer', 'host', 'assassin', 'bane', 'barrager', 'infestor', 'flagellant', 'illusionist'}, [3] = {'elementor', 'stormweaver', 'spellblade', 'psykeeper', 'engineer', 'juggernaut', 'pyromancer', 'host', 'assassin', 'bane', 'barrager', 'infestor', 'flagellant', 'illusionist', 'usurer', 'gambler'},
[4] = {'priest', 'highlander', 'psykino', 'fairy', 'blade', 'plague_doctor', 'cannoneer', 'vulcanist', 'warden', 'corruptor'}, [4] = {'priest', 'highlander', 'psykino', 'fairy', 'blade', 'plague_doctor', 'cannoneer', 'vulcanist', 'warden', 'corruptor', 'thief'},
} }
non_attacking_characters = {'cleric', 'stormweaver', 'squire', 'chronomancer', 'sage', 'psykeeper', 'bane', 'carver', 'fairy', 'priest', 'flagellant'} non_attacking_characters = {'cleric', 'stormweaver', 'squire', 'chronomancer', 'sage', 'psykeeper', 'bane', 'carver', 'fairy', 'priest', 'flagellant', 'merchant', 'treasure_hunter'}
non_cooldown_characters = {'squire', 'chronomancer', 'psykeeper'} non_cooldown_characters = {'squire', 'chronomancer', 'psykeeper', 'merchant', 'treasure_hunter'}
character_tiers = { character_tiers = {
['vagrant'] = 1, ['vagrant'] = 1,
@ -881,7 +940,6 @@ function init()
['archer'] = 1, ['archer'] = 1,
['scout'] = 1, ['scout'] = 1,
['cleric'] = 1, ['cleric'] = 1,
['arcanist'] = 1,
['outlaw'] = 2, ['outlaw'] = 2,
['blade'] = 4, ['blade'] = 4,
['elementor'] = 3, ['elementor'] = 3,
@ -918,12 +976,18 @@ function init()
['priest'] = 4, ['priest'] = 4,
['infestor'] = 3, ['infestor'] = 3,
['flagellant'] = 3, ['flagellant'] = 3,
['psychic'] = 2, ['arcanist'] = 1,
['illusionist'] = 3,
['witch'] = 2, ['witch'] = 2,
['silencer'] = 2, ['silencer'] = 2,
['illusionist'] = 3,
['warden'] = 4,
['vulcanist'] = 4, ['vulcanist'] = 4,
['warden'] = 4,
['psychic'] = 2,
['treasure_hunter'] = 1,
['merchant'] = 2,
['usurer'] = 3,
['gambler'] = 3,
['thief'] = 4,
} }
get_number_of_units_per_class = function(units) get_number_of_units_per_class = function(units)
@ -941,6 +1005,7 @@ function init()
local swarmers = 0 local swarmers = 0
local voiders = 0 local voiders = 0
local sorcerers = 0 local sorcerers = 0
local mercenaries = 0
for _, unit in ipairs(units) do for _, unit in ipairs(units) do
for _, unit_class in ipairs(character_classes[unit.character]) do for _, unit_class in ipairs(character_classes[unit.character]) do
if unit_class == 'ranger' then rangers = rangers + 1 end if unit_class == 'ranger' then rangers = rangers + 1 end
@ -957,10 +1022,11 @@ function init()
if unit_class == 'swarmer' then swarmers = swarmers + 1 end if unit_class == 'swarmer' then swarmers = swarmers + 1 end
if unit_class == 'voider' then voiders = voiders + 1 end if unit_class == 'voider' then voiders = voiders + 1 end
if unit_class == 'sorcerer' then sorcerers = sorcerers + 1 end if unit_class == 'sorcerer' then sorcerers = sorcerers + 1 end
if unit_class == 'mercenary' then mercenaries = mercenaries + 1 end
end end
end end
return {ranger = rangers, warrior = warriors, healer = healers, mage = mages, nuker = nukers, conjurer = conjurers, rogue = rogues, return {ranger = rangers, warrior = warriors, healer = healers, mage = mages, nuker = nukers, conjurer = conjurers, rogue = rogues,
enchanter = enchanters, psyker = psykers, curser = cursers, forcer = forcers, swarmer = swarmers, voider = voiders, sorcerer = sorcerers} enchanter = enchanters, psyker = psykers, curser = cursers, forcer = forcers, swarmer = swarmers, voider = voiders, sorcerer = sorcerers, mercenary = mercenaries}
end end
get_class_levels = function(units) get_class_levels = function(units)
@ -970,7 +1036,7 @@ function init()
if number_of_units >= 6 then return 2 if number_of_units >= 6 then return 2
elseif number_of_units >= 3 then return 1 elseif number_of_units >= 3 then return 1
else return 0 end else return 0 end
elseif class == 'healer' or class == 'conjurer' or class == 'enchanter' or class == 'psyker' or class == 'curser' or class == 'forcer' or class == 'swarmer' or class == 'voider' then elseif class == 'healer' or class == 'conjurer' or class == 'enchanter' or class == 'psyker' or class == 'curser' or class == 'forcer' or class == 'swarmer' or class == 'voider' or class == 'mercenary' then
if number_of_units >= 4 then return 2 if number_of_units >= 4 then return 2
elseif number_of_units >= 2 then return 1 elseif number_of_units >= 2 then return 1
else return 0 end else return 0 end
@ -996,6 +1062,7 @@ function init()
swarmer = units_to_class_level(units_per_class.swarmer, 'swarmer'), swarmer = units_to_class_level(units_per_class.swarmer, 'swarmer'),
voider = units_to_class_level(units_per_class.voider, 'voider'), voider = units_to_class_level(units_per_class.voider, 'voider'),
sorcerer = units_to_class_level(units_per_class.sorcerer, 'sorcerer'), sorcerer = units_to_class_level(units_per_class.sorcerer, 'sorcerer'),
mercenary = units_to_class_level(units_per_class.mercenary, 'mercenary'),
} }
end end
@ -1022,6 +1089,7 @@ function init()
['swarmer'] = function(units) return 2, 4, nil, get_number_of_units_per_class(units).swarmer end, ['swarmer'] = function(units) return 2, 4, nil, get_number_of_units_per_class(units).swarmer end,
['voider'] = function(units) return 2, 4, nil, get_number_of_units_per_class(units).voider end, ['voider'] = function(units) return 2, 4, nil, get_number_of_units_per_class(units).voider end,
['sorcerer'] = function(units) return 2, 4, 6, get_number_of_units_per_class(units).sorcerer end, ['sorcerer'] = function(units) return 2, 4, 6, get_number_of_units_per_class(units).sorcerer end,
['mercenary'] = function(units) return 2, 4, nil, get_number_of_units_per_class(units).mercenary end,
} }
passive_names = { passive_names = {
@ -1323,22 +1391,16 @@ function init()
end end
-- main_song_instance = _G[random:table{'song1', 'song2', 'song3', 'song4', 'song5'}]:play{volume = 0.5} -- main_song_instance = _G[random:table{'song1', 'song2', 'song3', 'song4', 'song5'}]:play{volume = 0.5}
main:add(BuyScreen'buy_screen') main:add(BuyScreen'buy_screen')
main:go_to('buy_screen', run.level or 0, run.units or {}, passives) main:go_to('buy_screen', run.level or 0, run.units or {}, passives)
-- main:go_to('buy_screen', 7, run.units or {}, {'unleash'}) -- main:go_to('buy_screen', 7, run.units or {}, {'unleash'})
--[[ --[[
main:add(Arena'arena') main:add(Arena'arena')
main:go_to('arena', 17, { main:go_to('arena', 13, {
{character = 'arcanist', level = 2}, {character = 'thief', level = 3},
{character = 'silencer', level = 2}, {character = 'scout', level = 1},
{character = 'warden', level = 3}, {character = 'beastmaster', level = 1},
{character = 'chronomancer', level = 1},
{character = 'witch', level = 3},
{character = 'illusionist', level = 3},
{character = 'psychic', level = 2},
{character = 'vulcanist', level = 3},
}, passives) }, passives)
]]-- ]]--

View File

@ -47,7 +47,6 @@ function Player:init(args)
self:attack(32, {x = enemy.x, y = enemy.y}) self:attack(32, {x = enemy.x, y = enemy.y})
end end
end, nil, nil, 'attack') end, nil, nil, 'attack')
if self.level == 3 then if self.level == 3 then
self.t:every(12, function() self.t:every(12, function()
self.magician_invulnerable = true self.magician_invulnerable = true
@ -55,6 +54,60 @@ function Player:init(args)
end) end)
end end
elseif self.character == 'gambler' then
self.sorcerer_count = 0
local cast = function(pitch_a)
local enemy = table.shuffle(main.current.main:get_objects_by_classes(main.current.enemies))[1]
if enemy then
gambler1:play{pitch = pitch_a, volume = math.remap(gold, 0, 50, 0, 0.8)}
enemy:hit(2*gold)
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_count = 0
self.t:after(0.25, function()
local enemy = table.shuffle(main.current.main:get_objects_by_classes(main.current.enemies))[1]
if enemy then
gambler1:play{pitch = pitch_a + 0.05, volume = math.remap(gold, 0, 50, 0, 0.8)}
enemy:hit(2*gold)
end
end)
end
end
end
end
self.t:every(2, function()
cast(1)
if self.level == 3 then
if random:bool(60) then
if random:bool(40) then
if random:bool(20) then
self.t:after(0.25, function()
cast(1.1)
self.t:after(0.25, function()
cast(1.2)
self.t:after(0.25, function()
cast(1.3)
end)
end)
end)
else
self.t:after(0.25, function()
cast(1.1)
self.t:after(0.25, function()
cast(1.2)
end)
end)
end
else
self.t:after(0.25, function()
cast(1.1)
end)
end
end
end
end, nil, nil, 'attack')
elseif self.character == 'archer' then elseif self.character == 'archer' then
self.attack_sensor = Circle(self.x, self.y, 160) self.attack_sensor = Circle(self.x, self.y, 160)
self.t:cooldown(2, function() local enemies = self:get_objects_in_shape(self.attack_sensor, main.current.enemies); return enemies and #enemies > 0 end, function() self.t:cooldown(2, function() local enemies = self:get_objects_in_shape(self.attack_sensor, main.current.enemies); return enemies and #enemies > 0 end, function()
@ -73,6 +126,15 @@ function Player:init(args)
end end
end, nil, nil, 'shoot') end, nil, nil, 'shoot')
elseif self.character == 'thief' then
self.attack_sensor = Circle(self.x, self.y, 64)
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), {chain = (self.level == 3 and 10 or 5)})
end
end, nil, nil, 'shoot')
elseif self.character == 'cleric' then elseif self.character == 'cleric' then
self.t:every(6, function() self.t:every(6, function()
local all_units = self:get_all_units() local all_units = self:get_all_units()
@ -261,12 +323,12 @@ function Player:init(args)
if x == 0 and y == 0 then x, y = gw/2, gh/2 end if x == 0 and y == 0 then x, y = gw/2, gh/2 end
x, y = x + self.x, y + self.y x, y = x + self.x, y + self.y
x, y = x/2, y/2 x, y = x/2, y/2
trigger:every_immediate(0.1, function() main.current.t:every_immediate(0.1, function()
local check_circle = Circle(x, y, 2) 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, Illusion, Saboteur, Pet, Turret})
if #objects == 0 then if #objects == 0 then
Volcano{group = main.current.main, x = x, y = y, color = self.color, parent = self, rs = 24, level = self.level} Volcano{group = main.current.main, x = x, y = y, color = self.color, parent = self, rs = 24, level = self.level}
trigger:cancel('volcano_spawn') main.current.t:cancel('volcano_spawn')
end end
end, nil, nil, 'volcano_spawn') end, nil, nil, 'volcano_spawn')
end end
@ -431,9 +493,10 @@ function Player:init(args)
elseif self.character == 'jester' then elseif self.character == 'jester' then
self.attack_sensor = Circle(self.x, self.y, 96) self.attack_sensor = Circle(self.x, self.y, 96)
self.wide_attack_sensor = Circle(self.x, self.y, 128)
self.t:cooldown(6, function() local enemies = self:get_objects_in_shape(self.attack_sensor, main.current.enemies); return enemies and #enemies > 0 end, function() self.t:cooldown(6, function() local enemies = self:get_objects_in_shape(self.attack_sensor, main.current.enemies); return enemies and #enemies > 0 end, function()
buff1:play{pitch = random:float(0.9, 1.1), volume = 0.5} buff1:play{pitch = random:float(0.9, 1.1), volume = 0.5}
local enemies = table.first(table.shuffle(main.current.main:get_objects_by_classes(main.current.enemies)), 6) local enemies = table.first(table.shuffle(self:get_objects_in_shape(self.wide_attack_sensor, main.current.enemies)), 6)
for _, enemy in ipairs(enemies) do for _, enemy in ipairs(enemies) do
if self:distance_to_object(enemy) < 128 then if self:distance_to_object(enemy) < 128 then
enemy:curse('jester', 6*(self.hex_duration_m or 1), self.level == 3, self) enemy:curse('jester', 6*(self.hex_duration_m or 1), self.level == 3, self)
@ -443,26 +506,39 @@ function Player:init(args)
end end
end, nil, nil, 'attack') end, nil, nil, 'attack')
elseif self.character == 'usurer' then
self.attack_sensor = Circle(self.x, self.y, 96)
self.wide_attack_sensor = Circle(self.x, self.y, 128)
self.t:cooldown(6, function() local enemies = self:get_objects_in_shape(self.attack_sensor, main.current.enemies); return enemies and #enemies > 0 end, function()
buff1:play{pitch = random:float(0.9, 1.1), volume = 0.5}
local enemies = table.first2(table.shuffle(self:get_objects_in_shape(self.wide_attack_sensor, main.current.enemies)), 3)
for _, enemy in ipairs(enemies) do
enemy:curse('usurer', 10000, self.level == 3, self)
enemy:apply_dot(self.dmg*(self.dot_dmg_m or 1)*(main.current.chronomancer_dot or 1), 10000)
HitCircle{group = main.current.effects, x = self.x, y = self.y, rs = 6, color = purple[0], duration = 0.1}
LightningLine{group = main.current.effects, src = self, dst = enemy, color = purple[0]}
end
end, nil, nil, 'attack')
elseif self.character == 'silencer' then elseif self.character == 'silencer' then
self.sorcerer_count = 0 self.sorcerer_count = 0
self.attack_sensor = Circle(self.x, self.y, 96) self.attack_sensor = Circle(self.x, self.y, 96)
self.wide_attack_sensor = Circle(self.x, self.y, 128)
self.t:cooldown(6, function() local enemies = self:get_objects_in_shape(self.attack_sensor, main.current.enemies); return enemies and #enemies > 0 end, function() self.t:cooldown(6, function() local enemies = self:get_objects_in_shape(self.attack_sensor, main.current.enemies); return enemies and #enemies > 0 end, function()
local curse = function() local curse = function()
buff1:play{pitch = random:float(0.9, 1.1), volume = 0.5} buff1:play{pitch = random:float(0.9, 1.1), volume = 0.5}
local enemies = table.first(table.shuffle(main.current.main:get_objects_by_classes(main.current.enemies)), 6) local enemies = table.first(table.shuffle(self:get_objects_in_shape(self.wide_attack_sensor, main.current.enemies)), 6)
for _, enemy in ipairs(enemies) do for _, enemy in ipairs(enemies) do
if self:distance_to_object(enemy) < 128 then enemy:curse('silencer', 6*(self.hex_duration_m or 1), self.level == 3, self)
enemy:curse('silencer', 6*(self.hex_duration_m or 1), self.level == 3, self) if self.level == 3 then
if self.level == 3 then local curse_m = 1
local curse_m = 1 if main.current.curser_level == 2 then curse_m = 1.5
if main.current.curser_level == 2 then curse_m = 1.5 elseif main.current.curser_level == 1 then curse_m = 1.25
elseif main.current.curser_level == 1 then curse_m = 1.25 else curse_m = 1 end
else curse_m = 1 end enemy:apply_dot(self.dmg*(self.dot_dmg_m or 1)*(main.current.chronomancer_dot or 1), 6*(self.hex_duration_m or 1)*(curse_m or 1))
enemy:apply_dot(self.dmg*(self.dot_dmg_m or 1)*(main.current.chronomancer_dot or 1), 6*(self.hex_duration_m or 1)*(curse_m or 1))
end
HitCircle{group = main.current.effects, x = self.x, y = self.y, rs = 6, color = blue2[0], duration = 0.1}
LightningLine{group = main.current.effects, src = self, dst = enemy, color = blue2[0]}
end end
HitCircle{group = main.current.effects, x = self.x, y = self.y, rs = 6, color = blue2[0], duration = 0.1}
LightningLine{group = main.current.effects, src = self, dst = enemy, color = blue2[0]}
end end
end end
curse() curse()
@ -508,15 +584,14 @@ function Player:init(args)
elseif self.character == 'bane' then elseif self.character == 'bane' then
self.attack_sensor = Circle(self.x, self.y, 96) self.attack_sensor = Circle(self.x, self.y, 96)
self.wide_attack_sensor = Circle(self.x, self.y, 128)
self.t:cooldown(6, function() local enemies = self:get_objects_in_shape(self.attack_sensor, main.current.enemies); return enemies and #enemies > 0 end, function() self.t:cooldown(6, function() local enemies = self:get_objects_in_shape(self.attack_sensor, main.current.enemies); return enemies and #enemies > 0 end, function()
buff1:play{pitch = random:float(0.9, 1.1), volume = 0.5} buff1:play{pitch = random:float(0.9, 1.1), volume = 0.5}
local enemies = table.first(table.shuffle(main.current.main:get_objects_by_classes(main.current.enemies)), 6) local enemies = table.first(table.shuffle(self:get_objects_in_shape(self.wide_attack_sensor, main.current.enemies)), 6)
for _, enemy in ipairs(enemies) do for _, enemy in ipairs(enemies) do
if self:distance_to_object(enemy) < 128 then enemy:curse('bane', 6*(self.hex_duration_m or 1), self.level == 3, self)
enemy:curse('bane', 6*(self.hex_duration_m or 1), self.level == 3, self) HitCircle{group = main.current.effects, x = self.x, y = self.y, rs = 6, color = purple[0], duration = 0.1}
HitCircle{group = main.current.effects, x = self.x, y = self.y, rs = 6, color = purple[0], duration = 0.1} LightningLine{group = main.current.effects, src = self, dst = enemy, color = purple[0]}
LightningLine{group = main.current.effects, src = self, dst = enemy, color = purple[0]}
end
end end
end, nil, nil, 'attack') end, nil, nil, 'attack')
@ -603,24 +678,24 @@ function Player:init(args)
local unit_2 = random:table_remove(units) local unit_2 = random:table_remove(units)
if unit_1 then if unit_1 then
illusion1:play{pitch = random:float(0.95, 1.05), volume = 0.5} illusion1:play{pitch = random:float(0.95, 1.05), volume = 0.5}
trigger:every_immediate(0.1, function() main.current.t:every_immediate(0.1, function()
local check_circle = Circle(unit_1.x, unit_1.y, 6) local check_circle = Circle(unit_1.x, unit_1.y, 6)
local objects = main.current.main:get_objects_in_shape(check_circle, {Seeker, EnemyCritter}) local objects = main.current.main:get_objects_in_shape(check_circle, {Seeker, EnemyCritter})
if #objects == 0 then if #objects == 0 then
ForceField{group = main.current.main, x = unit_1.x, y = unit_1.y, parent = unit_1} ForceField{group = main.current.main, x = unit_1.x, y = unit_1.y, parent = unit_1}
trigger:cancel('warden_force_field_1') main.current.t:cancel('warden_force_field_1')
end end
end, nil, nil, 'warden_force_field_1') end, nil, nil, 'warden_force_field_1')
end end
if unit_2 then if unit_2 then
illusion1:play{pitch = random:float(0.95, 1.05), volume = 0.5} illusion1:play{pitch = random:float(0.95, 1.05), volume = 0.5}
ForceField{group = main.current.main, x = unit_2.x, y = unit_2.y, parent = unit_2} ForceField{group = main.current.main, x = unit_2.x, y = unit_2.y, parent = unit_2}
trigger:every_immediate(0.1, function() main.current.t:every_immediate(0.1, function()
local check_circle = Circle(unit_2.x, unit_2.y, 6) local check_circle = Circle(unit_2.x, unit_2.y, 6)
local objects = main.current.main:get_objects_in_shape(check_circle, {Seeker, EnemyCritter}) local objects = main.current.main:get_objects_in_shape(check_circle, {Seeker, EnemyCritter})
if #objects == 0 then if #objects == 0 then
ForceField{group = main.current.main, x = unit_2.x, y = unit_2.y, parent = unit_2} ForceField{group = main.current.main, x = unit_2.x, y = unit_2.y, parent = unit_2}
trigger:cancel('warden_force_field_2') main.current.t:cancel('warden_force_field_2')
end end
end, nil, nil, 'warden_force_field_2') end, nil, nil, 'warden_force_field_2')
end end
@ -628,12 +703,12 @@ function Player:init(args)
local unit = random:table(self:get_all_units()) local unit = random:table(self:get_all_units())
if unit then if unit then
illusion1:play{pitch = random:float(0.95, 1.05), volume = 0.5} illusion1:play{pitch = random:float(0.95, 1.05), volume = 0.5}
trigger:every_immediate(0.1, function() main.current.t:every_immediate(0.1, function()
local check_circle = Circle(unit.x, unit.y, 6) local check_circle = Circle(unit.x, unit.y, 6)
local objects = main.current.main:get_objects_in_shape(check_circle, {Seeker, EnemyCritter}) local objects = main.current.main:get_objects_in_shape(check_circle, {Seeker, EnemyCritter})
if #objects == 0 then if #objects == 0 then
ForceField{group = main.current.main, x = unit.x, y = unit.y, parent = unit} ForceField{group = main.current.main, x = unit.x, y = unit.y, parent = unit}
trigger:cancel('warden_force_field_0') main.current.t:cancel('warden_force_field_0')
end end
end, nil, nil, 'warden_force_field_0') end, nil, nil, 'warden_force_field_0')
end end
@ -671,16 +746,15 @@ function Player:init(args)
end, nil, nil, 'heal') end, nil, nil, 'heal')
elseif self.character == 'infestor' then elseif self.character == 'infestor' then
self.attack_sensor = Circle(self.x, self.y, 128) self.attack_sensor = Circle(self.x, self.y, 96)
self.wide_attack_sensor = Circle(self.x, self.y, 128)
self.t:cooldown(6, function() local enemies = self:get_objects_in_shape(self.attack_sensor, main.current.enemies); return enemies and #enemies > 0 end, function() self.t:cooldown(6, function() local enemies = self:get_objects_in_shape(self.attack_sensor, main.current.enemies); return enemies and #enemies > 0 end, function()
buff1:play{pitch = random:float(0.9, 1.1), volume = 0.5} buff1:play{pitch = random:float(0.9, 1.1), volume = 0.5}
local enemies = table.first(table.shuffle(main.current.main:get_objects_by_classes(main.current.enemies)), 8) local enemies = table.first(table.shuffle(self:get_objects_in_shape(self.wide_attack_sensor, main.current.enemies)), 8)
for _, enemy in ipairs(enemies) do for _, enemy in ipairs(enemies) do
if self:distance_to_object(enemy) < 128 then enemy:curse('infestor', 6*(self.hex_duration_m or 1), (self.level == 3 and 6 or 2), self.dmg, self)
enemy:curse('infestor', 6*(self.hex_duration_m or 1), (self.level == 3 and 6 or 2), self.dmg, self) HitCircle{group = main.current.effects, x = self.x, y = self.y, rs = 6, color = orange[0], duration = 0.1}
HitCircle{group = main.current.effects, x = self.x, y = self.y, rs = 6, color = orange[0], duration = 0.1} LightningLine{group = main.current.effects, src = self, dst = enemy, color = orange[0]}
LightningLine{group = main.current.effects, src = self, dst = enemy, color = orange[0]}
end
end end
end, nil, nil, 'attack') end, nil, nil, 'attack')
@ -923,6 +997,7 @@ function Player:update(dt)
if class_levels.swarmer >= 1 then number_of_active_sets = number_of_active_sets + 1 end if class_levels.swarmer >= 1 then number_of_active_sets = number_of_active_sets + 1 end
if class_levels.voider >= 1 then number_of_active_sets = number_of_active_sets + 1 end if class_levels.voider >= 1 then number_of_active_sets = number_of_active_sets + 1 end
if class_levels.sorcerer >= 1 then number_of_active_sets = number_of_active_sets + 1 end if class_levels.sorcerer >= 1 then number_of_active_sets = number_of_active_sets + 1 end
if class_levels.mercenary >= 1 then number_of_active_sets = number_of_active_sets + 1 end
self.vagrant_dmg_m = 1 + 0.1*number_of_active_sets self.vagrant_dmg_m = 1 + 0.1*number_of_active_sets
self.vagrant_aspd_m = 1 + 0.1*number_of_active_sets self.vagrant_aspd_m = 1 + 0.1*number_of_active_sets
end end
@ -988,6 +1063,7 @@ function Player:update(dt)
if class_levels.swarmer >= 1 then number_of_active_sets = number_of_active_sets + 1 end if class_levels.swarmer >= 1 then number_of_active_sets = number_of_active_sets + 1 end
if class_levels.voider >= 1 then number_of_active_sets = number_of_active_sets + 1 end if class_levels.voider >= 1 then number_of_active_sets = number_of_active_sets + 1 end
if class_levels.sorcerer >= 1 then number_of_active_sets = number_of_active_sets + 1 end if class_levels.sorcerer >= 1 then number_of_active_sets = number_of_active_sets + 1 end
if class_levels.mercenary >= 1 then number_of_active_sets = number_of_active_sets + 1 end
if main.current.psyker_level == 2 then if main.current.psyker_level == 2 then
self.psyker_dmg_m = 1 + 0.2*number_of_active_sets self.psyker_dmg_m = 1 + 0.2*number_of_active_sets
self.psyker_aspd_m = 1 + 0.2*number_of_active_sets self.psyker_aspd_m = 1 + 0.2*number_of_active_sets
@ -1039,6 +1115,7 @@ function Player:update(dt)
self:calculate_stats() self:calculate_stats()
if self.attack_sensor then self.attack_sensor:move_to(self.x, self.y) end if self.attack_sensor then self.attack_sensor:move_to(self.x, self.y) end
if self.wide_attack_sensor then self.wide_attack_sensor:move_to(self.x, self.y) end
if self.gun_kata_sensor then self.gun_kata_sensor:move_to(self.x, self.y) end if self.gun_kata_sensor then self.gun_kata_sensor:move_to(self.x, self.y) end
self.t:set_every_multiplier('shoot', self.aspd_m) self.t:set_every_multiplier('shoot', self.aspd_m)
self.t:set_every_multiplier('attack', self.aspd_m) self.t:set_every_multiplier('attack', self.aspd_m)
@ -1371,6 +1448,14 @@ function Player:shoot(r, mods)
end end
end end
if self.character == 'thief' then
dmg_m = dmg_m*2
if self.level == 3 and crit then
dmg_m = dmg_m*10
main.current.gold_picked_up = main.current.gold_picked_up + 1
end
end
if crit and mods.spawn_critters_on_crit then if crit and mods.spawn_critters_on_crit then
critter1:play{pitch = random:float(0.95, 1.05), volume = 0.5} critter1:play{pitch = random:float(0.95, 1.05), volume = 0.5}
trigger:after(0.01, function() trigger:after(0.01, function()
@ -1450,7 +1535,8 @@ function Player:shoot(r, mods)
archer1:play{pitch = random:float(0.95, 1.05), volume = 0.35} archer1:play{pitch = random:float(0.95, 1.05), volume = 0.35}
elseif self.character == 'wizard' or self.character == 'lich' or self.character == 'arcanist' then elseif self.character == 'wizard' or self.character == 'lich' or self.character == 'arcanist' then
wizard1:play{pitch = random:float(0.95, 1.05), volume = 0.15} wizard1:play{pitch = random:float(0.95, 1.05), volume = 0.15}
elseif self.character == 'scout' or self.character == 'outlaw' or self.character == 'blade' or self.character == 'spellblade' or self.character == 'jester' or self.character == 'assassin' or self.character == 'beastmaster' then elseif self.character == 'scout' or self.character == 'outlaw' or self.character == 'blade' or self.character == 'spellblade' or self.character == 'jester' or self.character == 'assassin' or self.character == 'beastmaster' or
self.character == 'thief' then
_G[random:table{'scout1', 'scout2'}]:play{pitch = random:float(0.95, 1.05), volume = 0.35} _G[random:table{'scout1', 'scout2'}]:play{pitch = random:float(0.95, 1.05), volume = 0.35}
if self.character == 'spellblade' then if self.character == 'spellblade' then
wizard1:play{pitch = random:float(0.95, 1.05), volume = 0.15} wizard1:play{pitch = random:float(0.95, 1.05), volume = 0.15}
@ -1609,6 +1695,12 @@ function Projectile:init(args)
elseif self.character == 'witch' and self.level == 3 then elseif self.character == 'witch' and self.level == 3 then
self.chain = 1 self.chain = 1
elseif self.character == 'treasure_hunter' then
self.homing = true
if self.level == 3 then
self.pierce = 2
end
end end
if self.parent.divine_machine_arrow and table.any(self.parent.classes, function(v) return v == 'ranger' end) then if self.parent.divine_machine_arrow and table.any(self.parent.classes, function(v) return v == 'ranger' end) then
@ -1764,7 +1856,8 @@ function Projectile:on_collision_enter(other, contact)
self.ricochet = self.ricochet - 1 self.ricochet = self.ricochet - 1
end end
_G[random:table{'arrow_hit_wall1', 'arrow_hit_wall2'}]:play{pitch = random:float(0.9, 1.1), volume = 0.2} _G[random:table{'arrow_hit_wall1', 'arrow_hit_wall2'}]:play{pitch = random:float(0.9, 1.1), volume = 0.2}
elseif self.character == 'scout' or self.character == 'outlaw' or self.character == 'blade' or self.character == 'spellblade' or self.character == 'jester' or self.character == 'beastmaster' or self.character == 'witch' then elseif self.character == 'scout' or self.character == 'outlaw' or self.character == 'blade' or self.character == 'spellblade' or self.character == 'jester' or self.character == 'beastmaster' or self.character == 'witch' or
self.character == 'thief' then
self:die(x, y, r, 0) self:die(x, y, r, 0)
knife_hit_wall1:play{pitch = random:float(0.9, 1.1), volume = 0.2} knife_hit_wall1:play{pitch = random:float(0.9, 1.1), volume = 0.2}
local r = Unit.bounce(self, nx, ny) local r = Unit.bounce(self, nx, ny)
@ -1789,7 +1882,7 @@ function Projectile:on_collision_enter(other, contact)
elseif self.character == 'cannoneer' then elseif self.character == 'cannoneer' then
self:die(x, y, r, random:int(2, 3)) self:die(x, y, r, random:int(2, 3))
cannon_hit_wall1:play{pitch = random:float(0.95, 1.05), volume = 0.1} cannon_hit_wall1:play{pitch = random:float(0.95, 1.05), volume = 0.1}
elseif self.character == 'engineer' or self.character == 'dual_gunner' then elseif self.character == 'engineer' or self.character == 'dual_gunner' or self.character == 'treasure_hunter' then
self:die(x, y, r, random:int(2, 3)) self:die(x, y, r, random:int(2, 3))
_G[random:table{'turret_hit_wall1', 'turret_hit_wall2'}]:play{pitch = random:float(0.9, 1.1), volume = 0.2} _G[random:table{'turret_hit_wall1', 'turret_hit_wall2'}]:play{pitch = random:float(0.9, 1.1), volume = 0.2}
else else
@ -1838,7 +1931,7 @@ function Projectile:on_trigger_enter(other, contact)
end end
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 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' then self.character == 'jester' or self.character == 'assassin' or self.character == 'barrager' or self.character == 'beastmaster' or self.character == 'witch' or self.character == 'treasure_hunter' or self.character == 'thief' then
hit2:play{pitch = random:float(0.95, 1.05), volume = 0.35} hit2:play{pitch = random:float(0.95, 1.05), volume = 0.35}
if self.character == 'spellblade' then if self.character == 'spellblade' then
magic_area1:play{pitch = random:float(0.95, 1.05), volume = 0.15} magic_area1:play{pitch = random:float(0.95, 1.05), volume = 0.15}
@ -2427,6 +2520,7 @@ Volcano:implement(GameObject)
Volcano:implement(Physics) Volcano:implement(Physics)
function Volcano:init(args) function Volcano:init(args)
self:init_game_object(args) self:init_game_object(args)
if not self.group.world then self.dead = true; return end
self:set_as_rectangle(9, 9, 'static', 'player') self:set_as_rectangle(9, 9, 'static', 'player')
self:set_restitution(0.5) self:set_restitution(0.5)
self.hfx:add('hit', 1) self.hfx:add('hit', 1)
@ -2777,6 +2871,81 @@ function Illusion:draw()
end end
Gold = Object:extend()
Gold:implement(GameObject)
Gold:implement(Physics)
function Gold:init(args)
self:init_game_object(args)
self:set_as_rectangle(3, 3, 'dynamic', 'ghost')
self:set_restitution(0.5)
local r = random:float(0, 2*math.pi)
local f = random:float(2, 4)
self:apply_impulse(f*math.cos(r), f*math.sin(r))
self:apply_angular_impulse(random:table{random:float(-6*math.pi, -2*math.pi), random:float(2*math.pi, 6*math.pi)})
self:set_damping(2.5)
self:set_angular_damping(5)
self.color = yellow2[0]
self.hfx:add('hit', 1)
gold1:play{pitch = random:float(0.95, 1.05), volume = 0.5}
end
function Gold:update(dt)
self:update_game_object(dt)
self.r = self:get_angle()
end
function Gold: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, 1, 1, self.hfx.hit.f and fg[0] or self.color)
graphics.pop()
end
function Gold:on_trigger_enter(other, contact)
if other:is(Player) then
main.current.gold_picked_up = main.current.gold_picked_up + 1
self.dead = true
HitCircle{group = main.current.effects, x = self.x, y = self.y, rs = 4, color = fg[0], duration = 0.1}
for i = 1, 2 do HitParticle{group = main.current.effects, x = self.x, y = self.y, color = self.color} end
_G[random:table{'gold2', 'coins1', 'coins2', 'coins3'}]:play{pitch = random:float(0.9, 1.1), volume = 0.3}
local units = other:get_all_units()
local th
for _, unit in ipairs(units) do
if unit.character == 'treasure_hunter' then
th = unit
end
end
if th then
if th.level == 3 then
trigger:after(0.01, function()
_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)
for i = 1, 8 do
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 = yellow2[0], dmg = th.dmg, character = th.character, parent = th, level = th.level}
Projectile(table.merge(t, mods or {}))
r = r + math.pi/4
end
end)
else
trigger:after(0.01, function()
_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)
for i = 1, 4 do
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 = yellow2[0], dmg = th.dmg, character = th.character, parent = th, level = th.level}
Projectile(table.merge(t, mods or {}))
r = r + 2*math.pi/4
end
end)
end
end
end
end
Critter = Object:extend() Critter = Object:extend()

View File

@ -13,6 +13,7 @@ function shared_init()
red = ColorRamp(Color'#e91d39', 0.025), red = ColorRamp(Color'#e91d39', 0.025),
purple = ColorRamp(Color'#8e559e', 0.025), purple = ColorRamp(Color'#8e559e', 0.025),
blue2 = ColorRamp(Color'#4778ba', 0.025), blue2 = ColorRamp(Color'#4778ba', 0.025),
yellow2 = ColorRamp(Color'#f59f10', 0.025),
} }
for name, color in pairs(colors) do for name, color in pairs(colors) do
_G[name] = color _G[name] = color
@ -494,6 +495,7 @@ global_text_tags = {
red = TextTag{draw = function(c, i, text) graphics.set_color(red[0]) end}, red = TextTag{draw = function(c, i, text) graphics.set_color(red[0]) end},
orange = TextTag{draw = function(c, i, text) graphics.set_color(orange[0]) end}, orange = TextTag{draw = function(c, i, text) graphics.set_color(orange[0]) end},
yellow = TextTag{draw = function(c, i, text) graphics.set_color(yellow[0]) end}, yellow = TextTag{draw = function(c, i, text) graphics.set_color(yellow[0]) end},
yellow2 = TextTag{draw = function(c, i, text) graphics.set_color(yellow2[0]) end},
green = TextTag{draw = function(c, i, text) graphics.set_color(green[0]) end}, green = TextTag{draw = function(c, i, text) graphics.set_color(green[0]) end},
purple = TextTag{draw = function(c, i, text) graphics.set_color(purple[0]) end}, purple = TextTag{draw = function(c, i, text) graphics.set_color(purple[0]) end},
blue = TextTag{draw = function(c, i, text) graphics.set_color(blue[0]) end}, blue = TextTag{draw = function(c, i, text) graphics.set_color(blue[0]) end},
@ -501,6 +503,7 @@ global_text_tags = {
bg = TextTag{draw = function(c, i, text) graphics.set_color(bg[0]) end}, bg = TextTag{draw = function(c, i, text) graphics.set_color(bg[0]) end},
bg3 = TextTag{draw = function(c, i, text) graphics.set_color(bg[3]) end}, bg3 = TextTag{draw = function(c, i, text) graphics.set_color(bg[3]) end},
bg10 = TextTag{draw = function(c, i, text) graphics.set_color(bg[10]) end}, bg10 = TextTag{draw = function(c, i, text) graphics.set_color(bg[10]) end},
bgm2 = TextTag{draw = function(c, i, text) graphics.set_color(bg[-2]) end},
light_bg = TextTag{draw = function(c, i, text) graphics.set_color(bg[5]) end}, light_bg = TextTag{draw = function(c, i, text) graphics.set_color(bg[5]) end},
fg = TextTag{draw = function(c, i, text) graphics.set_color(fg[0]) end}, fg = TextTag{draw = function(c, i, text) graphics.set_color(fg[0]) end},
fgm5 = TextTag{draw = function(c, i, text) graphics.set_color(fg[-5]) end}, fgm5 = TextTag{draw = function(c, i, text) graphics.set_color(fg[-5]) end},
@ -511,6 +514,8 @@ global_text_tags = {
bluem5 = TextTag{draw = function(c, i, text) graphics.set_color(blue[-5]) end}, bluem5 = TextTag{draw = function(c, i, text) graphics.set_color(blue[-5]) end},
blue25 = TextTag{draw = function(c, i, text) graphics.set_color(blue2[5]) end}, blue25 = TextTag{draw = function(c, i, text) graphics.set_color(blue2[5]) end},
blue2m5 = TextTag{draw = function(c, i, text) graphics.set_color(blue2[-5]) end}, blue2m5 = TextTag{draw = function(c, i, text) graphics.set_color(blue2[-5]) end},
yellow25 = TextTag{draw = function(c, i, text) graphics.set_color(yellow2[5]) end},
yellow2m5 = TextTag{draw = function(c, i, text) graphics.set_color(yellow2[-5]) end},
redm5 = TextTag{draw = function(c, i, text) graphics.set_color(red[-5]) end}, redm5 = TextTag{draw = function(c, i, text) graphics.set_color(red[-5]) end},
orangem5 = TextTag{draw = function(c, i, text) graphics.set_color(orange[-5]) end}, orangem5 = TextTag{draw = function(c, i, text) graphics.set_color(orange[-5]) end},
purplem5 = TextTag{draw = function(c, i, text) graphics.set_color(purple[-5]) end}, purplem5 = TextTag{draw = function(c, i, text) graphics.set_color(purple[-5]) end},

96
todo
View File

@ -1,24 +1,24 @@
Shop Update Shop Update
New units New units
Assists (2/4) - +25/50% assist buff effectiveness * Mercenaries (2/4) - +10/20% chance for enemies to drop gold on death
Ringmaster (tier 4 assist, nuker) * Treasure Hunter (tier 1 mercenary) - picking up gold releases 4 homing projectiles that deal X damage, Lv.3 - release 8 homing projectiles instead and they pierce twice
Absorber (tier 2 assist, warrior) * Merchant (tier 2 mercenary) - gain +1 interest for every 10 gold, Lv.3 - your first item reroll is always free
Pardoner (tier 3 assist, mercenary) * Usurer (tier 3 curser, mercenary, voider) - curses 3 nearby enemies indefinitely with debt, dealing X damage over time, Lv.3 - if the same enemy is cursed 3 times it takes 50X damage
Oracle (tier 1 assist) * Gambler (tier 3 mercenary, sorcerer) - deal 2X damage to a single random enemy where X is how much gold you have, Lv.3 effect - 60%/40%/20% chance to cast the attack 2/3/4 times
Seraph (tier 2 assist, healer) * Thief (tier 4 mercenary, rogue) - throws a knife that deals 2X damage and chains 5 times, Lv.3 - if the knife crits it deals 10X damage, chains 10 times and grants 1 gold
Mercenaries (3/3) - enemies occasionally drop pieces of gold
Treasure Hunter (tier 1 mercenary)
Merchant (tier 2 mercenary)
Pardoner (tier 3 assist, mercenary)
Gambler (tier 4 mercenary, rogue)
Shop changes Shop changes
Owned units highlighted in shop Owned units highlighted in shop
Fix highlight colors and highlight reserve Fix highlight colors and highlight reserve
Shop level up Shop level up
Remove level 3 units from rotation
Item changes Item changes
Sorcerer items
Mercenary items
Unit changes
Launcher - remove
Beastmaster
QoL QoL
* Added sorcerer achievement * Added sorcerer achievement
@ -28,7 +28,7 @@ Shop Update
* Added mouse follow control mode * Added mouse follow control mode
* The cursor is now invisible during waves, unless mouse follow mode is enabled * The cursor is now invisible during waves, unless mouse follow mode is enabled
Options menu from buy screen Options menu from buy screen
Add visuals for defensive ouroboros, divine intervention, fairy buff Add visuals for tank attack
Balance Balance
* Decreased level 25 boss movement speed * Decreased level 25 boss movement speed
@ -38,34 +38,43 @@ Shop Update
* Fixed a bug where enemy critters would sometimes be unkillable * Fixed a bug where enemy critters would sometimes be unkillable
* Fixed a rare crash involving broken enemy critter state * Fixed a rare crash involving broken enemy critter state
* Fixed text for the illusionist's Lv.3 effect going outside the screen * Fixed text for the illusionist's Lv.3 effect going outside the screen
* Fixed a rare crash when hovering over your owned in the shop * Fixed a crash when hovering over the Lich in the shop due to playing with a save before the sorcerer update
* Fixed a crash when warden's force field would spawn on top of enemies * Fixed a crash when warden's force field would spawn on top of enemies
* Fixed a crash when a volcano would spawn on top of enemies
* Fixed a crash when a pet would spawn on top of enemies * Fixed a crash when a pet would spawn on top of enemies
* Fixed a bug where the maximum number of units would be wrong on certain conditions * Fixed a bug where the maximum number of units would be wrong on certain conditions
* Fixed a crash when clicking too fast after unpausing the game
Fix bug where quitting on level 2 arena goes back to level 1 shop Fix bug where quitting on level 2 arena goes back to level 1 shop
Fix fullscreen with different resolutions that don't scale properly Fix fullscreen with different resolutions that don't scale properly
Fix enemies still spawning after arena clear (this happens with the extra enemy spawns that were blocked earlier) Fix enemies still spawning after arena clear (this happens with the extra enemy spawns that were blocked earlier)
https://i.imgur.com/ieVqYNI.png
https://i.imgur.com/3JCeFuZ.png
https://i.imgur.com/cvC1TBz.png
Sacrifice Update Sacrifice Update
New mechanics New mechanics
Sacrifice units to level items up Sacrifice units to level items up
New items New items
https://steamcommunity.com/app/915310/discussions/0/3106901028662504698/
Reworked items Reworked items
Items shouldn't just be more powerful versions of other items Items shouldn't just be more powerful versions of other items
Items should have drawbacks Items should have drawbacks
Items that apply to a position on the snake (a good middle step between applying them to individual units like in Underlords) Items that apply to a position on the snake (a good middle step between applying them to individual units like in Underlords)
New units
Nocturnals/Darks/??? (3/6)
Shadowmancer
Necromancer
Demonologist
Demon
Lich
Lifestealer
Zombie
QoL QoL
Current items visible on item selection screen Current items visible on item selection screen
Endless mode Endless mode
Volume slider
Add visuals for defensive ouroboros, divine intervention, fairy buff
Melee Update
New Units
Guardians - https://i.imgur.com/Ynu5Cdw.png
Assists (2/4) -
Ringmaster (tier 4 assist, nuker) - +15% to all stats to adjacent units, Lv.3 effect - create a cross that deals AoE damage 5 times for 10 seconds
Absorber (tier 2 assist, warrior) - absorbs 50% damage from adjacent units, Lv.3 effect - absorbs 75% damage from adjacent units and gives the absorber +25% defense
Pardoner (tier 3 assist, mercenary) -
Oracle (tier 1 assist) - +10% dodge chance to adjacent units, Lv.3 effect - +20% dodge chance to adjacent units
Seraph (tier 2 assist, healer) - periodically chooses 1 random unit and gives it +100% defense for 6 seconds, Lv.3 - choose 2 units instead
--- ---
@ -83,25 +92,36 @@ Hexblaster? - curser that consumes curses to deal damage
Bench? - https://i.imgur.com/B1gNVKk.png Bench? - https://i.imgur.com/B1gNVKk.png
Balance option for when there are more sets - https://i.imgur.com/JMynwbL.png Balance option for when there are more sets - https://i.imgur.com/JMynwbL.png
Negative effect: colliding with yourself kills one of your units Negative effect: colliding with yourself kills one of your units
Go through this later https://i.imgur.com/4t7NA32.png <- lots of good improvements https://i.imgur.com/bxfvA7g.png
Roguelite update: Roguelite update:
Technical improvements: Technical improvements:
Spawn tech: spawn every entity in a grid, before spawning check to see if grid position is clear, this will prevent any issues due to entities spawning inside one another Spawn tech: spawn every entity in a grid, before spawning check to see if grid position is clear, this will prevent any issues due to entities spawning inside one another
Battle stats: DPS, damage taken, etc Battle stats: DPS, damage taken, etc
Key rebinding (for non-QWERTY keyboards) Key rebinding (for non-QWERTY keyboards)
StS-like map with nodes, node types: StS-like map with nodes, node types:
Arena Arena
Elite Elite
Boss Boss
Map (map of bigger size than arena with fixed spawns) Map (map of bigger size than arena with fixed spawns)
Unit shop Unit shop
Item shop Item shop
Text + reward Text + reward
Training grounds (upgrade unit) Training grounds (upgrade unit)
Tavern (heal units) Tavern (heal units)
Units die permanently when they die (dead units can be stored in bench to be revived later)
Units can have items attached to them like in Underlords
Unit item ideas:
This unit's projectiles pierce/chain/fork/seek/split/stun/etc
This unit is a [class]
New stat system:
All stats are values from 1 to 10 (can be lower than 1 or higher than due to debuffs/buffs only) that represent consistent internal values between all units
i.e. 3 attack speed means the same internal attack rate value (like say 6 seconds) for the entire game
In general it's better if units don't have hidden internal multipliers on these stats, although sometimes that may be inevitable
Damage:
Crash - damage dealt when bumping into enemies
Projectile - damage dealt by projectiles
AoE - damage dealt in an area
DoT - damage dealt over time
Units die permanently when they die (dead units can be stored in bench to be revived later)
Units can have items attached to them like in Underlords
Unit item ideas:
This unit's projectiles pierce/chain/fork/seek/split/stun/etc