master
a327ex 2021-04-10 22:37:11 -03:00
parent 1b7c4da414
commit f3c1475e37
9 changed files with 353 additions and 84 deletions

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -830,3 +830,36 @@ Lots of improvements and fixes to lots of different things that needed improving
| Blade | Blade Resonance | deal additional X/2 damage per enemy hit |
| Elementor | Windfield | slows enemies by 60% for 6 seconds on hit |
| Saboteur | Demoman | the explosion has 50% chance to crit, increasing in size and dealing 2X damage |
# Day 53 - 10/04/21
Implemented 11 characters today and was going to do more but spent a lot of time trying to make traps work and I couldn't figure it out. Have to idea guy a mechanic other than traps to fill their spot now...
Either way, 21 out of 40 characters 100% done is still good.
| Character | Classes | Description |
| --- | --- | --- |
| Stormweaver | enchanter | infuses projectiles with chain lightning that deals 20% damage to 2 enemies |
| Sage | nuker | shoots a slow projectile that pulls enemies in |
| Squire | warrior, enchanter | +15% damage and defense to all allies |
| Cannoneer | ranger, nuker | shoots a projectile that deals 2X AoE damage |
| Dual Gunner | ranger, rogue | shoots two parallel projectiles, each dealing X damage |
| Hunter | ranger, conjurer | shoots an arrow that deals X damage and has a 20% chance to summon a pet |
| Chronomancer | mage, enchanter | +20% attack speed to all allies |
| Spellblade | mage, rogue | throws knives that deal X damage, pierce and spiral outwards |
| Psykeeper | healer, psyker | all damage taken is stored up to 50% max HP and distributed as healing to all allies |
| Engineer | conjurer | drops sentries that shoot bursts of projectiles, each dealing X damage |
| Plague Doctor | nuker, voider | creates an area that deals X damage per second |
| Character | Lv.3 Effect Name | Lv.3 Effect Description |
| --- | --- | --- |
| Stormweaver | Wide Lightning | chain lightning's trigger area of effect and number of units hit is doubled |
| Sage | Dimension Compression | when the projectile expires deal 3X damage to all enemies under its influence |
| Squire | Repair | you can reroll your item choices once, these opportunities stack if unused |
| Cannoneer | Cannon Barrage | showers the area in 5 additional cannon shots that deal X/2 AoE damage |
| Dual Gunner | Gun Kata | every 5th attack shoots in rapid succession for 2 seconds |
| Hunter | Feral Pack | summons 3 pets and the pets ricochet off walls once |
| Chronomancer | Quicken | enemies take damage over time 50% faster |
| Spellblade | Spiralism | faster projectile speed and tighter turns |
| Psykeeper | Crucio | also redistributes damage taken as damage to all enemies at double value |
| Engineer | Upgrade | every 3rd sentry dropped upgrade all sentries with +100% damage and attack speed |
| Plague Doctor | Black Death Steam | nearby enemies take an additional X damage per second |

View File

@ -19,6 +19,11 @@ function init()
music.volume = 0
local s = {tags = {sfx}}
dot1 = Sound('Magical Swoosh 18.ogg', s)
gun_kata1 = Sound('Pistol Shot_07.ogg', s)
gun_kata2 = Sound('Pistol Shot_08.ogg', s)
dual_gunner1 = Sound('Revolver Shot_07.ogg', s)
dual_gunner2 = Sound('Revolver Shot_08.ogg', s)
ui_hover1 = Sound('bamboo_hit_by_lord.ogg', s)
ui_switch1 = Sound('Switch.ogg', s)
ui_switch2 = Sound('Switch 3.ogg', s)
@ -148,7 +153,7 @@ function init()
['elementor'] = blue[0],
['saboteur'] = orange[0],
['stormweaver'] = blue[0],
['sage'] = red[0],
['sage'] = purple[0],
['squire'] = yellow[0],
['cannoneer'] = orange[0],
['dual_gunner'] = green[0],
@ -177,7 +182,7 @@ function init()
['sapper'] = blue[0],
['priest'] = green[0],
['burrower'] = orange[0],
['flagellant'] = red[0],
['flagellant'] = fg[0],
}
character_color_strings = {
@ -192,7 +197,7 @@ function init()
['elementor'] = 'blue',
['saboteur'] = 'orange',
['stormweaver'] = 'blue',
['sage'] = 'red',
['sage'] = 'purple',
['squire'] = 'yellow',
['cannoneer'] = 'orange',
['dual_gunner'] = 'green',
@ -221,7 +226,7 @@ function init()
['sapper'] = 'blue',
['priest'] = 'green',
['burrower'] = 'orange',
['flagellant'] = 'red',
['flagellant'] = 'fg',
}
character_classes = {
@ -314,17 +319,17 @@ function init()
get_character_stat_string = function(character, level)
local group = Group():set_as_physics_world(32, 0, 0, {'player', 'enemy', 'projectile', 'enemy_projectile'})
local mock = Player{group = group, leader = true, character = character, level = level, follower_index = 1}
mock:update(0)
return '[red]HP: [red]' .. mock.max_hp .. '[fg], [red]DMG: [red]' .. mock.dmg .. '[fg], [green]ASPD: [green]' .. math.round(mock.aspd_m, 2) .. 'x[fg], [blue]AREA: [blue]' ..
math.round(mock.area_dmg_m*mock.area_size_m, 2) .. 'x[fg], [yellow]DEF: [yellow]' .. math.round(mock.def, 2) .. '[fg], [green]MVSPD: [green]' .. math.round(mock.v, 2) .. '[fg]'
local player = Player{group = group, leader = true, character = character, level = level, follower_index = 1}
player:update(0)
return '[red]HP: [red]' .. player.max_hp .. '[fg], [red]DMG: [red]' .. player.dmg .. '[fg], [green]ASPD: [green]' .. math.round(player.aspd_m, 2) .. 'x[fg], [blue]AREA: [blue]' ..
math.round(player.area_dmg_m*player.area_size_m, 2) .. 'x[fg], [yellow]DEF: [yellow]' .. math.round(player.def, 2) .. '[fg], [green]MVSPD: [green]' .. math.round(player.v, 2) .. '[fg]'
end
get_character_stat = function(character, level, stat)
local group = Group():set_as_physics_world(32, 0, 0, {'player', 'enemy', 'projectile', 'enemy_projectile'})
local mock = Player{group = group, leader = true, character = character, level = level, follower_index = 1}
mock:update(0)
return math.round(mock[stat], 2)
local player = Player{group = group, leader = true, character = character, level = level, follower_index = 1}
player:update(0)
return math.round(player[stat], 2)
end
character_descriptions = {
@ -339,18 +344,18 @@ function init()
['blade'] = function(lvl) return '[fg]throws multiple blades that deal [yellow]' .. get_character_stat('blade', lvl, 'dmg') .. ' AoE[fg] damage' end,
['elementor'] = function(lvl) return '[fg]deals [yellow]' .. get_character_stat('elementor', lvl, 'dmg') .. ' AoE[fg] damage in a large area centered on a random target' end,
['saboteur'] = function(lvl) return '[fg]calls [yellow]2[fg] saboteurs to seek targets and deal [yellow]' .. get_character_stat('saboteur', lvl, 'dmg') .. ' AoE[fg] damage' end,
['stormweaver'] = function(lvl) return '[fg]infuses all allied projectiles with chain lightning that deals [yellow]+20%[fg] damage on hit' end,
['stormweaver'] = function(lvl) return '[fg]infuses projectiles with chain lightning that deals [yellow]20%[fg] damage to [yellow]2[fg] enemies' end,
['sage'] = function(lvl) return '[fg]shoots a slow projectile that draws enemies in' end,
['squire'] = function(lvl) return '[yellow]+10%[fg] damage and defense to all allies' end,
['cannoneer'] = function(lvl) return '[fg]shoots a projectile that deals [yellow]' .. get_character_stat('cannoneer', lvl, 'dmg') .. ' AoE[fg] damage' end,
['dual_gunner'] = function(lvl) return '[fg]shoots two parallel projectiles' end,
['squire'] = function(lvl) return '[yellow]+15%[fg] damage and defense to all allies' end,
['cannoneer'] = function(lvl) return '[fg]shoots a projectile that deals [yellow]' .. 2*get_character_stat('cannoneer', lvl, 'dmg') .. ' AoE[fg] damage' end,
['dual_gunner'] = function(lvl) return '[fg]shoots two parallel projectiles, each dealing [yellow]' .. get_character_stat('dual_gunner', lvl, 'dmg') .. '[fg] damage' end,
['hunter'] = function(lvl) return '[fg]shoots an arrow that deals [yellow]' .. get_character_stat('hunter', lvl, 'dmg') .. '[fg] damage and has a [yellow]20%[fg] chance to summon a pet' end,
['chronomancer'] = function(lvl) return '[yellow]+20%[fg] attack speed to all allies' end,
['spellblade'] = function(lvl) return '[fg]throws knives that deal [yellow]' .. get_character_stat('spellblade', lvl, 'dmg') .. '[fg] damage, pierce and spiral outwards' end,
['psykeeper'] = function(lvl) return '[fg]all damage taken is stored up to [yellow]50%[fg] max HP and distributed as healing to all allies' end,
['engineer'] = function(lvl) return '[fg]drops sentries that shoot bursts of projectiles, each dealing [yellow]' .. get_character_stat('engineer', lvl, 'dmg') .. '[fg] damage' end,
['plague_doctor'] = function(lvl) return '[fg]creates an area that deals [yellow]' .. get_character_stat('plague_doctor', lvl, 'dmg') .. '[fg] damage per second' end,
['fisherman'] = function(lvl) return '[fg]throws a net that entangles enemies and prevents them from moving for [yellow]2[fg] seconds' end,
['fisherman'] = function(lvl) return '[fg]throws a net that entangles enemies and prevents them from moving for [yellow]4[fg] seconds' end,
['juggernaut'] = function(lvl) return '[fg]creates a small area that deals [yellow]' .. get_character_stat('juggernaut', lvl, 'dmg') .. '[fg] damage and pushes enemies away with a strong force' end,
['lich'] = function(lvl) return '[fg]launches a chain frost that jumps [yellow]7[fg] times, dealing [yellow]' ..
get_character_stat('lich', lvl, 'dmg') .. '[fg] damage and slowing enemies by [yellow]50%[fg] for [yellow]2[fg] seconds on hit' end,
@ -385,7 +390,7 @@ function init()
['blade'] = '[yellow]Blade Resonance',
['elementor'] = '[blue]Windfield',
['saboteur'] = '[orange]Demoman',
['stormweaver'] = '[blue]Lightning Spire',
['stormweaver'] = '[blue]Wide Lightning',
['sage'] = '[purple]Dimension Compression',
['squire'] = '[yellow]Repair',
['cannoneer'] = '[orange]Cannon Barrage',
@ -395,7 +400,7 @@ function init()
['spellblade'] = '[blue]Spiralism',
['psykeeper'] = '[fg]Crucio',
['engineer'] = '[orange]Upgrade',
['plague_doctor'] = '[purple]Pandemic',
['plague_doctor'] = '[purple]Black Death Steam',
['fisherman'] = '[yellow]Electric Net',
['juggernaut'] = '[yellow]Brutal Impact',
['lich'] = '[blue]Piercing Frost',
@ -423,13 +428,13 @@ function init()
['swordsman'] = '[light_bg]Cleave',
['wizard'] = '[light_bg]Magic Missile',
['archer'] = '[light_bg]Bounce Shot',
['scout'] = '[light_bg]Replica',
['scout'] = '[light_bg]Dagger Resonance',
['cleric'] = '[light_bg]Mass Heal ',
['outlaw'] = '[light_bg]Fatal Roulette',
['outlaw'] = '[light_bg]Flying Daggers',
['blade'] = '[light_bg]Blade Resonance',
['elementor'] = '[light_bg]Windfield',
['saboteur'] = '[light_bg]Chain Reaction',
['stormweaver'] = '[light_bg]Lightning Spire',
['saboteur'] = '[light_bg]Demoman',
['stormweaver'] = '[light_bg]Wide Lightning',
['sage'] = '[light_bg]Dimension Compression',
['squire'] = '[light_bg]Repair',
['cannoneer'] = '[light_bg]Cannon Barrage',
@ -439,7 +444,7 @@ function init()
['spellblade'] = '[light_bg]Spiralism',
['psykeeper'] = '[light_bg]Crucio',
['engineer'] = '[light_bg]Upgrade',
['plague_doctor'] = '[light_bg]Pandemic',
['plague_doctor'] = '[light_bg]Black Death Steam',
['fisherman'] = '[light_bg]Electric Net',
['juggernaut'] = '[light_bg]Brutal Impact',
['lich'] = '[light_bg]Piercing Frost',
@ -473,17 +478,17 @@ function init()
['blade'] = function() return '[fg]deal additional [yellow]' .. get_character_stat('blade', 3, 'dmg')/2 .. '[fg] damage per enemy hit' end,
['elementor'] = function() return '[fg]slows enemies by [yellow]60%[fg] for [yellow]6[fg] seconds on hit' end,
['saboteur'] = function() return '[fg]the explosion has [yellow]50%[fg] chance to crit, increasing in size and dealing [yellow]2x[fg] damage' end,
['stormweaver'] = function() return '[fg]cast a spire of lightning periodically' end,
['sage'] = function() return '[fg]when the projectile expires deal [yellow]' .. get_character_stat('sage', 3, 'dmg') .. '[fg] to all enemies under its influence' end,
['stormweaver'] = function() return "[fg]chain lightning's trigger area of effect and number of units hit is [yellow]doubled" end,
['sage'] = function() return '[fg]when the projectile expires deal [yellow]' .. 3*get_character_stat('sage', 3, 'dmg') .. '[fg] damage to all enemies under its influence' end,
['squire'] = function() return '[fg]you can reroll your item choices once, these opportunities stack if unused' end,
['cannoneer'] = function() return '[fg]showers the area in additional cannon shots that deal [yellow]' .. get_character_stat('cannoneer', 3, 'dmg') .. '[fg] AoE damage' end,
['dual_gunner'] = function() return '[fg]every 5th attack shoots projectiles in rapid succession targetting all nearby enemies for [yellow]2[fg] seconds' end,
['hunter'] = function() return '[fg]summons 3 pets' end,
['cannoneer'] = function() return '[fg]showers the hit area in [yellow]5[fg] additional cannon shots that deal [yellow]' .. get_character_stat('cannoneer', 3, 'dmg')/2 .. '[fg] AoE damage' end,
['dual_gunner'] = function() return '[fg]every 5th attack shoot in rapid succession for [yellow]2[fg] seconds' end,
['hunter'] = function() return '[fg]summons [yellow]3[fg] pets and the pets ricochet off walls once' end,
['chronomancer'] = function() return '[fg]enemies take damave over time [yellow]50%[fg] faster' end,
['spellblade'] = function() return '[fg]faster projectile speed and tighter turns' end,
['psykeeper'] = function() return '[fg]also redistributes damage taken as damage to all enemies' end,
['engineer'] = function() return '[fg]every 3rd sentry dropped upgrade all sentries, granting them [yellow]+100%[fg] damage and attack speed' end,
['plague_doctor'] = function() return '[fg]inflicts enemies with a contagion that deals additional [yellow]' .. get_character_stat('plague_doctor', 3, 'dmg') .. '[fg] damage per second and spreads to nearby enemies' end,
['psykeeper'] = function() return '[fg]also redistributes damage taken as damage to all enemies at [yellow]double[fg] value' end,
['engineer'] = function() return '[fg]every 3rd sentry dropped upgrade all sentries with [yellow]+100%[fg] damage and attack speed' end,
['plague_doctor'] = function() return '[fg]nearby enemies take an additional [yellow]' .. get_character_stat('plague_doctor', 3, 'dmg') .. '[fg] damage per second' end,
['fisherman'] = function() return '[fg]enemies caught take [yellow]' .. get_character_stat('fisherman', 3, 'dmg')/4 .. '[fg] damage per second' end,
['juggernaut'] = function() return '[fg]enemies pushed away by the juggernaut are instantly killed if they hit a wall' end,
['lich'] = function() return '[fg]chain frost decreases enemy defenses by [yellow]30[fg] for [yellow]4[fg] seconds' end,
@ -798,8 +803,8 @@ function init()
main = Main()
main:add(BuyScreen'buy_screen')
main:go_to('buy_screen', 15, {
{character = 'saboteur', level = 3},
main:go_to('buy_screen', 22, {
{character = 'fisherman', level = 3},
})
--[[
main:add(Arena'arena')

View File

@ -156,13 +156,9 @@ function Player:init(args)
self.t:every(8, function()
stormweaver1:play{pitch = random:float(0.95, 1.05), volume = 0.5}
local followers
local leader = (self.leader and self) or self.parent
if self.leader then followers = self.followers else followers = self.parent.followers end
for _, f in ipairs(followers) do
if f.character ~= 'swordsman' and f.character ~= 'cleric' and f.character ~= 'elementor' and f.character ~= 'saboteur' then
f:chain_infuse(4)
end
local units = self:get_all_units()
for _, unit in ipairs(units) do
unit:chain_infuse(4)
end
end)
@ -206,7 +202,9 @@ function Player:init(args)
self.visual_shape = 'rectangle'
self.classes = character_classes.dual_gunner
self.dg_counter = 0
self.attack_sensor = Circle(self.x, self.y, 96)
self.gun_kata_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()
local closest_enemy = self:get_closest_object_in_shape(self.attack_sensor, main.current.enemies)
if closest_enemy then
@ -234,6 +232,10 @@ function Player:init(args)
self.visual_shape = 'rectangle'
self.classes = character_classes.chronomancer
if self.level == 3 then
main.current.chronomancer_dot = 0.5
end
elseif self.character == 'spellblade' then
self.color = character_colors.spellblade
self:set_as_rectangle(9, 9, 'dynamic', 'player')
@ -249,6 +251,8 @@ function Player:init(args)
self:set_as_rectangle(9, 9, 'dynamic', 'player')
self.visual_shape = 'rectangle'
self.classes = character_classes.psykeeper
self.stored_heal = 0
self.last_heal_time = love.timer.getTime()
elseif self.character == 'engineer' then
self.color = character_colors.engineer
@ -256,25 +260,50 @@ function Player:init(args)
self.visual_shape = 'rectangle'
self.classes = character_classes.engineer
self.turret_counter = 0
self.t:every(8, function()
SpawnEffect{group = main.current.effects, x = self.x, y = self.y, color = orange[0], action = function(x, y)
Turret{group = main.current.main, x = x, y = y, parent = self}
end}
self.turret_counter = self.turret_counter + 1
if self.turret_counter == 3 then
self.turret_counter = 0
local turrets = main.current.main:get_objects_by_class(Turret)
buff1:play{pitch = random:float(0.95, 1.05), volume = 0.5}
for _, turret in ipairs(turrets) do
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 = turret, color = orange[0]}
turret:upgrade()
end
end
end)
end
--[[
elseif self.character == 'plague_doctor' then
self.color = character_colors.plague_doctor
self:set_as_rectangle(9, 9, 'dynamic', 'player')
self.visual_shape = 'rectangle'
self.classes = character_classes.plague_doctor
self.t:every(5, function()
self:attack(64)
end, nil, nil, 'attack')
self:dot_attack(24, {duration = 12})
end)
if self.level == 3 then
self.t:after(0.01, function()
self.dot_area = DotArea{group = main.current.effects, x = self.x, y = self.y, rs = self.area_size_m*48, color = self.color, dmg = self.area_dmg_m*self.dmg, character = self.character, level = self.level, parent = self}
end)
end
elseif self.character == 'fisherman' then
self.color = character_colors.fisherman
self:set_as_rectangle(9, 9, 'dynamic', 'player')
self.visual_shape = 'rectangle'
self.classes = character_classes.fisherman
self.t:every(10, function()
Trap{group = main.current.main, x = self.x, y = self.y, color = self.color, v = 50, r = random:float(0, 2*math.pi), dmg = self.dmg, character = self.character, level = self.level, parent = self}
end)
end
]]--
self:calculate_stats(true)
@ -295,8 +324,8 @@ function Player:update(dt)
if self.character == 'squire' then
local all_units = self:get_all_units()
for _, unit in ipairs(all_units) do
unit.squire_dmg_m = 1.1
unit.squire_def_m = 1.1
unit.squire_dmg_m = 1.15
unit.squire_def_m = 1.15
end
elseif self.character == 'chronomancer' then
local all_units = self:get_all_units()
@ -380,10 +409,10 @@ function Player:update(dt)
self:calculate_stats()
if self.attack_sensor then self.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)
if self.leader then
if input.move_left.down then self.r = self.r - 1.66*math.pi*dt end
if input.move_right.down then self.r = self.r + 1.66*math.pi*dt end
@ -496,10 +525,20 @@ function Player:hit(damage)
local psykeeper = self:get_unit'psykeeper'
if psykeeper then
psykeeper.stored_heal = psykeeper.stored_heal + actual_damage
if psykeeper.stored_heal > (0.1*self.level*psykeeper.max_hp) then
if psykeeper.stored_heal > (0.5*psykeeper.max_hp) and love.timer.getTime() - self.last_heal_time > 6 then
self.last_heal_time = love.timer.getTime()
local all_units = self:get_all_units()
for _, unit in ipairs(all_units) do
unit:heal(psykeeper.stored_heal*(self.heal_effect_m or 1))
unit:heal(psykeeper.stored_heal*(self.heal_effect_m or 1)/#all_units)
end
if self.level == 3 then
buff1:play{pitch = random:float(0.95, 1.05), volume = 0.5}
local enemies = main.current.main:get_objects_by_classes(main.current.enemies)
for _, enemy in ipairs(enemies) do
enemy:hit(2*psykeeper.stored_heal/#enemies)
HitCircle{group = main.current.effects, x = self.x, y = self.y, rs = 6, color = fg[0], duration = 0.1}
LightningLine{group = main.current.effects, src = self, dst = enemy, color = fg[0]}
end
end
psykeeper.stored_heal = 0
heal1:play{pitch = random:float(0.95, 1.05), volume = 0.5}
@ -632,12 +671,28 @@ function Player:shoot(r, mods)
HitCircle{group = main.current.effects, x = self.x + 0.8*self.shape.w*math.cos(r) + 4*math.cos(r - math.pi/2), y = self.y + 0.8*self.shape.w*math.sin(r) + 4*math.sin(r - math.pi/2), rs = 6}
HitCircle{group = main.current.effects, x = self.x + 0.8*self.shape.w*math.cos(r) + 4*math.cos(r + math.pi/2), y = self.y + 0.8*self.shape.w*math.sin(r) + 4*math.sin(r + math.pi/2), rs = 6}
local t1 = {group = main.current.main, x = self.x + 1.6*self.shape.w*math.cos(r) + 4*math.cos(r - math.pi/2) , y = self.y + 1.6*self.shape.w*math.sin(r) + 4*math.sin(r - math.pi/2),
v = 250, r = r, color = self.color, dmg = self.dmg*dmg_m, crit = crit, character = self.character, parent = self, level = self.level}
v = 300, r = r, color = self.color, dmg = self.dmg*dmg_m, crit = crit, character = self.character, parent = self, level = self.level}
local t2 = {group = main.current.main, x = self.x + 1.6*self.shape.w*math.cos(r) + 4*math.cos(r + math.pi/2) , y = self.y + 1.6*self.shape.w*math.sin(r) + 4*math.sin(r + math.pi/2),
v = 250, r = r, color = self.color, dmg = self.dmg*dmg_m, crit = crit, character = self.character, parent = self, level = self.level}
v = 300, r = r, color = self.color, dmg = self.dmg*dmg_m, crit = crit, character = self.character, parent = self, level = self.level}
Projectile(table.merge(t1, mods or {}))
Projectile(table.merge(t2, mods or {}))
self.dg_counter = self.dg_counter + 1
if self.dg_counter == 5 then
self.dg_counter = 0
self.t:every(0.1, function()
local random_enemy = self:get_random_object_in_shape(self.attack_sensor, main.current.enemies)
if random_enemy then
_G[random:table{'gun_kata1', 'gun_kata2'}]:play{pitch = random:float(0.95, 1.05), volume = 0.35}
camera:spring_shake(2, r)
self.hfx:use('shoot', 0.25)
local r = self:angle_to_object(random_enemy)
HitCircle{group = main.current.effects, x = self.x + 0.8*self.shape.w*math.cos(r) + 4*math.cos(r - math.pi/2), y = self.y + 0.8*self.shape.w*math.sin(r) + 4*math.sin(r - math.pi/2), rs = 6}
local t = {group = main.current.main, x = self.x + 1.6*self.shape.w*math.cos(r), y = self.y + 1.6*self.shape.w*math.sin(r), v = 300, r = r, color = self.color, dmg = self.dmg, character = self.character,
parent = self, level = self.level}
Projectile(table.merge(t, mods or {}))
end
end, 20)
end
else
HitCircle{group = main.current.effects, x = self.x + 0.8*self.shape.w*math.cos(r), y = self.y + 0.8*self.shape.w*math.sin(r), rs = 6}
local t = {group = main.current.main, x = self.x + 1.6*self.shape.w*math.cos(r), y = self.y + 1.6*self.shape.w*math.sin(r), v = 250, r = r, color = self.color, dmg = self.dmg*dmg_m, crit = crit, character = self.character,
@ -645,8 +700,11 @@ function Player:shoot(r, mods)
Projectile(table.merge(t, mods or {}))
end
if self.character == 'vagrant' or self.character == 'dual_gunner' then
if self.character == 'vagrant' then
shoot1:play{pitch = random:float(0.95, 1.05), volume = 0.2}
elseif self.character == 'dual_gunner' then
dual_gunner1:play{pitch = random:float(0.95, 1.05), volume = 0.3}
dual_gunner2:play{pitch = random:float(0.95, 1.05), volume = 0.3}
elseif self.character == 'archer' or self.character == 'hunter' then
archer1:play{pitch = random:float(0.95, 1.05), volume = 0.35}
elseif self.character == 'wizard' then
@ -681,6 +739,17 @@ function Player:attack(area, mods)
end
function Player:dot_attack(area, mods)
mods = mods or {}
camera:shake(2, 0.5)
self.hfx:use('shoot', 0.25)
local t = {group = main.current.effects, x = mods.x or self.x, y = mods.y or self.y, r = self.r, rs = self.area_size_m*(area or 64), color = self.color, dmg = self.area_dmg_m*self.dmg, character = self.character, level = self.level}
DotArea(table.merge(t, mods))
dot1:play{pitch = random:float(0.9, 1.1), volume = 0.5}
end
function Player:barrage(r, n)
n = n or 8
for i = 1, n do
@ -710,12 +779,24 @@ function Projectile:init(args)
self.infused_enemies_hit = {}
if self.character == 'sage' then
elementor1:play{pitch = random:float(0.9, 1.1), volume = 0.5}
self.compression_dmg = self.dmg
self.dmg = 0
self.pull_sensor = Circle(self.x, self.y, 64*self.parent.area_size_m)
self.rs = 0
self.t:tween(0.05, self, {rs = self.shape.w/2.5}, math.cubic_in_out, function() self.spring:pull(0.15) end)
self.t:after(4, function()
self.t:every_immediate(0.05, function() self.hidden = not self.hidden end, 7, function() self:die() end)
self.t:every_immediate(0.05, function() self.hidden = not self.hidden end, 7, function()
self:die()
if self.level == 3 then
_G[random:table{'saboteur_hit1', 'saboteur_hit2'}]:play{pitch = random:float(0.95, 1.05), volume = 0.2}
magic_area1:play{pitch = random:float(0.95, 1.05), volume = 0.075}
local enemies = self:get_objects_in_shape(self.pull_sensor, main.current.enemies)
for _, enemy in ipairs(enemies) do
enemy:hit(3*self.compression_dmg)
end
end
end)
end)
self.color_transparent = Color(args.color.r, args.color.g, args.color.b, 0.08)
@ -726,12 +807,22 @@ function Projectile:init(args)
self.dvr = random:float(-math.pi/4, math.pi/4)
elseif self.character == 'spellblade' then
self.pierce = 1000
self.orbit_r = 0
self.orbit_vr = 8*math.pi
self.t:tween(6.25, self, {orbit_vr = math.pi}, math.expo_out, function()
self.t:tween(12.25, self, {orbit_vr = 0}, math.linear)
end)
if self.level == 3 then
self.v = 1.5*self.v
self.pierce = 1000
self.orbit_r = 0
self.orbit_vr = 12*math.pi
self.t:tween(6.25, self, {orbit_vr = 4*math.pi}, math.expo_out, function()
self.t:tween(12.25, self, {orbit_vr = 0}, math.linear)
end)
else
self.pierce = 1000
self.orbit_r = 0
self.orbit_vr = 8*math.pi
self.t:tween(6.25, self, {orbit_vr = math.pi}, math.expo_out, function()
self.t:tween(12.25, self, {orbit_vr = 0}, math.linear)
end)
end
end
if self.homing then
@ -813,6 +904,11 @@ function Projectile:die(x, y, r, n)
Area{group = main.current.effects, x = self.x, y = self.y, r = self.r, w = self.parent.area_size_m*64, color = self.color, dmg = self.parent.area_dmg_m*self.dmg, character = self.character, level = self.level}
elseif self.character == 'cannoneer' then
Area{group = main.current.effects, x = self.x, y = self.y, r = self.r, w = self.parent.area_size_m*96, color = self.color, dmg = 2*self.parent.area_dmg_m*self.dmg, character = self.character, level = self.level}
self.parent.t:every(0.2, function()
_G[random:table{'cannoneer1', 'cannoneer2'}]:play{pitch = random:float(0.95, 1.05), volume = 0.5}
Area{group = main.current.effects, x = self.x + random:float(-32, 32), y = self.y + random:float(-32, 32), r = self.r + random:float(0, 2*math.pi), w = self.parent.area_size_m*48, color = self.color,
dmg = 0.5*self.parent.area_dmg_m*self.dmg, character = self.character, level = self.level}
end, 5)
end
end
@ -853,7 +949,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' then
elseif self.character == 'engineer' or self.character == 'dual_gunner' 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
@ -910,14 +1006,22 @@ function Projectile:on_trigger_enter(other, contact)
if self.character == 'hunter' and random:bool(40) then
trigger:after(0.01, function()
SpawnEffect{group = main.current.effects, x = self.parent.x, y = self.parent.y, color = orange[0], action = function(x, y)
Pet{group = main.current.main, x = x, y = y, r = self.parent:angle_to_object(other), v = 150, parent = self.parent, conjurer_buff_m = self.conjurer_buff_m or 1}
end}
if self.level == 3 then
local r = self.parent:angle_to_object(other)
SpawnEffect{group = main.current.effects, x = self.parent.x, y = self.parent.y, color = green[0], action = function(x, y)
Pet{group = main.current.main, x = x, y = y, r = r, v = 150, parent = self.parent, conjurer_buff_m = self.conjurer_buff_m or 1}
Pet{group = main.current.main, x = x + 12*math.cos(r + math.pi/2), y = y + 12*math.sin(r + math.pi/2), r = r, v = 150, parent = self.parent, conjurer_buff_m = self.conjurer_buff_m or 1}
Pet{group = main.current.main, x = x + 12*math.cos(r - math.pi/2), y = y + 12*math.sin(r - math.pi/2), r = r, v = 150, parent = self.parent, conjurer_buff_m = self.conjurer_buff_m or 1}
end}
else
SpawnEffect{group = main.current.effects, x = self.parent.x, y = self.parent.y, color = orange[0], action = function(x, y)
Pet{group = main.current.main, x = x, y = y, r = self.parent:angle_to_object(other), v = 150, parent = self.parent, conjurer_buff_m = self.conjurer_buff_m or 1}
end}
end
end)
end
if self.parent.chain_infused then
--[[
local units = self.parent:get_all_units()
local stormweaver_level = 0
for _, unit in ipairs(units) do
@ -926,12 +1030,11 @@ function Projectile:on_trigger_enter(other, contact)
break
end
end
]]--
local src = other
for i = 1, 2 do
for i = 1, 2 + (stormweaver_level == 3 and 2 or 0) do
_G[random:table{'spark1', 'spark2', 'spark3'}]:play{pitch = random:float(0.9, 1.1), volume = 0.3}
table.insert(self.infused_enemies_hit, src)
local dst = src:get_random_object_in_shape(Circle(src.x, src.y, 64), main.current.enemies, self.infused_enemies_hit)
local dst = src:get_random_object_in_shape(Circle(src.x, src.y, (stormweaver_level == 3 and 128 or 64)), main.current.enemies, self.infused_enemies_hit)
if dst then
dst:hit(0.2*self.dmg)
LightningLine{group = main.current.effects, src = src, dst = dst}
@ -1027,6 +1130,111 @@ end
DotArea = Object:extend()
DotArea:implement(GameObject)
function DotArea:init(args)
self:init_game_object(args)
self.shape = Circle(self.x, self.y, self.rs)
self.t:every(0.2, function()
local enemies = main.current.main:get_objects_in_shape(self.shape, main.current.enemies)
for _, enemy in ipairs(enemies) do
hit2:play{pitch = random:float(0.8, 1.2), volume = 0.2}
enemy:hit(self.dmg/5)
HitCircle{group = main.current.effects, x = enemy.x, y = enemy.y, rs = 6, color = fg[0], duration = 0.1}
for i = 1, 1 do HitParticle{group = main.current.effects, x = enemy.x, y = enemy.y, color = self.color} end
for i = 1, 1 do HitParticle{group = main.current.effects, x = enemy.x, y = enemy.y, color = enemy.color} end
end
end, nil, nil, 'dot')
self.color = fg[0]
self.color_transparent = Color(args.color.r, args.color.g, args.color.b, 0.08)
self.rs = 0
self.hidden = false
self.t:tween(0.05, self, {rs = args.rs}, math.cubic_in_out, function() self.spring:pull(0.15) end)
self.t:after(0.2, function() self.color = args.color end)
if self.duration and self.duration > 0.5 then
self.t:after(self.duration - 0.35, function()
self.t:every_immediate(0.05, function() self.hidden = not self.hidden end, 7, function() self.dead = true end)
end)
end
self.vr = 0
self.dvr = random:float(-math.pi/4, math.pi/4)
end
function DotArea:update(dt)
self:update_game_object(dt)
self.t:set_every_multiplier('dot', (main.current.chronomancer_dot or 1))
self.vr = self.vr + self.dvr*dt
if self.parent then
if self.character == 'plague_doctor' and self.level == 3 then
self.x, self.y = self.parent.x, self.parent.y
self.shape:move_to(self.x, self.y)
end
end
end
function DotArea:draw()
if self.hidden then return end
graphics.push(self.x, self.y, self.r + self.vr, self.spring.x, self.spring.x)
-- graphics.circle(self.x, self.y, self.shape.rs + random:float(-1, 1), self.color, 2)
graphics.circle(self.x, self.y, self.shape.rs, self.color_transparent)
local lw = math.remap(self.shape.rs, 32, 256, 2, 4)
for i = 1, 4 do graphics.arc('open', self.x, self.y, self.shape.rs, (i-1)*math.pi/2 + math.pi/4 - math.pi/8, (i-1)*math.pi/2 + math.pi/4 + math.pi/8, self.color, lw) end
graphics.pop()
end
--[[
Trap = Object:extend()
Trap:implement(GameObject)
Trap:implement(Physics)
function Trap:init(args)
self:init_game_object(args)
self.shape = Rectangle(self.x, self.y, 6, 6)
self.spring:pull(0.15, 200, 20)
self.vr = 0
self.dvr = random:float(-2*math.pi, 2*math.pi)
self.t:tween(1, self, {v = 0, dvr = 0}, math.linear)
end
function Trap:update(dt)
self:update_game_object(dt)
self:move_along_angle(self.v, self.r)
self.vr = self.vr + self.dvr*dt
self.shape:move_to(self.x, self.y)
local enemies = self:get_objects_in_shape(self.shape, main.current.enemies)
if #enemies > 0 then
self.dead = true
local enemies = self:get_objects_in_shape(Circle(self.x, self.y, 24), main.current.enemies)
for _, enemy in ipairs(enemies) do
enemy:slow(0.1, 4)
end
end
end
function Trap:draw()
graphics.push(self.x, self.y, self.r + self.vr, self.spring.x, self.spring.x)
graphics.line(self.x - 6, self.y, self.x + 6, self.y, self.color, 3)
graphics.line(self.x, self.y - 6, self.x, self.y + 6, self.color, 3)
graphics.pop()
end
]]--
Turret = Object:extend()
Turret:implement(GameObject)
Turret:implement(Physics)
@ -1044,12 +1252,12 @@ function Turret:init(args)
self.hfx:use('hit', 0.25, 200, 10)
HitCircle{group = main.current.effects, x = self.x + 0.8*self.shape.w*math.cos(self.r), y = self.y + 0.8*self.shape.w*math.sin(self.r), rs = 6}
local t = {group = main.current.main, x = self.x + 1.6*self.shape.w*math.cos(self.r), y = self.y + 1.6*self.shape.w*math.sin(self.r), v = 200, r = self.r, color = self.color,
dmg = self.parent.dmg*(self.parent.conjurer_buff_m or 1), character = self.parent.character, parent = self.parent}
dmg = self.parent.dmg*(self.parent.conjurer_buff_m or 1)*self.upgrade_dmg_m, character = self.parent.character, parent = self.parent}
Projectile(table.merge(t, mods or {}))
turret1:play{pitch = random:float(0.95, 1.05), volume = 0.35}
turret2:play{pitch = random:float(0.95, 1.05), volume = 0.35}
end, 3)
end)
end, nil, nil, 'shoot')
self.t:after(24*(self.parent.conjurer_buff_m or 1), function()
local n = n or random:int(3, 4)
@ -1057,12 +1265,17 @@ function Turret:init(args)
HitCircle{group = main.current.effects, x = self.x, y = self.y}:scale_down()
self.dead = true
end)
self.upgrade_dmg_m = 1
self.upgrade_aspd_m = 1
end
function Turret:update(dt)
self:update_game_object(dt)
self.t:set_every_multiplier('shoot', 1/self.upgrade_aspd_m)
local closest_enemy = self:get_closest_object_in_shape(self.attack_sensor, main.current.enemies)
if closest_enemy then
self:rotate_towards_object(closest_enemy, 0.2)
@ -1078,6 +1291,14 @@ function Turret:draw()
end
function Turret:upgrade()
self.upgrade_dmg_m = self.upgrade_dmg_m + 1
self.upgrade_aspd_m = self.upgrade_aspd_m + 1
for i = 1, 6 do HitParticle{group = main.current.effects, x = self.x, y = self.y, r = random:float(0, 2*math.pi), color = self.color} end
HitCircle{group = main.current.effects, x = self.x, y = self.y}:scale_down()
end
Pet = Object:extend()
@ -1091,6 +1312,7 @@ function Pet:init(args)
self.color = character_colors.hunter
self.pierce = 6
pet1:play{pitch = random:float(0.95, 1.05), volume = 0.35}
self.ricochet = 1
end
@ -1122,8 +1344,15 @@ function Pet:on_collision_enter(other, contact)
local n = n or random:int(3, 4)
for i = 1, n do HitParticle{group = main.current.effects, x = x, y = y, r = random:float(0, 2*math.pi), color = self.color} end
HitCircle{group = main.current.effects, x = x, y = y}:scale_down()
self.dead = true
hit2:play{pitch = random:float(0.95, 1.05), volume = 0.35}
if self.parent.level == 3 and self.ricochet > 0 then
local r = Unit.bounce(self, nx, ny)
self.r = r
self.ricochet = self.ricochet - 1
else
self.dead = true
end
end
end
@ -1144,6 +1373,8 @@ function Pet:on_trigger_enter(other)
other:push(35, self:angle_to_object(other))
self.pierce = self.pierce - 1
end
hit2:play{pitch = random:float(0.95, 1.05), volume = 0.35}
elseif self.character == 'blade' then
self.hfx:use('hit', 0.25)
HitCircle{group = main.current.effects, x = self.x, y = self.y, rs = 6, color = fg[0], duration = 0.1}
HitParticle{group = main.current.effects, x = self.x, y = self.y, color = self.color}

22
todo
View File

@ -50,17 +50,17 @@
* Blade [warrior, nuker]: throws multiple blades that deal AoE damage - Lv.3: Blade Resonance - deal additional damage based on number of enemies hit
* Elementor [mage, nuker]: deals AoE damage to a random target in a large area - Lv.3: Windfield - slows enemies hit
* Saboteur [rogue, conjurer, nuker]: calls saboteurs to seek targets and deal AoE damage - Lv.3: Chain Reaction - should an enemy die from a saboteur explosion, it also explodes
Stormweaver [enchanter]: infuses all allied projectiles with chain lightning that deals extra damage - Lv.3: Lightning Spire - cast a spire of lightning periodically
Sage [nuker]: shoots a slow moving projectile that pulls enemies in - Lv.3: Dimension Compression - when the projectile expires deal massive damage to all enemies under its influence
Squire [warrior, enchanter]: increased damage and defense to all allies - Lv.3: Repair - you can reroll your item choice once every 3 levels, these opportunities stack if unused
Cannoneer [ranger, nuker]: shoots a projectile that deals AoE damage - Lv.3: Cannon Barrage - showers the hit area in additional cannon shots that deal AoE damage
Dual Gunner [ranger, rogue]: shoots two parallel projectiles - Lv.3: Gun Kata - every 5th attack shoots projectiles in a rapid succession for a duration, targetting all nearby enemies
Hunter [ranger, conjurer]: shoots an arrow that summons a pet - Lv.3: Feral Pack - summons 3 pets
Chronomancer [mage, enchanter]: increased attack speed to all allies - Lv.3: Quicken - enemies take DoT faster
Spellblade [mage, rogue]: throws knives that spiral outwards and pierce - Lv.3: Spiralism - faster projectile speed and tighter turns
Psykeeper [healer, psyker]: stores damage taken by all allies and redistributes it as healing - Lv.3: Crucio - also redistributes it as damage to all enemies
Engineer [conjurer]: drops sentries that shoot bursts of projectils - Lv.3: Upgrade - every 3rd sentry dropped, upgrade all sentries temporarily, giving increased damage and attack speed
Plague Doctor [nuker, voider]: creates an area that deals DoT - Lv.3: Pandemic - inflicts enemies with a contagion that deals additional DoT, if they die from it it passes to a nearby enemy
* Stormweaver [enchanter]: infuses all allied projectiles with chain lightning that deals extra damage - Lv.3: Lightning Spire - cast a spire of lightning periodically
* Sage [nuker]: shoots a slow moving projectile that pulls enemies in - Lv.3: Dimension Compression - when the projectile expires deal massive damage to all enemies under its influence
(Repair to do after items implemented) * Squire [warrior, enchanter]: increased damage and defense to all allies - Lv.3: Repair - you can reroll your item choice once every 3 levels, these opportunities stack if unused
* Cannoneer [ranger, nuker]: shoots a projectile that deals AoE damage - Lv.3: Cannon Barrage - showers the hit area in additional cannon shots that deal AoE damage
* Dual Gunner [ranger, rogue]: shoots two parallel projectiles - Lv.3: Gun Kata - every 5th attack shoots projectiles in a rapid succession for a duration, targetting all nearby enemies
* Hunter [ranger, conjurer]: shoots an arrow that summons a pet - Lv.3: Feral Pack - summons 3 pets
(Apply arena.chronomancer_dot when damage over time is implemented) * Chronomancer [mage, enchanter]: increased attack speed to all allies - Lv.3: Quicken - enemies take DoT faster
* Spellblade [mage, rogue]: throws knives that spiral outwards and pierce - Lv.3: Spiralism - faster projectile speed and tighter turns
* Psykeeper [healer, psyker]: stores damage taken by all allies and redistributes it as healing - Lv.3: Crucio - also redistributes it as damage to all enemies
* Engineer [conjurer]: drops sentries that shoot bursts of projectils - Lv.3: Upgrade - every 3rd sentry dropped, upgrade all sentries temporarily, giving increased damage and attack speed
* Plague Doctor [nuker, voider]: creates an area that deals DoT - Lv.3: Pandemic - inflicts enemies with a contagion that deals additional DoT, if they die from it it passes to a nearby enemy
Fisherman [trapper, warrior]: throws a net that entangles enemies and prevents them from moving - Lv.3: Electric Net - enemies caught take DoT
Juggernaut [forcer, warrior]: creates a small area that deals AoE damage and pushes enemies away - Lv.3: Brutal Impact - enemies pushed away are instantly killed if they hit a wall
Lich [mage]: launches a chain frost that chains 7 times, dealing damage and slowing enemies it hits - Lv.3: Piercing Frost - chain frost ignores enemy defenses