master
a327ex 2021-02-21 23:54:50 -03:00
parent 9d3f777342
commit 2ff3cb9445
41 changed files with 318 additions and 55 deletions

View File

@ -13,6 +13,7 @@ function Arena:on_enter(from, level)
self.level = level or 1 self.level = level or 1
self.main = Group():set_as_physics_world(32, 0, 0, {'player', 'enemy', 'projectile', 'enemy_projectile'}) self.main = Group():set_as_physics_world(32, 0, 0, {'player', 'enemy', 'projectile', 'enemy_projectile'})
self.post_main = Group()
self.effects = Group() self.effects = Group()
self.ui = Group():no_camera() self.ui = Group():no_camera()
self.main:disable_collision_between('player', 'player') 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.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, -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]} 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 = Player{group = self.main, x = gw/2, y = gh/2, leader = true, character = 'swordsman'}
--self.player:add_follower(Player{group = self.main, character = 'scout'}) 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 = 'cleric'})
self.player:add_follower(Player{group = self.main, character = 'vagrant'}) self.player:add_follower(Player{group = self.main, character = 'scout'})
self.player:add_follower(Player{group = self.main, character = 'vagrant'}) self.player:add_follower(Player{group = self.main, character = 'wizard'})
self.player:add_follower(Player{group = self.main, character = 'vagrant'})
]]--
self.win_condition = random:table{'time', 'enemy_kill', 'wave'} self.win_condition = random:table{'time', 'enemy_kill', 'wave'}
if self.win_condition == 'wave' then if self.win_condition == 'wave' then
@ -68,9 +70,11 @@ function Arena:on_enter(from, level)
self.start_time = 3 self.start_time = 3
self.t:after(1, function() self.t:after(1, function()
self.t:every(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.start_time = self.start_time - 1
self.hfx:use('condition1', 0.25, 200, 10) self.hfx:use('condition1', 0.25, 200, 10)
end, 3, function() end, 3, function()
alert1:play{pitch = 1.2, volume = 0.5}
camera:shake(4, 0.25) camera:shake(4, 0.25)
SpawnEffect{group = self.effects, x = gw/2, y = gh/2 - 48} SpawnEffect{group = self.effects, x = gw/2, y = gh/2 - 48}
self.wave = 0 self.wave = 0
@ -101,9 +105,11 @@ function Arena:on_enter(from, level)
self.start_time = 3 self.start_time = 3
self.t:after(1, function() self.t:after(1, function()
self.t:every(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.start_time = self.start_time - 1
self.hfx:use('condition1', 0.25, 200, 10) self.hfx:use('condition1', 0.25, 200, 10)
end, 3, function() end, 3, function()
alert1:play{pitch = 1.2, volume = 0.5}
camera:shake(4, 0.25) camera:shake(4, 0.25)
SpawnEffect{group = self.effects, x = gw/2, y = gh/2 - 48} SpawnEffect{group = self.effects, x = gw/2, y = gh/2 - 48}
self:spawn_distributed_enemies() self:spawn_distributed_enemies()
@ -127,9 +133,11 @@ function Arena:on_enter(from, level)
self.start_time = 3 self.start_time = 3
self.t:after(1, function() self.t:after(1, function()
self.t:every(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.start_time = self.start_time - 1
self.hfx:use('condition1', 0.25, 200, 10) self.hfx:use('condition1', 0.25, 200, 10)
end, 3, function() end, 3, function()
alert1:play{pitch = 1.2, volume = 0.5}
camera:shake(4, 0.25) camera:shake(4, 0.25)
SpawnEffect{group = self.effects, x = gw/2, y = gh/2 - 48} SpawnEffect{group = self.effects, x = gw/2, y = gh/2 - 48}
self.t:every(1, function() self.t:every(1, function()
@ -152,6 +160,7 @@ end
function Arena:update(dt) function Arena:update(dt)
self:update_game_object(dt*slow_amount) self:update_game_object(dt*slow_amount)
self.main:update(dt*slow_amount) self.main:update(dt*slow_amount)
self.post_main:update(dt*slow_amount)
self.effects:update(dt*slow_amount) self.effects:update(dt*slow_amount)
self.ui:update(dt*slow_amount) self.ui:update(dt*slow_amount)
@ -179,6 +188,7 @@ end
function Arena:draw() function Arena:draw()
self.main:draw() self.main:draw()
self.post_main:draw()
self.effects:draw() self.effects:draw()
self.ui:draw() self.ui:draw()
@ -279,6 +289,7 @@ function Arena:spawn_n_enemies(p, j, n)
self.t:every(0.1, function() self.t:every(0.1, function()
local o = self.spawn_offsets[(self.t:get_every_iteration('spawn_enemies_' .. j) % 5) + 1] 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) 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 if self.level == 1 then
Seeker{group = self.main, x = x, y = y, character = 'seeker'} Seeker{group = self.main, x = x, y = y, character = 'seeker'}
elseif self.level == 2 then elseif self.level == 2 then

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -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'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. 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.

View File

@ -59,11 +59,29 @@ function Seeker:on_collision_enter(other, contact)
other:push(random:float(10, 15), other:angle_to_object(self)) 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} 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 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 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) function Seeker:push(f, r)
self.being_pushed = true self.being_pushed = true
self.steering_enabled = false self.steering_enabled = false

View File

@ -167,6 +167,7 @@ end
function Group:add(object) function Group:add(object)
local class = getmetatable(object) local class = getmetatable(object)
object.group = self object.group = self
if not object.id then object.id = random:uid() end if not object.id then object.id = random:uid() end
self.objects.by_id[object.id] = object self.objects.by_id[object.id] = object
if not self.objects.by_class[class] then self.objects.by_class[class] = {} end if not self.objects.by_class[class] then self.objects.by_class[class] = {} end

View File

@ -14,6 +14,34 @@ function init()
input:bind('move_up', {'w', 'up'}) input:bind('move_up', {'w', 'up'})
input:bind('move_down', {'s', 'down'}) 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 = Main()
main:add(Arena'arena') main:add(Arena'arena')
main:go_to'arena' main:go_to'arena'

View File

@ -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() Unit = Object:extend()
function Unit:init_unit() function Unit:init_unit()
self.hfx:add('hit', 1) self.hfx:add('hit', 1)
self.hfx:add('shoot', 1) self.hfx:add('shoot', 1)
self.hp_bar = HPBar{group = main.current.effects, parent = self} self.hp_bar = HPBar{group = main.current.effects, parent = self}
self.heal_bar = HealBar{group = main.current.effects, parent = self}
end end
@ -16,6 +81,7 @@ function Unit:bounce(nx, ny)
self:set_velocity(-vx, vy) self:set_velocity(-vx, vy)
self.r = math.pi - self.r self.r = math.pi - self.r
end end
return self.r
end end
@ -25,32 +91,9 @@ function Unit:show_hp(n)
end end
function Unit:hit(damage) function Unit:show_heal(n)
if self.dead then return end self.heal_bar.hidden = false
self.t:after(n or 4, function() self.heal_bar.hidden = true end, 'heal_bar')
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
end 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 = Object:extend()
HPBar:implement(GameObject) HPBar:implement(GameObject)
HPBar:implement(Parent) HPBar:implement(Parent)

View File

@ -30,7 +30,7 @@ function Player:init(args)
self.t:every(3, function() self.t:every(3, function()
local enemies = self:get_objects_in_shape(self.attack_sensor, main.current.enemies) local enemies = self:get_objects_in_shape(self.attack_sensor, main.current.enemies)
if enemies and #enemies > 0 then if enemies and #enemies > 0 then
self:attack() self:attack(96)
end end
end, nil, nil, 'attack') end, nil, nil, 'attack')
@ -40,7 +40,7 @@ function Player:init(args)
self.visual_shape = 'rectangle' self.visual_shape = 'rectangle'
self.classes = {'mage'} 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() self.t:every(2, function()
local closest_enemy = self:get_closest_object_in_shape(self.attack_sensor, main.current.enemies) local closest_enemy = self:get_closest_object_in_shape(self.attack_sensor, main.current.enemies)
if closest_enemy then if closest_enemy then
@ -75,6 +75,25 @@ function Player:init(args)
self:shoot(self:angle_to_object(closest_enemy), {chain = 3}) self:shoot(self:angle_to_object(closest_enemy), {chain = 3})
end end
end, nil, nil, 'shoot') 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 end
self:calculate_stats(true) self:calculate_stats(true)
@ -93,9 +112,7 @@ function Player:update(dt)
self:update_game_object(dt) self:update_game_object(dt)
self:calculate_stats() self:calculate_stats()
if self.character == 'archer' then print(self.aspd_m) end if self.attack_sensor then self.attack_sensor:move_to(self.x, self.y) end
self.attack_sensor:move_to(self.x, self.y)
self.t:set_every_multiplier('shoot', self.aspd_m) self.t:set_every_multiplier('shoot', self.aspd_m)
self.t:set_every_multiplier('attack', self.aspd_m) self.t:set_every_multiplier('attack', self.aspd_m)
@ -136,6 +153,7 @@ function Player:update(dt)
self:set_position(p.x, p.y) self:set_position(p.x, p.y)
self.r = p.r self.r = p.r
if not self.following then 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 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) 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 self.following = true
@ -161,9 +179,22 @@ function Player:on_collision_enter(other, contact)
local x, y = contact:getPositions() local x, y = contact:getPositions()
if other:is(Wall) then if other:is(Wall) then
self.hfx:use('hit', 0.5, 200, 10, 0.1) if self.leader then
camera:spring_shake(2, math.pi - self.r) self.hfx:use('hit', 0.5, 200, 10, 0.1)
self:bounce(contact:getNormal()) 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 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)) other:push(random:float(25, 35), self:angle_to_object(other))
@ -176,6 +207,36 @@ function Player:on_collision_enter(other, contact)
end 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) function Player:add_follower(unit)
table.insert(self.followers, unit) table.insert(self.followers, unit)
unit.parent = self unit.parent = self
@ -187,16 +248,30 @@ function Player:shoot(r, mods)
camera:spring_shake(2, r) camera:spring_shake(2, r)
self.hfx:use('shoot', 0.25) 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} 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 {})) 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 end
function Player:attack(mods) function Player:attack(area, mods)
camera:shake(2, 0.5) camera:shake(2, 0.5)
self.hfx:use('shoot', 0.25) 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 {})) 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 end
@ -238,8 +313,8 @@ function Projectile:die(x, y, r, n)
HitCircle{group = main.current.effects, x = x, y = y}:scale_down() HitCircle{group = main.current.effects, x = x, y = y}:scale_down()
self.dead = true self.dead = true
if self.wizard then 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} 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
end end
@ -254,7 +329,24 @@ function Projectile:on_collision_enter(other, contact)
else r = 0 end else r = 0 end
if other:is(Wall) then 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
end end
@ -274,11 +366,19 @@ function Projectile:on_trigger_enter(other, contact)
if object then if object then
self.r = self:angle_to_object(object) self.r = self:angle_to_object(object)
self.v = self.v*1.25 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
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 end
other:hit(self.dmg) other:hit(self.dmg)
end 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} 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 = self.color} end
for i = 1, 2 do HitParticle{group = main.current.effects, x = enemy.x, y = enemy.y, color = enemy.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 end
self.color = fg[0] self.color = fg[0]

View File

@ -7,7 +7,6 @@ function shared_init()
fg_alt = ColorRamp(Color'#b0a89f', 0.025), fg_alt = ColorRamp(Color'#b0a89f', 0.025),
yellow = ColorRamp(Color'#facf00', 0.025), yellow = ColorRamp(Color'#facf00', 0.025),
orange = ColorRamp(Color'#f07021', 0.025), orange = ColorRamp(Color'#f07021', 0.025),
yellow_orange = ColorRamp(Color(245, 160, 16), 0.025),
blue = ColorRamp(Color'#019bd6', 0.025), blue = ColorRamp(Color'#019bd6', 0.025),
green = ColorRamp(Color'#8bbf40', 0.025), green = ColorRamp(Color'#8bbf40', 0.025),
red = ColorRamp(Color'#e91d39', 0.025), red = ColorRamp(Color'#e91d39', 0.025),
@ -663,3 +662,24 @@ end
function Wall:draw() function Wall:draw()
self.shape:draw(self.color) self.shape:draw(self.color)
end 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

7
todo
View File

@ -1,9 +1,10 @@
Vagrant: shoots an ethereal projectile at any nearby enemy that deals physical and magical damage, medium range 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 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 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 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 Engineer: drops a turret that shoots secondary projectiles very fast, medium range
Ranger: yellow, buff attack speed 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 * Stats: attack speed, damage, area
* One or a few of the characters * One or a few of the characters
* Port over enemy spawn logic from SHOOTRX * Port over enemy spawn logic from SHOOTRX
Sounds * Sounds