diff --git a/arena.lua b/arena.lua index 587464a..f1302a1 100644 --- a/arena.lua +++ b/arena.lua @@ -13,6 +13,7 @@ function Arena:on_enter(from, level) self.level = level or 1 self.main = Group():set_as_physics_world(32, 0, 0, {'player', 'enemy', 'projectile', 'enemy_projectile'}) + self.post_main = Group() self.effects = Group() self.ui = Group():no_camera() self.main:disable_collision_between('player', 'player') @@ -44,16 +45,17 @@ function Arena:on_enter(from, level) Wall{group = self.main, vertices = math.to_rectangle_vertices(self.x2, -40, gw + 40, gh + 40), color = bg[-1]} Wall{group = self.main, vertices = math.to_rectangle_vertices(self.x1, -40, self.x2, self.y1), color = bg[-1]} Wall{group = self.main, vertices = math.to_rectangle_vertices(self.x1, self.y2, self.x2, gh + 40), color = bg[-1]} + WallCover{group = self.post_main, vertices = math.to_rectangle_vertices(-40, -40, self.x1, gh + 40), color = bg[-1]} + WallCover{group = self.post_main, vertices = math.to_rectangle_vertices(self.x2, -40, gw + 40, gh + 40), color = bg[-1]} + 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 = 'scout'} - --self.player:add_follower(Player{group = self.main, character = 'scout'}) - --[[ + 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 = 'archer'}) self.player:add_follower(Player{group = self.main, character = 'vagrant'}) - self.player:add_follower(Player{group = self.main, character = 'vagrant'}) - self.player:add_follower(Player{group = self.main, character = 'vagrant'}) - self.player:add_follower(Player{group = self.main, character = 'vagrant'}) - self.player:add_follower(Player{group = self.main, character = 'vagrant'}) - ]]-- + self.player:add_follower(Player{group = self.main, character = 'cleric'}) + self.player:add_follower(Player{group = self.main, character = 'scout'}) + self.player:add_follower(Player{group = self.main, character = 'wizard'}) self.win_condition = random:table{'time', 'enemy_kill', 'wave'} if self.win_condition == 'wave' then @@ -68,9 +70,11 @@ function Arena:on_enter(from, level) self.start_time = 3 self.t:after(1, function() self.t:every(1, function() + if self.start_time > 1 then alert1:play{volume = 0.5} end self.start_time = self.start_time - 1 self.hfx:use('condition1', 0.25, 200, 10) end, 3, function() + alert1:play{pitch = 1.2, volume = 0.5} camera:shake(4, 0.25) SpawnEffect{group = self.effects, x = gw/2, y = gh/2 - 48} self.wave = 0 @@ -101,9 +105,11 @@ function Arena:on_enter(from, level) self.start_time = 3 self.t:after(1, function() self.t:every(1, function() + if self.start_time > 1 then alert1:play{volume = 0.5} end self.start_time = self.start_time - 1 self.hfx:use('condition1', 0.25, 200, 10) end, 3, function() + alert1:play{pitch = 1.2, volume = 0.5} camera:shake(4, 0.25) SpawnEffect{group = self.effects, x = gw/2, y = gh/2 - 48} self:spawn_distributed_enemies() @@ -127,9 +133,11 @@ function Arena:on_enter(from, level) self.start_time = 3 self.t:after(1, function() self.t:every(1, function() + if self.start_time > 1 then alert1:play{volume = 0.5} end self.start_time = self.start_time - 1 self.hfx:use('condition1', 0.25, 200, 10) end, 3, function() + alert1:play{pitch = 1.2, volume = 0.5} camera:shake(4, 0.25) SpawnEffect{group = self.effects, x = gw/2, y = gh/2 - 48} self.t:every(1, function() @@ -152,6 +160,7 @@ end function Arena:update(dt) self:update_game_object(dt*slow_amount) self.main:update(dt*slow_amount) + self.post_main:update(dt*slow_amount) self.effects:update(dt*slow_amount) self.ui:update(dt*slow_amount) @@ -179,6 +188,7 @@ end function Arena:draw() self.main:draw() + self.post_main:draw() self.effects:draw() self.ui:draw() @@ -279,6 +289,7 @@ function Arena:spawn_n_enemies(p, j, n) self.t:every(0.1, function() local o = self.spawn_offsets[(self.t:get_every_iteration('spawn_enemies_' .. j) % 5) + 1] SpawnEffect{group = self.effects, x = p.x + o.x, y = p.y + o.y, action = function(x, y) + spawn1:play{pitch = random:float(0.8, 1.2), volume = 0.15} if self.level == 1 then Seeker{group = self.main, x = x, y = y, character = 'seeker'} elseif self.level == 2 then diff --git a/assets/sounds/Alert sounds 3.ogg b/assets/sounds/Alert sounds 3.ogg new file mode 100644 index 0000000..0160c3e Binary files /dev/null and b/assets/sounds/Alert sounds 3.ogg differ diff --git a/assets/sounds/Arrow Impact wood 1.ogg b/assets/sounds/Arrow Impact wood 1.ogg new file mode 100644 index 0000000..f726f39 Binary files /dev/null and b/assets/sounds/Arrow Impact wood 1.ogg differ diff --git a/assets/sounds/Arrow Impact wood 3.ogg b/assets/sounds/Arrow Impact wood 3.ogg new file mode 100644 index 0000000..8ca6a31 Binary files /dev/null and b/assets/sounds/Arrow Impact wood 3.ogg differ diff --git a/assets/sounds/Bloody punches 10.ogg b/assets/sounds/Bloody punches 10.ogg new file mode 100644 index 0000000..fe272d2 Binary files /dev/null and b/assets/sounds/Bloody punches 10.ogg differ diff --git a/assets/sounds/Bloody punches 7.ogg b/assets/sounds/Bloody punches 7.ogg new file mode 100644 index 0000000..03feaf6 Binary files /dev/null and b/assets/sounds/Bloody punches 7.ogg differ diff --git a/assets/sounds/Body Fall 18.ogg b/assets/sounds/Body Fall 18.ogg new file mode 100644 index 0000000..a1c0f67 Binary files /dev/null and b/assets/sounds/Body Fall 18.ogg differ diff --git a/assets/sounds/Body Fall 2.ogg b/assets/sounds/Body Fall 2.ogg new file mode 100644 index 0000000..293caf4 Binary files /dev/null and b/assets/sounds/Body Fall 2.ogg differ diff --git a/assets/sounds/Body Head (Headshot) 1.ogg b/assets/sounds/Body Head (Headshot) 1.ogg new file mode 100644 index 0000000..f81d9b5 Binary files /dev/null and b/assets/sounds/Body Head (Headshot) 1.ogg differ diff --git a/assets/sounds/Buff 13.ogg b/assets/sounds/Buff 13.ogg new file mode 100644 index 0000000..d6d173a Binary files /dev/null and b/assets/sounds/Buff 13.ogg differ diff --git a/assets/sounds/Buff 3.ogg b/assets/sounds/Buff 3.ogg new file mode 100644 index 0000000..ca7ea25 Binary files /dev/null and b/assets/sounds/Buff 3.ogg differ diff --git a/assets/sounds/Fire bolt 10.ogg b/assets/sounds/Fire bolt 10.ogg new file mode 100644 index 0000000..f5a3e8a Binary files /dev/null and b/assets/sounds/Fire bolt 10.ogg differ diff --git a/assets/sounds/Heavy sword woosh 1.ogg b/assets/sounds/Heavy sword woosh 1.ogg new file mode 100644 index 0000000..3739a2c Binary files /dev/null and b/assets/sounds/Heavy sword woosh 1.ogg differ diff --git a/assets/sounds/Heavy sword woosh 19.ogg b/assets/sounds/Heavy sword woosh 19.ogg new file mode 100644 index 0000000..6154b77 Binary files /dev/null and b/assets/sounds/Heavy sword woosh 19.ogg differ diff --git a/assets/sounds/Kick 16.ogg b/assets/sounds/Kick 16.ogg new file mode 100644 index 0000000..58272ff Binary files /dev/null and b/assets/sounds/Kick 16.ogg differ diff --git a/assets/sounds/Magical Impact 27.ogg b/assets/sounds/Magical Impact 27.ogg new file mode 100644 index 0000000..7aa92e2 Binary files /dev/null and b/assets/sounds/Magical Impact 27.ogg differ diff --git a/assets/sounds/Mud footsteps 7.ogg b/assets/sounds/Mud footsteps 7.ogg new file mode 100644 index 0000000..29a622d Binary files /dev/null and b/assets/sounds/Mud footsteps 7.ogg differ diff --git a/assets/sounds/Mud footsteps 9.ogg b/assets/sounds/Mud footsteps 9.ogg new file mode 100644 index 0000000..b737732 Binary files /dev/null and b/assets/sounds/Mud footsteps 9.ogg differ diff --git a/assets/sounds/Player Takes Damage 17.ogg b/assets/sounds/Player Takes Damage 17.ogg new file mode 100644 index 0000000..7d4fc0f Binary files /dev/null and b/assets/sounds/Player Takes Damage 17.ogg differ diff --git a/assets/sounds/Player Takes Damage 2.ogg b/assets/sounds/Player Takes Damage 2.ogg new file mode 100644 index 0000000..602fa21 Binary files /dev/null and b/assets/sounds/Player Takes Damage 2.ogg differ diff --git a/assets/sounds/Pop sounds 10.ogg b/assets/sounds/Pop sounds 10.ogg new file mode 100644 index 0000000..2fa04b8 Binary files /dev/null and b/assets/sounds/Pop sounds 10.ogg differ diff --git a/assets/sounds/Releasing Bow String 1.ogg b/assets/sounds/Releasing Bow String 1.ogg new file mode 100644 index 0000000..519d4a5 Binary files /dev/null and b/assets/sounds/Releasing Bow String 1.ogg differ diff --git a/assets/sounds/Shadow Punch 1.ogg b/assets/sounds/Shadow Punch 1.ogg new file mode 100644 index 0000000..68e74e4 Binary files /dev/null and b/assets/sounds/Shadow Punch 1.ogg differ diff --git a/assets/sounds/Shield Impacts Sword 1.ogg b/assets/sounds/Shield Impacts Sword 1.ogg new file mode 100644 index 0000000..3495761 Binary files /dev/null and b/assets/sounds/Shield Impacts Sword 1.ogg differ diff --git a/assets/sounds/Shooting Projectile (Classic) 11.ogg b/assets/sounds/Shooting Projectile (Classic) 11.ogg new file mode 100644 index 0000000..41c8876 Binary files /dev/null and b/assets/sounds/Shooting Projectile (Classic) 11.ogg differ diff --git a/assets/sounds/Spawn 10.ogg b/assets/sounds/Spawn 10.ogg new file mode 100644 index 0000000..8fee141 Binary files /dev/null and b/assets/sounds/Spawn 10.ogg differ diff --git a/assets/sounds/Switch.ogg b/assets/sounds/Switch.ogg new file mode 100644 index 0000000..f5cc37b Binary files /dev/null and b/assets/sounds/Switch.ogg differ diff --git a/assets/sounds/Sword impact (Flesh) 2.ogg b/assets/sounds/Sword impact (Flesh) 2.ogg new file mode 100644 index 0000000..08555e3 Binary files /dev/null and b/assets/sounds/Sword impact (Flesh) 2.ogg differ diff --git a/assets/sounds/Throwing Knife (Thrown) 3.ogg b/assets/sounds/Throwing Knife (Thrown) 3.ogg new file mode 100644 index 0000000..33d463f Binary files /dev/null and b/assets/sounds/Throwing Knife (Thrown) 3.ogg differ diff --git a/assets/sounds/Throwing Knife (Thrown) 4.ogg b/assets/sounds/Throwing Knife (Thrown) 4.ogg new file mode 100644 index 0000000..7af9f6a Binary files /dev/null and b/assets/sounds/Throwing Knife (Thrown) 4.ogg differ diff --git a/assets/sounds/Wind Bolt 20.ogg b/assets/sounds/Wind Bolt 20.ogg new file mode 100644 index 0000000..dda836f Binary files /dev/null and b/assets/sounds/Wind Bolt 20.ogg differ diff --git a/assets/sounds/Wood Heavy 5.ogg b/assets/sounds/Wood Heavy 5.ogg new file mode 100644 index 0000000..b046dad Binary files /dev/null and b/assets/sounds/Wood Heavy 5.ogg differ diff --git a/assets/sounds/sounds_go_here.txt b/assets/sounds/sounds_go_here.txt deleted file mode 100644 index e69de29..0000000 diff --git a/devlog.md b/devlog.md index 83ac525..c2d706e 100644 --- a/devlog.md +++ b/devlog.md @@ -90,3 +90,10 @@ The classes are: I'm not sure what I should focus on next. I know that there's sounds left to add, but after that I should probably start doing the actual game progression, but for that I think need a bunch more characters. I should probably try to think of a cast of maybe 15-20 characters (however that number should be as low as possible) that fills up the current classes to their multiple levels. Levels possible are: 1, 2, 3, 2/4, 2/4/6 and 3/6. + +# Day 5 - 21/02/21 + +Sounds done for everything. Surprising to me how much sounds added to the game and helped me sell all the different attacks way better than with just graphics. +I should probably make it a habit to add sounds earlier rather than later from now on. + +Tomorrow I should probably ideaguy the full set of characters and classes that I'll need so that the game is playable and start on implementing those additional characters as well as some class bonuses. diff --git a/enemies.lua b/enemies.lua index a9ce680..a4b545e 100644 --- a/enemies.lua +++ b/enemies.lua @@ -59,11 +59,29 @@ function Seeker:on_collision_enter(other, contact) other:push(random:float(10, 15), other:angle_to_object(self)) HitCircle{group = main.current.effects, x = x, y = y, rs = 6, color = fg[0], duration = 0.1} for i = 1, 2 do HitParticle{group = main.current.effects, x = x, y = y, color = self.color} end + hit2:play{pitch = random:float(0.95, 1.05), volume = 0.35} end end end +function Seeker:hit(damage) + if self.dead then return end + self.hfx:use('hit', 0.25, 200, 10) + self:show_hp() + + local actual_damage = self:calculate_damage(damage) + self.hp = self.hp - actual_damage + if self.hp <= 0 then + self.dead = true + for i = 1, random:int(4, 6) do HitParticle{group = main.current.effects, x = self.x, y = self.y, color = self.color} end + HitCircle{group = main.current.effects, x = self.x, y = self.y, rs = 12}:scale_down(0.3):change_color(0.5, self.color) + main.current:enemy_killed() + _G[random:table{'enemy_die1', 'enemy_die2'}]:play{pitch = random:float(0.9, 1.1), volume = 0.5} + end +end + + function Seeker:push(f, r) self.being_pushed = true self.steering_enabled = false diff --git a/engine/game/group.lua b/engine/game/group.lua index 763f449..25a37e7 100644 --- a/engine/game/group.lua +++ b/engine/game/group.lua @@ -167,6 +167,7 @@ end function Group:add(object) local class = getmetatable(object) object.group = self + if not object.id then object.id = random:uid() end self.objects.by_id[object.id] = object if not self.objects.by_class[class] then self.objects.by_class[class] = {} end diff --git a/main.lua b/main.lua index 75196af..05b63b9 100644 --- a/main.lua +++ b/main.lua @@ -14,6 +14,34 @@ function init() input:bind('move_up', {'w', 'up'}) input:bind('move_down', {'s', 'down'}) + sfx_tag = {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) + swordsman1 = Sound('Heavy sword woosh 1.ogg', s) + swordsman2 = Sound('Heavy sword woosh 19.ogg', s) + scout1 = Sound('Throwing Knife (Thrown) 3.ogg', s) + scout2 = Sound('Throwing Knife (Thrown) 4.ogg', s) + arrow_hit_wall1 = Sound('Arrow Impact wood 3.ogg', s) + arrow_hit_wall2 = Sound('Arrow Impact wood 1.ogg', s) + hit1 = Sound('Player Takes Damage 17.ogg', s) + hit2 = Sound('Body Head (Headshot) 1.ogg', s) + hit3 = Sound('Kick 16.ogg', s) + proj_hit_wall1 = Sound('Player Takes Damage 2.ogg', s) + enemy_die1 = Sound('Bloody punches 7.ogg', s) + enemy_die2 = Sound('Bloody punches 10.ogg', s) + magic_area1 = Sound('Fire bolt 10.ogg', s) + magic_hit1 = Sound('Shadow Punch 1.ogg', s) + magic_die1 = Sound('Magical Impact 27.ogg', s) + knife_hit_wall1 = Sound('Shield Impacts Sword 1.ogg', s) + player_hit1 = Sound('Body Fall 2.ogg', s) + player_hit2 = Sound('Body Fall 18.ogg', s) + player_hit_wall1 = Sound('Wood Heavy 5.ogg', s) + pop1 = Sound('Pop sounds 10.ogg', s) + heal1 = Sound('Buff 3.ogg', s) + spawn1 = Sound('Buff 13.ogg', s) + alert1 = Sound('Alert sounds 3.ogg', s) + main = Main() main:add(Arena'arena') main:go_to'arena' diff --git a/objects.lua b/objects.lua index 7c064e4..8d53d0e 100644 --- a/objects.lua +++ b/objects.lua @@ -1,8 +1,73 @@ +WallKnife = Object:extend() +WallKnife:implement(GameObject) +WallKnife:implement(Physics) +function WallKnife:init(args) + self:init_game_object(args) + self:set_as_rectangle(10, 4, 'dynamic', 'projectile') + self.hfx:add('hit', 1) + self.hfx:use('hit', 0.25) + self.t:tween({0.8, 1.6}, self, {v = 0}, math.linear, function() + self.t:every_immediate(0.05, function() self.hidden = not self.hidden end, 7, function() self.dead = true end) + end) + + self.vr = self.r + self.dvr = random:table{random:float(-8*math.pi, -4*math.pi), random:float(4*math.pi, 8*math.pi)} +end + + +function WallKnife:update(dt) + self:update_game_object(dt) + + self:set_angle(self.r) + self:move_along_angle(self.v, self.r) + self.vr = self.vr + self.dvr*dt +end + + +function WallKnife:draw() + if self.hidden then return end + graphics.push(self.x, self.y, self.vr, self.hfx.hit.x, self.hfx.hit.x) + graphics.rectangle(self.x, self.y, self.shape.w, self.shape.h, 2, 2, self.hfx.hit.f and fg[0] or self.color) + graphics.pop() +end + + + + +WallArrow = Object:extend() +WallArrow:implement(GameObject) +function WallArrow:init(args) + self:init_game_object(args) + self.shape = Rectangle(self.x, self.y, 10, 4) + self.hfx:add('hit', 1) + self.hfx:use('hit', 0.25) + self.t:after({0.8, 2}, function() + self.t:every_immediate(0.05, function() self.hidden = not self.hidden end, 7, function() self.dead = true end) + end) +end + + +function WallArrow:update(dt) + self:update_game_object(dt) +end + + +function WallArrow:draw() + if self.hidden then return end + 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, 2, 2, self.hfx.hit.f and fg[0] or self.color) + graphics.pop() +end + + + + Unit = Object:extend() function Unit:init_unit() self.hfx:add('hit', 1) self.hfx:add('shoot', 1) self.hp_bar = HPBar{group = main.current.effects, parent = self} + self.heal_bar = HealBar{group = main.current.effects, parent = self} end @@ -16,6 +81,7 @@ function Unit:bounce(nx, ny) self:set_velocity(-vx, vy) self.r = math.pi - self.r end + return self.r end @@ -25,32 +91,9 @@ function Unit:show_hp(n) end -function Unit:hit(damage) - if self.dead then return end - - self.hfx:use('hit', 0.25, 200, 10) - self:show_hp() - - local actual_damage = self:calculate_damage(damage) - self.hp = self.hp - actual_damage - if self:is(Player) then - if actual_damage >= 20 then - camera:shake(2, 1) - slow(0.25, 1) - else - camera:shake(2, 0.5) - end - end - - if self.hp <= 0 then - self.dead = true - for i = 1, random:int(4, 6) do HitParticle{group = main.current.effects, x = self.x, y = self.y, color = self.color} end - HitCircle{group = main.current.effects, x = self.x, y = self.y, rs = 12}:scale_down(0.3):change_color(0.5, self.color) - - if table.any(main.current.enemies, function(v) return self:is(v) end) then - main.current:enemy_killed() - end - end +function Unit:show_heal(n) + self.heal_bar.hidden = false + self.t:after(n or 4, function() self.heal_bar.hidden = true end, 'heal_bar') end @@ -155,6 +198,35 @@ end +HealBar = Object:extend() +HealBar:implement(GameObject) +HealBar:implement(Parent) +function HealBar:init(args) + self:init_game_object(args) + self.hidden = true + self.color = green[0] + self.color_transparent = Color(self.color.r, self.color.g, self.color.b, 0.2) +end + + +function HealBar:update(dt) + self:update_game_object(dt) + self:follow_parent_exclusively() +end + + +function HealBar:draw() + if self.hidden then return end + local p = self.parent + graphics.push(p.x, p.y, 0, p.hfx.hit.x, p.hfx.hit.x) + graphics.rectangle(p.x, p.y, 1.25*p.shape.w, 1.25*p.shape.h, 2, 2, self.color_transparent) + graphics.rectangle(p.x, p.y, 1.25*p.shape.w, 1.25*p.shape.h, 2, 2, self.color, 1) + graphics.pop() +end + + + + HPBar = Object:extend() HPBar:implement(GameObject) HPBar:implement(Parent) diff --git a/player.lua b/player.lua index cb7577e..474b971 100644 --- a/player.lua +++ b/player.lua @@ -30,7 +30,7 @@ function Player:init(args) self.t:every(3, function() local enemies = self:get_objects_in_shape(self.attack_sensor, main.current.enemies) if enemies and #enemies > 0 then - self:attack() + self:attack(96) end end, nil, nil, 'attack') @@ -40,7 +40,7 @@ function Player:init(args) self.visual_shape = 'rectangle' self.classes = {'mage'} - self.attack_sensor = Circle(self.x, self.y, 64) + self.attack_sensor = Circle(self.x, self.y, 128) self.t:every(2, function() local closest_enemy = self:get_closest_object_in_shape(self.attack_sensor, main.current.enemies) if closest_enemy then @@ -75,6 +75,25 @@ function Player:init(args) self:shoot(self:angle_to_object(closest_enemy), {chain = 3}) end end, nil, nil, 'shoot') + + elseif self.character == 'cleric' then + self.color = green[0] + self:set_as_rectangle(9, 9, 'dynamic', 'player') + self.visual_shape = 'rectangle' + self.classes = {'healer'} + + self.last_heal_time = love.timer.getTime() + self.t:every(2, function() + local followers + local leader = (self.leader and self) or self.parent + 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 + heal1:play{pitch = random:float(0.95, 1.05), volume = 0.5} + end + end) end self:calculate_stats(true) @@ -93,9 +112,7 @@ function Player:update(dt) self:update_game_object(dt) self:calculate_stats() - if self.character == 'archer' then print(self.aspd_m) end - - self.attack_sensor:move_to(self.x, self.y) + if self.attack_sensor then self.attack_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) @@ -136,6 +153,7 @@ function Player:update(dt) self:set_position(p.x, p.y) self.r = p.r if not self.following then + spawn1:play{pitch = random:float(0.8, 1.2), volume = 0.15} for i = 1, random:int(3, 4) do HitParticle{group = main.current.effects, x = self.x, y = self.y, color = self.color} end HitCircle{group = main.current.effects, x = self.x, y = self.y, rs = 10, color = fg[0]}:scale_down(0.3):change_color(0.5, self.color) self.following = true @@ -161,9 +179,22 @@ function Player:on_collision_enter(other, contact) local x, y = contact:getPositions() if other:is(Wall) then - self.hfx:use('hit', 0.5, 200, 10, 0.1) - camera:spring_shake(2, math.pi - self.r) - self:bounce(contact:getNormal()) + if self.leader then + self.hfx:use('hit', 0.5, 200, 10, 0.1) + camera:spring_shake(2, math.pi - self.r) + self:bounce(contact:getNormal()) + local r = random:float(0.9, 1.1) + player_hit_wall1:play{pitch = r, volume = 0.1} + pop1:play{pitch = r, volume = 0.2} + + for i, f in ipairs(self.followers) do + trigger:after(i*0.002*self.v, function() + f.hfx:use('hit', 0.5, 200, 10, 0.1) + player_hit_wall1:play{pitch = r + 0.025*i, volume = 0.1} + pop1:play{pitch = r + 0.05*i, volume = 0.2} + end) + end + end elseif table.any(main.current.enemies, function(v) return other:is(v) end) then other:push(random:float(25, 35), self:angle_to_object(other)) @@ -176,6 +207,36 @@ function Player:on_collision_enter(other, contact) end +function Player:hit(damage) + if self.dead then return end + self.hfx:use('hit', 0.25, 200, 10) + self:show_hp() + + local actual_damage = self:calculate_damage(damage) + self.hp = self.hp - actual_damage + _G[random:table{'player_hit1', 'player_hit2'}]:play{pitch = random:float(0.95, 1.05), volume = 0.5} + camera:shake(4, 0.5) + + if self.hp <= 0 then + slow(0.25, 1) + self.dead = true + for i = 1, random:int(4, 6) do HitParticle{group = main.current.effects, x = self.x, y = self.y, color = self.color} end + HitCircle{group = main.current.effects, x = self.x, y = self.y, rs = 12}:scale_down(0.3):change_color(0.5, self.color) + end +end + + +function Player:heal(amount) + local hp = self.hp + + self.hfx:use('hit', 0.25, 200, 10) + self:show_hp(1.5) + self:show_heal(1.5) + self.hp = self.hp + amount + if self.hp > self.max_hp then self.hp = self.max_hp end +end + + function Player:add_follower(unit) table.insert(self.followers, unit) unit.parent = self @@ -187,16 +248,30 @@ function Player:shoot(r, mods) camera:spring_shake(2, r) self.hfx:use('shoot', 0.25) 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} + 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} Projectile(table.merge(t, mods or {})) + + if self.character == 'vagrant' then + shoot1:play{pitch = random:float(0.95, 1.05), volume = 0.3} + elseif self.character == 'archer' then + archer1:play{pitch = random:float(0.95, 1.05), volume = 0.5} + elseif self.character == 'wizard' then + wizard1:play{pitch = random:float(0.95, 1.05), volume = 0.15} + elseif self.character == 'scout' then + _G[random:table{'scout1', 'scout2'}]:play{pitch = random:float(0.95, 1.05), volume = 0.5} + end end -function Player:attack(mods) +function Player:attack(area, mods) camera:shake(2, 0.5) self.hfx:use('shoot', 0.25) - local t = {group = main.current.effects, x = self.x, y = self.y, r = self.r, w = self.area_size_m*64, color = self.color, dmg = self.area_dmg_m*self.dmg} + local t = {group = main.current.effects, x = self.x, y = self.y, r = self.r, w = self.area_size_m*(area or 64), color = self.color, dmg = self.area_dmg_m*self.dmg, character = self.character} Area(table.merge(t, mods or {})) + + if self.character == 'swordsman' then + _G[random:table{'swordsman1', 'swordsman2'}]:play{pitch = random:float(0.9, 1.1), volume = 0.75} + end end @@ -238,8 +313,8 @@ function Projectile:die(x, y, r, n) HitCircle{group = main.current.effects, x = x, y = y}:scale_down() self.dead = true - if self.wizard then - Area{group = main.current.effects, x = self.x, y = self.y, r = self.r, w = self.wizard.area_size_m*32, color = self.color, dmg = self.wizard.area_dmg_m*self.dmg} + if self.character == 'wizard' then + Area{group = main.current.effects, x = self.x, y = self.y, r = self.r, w = self.wizard.area_size_m*32, color = self.color, dmg = self.wizard.area_dmg_m*self.dmg, character = self.character} end end @@ -254,7 +329,24 @@ function Projectile:on_collision_enter(other, contact) else r = 0 end if other:is(Wall) then - self:die(x, y, r, random:int(2, 3)) + if self.character == 'archer' 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} + elseif self.character == 'scout' 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) + trigger:after(0.01, function() + WallKnife{group = main.current.main, x = x, y = y, r = r, v = self.v*0.1, color = self.color} + end) + elseif self.character == 'wizard' then + self:die(x, y, r, random:int(2, 3)) + magic_area1:play{pitch = random:float(0.95, 1.05), volume = 0.075} + else + self:die(x, y, r, random:int(2, 3)) + proj_hit_wall1:play{pitch = random:float(0.9, 1.1), volume = 0.2} + end end end @@ -274,11 +366,19 @@ function Projectile:on_trigger_enter(other, contact) if object then self.r = self:angle_to_object(object) self.v = self.v*1.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} - HitParticle{group = main.current.effects, x = self.x, y = self.y, color = other.color} end end + 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} + HitParticle{group = main.current.effects, x = self.x, y = self.y, color = other.color} + end + + if self.character == 'archer' or self.character == 'scout' then + hit2:play{pitch = random:float(0.95, 1.05), volume = 0.35} + elseif self.character == 'wizard' then + magic_area1:play{pitch = random:float(0.95, 1.05), volume = 0.15} + else + hit3:play{pitch = random:float(0.95, 1.05), volume = 0.35} end other:hit(self.dmg) end @@ -298,6 +398,11 @@ function Area:init(args) HitCircle{group = main.current.effects, x = enemy.x, y = enemy.y, rs = 6, color = fg[0], duration = 0.1} for i = 1, 2 do HitParticle{group = main.current.effects, x = enemy.x, y = enemy.y, color = self.color} end for i = 1, 2 do HitParticle{group = main.current.effects, x = enemy.x, y = enemy.y, color = enemy.color} end + if self.character == 'wizard' then + magic_hit1:play{pitch = random:float(0.95, 1.05), volume = 0.5} + elseif self.character == 'swordsman' then + hit2:play{pitch = random:float(0.95, 1.05), volume = 0.35} + end end self.color = fg[0] diff --git a/shared.lua b/shared.lua index 70d27ff..0372804 100644 --- a/shared.lua +++ b/shared.lua @@ -7,7 +7,6 @@ function shared_init() fg_alt = ColorRamp(Color'#b0a89f', 0.025), yellow = ColorRamp(Color'#facf00', 0.025), orange = ColorRamp(Color'#f07021', 0.025), - yellow_orange = ColorRamp(Color(245, 160, 16), 0.025), blue = ColorRamp(Color'#019bd6', 0.025), green = ColorRamp(Color'#8bbf40', 0.025), red = ColorRamp(Color'#e91d39', 0.025), @@ -663,3 +662,24 @@ end function Wall:draw() self.shape:draw(self.color) end + + + + +WallCover = Object:extend() +WallCover:implement(GameObject) +function WallCover:init(args) + self:init_game_object(args) + self.shape = Polygon(self.vertices) + self.color = self.color or fg[0] +end + + +function WallCover:update(dt) + self:update_game_object(dt) +end + + +function WallCover:draw() + self.shape:draw(self.color) +end diff --git a/todo b/todo index 751e480..b0c8ee5 100644 --- a/todo +++ b/todo @@ -1,9 +1,10 @@ Vagrant: shoots an ethereal projectile at any nearby enemy that deals physical and magical damage, medium range Scout: throws a knife that chains 3 times at any nearby enemy, small range Cleric: heals every unit when any one drops below 50% HP -Swordsman: deals physical damage in an area around the unit, small range +Swordsman: deals physical damage in a medium area around the unit, small range Archer: shoots an arrow that pierces at any nearby enemy, very long range -Wizard: shoots a projectile at any nearby enemy and deals AoE magical damage on contact, small range, AoE has very small range +Wizard: shoots a projectile at any nearby enemy and deals AoE magical damage on contact, long range, AoE has very small range + Engineer: drops a turret that shoots secondary projectiles very fast, medium range Ranger: yellow, buff attack speed @@ -27,4 +28,4 @@ Defense -> if defense >= 0 then dmg_m = 100/(100+defense) else dmg_m = 2-100/(10 * Stats: attack speed, damage, area * One or a few of the characters * Port over enemy spawn logic from SHOOTRX -Sounds +* Sounds