diff --git a/arena.lua b/arena.lua index d09840c..fa4ed04 100644 --- a/arena.lua +++ b/arena.lua @@ -50,12 +50,14 @@ function Arena:on_enter(from, level) WallCover{group = self.post_main, vertices = math.to_rectangle_vertices(self.x1, -40, self.x2, self.y1), color = bg[-1]} WallCover{group = self.post_main, vertices = math.to_rectangle_vertices(self.x1, self.y2, self.x2, gh + 40), color = bg[-1]} - self.player = Player{group = self.main, x = gw/2, y = gh/2, leader = true, character = 'engineer'} + self.player = Player{group = self.main, x = gw/2, y = gh/2, leader = true, character = 'swordsman'} + self.player:add_follower(Player{group = self.main, character = 'outlaw'}) + -- self.player:add_follower(Player{group = self.main, character = 'squire'}) + self.player:add_follower(Player{group = self.main, character = 'blade'}) --[[ self.player:add_follower(Player{group = self.main, character = 'sage'}) self.player:add_follower(Player{group = self.main, character = 'archer'}) self.player:add_follower(Player{group = self.main, character = 'spellblade'}) - self.player:add_follower(Player{group = self.main, character = 'hunter'}) self.player:add_follower(Player{group = self.main, character = 'cleric'}) self.player:add_follower(Player{group = self.main, character = 'wizard'}) self.player:add_follower(Player{group = self.main, character = 'squire'}) @@ -164,11 +166,65 @@ function Arena:on_enter(from, level) end) end) end + + local units = {} + table.insert(units, self.player) + for _, f in ipairs(self.player.followers) do table.insert(units, f) end + + local rangers = 0 + local warriors = 0 + local healers = 0 + local mages = 0 + local nukers = 0 + local conjurers = 0 + local rogues = 0 + local enchanters = 0 + local psys = 0 + for _, unit in ipairs(units) do + for _, unit_class in ipairs(unit.classes) do + if unit_class == 'ranger' then rangers = rangers + 1 end + if unit_class == 'warrior' then warriors = warriors + 1 end + if unit_class == 'healer' then healers = healers + 1 end + if unit_class == 'mage' then mages = mages + 1 end + if unit_class == 'nuker' then nukers = nukers + 1 end + if unit_class == 'conjurer' then conjurers = conjurers + 1 end + if unit_class == 'rogue' then rogues = rogues + 1 end + if unit_class == 'enchanter' then enchanters = enchanters + 1 end + if unit_class == 'psy' then psys = psys + 1 end + end + end + + self.ranger_level = 0 + if rangers >= 2 then self.ranger_level = 1 end + if rangers >= 4 then self.ranger_level = 2 end + self.warrior_level = 0 + if warriors >= 2 then self.warrior_level = 1 end + if warriors >= 4 then self.warrior_level = 2 end + self.healer_level = 0 + if healers >= 3 then self.healer_level = 1 end + self.mage_level = 0 + if mages >= 2 then self.mage_level = 1 end + if mages >= 4 then self.mage_level = 2 end + self.nuke_level = 0 + if nukers >= 2 then self.nuke_level = 1 end + if nukers >= 4 then self.nuke_level = 2 end + self.conjurer_level = 0 + if conjurers >= 2 then self.conjurer_level = 1 end + self.rogue_level = 0 + if rogues >= 2 then self.rogue_level = 1 end + if rogues >= 4 then self.rogue_level = 2 end + self.enchanter_level = 0 + if enchanters >= 3 then self.enchanter_level = 1 end + self.psy_level = psys end function Arena:update(dt) self:update_game_object(dt*slow_amount) + + if self.enchanter_level == 1 then self.enchanter_dmg_m = 1.25 + else self.enchanter_dmg_m = 1 end + self.main:update(dt*slow_amount) self.post_main:update(dt*slow_amount) self.effects:update(dt*slow_amount) diff --git a/assets/sounds/Dagger Stab (Flesh) 4.ogg b/assets/sounds/Dagger Stab (Flesh) 4.ogg new file mode 100644 index 0000000..57b50a3 Binary files /dev/null and b/assets/sounds/Dagger Stab (Flesh) 4.ogg differ diff --git a/assets/sounds/Glass item Breaks 2.ogg b/assets/sounds/Glass item Breaks 2.ogg new file mode 100644 index 0000000..397732a Binary files /dev/null and b/assets/sounds/Glass item Breaks 2.ogg differ diff --git a/assets/sounds/Sword hits another sword 6.ogg b/assets/sounds/Sword hits another sword 6.ogg new file mode 100644 index 0000000..07137af Binary files /dev/null and b/assets/sounds/Sword hits another sword 6.ogg differ diff --git a/devlog.md b/devlog.md index 9db95e4..4b9dfbf 100644 --- a/devlog.md +++ b/devlog.md @@ -214,3 +214,20 @@ Tomorrow I'll probably do some UI work so the player can buy new characters as h and I have a essentially 1 week to do them, which should be more than enough. Note: remember to attribute https://freesound.org/people/Hybrid_V/sounds/321215/ for turret_deploy sound in credits. + +# Day 10 - 26/02/21 + +Another day with almost no sleep and just general low energy because of it... I managed to things done though. I got all the class set bonuses working. Here's what they do: + +| Class | Set Numbers | Set Effect | +| --- | --- | --- | +| Ranger | 2/4 | 10/20% chance to release a barrage | +| Warrior | 2/4 | +25/+50 defense | +| Healer | 3 | +25% healing effectiveness | +| Mage | 2/4 | -15/-30 enemy defense | +| Nuker | 2/4 | +15/25% area damage and size | +| Conjurer | 2 | +25% construct damage and duration | +| Rogue | 2/4 | 10/20% chance to crit dealing 4x damage | +| Enchanter | 3 | +25% damage to all allies | + +Tomorrow I should get started on going from arena to arena, buying characters and figuring out enemy scaling. diff --git a/enemies.lua b/enemies.lua index df1829d..94b95c5 100644 --- a/enemies.lua +++ b/enemies.lua @@ -17,6 +17,10 @@ end function Seeker:update(dt) self:update_game_object(dt) + + if main.current.mage_level == 2 then self.buff_def_a = -30 + elseif main.current.mage_level == 1 then self.buff_def_a = -15 + else self.buff_def_a = 0 end self:calculate_stats() if self.being_pushed then diff --git a/main.lua b/main.lua index 15b7dca..62b1fd5 100644 --- a/main.lua +++ b/main.lua @@ -14,7 +14,7 @@ function init() input:bind('move_up', {'w', 'up'}) input:bind('move_down', {'s', 'down'}) - sfx_tag = {tags = {sfx}} + local s = {tags = {sfx}} shoot1 = Sound('Shooting Projectile (Classic) 11.ogg', s) archer1 = Sound('Releasing Bow String 1.ogg', s) wizard1 = Sound('Wind Bolt 20.ogg', s) @@ -61,6 +61,8 @@ function init() turret_hit_wall1 = Sound('Concrete 6.ogg', s) turret_hit_wall2 = Sound('Concrete 7.ogg', s) turret_deploy = Sound('321215__hybrid-v__sci-fi-weapons-deploy.ogg', s) + rogue_crit1 = Sound('Dagger Stab (Flesh) 4.ogg', s) + rogue_crit2 = Sound('Sword hits another sword 6.ogg', s) main = Main() main:add(Arena'arena') diff --git a/player.lua b/player.lua index f291028..0ec0deb 100644 --- a/player.lua +++ b/player.lua @@ -86,8 +86,8 @@ function Player:init(args) if self.leader then followers = self.followers else followers = self.parent.followers end if (table.any(followers, function(v) return v.hp <= 0.5*v.max_hp end) or (leader.hp <= 0.5*leader.max_hp)) and love.timer.getTime() - self.last_heal_time > 6 then self.last_heal_time = love.timer.getTime() - if self.leader then self:heal(0.1*self.max_hp) else self.parent:heal(0.1*self.parent.max_hp) end - for _, f in ipairs(followers) do f:heal(0.1*f.max_hp) end + if self.leader then self:heal(0.1*self.max_hp*(self.heal_effect_m or 1)) else self.parent:heal(0.1*self.parent.max_hp*(self.heal_effect_m or 1)) end + for _, f in ipairs(followers) do f:heal(0.1*f.max_hp*(self.heal_effect_m or 1)) end heal1:play{pitch = random:float(0.95, 1.05), volume = 0.5} end end) @@ -110,7 +110,7 @@ function Player:init(args) self.color = orange[0] self:set_as_rectangle(9, 9, 'dynamic', 'player') self.visual_shape = 'rectangle' - self.classes = {'warrior', 'rogue'} + self.classes = {'warrior', 'nuker'} self.attack_sensor = Circle(self.x, self.y, 64) self.t:cooldown(4, function() local enemies = self:get_objects_in_shape(self.attack_sensor, main.current.enemies); return enemies and #enemies > 0 end, function() @@ -194,8 +194,8 @@ function Player:init(args) if previous_character then previous_character:squire_buff(8) end self.t:after(8, function() self.applying_buff = false end, 'squire_buff_apply') heal1:play{pitch = random:float(0.95, 1.05), volume = 0.5} - if next_character then next_character:heal(0.1*next_character.max_hp) end - if previous_character then previous_character:heal(0.1*previous_character.max_hp) end + if next_character then next_character:heal(0.1*next_character.max_hp*(self.heal_effect_m or 1)) end + if previous_character then previous_character:heal(0.1*previous_character.max_hp*(self.heal_effect_m or 1)) end end) elseif self.character == 'cannoneer' then @@ -280,8 +280,8 @@ function Player:init(args) if self.psykeeper_heal > 0 then local heal_amount = math.floor(self.psykeeper_heal/(#followers+1)) - if self.leader then self:heal(heal_amount) else self.parent:heal(heal_amount) end - for _, f in ipairs(followers) do f:heal(heal_amount) end + if self.leader then self:heal(heal_amount*(self.heal_effect_m or 1)) else self.parent:heal(heal_amount*(self.heal_effect_m or 1)) end + for _, f in ipairs(followers) do f:heal(heal_amount*(self.heal_effect_m or 1)) end heal1:play{pitch = random:float(0.95, 1.05), volume = 0.5} self.psykeeper_heal = 0 end @@ -313,7 +313,6 @@ end function Player:update(dt) - if self.attack_sensor then self.enemies = self:get_objects_in_shape(self.attack_sensor, main.current.enemies) end self:update_game_object(dt) if self.character == 'squire' then @@ -352,9 +351,51 @@ function Player:update(dt) if previous_character then previous_character.chronomancer_aspd_m = 1.25 end end - self.buff_dmg_a = self.squire_dmg_a or 0 - self.buff_def_a = self.squire_def_a or 0 - self.buff_aspd_m = self.chronomancer_aspd_m or 1 + if table.any(self.classes, function(v) return v == 'ranger' end) then + if main.current.ranger_level == 2 then self.chance_to_barrage = 20 + elseif main.current.ranger_level == 1 then self.chance_to_barrage = 10 + elseif main.current.ranger_level == 0 then self.chance_to_barrage = 0 end + end + + if table.any(self.classes, function(v) return v == 'warrior' end) then + if main.current.warrior_level == 2 then self.warrior_def_a = 50 + elseif main.current.warrior_level == 1 then self.warrior_def_a = 25 + elseif main.current.warrior_level == 0 then self.warrior_def_a = 0 end + end + + if table.any(self.classes, function(v) return v == 'healer' end) then + if main.current.healer_level == 1 then self.heal_effect_m = 1.25 + else self.heal_effect_m = 1 end + end + + if table.any(self.classes, function(v) return v == 'nuker' end) then + if main.current.nuker_level == 2 then self.nuker_area_size_m = 1.25; self.nuker_area_dmg_m = 1.25 + elseif main.current.nuker_level == 1 then self.nuker_area_size_m = 1.15; self.nuker_area_dmg_m = 1.15 + elseif main.current.nuker_level == 0 then self.nuker_area_size_m = 1; self.nuker_area_dmg_m = 1 end + end + + if table.any(self.classes, function(v) return v == 'conjurer' end) then + if main.current.conjurer_level == 1 then self.conjurer_buff_m = 1.25 + else self.conjurer_buff_m = 1 end + end + + if table.any(self.classes, function(v) return v == 'rogue' end) then + if main.current.rogue_level == 2 then self.chance_to_crit = 20 + elseif main.current.rogue_level == 1 then self.chance_to_crit = 10 + elseif main.current.rogue_level == 0 then self.chance_to_crit = 0 end + end + + if table.any(self.classes, function(v) return v == 'enchanter' end) then + if main.current.enchanter_level == 1 then self.enchanter_dmg_m = 1.25 + else self.enchanter_dmg_m = 1 end + end + + self.buff_dmg_a = (self.squire_dmg_a or 0) + self.buff_def_a = (self.squire_def_a or 0) + (self.warrior_def_a or 0) + self.buff_aspd_m = (self.chronomancer_aspd_m or 1) + self.buff_dmg_m = (main.current.enchanter_dmg_m or 1) + self.buff_area_size_m = (self.nuker_area_size_m or 1) + self.buff_area_dmg_m = (self.nuker_area_dmg_m or 1) self:calculate_stats() if self.attack_sensor then self.attack_sensor:move_to(self.x, self.y) end @@ -408,6 +449,10 @@ function Player:update(dt) self.r = self:get_angle() end end + + if self.character == 'blade' then + print(self.def) + end end @@ -509,14 +554,20 @@ end function Player:shoot(r, mods) + mods = mods or {} camera:spring_shake(2, r) self.hfx:use('shoot', 0.25) + local dmg_m = 1 + local crit = false + if self.chance_to_crit and random:bool(self.chance_to_crit) then dmg_m = 4; crit = true end + if self.character == 'outlaw' then 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} r = r - 2*math.pi/8 for i = 1, 5 do - 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, character = self.character, parent = self} + 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, + parent = self} Projectile(table.merge(t, mods or {})) r = r + math.pi/8 end @@ -527,7 +578,8 @@ function Player:shoot(r, mods) for _, enemy in ipairs(enemies) do local r = self:angle_to_object(enemy) HitCircle{group = main.current.effects, x = self.x + 0.8*self.shape.w*math.cos(r), y = self.y + 0.8*self.shape.w*math.sin(r), rs = 6} - local t = {group = main.current.main, x = self.x + 1.6*self.shape.w*math.cos(r), y = self.y + 1.6*self.shape.w*math.sin(r), v = 250, r = r, color = self.color, dmg = self.dmg, character = self.character, parent = self} + 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, + parent = self} Projectile(table.merge(t, mods or {})) end end @@ -541,32 +593,37 @@ 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, character = self.character, parent = self} + v = 250, r = r, color = self.color, dmg = self.dmg*dmg_m, crit = crit, character = self.character, parent = self} 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, character = self.character, parent = self} + v = 250, r = r, color = self.color, dmg = self.dmg*dmg_m, crit = crit, character = self.character, parent = self} Projectile(table.merge(t1, mods or {})) Projectile(table.merge(t2, mods or {})) 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, character = self.character, parent = self} + 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, + parent = self} Projectile(table.merge(t, mods or {})) end if self.character == 'vagrant' or self.character == 'dual_gunner' then - shoot1:play{pitch = random:float(0.95, 1.05), volume = 0.3} + shoot1:play{pitch = random:float(0.95, 1.05), volume = 0.2} elseif self.character == 'archer' or self.character == 'hunter' then - archer1:play{pitch = random:float(0.95, 1.05), volume = 0.5} + archer1:play{pitch = random:float(0.95, 1.05), volume = 0.35} elseif self.character == 'wizard' 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' then - _G[random:table{'scout1', 'scout2'}]:play{pitch = random:float(0.95, 1.05), volume = 0.5} + _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} end elseif self.character == 'cannoneer' then _G[random:table{'cannoneer1', 'cannoneer2'}]:play{pitch = random:float(0.95, 1.05), volume = 0.5} end + + if self.chance_to_barrage and random:bool(self.chance_to_barrage) then + self:barrage(r, 4) + end end @@ -585,6 +642,20 @@ function Player:attack(area, mods) end +function Player:barrage(r, n) + n = n or 8 + for i = 1, n do + self.t:after((i-1)*0.075, function() + archer1:play{pitch = random:float(0.95, 1.05), volume = 0.35} + HitCircle{group = main.current.effects, x = self.x + 0.8*self.shape.w*math.cos(r), y = self.y + 0.8*self.shape.w*math.sin(r), rs = 6} + local t = {group = main.current.main, x = self.x + 1.6*self.shape.w*math.cos(r), y = self.y + 1.6*self.shape.w*math.sin(r), v = 250, r = r + random:float(-math.pi/16, math.pi/16), color = self.color, dmg = self.dmg, + parent = self, character = 'barrage'} + Projectile(table.merge(t, mods or {})) + end) + end +end + + Projectile = Object:extend() @@ -694,7 +765,7 @@ function Projectile:on_collision_enter(other, contact) else r = 0 end if other:is(Wall) then - if self.character == 'archer' or self.character == 'hunter' then + if self.character == 'archer' or self.character == 'hunter' or self.character == 'barrage' then self:die(x, y, r, 0) _G[random:table{'arrow_hit_wall1', 'arrow_hit_wall2'}]:play{pitch = random:float(0.9, 1.1), volume = 0.2} WallArrow{group = main.current.main, x = x, y = y, r = self.r, color = self.color} @@ -783,6 +854,15 @@ function Projectile:on_trigger_enter(other, contact) end end end + + if self.crit then + camera:shake(5, 0.25) + rogue_crit1:play{pitch = random:float(0.95, 1.05), volume = 0.5} + rogue_crit2:play{pitch = random:float(0.95, 1.05), volume = 0.15} + for i = 1, 3 do HitParticle{group = main.current.effects, x = other.x, y = other.y, color = self.color, v = random:float(100, 400)} end + for i = 1, 3 do HitParticle{group = main.current.effects, x = other.x, y = other.y, color = other.color, v = random:float(100, 400)} end + HitCircle{group = main.current.effects, x = other.x, y = other.y, rs = 12, color = fg[0], duration = 0.3}:scale_down():change_color(0.5, self.color) + end end end @@ -866,15 +946,15 @@ function Turret:init(args) self.t:every({0.1, 0.2}, function() 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, - character = self.parent.character, parent = self.parent} + 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} 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) - self.t:after(24, function() + self.t:after(24*(self.parent.conjurer_buff_m or 1), function() local n = n or random:int(3, 4) for i = 1, n do HitParticle{group = main.current.effects, x = self.x, y = self.y, r = random:float(0, 2*math.pi), color = self.color} end HitCircle{group = main.current.effects, x = self.x, y = self.y}:scale_down() diff --git a/todo b/todo index 6983700..f18b6f1 100644 --- a/todo +++ b/todo @@ -19,15 +19,15 @@ Spellblade: knives spiral outwards and pierce enemies Psykeeper: all damage taken is stored and distributed as healing Engineer: drops a sentry that uses random attacks, medium range -Ranger: yellow, chance to release a barrage -Warrior: orange, increased defense -Healer: green, increased healing effectiveness -Mage: blue, decreased enemy defense -Nuker: purple, increased area damage and size -Conjurer: orange, increased construct damage and duration -Rogue: red, chance to crit dealing 4x damage -Enchanter: pink, increased damage to all allies -Psy: white, returns damage taken based on number of active psy units +Ranger: yellow, 10%/20% chance to release a barrage on attack +Warrior: orange, +25/+50 defense to all warriors +Healer: green, 25% increased healing effectiveness +Mage: blue, -15/-30 enemy defense +Nuker: purple, 15%/25% increased area damage and size +Conjurer: orange, 25% increased construct damage and duration +Rogue: red, 10%/20% chance to crit dealing 4x damage +Enchanter: pink, 25% increased damage to all allies +Psy: white, 2X damage taken returned Vagrant [warrior, ranger, psy] Scout [rogue] @@ -48,7 +48,6 @@ Hunter [ranger, conjurer] Chronomancer [mage, enchanter] Spellblade [mage, rogue] Psykeeper [healer, psy] - Engineer [conjurer] Ranger [2, 4] (5)