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)
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.effects = 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('player', '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('enemy_projectile', 'player')
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_taken = 0
self.main_slow_amount = 1
@ -257,6 +266,7 @@ function Arena:on_enter(from, level, units, passives)
self.psyker_level = class_levels.psyker
self.conjurer_level = class_levels.conjurer
self.sorcerer_level = class_levels.sorcerer
self.mercenary_level = class_levels.mercenary
self.t:every(0.375, function()
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()
slow_amount = 1
self.paused = false
self.paused_t1.dead = true
self.paused_t2.dead = true
self.paused_t1 = nil
self.paused_t2 = nil
self.ng_t.dead = true
self.ng_t = nil
if self.paused_t1 then self.paused_t1.dead = true; self.paused_t1 = nil end
if self.paused_t2 then self.paused_t2.dead = true; self.paused_t2 = nil end
if self.ng_t then self.ng_t.dead = true; self.ng_t = 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.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()
slow_amount = 1
self.paused = false
self.paused_t1.dead = true
self.paused_t2.dead = true
self.paused_t1 = nil
self.paused_t2 = nil
self.ng_t.dead = true
self.ng_t = nil
if self.paused_t1 then self.paused_t1.dead = true; self.paused_t1 = nil end
if self.paused_t2 then self.paused_t2.dead = true; self.paused_t2 = nil end
if self.ng_t then self.ng_t.dead = true; self.ng_t = 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.mouse_button then self.mouse_button.dead = true; self.mouse_button = nil end
@ -608,6 +612,7 @@ function Arena:quit()
self.quitting = true
if self.level == 25 then
if not self.win_text and not self.win_text2 then
input:set_mouse_visible(true)
self.won = true
locked_state = nil
system.save_run()
@ -800,6 +805,13 @@ function Arena:quit()
steam.userStats.storeStats()
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 all_units_level_2 = true
for _, unit in ipairs(units) do
@ -836,6 +848,7 @@ function Arena:quit()
self:gain_gold()
self.t:after(3, function()
if self.level % 3 == 0 then
input:set_mouse_visible(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(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()
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.interest = math.min(math.floor(gold/5), 5)
gold = gold + self.gold_gained + self.interest
self.interest = math.min(math.floor(gold/5), 5) + (merchant and math.floor(gold/10) or 0)
gold = gold + self.gold_gained + self.gold_picked_up + self.interest
end
@ -1074,30 +1088,30 @@ function Arena:transition()
main:go_to('buy_screen', self.level, self.units, self.passives)
t.t:after(0.1, function()
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]total: 0', font = pixul_font, alignment = 'center'}
})
_G[random:table{'coins1', 'coins2', 'coins3'}]:play{pitch = random:float(0.95, 1.05), volume = 0.5}
t.t:after(0.2, function()
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 = '[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}
t.t:after(0.2, function()
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 = '[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}
end)
end)
end)
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]total: 0', font = pixul_font, alignment = 'center'}
}, 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)
elseif self.parent:is(Arena) then
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.text = Text({{text = '[bg10]reroll: [yellow]0', font = pixul_font, alignment = 'center'}}, global_text_tags)
else
@ -847,7 +848,11 @@ end
function RerollButton:draw()
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)
graphics.pop()
end
@ -1077,6 +1082,7 @@ end
function CharacterPart:get_sale_price()
if not character_tiers[self.character] then return 0 end
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)
if self.reserve then

View File

@ -253,6 +253,8 @@ function Seeker:init(args)
if player and player.temporal_chains then
self.temporal_chains_mvspd_m = 0.8
end
self.usurer_count = 0
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)
_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
slow(0.25, 1)
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
self.silenced = true
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
@ -628,6 +649,7 @@ function EnemyCritter:init(args)
self:push(args.v, args.r)
self.invulnerable_to = args.projectile
self.t:after(0.5, function() self.invulnerable_to = false end)
self.usurer_count = 0
end
@ -778,6 +800,14 @@ function EnemyCritter:curse(curse, duration, arg1, arg2, arg3)
elseif curse == 'silencer' then
self.silenced = true
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

View File

@ -418,6 +418,16 @@ function table.first(t, n)
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
-- t = {4, 3, 2, 1}
-- table.last(t) -> 1

120
main.lua
View File

@ -9,7 +9,6 @@ require 'media'
function init()
print('Initializing engine...')
shared_init()
input:bind('move_left', {'a', 'left', 'dpleft', 'm1'})
@ -18,8 +17,11 @@ function init()
input:bind('move_down', {'s', 'down', 'dpdown'})
input:bind('enter', {'space', 'return', 'fleft', 'fdown', 'fright'})
print('Loading sounds...')
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)
fire1 = Sound('Fire bolt 3.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_crit2 = Sound('Sword hits another sword 6.ogg', s)
print('Loading songs...')
song1 = Sound('Kubbi - Ember - 01 Pathfinder.ogg', {tags = {music}})
song2 = Sound('Kubbi - Ember - 02 Ember.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}})
death_song = Sound('Kubbi - Ember - 09 Formed by Glaciers.ogg', {tags = {music}})
print('Loading images...')
lock_image = Image('lock')
speed_booster_elite = Image('speed_booster_elite')
exploder_elite = Image('exploder_elite')
@ -140,6 +140,7 @@ function init()
swarmer = Image('swarmer')
voider = Image('voider')
sorcerer = Image('sorcerer')
mercenary = Image('mercenary')
ouroboros_technique_r = Image('ouroboros_technique_r')
ouroboros_technique_l = Image('ouroboros_technique_l')
wall_echo = Image('wall_echo')
@ -185,7 +186,6 @@ function init()
star = Image('star')
arrow = Image('arrow')
print('Initializing game...')
class_colors = {
['warrior'] = yellow[0],
['ranger'] = green[0],
@ -201,6 +201,7 @@ function init()
['swarmer'] = orange[0],
['voider'] = purple[0],
['sorcerer'] = blue2[0],
['mercenary'] = yellow2[0],
}
class_color_strings = {
@ -218,6 +219,7 @@ function init()
['swarmer'] = 'orange',
['voider'] = 'purple',
['sorcerer'] = 'blue2',
['mercenary'] = 'yellow2',
}
character_names = {
@ -270,6 +272,11 @@ function init()
['vulcanist'] = 'Vulcanist',
['warden'] = 'Warden',
['psychic'] = 'Psychic',
['treasure_hunter'] = 'Treasure Hunter',
['merchant'] = 'Merchant',
['usurer'] = 'Usurer',
['gambler'] = 'Gambler',
['thief'] = 'Thief',
}
character_colors = {
@ -322,6 +329,11 @@ function init()
['vulcanist'] = red[0],
['warden'] = yellow[0],
['psychic'] = fg[0],
['treasure_hunter'] = yellow2[0],
['merchant'] = yellow2[0],
['usurer'] = purple[0],
['gambler'] = yellow2[0],
['thief'] = red[0],
}
character_color_strings = {
@ -374,6 +386,11 @@ function init()
['vulcanist'] = 'red',
['warden'] = 'yellow',
['psychic'] = 'fg',
['treasure_hunter'] = 'yellow2',
['merchant'] = 'yellow2',
['usurer'] = 'purple',
['gambler'] = 'yellow2',
['thief'] = 'red',
}
character_classes = {
@ -426,6 +443,11 @@ function init()
['vulcanist'] = {'sorcerer', 'nuker'},
['warden'] = {'sorcerer', 'forcer'},
['psychic'] = {'sorcerer', 'psyker'},
['treasure_hunter'] = {'mercenary'},
['merchant'] = {'mercenary'},
['usurer'] = {'curser', 'mercenary', 'voider'},
['gambler'] = {'mercenary', 'sorcerer'},
['thief'] = {'rogue', 'mercenary'},
}
character_class_strings = {
@ -478,6 +500,11 @@ function init()
['vulcanist'] = '[blue2]Sorcerer, [red]Nuker',
['warden'] = '[blue2]Sorcerer, [yellow]Forcer',
['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)
@ -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,
['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,
['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 = {
@ -598,7 +630,12 @@ function init()
['silencer'] = '[blue2]Arcane Curse',
['vulcanist'] = '[red]Lava Burst',
['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 = {
@ -650,7 +687,12 @@ function init()
['silencer'] = '[light_bg]Arcane Curse',
['vulcanist'] = '[light_bg]Lava Burst',
['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 = {
@ -703,6 +745,11 @@ function init()
['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,
['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 = {
@ -755,6 +802,11 @@ function init()
['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,
['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 = {
@ -807,6 +859,11 @@ function init()
['vulcanist'] = function(lvl) return get_character_stat_string('vulcanist', lvl) end,
['warden'] = function(lvl) return get_character_stat_string('warden', lvl) end,
['psychic'] = function(lvl) return get_character_stat_string('psychic', lvl) end,
['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 = {
@ -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},
['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},
['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},
['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},
@ -862,17 +920,18 @@ function init()
['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'
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 = {
[1] = {'vagrant', 'swordsman', 'magician', 'archer', 'scout', 'cleric', 'arcanist'},
[2] = {'wizard', 'saboteur', 'sage', 'squire', 'dual_gunner', 'hunter', 'chronomancer', 'barbarian', 'cryomancer', 'beastmaster', 'launcher', 'jester', 'carver', 'psychic', 'witch', 'silencer', 'outlaw'},
[3] = {'elementor', 'stormweaver', 'spellblade', 'psykeeper', 'engineer', 'juggernaut', 'pyromancer', 'host', 'assassin', 'bane', 'barrager', 'infestor', 'flagellant', 'illusionist'},
[4] = {'priest', 'highlander', 'psykino', 'fairy', 'blade', 'plague_doctor', 'cannoneer', 'vulcanist', 'warden', 'corruptor'},
[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', 'merchant'},
[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', 'thief'},
}
non_attacking_characters = {'cleric', 'stormweaver', 'squire', 'chronomancer', 'sage', 'psykeeper', 'bane', 'carver', 'fairy', 'priest', 'flagellant'}
non_cooldown_characters = {'squire', 'chronomancer', 'psykeeper'}
non_attacking_characters = {'cleric', 'stormweaver', 'squire', 'chronomancer', 'sage', 'psykeeper', 'bane', 'carver', 'fairy', 'priest', 'flagellant', 'merchant', 'treasure_hunter'}
non_cooldown_characters = {'squire', 'chronomancer', 'psykeeper', 'merchant', 'treasure_hunter'}
character_tiers = {
['vagrant'] = 1,
@ -881,7 +940,6 @@ function init()
['archer'] = 1,
['scout'] = 1,
['cleric'] = 1,
['arcanist'] = 1,
['outlaw'] = 2,
['blade'] = 4,
['elementor'] = 3,
@ -918,12 +976,18 @@ function init()
['priest'] = 4,
['infestor'] = 3,
['flagellant'] = 3,
['psychic'] = 2,
['arcanist'] = 1,
['illusionist'] = 3,
['witch'] = 2,
['silencer'] = 2,
['illusionist'] = 3,
['warden'] = 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)
@ -941,6 +1005,7 @@ function init()
local swarmers = 0
local voiders = 0
local sorcerers = 0
local mercenaries = 0
for _, unit in ipairs(units) do
for _, unit_class in ipairs(character_classes[unit.character]) do
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 == 'voider' then voiders = voiders + 1 end
if unit_class == 'sorcerer' then sorcerers = sorcerers + 1 end
if unit_class == 'mercenary' then mercenaries = mercenaries + 1 end
end
end
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
get_class_levels = function(units)
@ -970,7 +1036,7 @@ function init()
if number_of_units >= 6 then return 2
elseif number_of_units >= 3 then return 1
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
elseif number_of_units >= 2 then return 1
else return 0 end
@ -996,6 +1062,7 @@ function init()
swarmer = units_to_class_level(units_per_class.swarmer, 'swarmer'),
voider = units_to_class_level(units_per_class.voider, 'voider'),
sorcerer = units_to_class_level(units_per_class.sorcerer, 'sorcerer'),
mercenary = units_to_class_level(units_per_class.mercenary, 'mercenary'),
}
end
@ -1022,6 +1089,7 @@ function init()
['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,
['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 = {
@ -1323,22 +1391,16 @@ function init()
end
-- main_song_instance = _G[random:table{'song1', 'song2', 'song3', 'song4', 'song5'}]:play{volume = 0.5}
main:add(BuyScreen'buy_screen')
main:go_to('buy_screen', run.level or 0, run.units or {}, passives)
-- main:go_to('buy_screen', 7, run.units or {}, {'unleash'})
--[[
main:add(Arena'arena')
main:go_to('arena', 17, {
{character = 'arcanist', level = 2},
{character = 'silencer', level = 2},
{character = 'warden', level = 3},
{character = 'chronomancer', level = 1},
{character = 'witch', level = 3},
{character = 'illusionist', level = 3},
{character = 'psychic', level = 2},
{character = 'vulcanist', level = 3},
main:go_to('arena', 13, {
{character = 'thief', level = 3},
{character = 'scout', level = 1},
{character = 'beastmaster', level = 1},
}, passives)
]]--

View File

@ -47,7 +47,6 @@ function Player:init(args)
self:attack(32, {x = enemy.x, y = enemy.y})
end
end, nil, nil, 'attack')
if self.level == 3 then
self.t:every(12, function()
self.magician_invulnerable = true
@ -55,6 +54,60 @@ function Player:init(args)
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
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()
@ -73,6 +126,15 @@ function Player:init(args)
end
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
self.t:every(6, function()
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
x, y = x + self.x, y + self.y
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 objects = main.current.main:get_objects_in_shape(check_circle, {Player, Seeker, EnemyCritter, Critter, Illusion, Saboteur, Pet, Turret})
if #objects == 0 then
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, nil, nil, 'volcano_spawn')
end
@ -431,9 +493,10 @@ function Player:init(args)
elseif self.character == 'jester' 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.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
if self:distance_to_object(enemy) < 128 then
enemy:curse('jester', 6*(self.hex_duration_m or 1), self.level == 3, self)
@ -443,26 +506,39 @@ function Player:init(args)
end
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
self.sorcerer_count = 0
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()
local curse = function()
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
if self:distance_to_object(enemy) < 128 then
enemy:curse('silencer', 6*(self.hex_duration_m or 1), self.level == 3, self)
if self.level == 3 then
local curse_m = 1
if main.current.curser_level == 2 then curse_m = 1.5
elseif main.current.curser_level == 1 then curse_m = 1.25
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))
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]}
enemy:curse('silencer', 6*(self.hex_duration_m or 1), self.level == 3, self)
if self.level == 3 then
local curse_m = 1
if main.current.curser_level == 2 then curse_m = 1.5
elseif main.current.curser_level == 1 then curse_m = 1.25
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))
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
curse()
@ -508,15 +584,14 @@ function Player:init(args)
elseif self.character == 'bane' 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.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
if self:distance_to_object(enemy) < 128 then
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}
LightningLine{group = main.current.effects, src = self, dst = enemy, color = purple[0]}
end
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}
LightningLine{group = main.current.effects, src = self, dst = enemy, color = purple[0]}
end
end, nil, nil, 'attack')
@ -603,24 +678,24 @@ function Player:init(args)
local unit_2 = random:table_remove(units)
if unit_1 then
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 objects = main.current.main:get_objects_in_shape(check_circle, {Seeker, EnemyCritter})
if #objects == 0 then
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, nil, nil, 'warden_force_field_1')
end
if unit_2 then
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}
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 objects = main.current.main:get_objects_in_shape(check_circle, {Seeker, EnemyCritter})
if #objects == 0 then
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, nil, nil, 'warden_force_field_2')
end
@ -628,12 +703,12 @@ function Player:init(args)
local unit = random:table(self:get_all_units())
if unit then
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 objects = main.current.main:get_objects_in_shape(check_circle, {Seeker, EnemyCritter})
if #objects == 0 then
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, nil, nil, 'warden_force_field_0')
end
@ -671,16 +746,15 @@ function Player:init(args)
end, nil, nil, 'heal')
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()
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
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)
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]}
end
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}
LightningLine{group = main.current.effects, src = self, dst = enemy, color = orange[0]}
end
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.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.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_aspd_m = 1 + 0.1*number_of_active_sets
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.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.mercenary >= 1 then number_of_active_sets = number_of_active_sets + 1 end
if main.current.psyker_level == 2 then
self.psyker_dmg_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()
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
self.t:set_every_multiplier('shoot', self.aspd_m)
self.t:set_every_multiplier('attack', self.aspd_m)
@ -1371,6 +1448,14 @@ function Player:shoot(r, mods)
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
critter1:play{pitch = random:float(0.95, 1.05), volume = 0.5}
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}
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}
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}
if self.character == 'spellblade' then
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
self.chain = 1
elseif self.character == 'treasure_hunter' then
self.homing = true
if self.level == 3 then
self.pierce = 2
end
end
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
end
_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)
knife_hit_wall1:play{pitch = random:float(0.9, 1.1), volume = 0.2}
local r = Unit.bounce(self, nx, ny)
@ -1789,7 +1882,7 @@ function Projectile:on_collision_enter(other, contact)
elseif self.character == 'cannoneer' then
self:die(x, y, r, random:int(2, 3))
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))
_G[random:table{'turret_hit_wall1', 'turret_hit_wall2'}]:play{pitch = random:float(0.9, 1.1), volume = 0.2}
else
@ -1838,7 +1931,7 @@ function Projectile:on_trigger_enter(other, contact)
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
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}
if self.character == 'spellblade' then
magic_area1:play{pitch = random:float(0.95, 1.05), volume = 0.15}
@ -2427,6 +2520,7 @@ Volcano:implement(GameObject)
Volcano:implement(Physics)
function Volcano:init(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_restitution(0.5)
self.hfx:add('hit', 1)
@ -2777,6 +2871,81 @@ function Illusion:draw()
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()

View File

@ -13,6 +13,7 @@ function shared_init()
red = ColorRamp(Color'#e91d39', 0.025),
purple = ColorRamp(Color'#8e559e', 0.025),
blue2 = ColorRamp(Color'#4778ba', 0.025),
yellow2 = ColorRamp(Color'#f59f10', 0.025),
}
for name, color in pairs(colors) do
_G[name] = color
@ -494,6 +495,7 @@ global_text_tags = {
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},
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},
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},
@ -501,6 +503,7 @@ global_text_tags = {
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},
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},
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},
@ -511,6 +514,8 @@ global_text_tags = {
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},
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},
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},

96
todo
View File

@ -1,24 +1,24 @@
Shop Update
New units
Assists (2/4) - +25/50% assist buff effectiveness
Ringmaster (tier 4 assist, nuker)
Absorber (tier 2 assist, warrior)
Pardoner (tier 3 assist, mercenary)
Oracle (tier 1 assist)
Seraph (tier 2 assist, healer)
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)
* Mercenaries (2/4) - +10/20% chance for enemies to drop gold on death
* 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
* Merchant (tier 2 mercenary) - gain +1 interest for every 10 gold, Lv.3 - your first item reroll is always free
* 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
* 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
* 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
Shop changes
Owned units highlighted in shop
Fix highlight colors and highlight reserve
Shop level up
Remove level 3 units from rotation
Item changes
Sorcerer items
Mercenary items
Unit changes
Launcher - remove
Beastmaster
QoL
* Added sorcerer achievement
@ -28,7 +28,7 @@ Shop Update
* Added mouse follow control mode
* The cursor is now invisible during waves, unless mouse follow mode is enabled
Options menu from buy screen
Add visuals for defensive ouroboros, divine intervention, fairy buff
Add visuals for tank attack
Balance
* Decreased level 25 boss movement speed
@ -38,34 +38,43 @@ Shop Update
* Fixed a bug where enemy critters would sometimes be unkillable
* Fixed a rare crash involving broken enemy critter state
* 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 a volcano 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 crash when clicking too fast after unpausing the game
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 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
New mechanics
Sacrifice units to level items up
New items
https://steamcommunity.com/app/915310/discussions/0/3106901028662504698/
Reworked items
Items shouldn't just be more powerful versions of other items
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)
New units
Nocturnals/Darks/??? (3/6)
Shadowmancer
Necromancer
Demonologist
Demon
Lich
Lifestealer
Zombie
QoL
Current items visible on item selection screen
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
Balance option for when there are more sets - https://i.imgur.com/JMynwbL.png
Negative effect: colliding with yourself kills one of your units
Go through this later https://i.imgur.com/4t7NA32.png <- lots of good improvements
https://i.imgur.com/bxfvA7g.png
Roguelite update:
Technical improvements:
Spawn tech: spawn every entity in a grid, before spawning check to see if grid position is clear, this will prevent any issues due to entities spawning inside one another
Battle stats: DPS, damage taken, etc
Key rebinding (for non-QWERTY keyboards)
StS-like map with nodes, node types:
Arena
Elite
Boss
Map (map of bigger size than arena with fixed spawns)
Unit shop
Item shop
Text + reward
Training grounds (upgrade unit)
Tavern (heal units)
StS-like map with nodes, node types:
Arena
Elite
Boss
Map (map of bigger size than arena with fixed spawns)
Unit shop
Item shop
Text + reward
Training grounds (upgrade unit)
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