Day 8-9
parent
57e7b3cab7
commit
7829584241
13
arena.lua
13
arena.lua
|
@ -50,13 +50,16 @@ 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 = 'stormweaver'}
|
||||
self.player:add_follower(Player{group = self.main, character = 'vagrant'})
|
||||
self.player:add_follower(Player{group = self.main, character = 'archer'})
|
||||
self.player:add_follower(Player{group = self.main, character = 'wizard'})
|
||||
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 = 'engineer'}
|
||||
--[[
|
||||
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'})
|
||||
self.player:add_follower(Player{group = self.main, character = 'scout'})
|
||||
self.player:add_follower(Player{group = self.main, character = 'swordsman'})
|
||||
self.player:add_follower(Player{group = self.main, character = 'scout'})
|
||||
self.player:add_follower(Player{group = self.main, character = 'wizard'})
|
||||
|
|
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.
13
devlog.md
13
devlog.md
|
@ -201,6 +201,19 @@ I've implemented up to Elementor today and ATM in the process of doing Ninja, bu
|
|||
Not a lot done today... My sleep schedule is fucked up and I've been unable to focus properly. I managed to get 2 characters done though and also changed their definitions a bit:
|
||||
|
||||
Ninja -> Saboteur: calls on other saboteurs to seek targets and explode on contact, AoE has small range
|
||||
|
||||
Ninja -> Saboteur: rogue, conjurer, nuker
|
||||
|
||||
Linker -> Stormweaver: infuses all projetile attacks with chain lightning, medium range
|
||||
|
||||
Linker -> Stormweaver: enchanter, ~~nuker~~
|
||||
|
||||
# Day 8-9 - 24-25/02/21
|
||||
|
||||
Finished all characters finally. My sleep is so fucked these two days blended together seamlessly. It's so fucking hot and I'm so tired. God damn I fucking hate the summer so fucking much. I hope I can sleep properly today.
|
||||
Definition changes for one character: Spellblade - knives slowly spiral outwards.
|
||||
|
||||
Tomorrow I'll probably do some UI work so the player can buy new characters as he goes from arena to arena, or work on the game's progression in terms of enemy HP and DMG. These are fundamentally the only two things missing
|
||||
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.
|
||||
|
|
|
@ -31,7 +31,7 @@ function Seeker:update(dt)
|
|||
local player = main.current.player
|
||||
self:seek_point(player.x, player.y)
|
||||
self:wander(50, 100, 20)
|
||||
self:separate(16, main.current.enemies)
|
||||
self:steering_separate(16, main.current.enemies)
|
||||
self:rotate_towards_velocity(0.5)
|
||||
end
|
||||
self.r = self:get_angle()
|
||||
|
@ -61,6 +61,11 @@ function Seeker:on_collision_enter(other, contact)
|
|||
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
|
||||
|
||||
elseif other:is(Turret) then
|
||||
_G[random:table{'player_hit1', 'player_hit2'}]:play{pitch = random:float(0.95, 1.05), volume = 0.35}
|
||||
self:hit(0)
|
||||
self:push(random:float(2.5, 7), other:angle_to_object(self))
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -8,6 +8,9 @@ function Physics:set_as_steerable(max_v, max_f, max_turn_rate, turn_multiplier)
|
|||
self.steering_enabled = true
|
||||
self.heading = Vector()
|
||||
self.side = Vector()
|
||||
self.steering_force = Vector()
|
||||
self.applied_force = Vector()
|
||||
self.applied_impulse = Vector()
|
||||
self.mass = 1
|
||||
self.max_v = max_v or 100
|
||||
self.max_f = max_f or 2000
|
||||
|
@ -32,29 +35,33 @@ end
|
|||
function Physics:steering_update(dt)
|
||||
if self.steerable and self.steering_enabled then
|
||||
local steering_force = self:calculate_steering_force(dt):div(self.mass)
|
||||
self:apply_force(steering_force.x, steering_force.y)
|
||||
local applied_force = self:calculate_applied_force(dt):div(self.mass)
|
||||
local applied_impulse = self:calculate_applied_impulse(dt):div(self.mass)
|
||||
self:apply_force(steering_force.x + applied_force.x, steering_force.y + applied_force.y)
|
||||
local vx, vy = self:get_velocity()
|
||||
local v = Vector(vx, vy):truncate(self.max_v)
|
||||
self:set_velocity(v.x, v.y)
|
||||
self:set_velocity(v.x + applied_impulse.x, v.y + applied_impulse.y)
|
||||
if v:length_squared() > 0.00001 then
|
||||
self.heading = v:clone():normalize()
|
||||
self.side = self.heading:perpendicular()
|
||||
end
|
||||
self.apply_force_f:set(0, 0)
|
||||
self.apply_impulse_f:set(0, 0)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
function Physics:calculate_steering_force(dt)
|
||||
local steering_force = Vector(0, 0)
|
||||
if self.seeking then steering_force:add(self.seek_f) end
|
||||
if self.fleeing then steering_force:add(self.flee_f) end
|
||||
if self.pursuing then steering_force:add(self.pursuit_f) end
|
||||
if self.evading then steering_force:add(self.evade_f) end
|
||||
if self.wandering then steering_force:add(self.wander_f) end
|
||||
if self.path_following then steering_force:add(self.path_follow_f) end
|
||||
if self.separating then steering_force:add(self.separation_f) end
|
||||
if self.aligning then steering_force:add(self.alignment_f) end
|
||||
if self.cohesing then steering_force:add(self.cohesion_f) end
|
||||
self.steering_force:set(0, 0)
|
||||
if self.seeking then self.steering_force:add(self.seek_f) end
|
||||
if self.fleeing then self.steering_force:add(self.flee_f) end
|
||||
if self.pursuing then self.steering_force:add(self.pursuit_f) end
|
||||
if self.evading then self.steering_force:add(self.evade_f) end
|
||||
if self.wandering then self.steering_force:add(self.wander_f) end
|
||||
if self.path_following then self.steering_force:add(self.path_follow_f) end
|
||||
if self.separating then self.steering_force:add(self.separation_f) end
|
||||
if self.aligning then self.steering_force:add(self.alignment_f) end
|
||||
if self.cohesing then self.steering_force:add(self.cohesion_f) end
|
||||
self.seeking = false
|
||||
self.fleeing = false
|
||||
self.pursuing = false
|
||||
|
@ -64,7 +71,55 @@ function Physics:calculate_steering_force(dt)
|
|||
self.separating = false
|
||||
self.aligning = false
|
||||
self.cohesing = false
|
||||
return steering_force:truncate(self.max_f)
|
||||
return self.steering_force:truncate(self.max_f)
|
||||
end
|
||||
|
||||
|
||||
function Physics:calculate_applied_force(dt)
|
||||
self.applied_force:set(0, 0)
|
||||
if self.applying_force then self.applied_force:add(self.apply_force_f) end
|
||||
return self.applied_force
|
||||
end
|
||||
|
||||
|
||||
function Physics:calculate_applied_impulse(dt)
|
||||
self.applied_impulse:set(0, 0)
|
||||
if self.applying_impulse then self.applied_impulse:add(self.apply_impulse_f) end
|
||||
return self.applied_impulse
|
||||
end
|
||||
|
||||
|
||||
-- Applies force f to the object at the given angle r for duration s
|
||||
-- This plays along with steering behaviors, whereas the apply_force function simply applies it directly to the body and doesn't work when steering behaviors are enabled
|
||||
-- self:apply_steering_force(100, math.pi/4)
|
||||
function Physics:apply_steering_force(f, r, s)
|
||||
self.applying_force = true
|
||||
self.apply_force_f:set(f*math.cos(r), f*math.sin(r))
|
||||
if s then
|
||||
self.t:after((s or 0.01)/2, function()
|
||||
self.t:tween((s or 0.01)/2, self.apply_force_f, {x = 0, y = 0}, math.linear, function()
|
||||
self.applying_force = false
|
||||
self.apply_force_f:set(0, 0)
|
||||
end, 'apply_steering_force_2')
|
||||
end, 'apply_steering_force_1')
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
-- Applies impulse f to the object at the given angle r for duration s
|
||||
-- This plays along with steering behaviors, whereas the apply_impulse function simply applies it directly to the body and doesn't work when steering behaviors are enabled
|
||||
-- self:apply_steering_impulse(100, math.pi/4, 0.5)
|
||||
function Physics:apply_steering_impulse(f, r, s)
|
||||
self.applying_impulse = true
|
||||
self.apply_impulse_f:set(f*math.cos(r), f*math.sin(r))
|
||||
if s then
|
||||
self.t:after((s or 0.01)/2, function()
|
||||
self.t:tween((s or 0.01)/2, self.apply_impulse_f, {x = 0, y = 0}, math.linear, function()
|
||||
self.applying_impulse = false
|
||||
self.apply_impulse_f:set(0, 0)
|
||||
end, 'apply_steering_impulse_2')
|
||||
end, 'apply_steering_impulse_1')
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
|
|
@ -31,6 +31,20 @@ function Trigger:after(delay, action, tag)
|
|||
end
|
||||
|
||||
|
||||
-- Calls the action every delay seconds if the condition is true.
|
||||
-- If the condition isn't true when delay seconds are up then it waits and only performs the action and resets the timer when that happens.
|
||||
-- If times is passed in then it only calls action for that amount of times.
|
||||
-- If after is passed in then it is called after the last time action is called.
|
||||
-- If tag is passed in then any other trigger actions with the same tag are automatically cancelled.
|
||||
-- trigger:cooldown(2, function() return #self:get_objects_in_shape(self.attack_sensor, enemies) > 0 end, function() self:attack() end) -> only attacks when 2 seconds have passed and there are more than 0 enemies around
|
||||
function Trigger:cooldown(delay, condition, action, times, after, tag)
|
||||
local times = times or 0
|
||||
local after = after or function() end
|
||||
local tag = tag or random:uid()
|
||||
self.triggers[tag] = {type = "cooldown", timer = 0, unresolved_delay = delay, delay = self:resolve_delay(delay), condition = condition, action = action, times = times, max_times = times, after = after, multiplier = 1}
|
||||
end
|
||||
|
||||
|
||||
-- Calls the action every delay seconds.
|
||||
-- Or calls the action once every time the condition becomes true.
|
||||
-- If times is passed in then it only calls action for that amount of times.
|
||||
|
@ -162,6 +176,20 @@ function Trigger:update(dt)
|
|||
if trigger.type == "run" then
|
||||
trigger.action()
|
||||
|
||||
elseif trigger.type == "cooldown" then
|
||||
if trigger.timer > trigger.delay*trigger.multiplier and trigger.condition() then
|
||||
trigger.action()
|
||||
trigger.timer = 0
|
||||
trigger.delay = self:resolve_delay(trigger.unresolved_delay)
|
||||
if trigger.times > 0 then
|
||||
trigger.times = trigger.times - 1
|
||||
if trigger.times <= 0 then
|
||||
trigger.after()
|
||||
self.triggers[tag] = nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
elseif trigger.type == "after" then
|
||||
if trigger.timer > trigger.delay then
|
||||
trigger.action()
|
||||
|
|
|
@ -148,6 +148,14 @@ function graphics.circle(x, y, r, color, line_width)
|
|||
end
|
||||
|
||||
|
||||
-- Draws an arc of radius r from angle r1 to angle r2 centered on x, y.
|
||||
-- If color is passed in then the arc will be filled with that color (color is Color object)
|
||||
-- If line_width is passed in then the arc will not be filled and will instead be drawn as a set of lines of the given width.
|
||||
function graphics.arc(arctype, x, y, r, r1, r2, color, line_width)
|
||||
graphics.shape("arc", color, line_width, arctype, x, y, r, r1, r2)
|
||||
end
|
||||
|
||||
|
||||
-- Draws a polygon with the given points.
|
||||
-- If color is passed in then the polygon will be filled with that color (color is Color object)
|
||||
-- If line_width is passed in then the polygon will not be filled and will instead be drawn as a set of lines of the given width.
|
||||
|
|
9
main.lua
9
main.lua
|
@ -52,6 +52,15 @@ function init()
|
|||
spark2 = Sound('Spark 2.ogg', s)
|
||||
spark3 = Sound('Spark 3.ogg', s)
|
||||
stormweaver1 = Sound('Buff 8.ogg', s)
|
||||
cannoneer1 = Sound('Cannon shots 1.ogg', s)
|
||||
cannoneer2 = Sound('Cannon shots 7.ogg', s)
|
||||
cannon_hit_wall1 = Sound('Cannon impact sounds (Hitting ship) 4.ogg', s)
|
||||
pet1 = Sound('Wolf barks 5.ogg', s)
|
||||
turret1 = Sound('Sci Fi Machine Gun 7.ogg', s)
|
||||
turret2 = Sound('Sniper Shot_09.ogg', s)
|
||||
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)
|
||||
|
||||
main = Main()
|
||||
main:add(Arena'arena')
|
||||
|
|
103
objects.lua
103
objects.lua
|
@ -135,8 +135,7 @@ 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}
|
||||
self.infused_bar = InfusedBar{group = main.current.effects, parent = self}
|
||||
self.effect_bar = EffectBar{group = main.current.effects, parent = self}
|
||||
end
|
||||
|
||||
|
||||
|
@ -156,19 +155,36 @@ end
|
|||
|
||||
function Unit:show_hp(n)
|
||||
self.hp_bar.hidden = false
|
||||
self.hp_bar.color = red[0]
|
||||
self.t:after(n or 2, function() self.hp_bar.hidden = true end, 'hp_bar')
|
||||
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')
|
||||
self.effect_bar.hidden = false
|
||||
self.effect_bar.color = green[0]
|
||||
self.t:after(n or 4, function() self.effect_bar.hidden = true end, 'effect_bar')
|
||||
end
|
||||
|
||||
|
||||
function Unit:show_infused(n)
|
||||
self.infused_bar.hidden = false
|
||||
self.t:after(n, function() self.infused_bar.hidden = true end, 'infused_bar')
|
||||
self.effect_bar.hidden = false
|
||||
self.effect_bar.color = blue[0]
|
||||
self.t:after(n or 4, function() self.effect_bar.hidden = true end, 'effect_bar')
|
||||
end
|
||||
|
||||
|
||||
function Unit:show_squire(n)
|
||||
self.effect_bar.hidden = false
|
||||
self.effect_bar.color = purple[0]
|
||||
self.t:after(n or 4, function() self.effect_bar.hidden = false end, 'effect_bar')
|
||||
end
|
||||
|
||||
|
||||
function Unit:show_chronomancer(n)
|
||||
self.effect_bar.hidden = false
|
||||
self.effect_bar.color = purple[0]
|
||||
self.t:after(n or 2, function() self.effect_bar.hidden = false end, 'effect_bar')
|
||||
end
|
||||
|
||||
|
||||
|
@ -198,17 +214,19 @@ function Unit:calculate_stats(first_run)
|
|||
self.class_area_size_m = 1
|
||||
self.class_def_m = 1
|
||||
self.class_mvspd_m = 1
|
||||
self.buff_hp_a = 0
|
||||
self.buff_dmg_a = 0
|
||||
self.buff_def_a = 0
|
||||
self.buff_mvspd_a = 0
|
||||
self.buff_hp_m = 1
|
||||
self.buff_dmg_m = 1
|
||||
self.buff_aspd_m = 1
|
||||
self.buff_area_dmg_m = 1
|
||||
self.buff_area_size_m = 1
|
||||
self.buff_def_m = 1
|
||||
self.buff_mvspd_m = 1
|
||||
if first_run then
|
||||
self.buff_hp_a = 0
|
||||
self.buff_dmg_a = 0
|
||||
self.buff_def_a = 0
|
||||
self.buff_mvspd_a = 0
|
||||
self.buff_hp_m = 1
|
||||
self.buff_dmg_m = 1
|
||||
self.buff_aspd_m = 1
|
||||
self.buff_area_dmg_m = 1
|
||||
self.buff_area_size_m = 1
|
||||
self.buff_def_m = 1
|
||||
self.buff_mvspd_m = 1
|
||||
end
|
||||
|
||||
for _, class in ipairs(self.classes) do
|
||||
if class == 'warrior' then self.class_hp_m = self.class_hp_m*1.4
|
||||
|
@ -280,59 +298,30 @@ end
|
|||
|
||||
|
||||
|
||||
InfusedBar = Object:extend()
|
||||
InfusedBar:implement(GameObject)
|
||||
InfusedBar:implement(Parent)
|
||||
function InfusedBar:init(args)
|
||||
EffectBar = Object:extend()
|
||||
EffectBar:implement(GameObject)
|
||||
EffectBar:implement(Parent)
|
||||
function EffectBar:init(args)
|
||||
self:init_game_object(args)
|
||||
self.hidden = true
|
||||
self.color = blue[0]
|
||||
self.color_transparent = Color(self.color.r, self.color.g, self.color.b, 0.2)
|
||||
self.color = fg[0]
|
||||
end
|
||||
|
||||
|
||||
function InfusedBar:update(dt)
|
||||
function EffectBar:update(dt)
|
||||
self:update_game_object(dt)
|
||||
self:follow_parent_exclusively()
|
||||
end
|
||||
|
||||
|
||||
function InfusedBar:draw()
|
||||
function EffectBar: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
|
||||
|
||||
|
||||
|
||||
|
||||
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.push(p.x, p.y, p.r, p.hfx.hit.x, p.hfx.hit.x)
|
||||
graphics.rectangle(p.x, p.y, 3, 3, 1, 1, self.color)
|
||||
graphics.pop()
|
||||
]]--
|
||||
end
|
||||
|
||||
|
||||
|
|
477
player.lua
477
player.lua
|
@ -13,7 +13,7 @@ function Player:init(args)
|
|||
self.classes = {'ranger', 'warrior', 'psy'}
|
||||
|
||||
self.attack_sensor = Circle(self.x, self.y, 96)
|
||||
self.t:every(2, function()
|
||||
self.t:cooldown(2, function() local enemies = self:get_objects_in_shape(self.attack_sensor, main.current.enemies); return enemies and #enemies > 0 end, function()
|
||||
local closest_enemy = self:get_closest_object_in_shape(self.attack_sensor, main.current.enemies)
|
||||
if closest_enemy then
|
||||
self:shoot(self:angle_to_object(closest_enemy))
|
||||
|
@ -27,11 +27,8 @@ function Player:init(args)
|
|||
self.classes = {'warrior'}
|
||||
|
||||
self.attack_sensor = Circle(self.x, self.y, 64)
|
||||
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(96)
|
||||
end
|
||||
self.t:cooldown(3, function() local enemies = self:get_objects_in_shape(self.attack_sensor, main.current.enemies); return enemies and #enemies > 0 end, function()
|
||||
self:attack(96)
|
||||
end, nil, nil, 'attack')
|
||||
|
||||
elseif self.character == 'wizard' then
|
||||
|
@ -41,10 +38,10 @@ function Player:init(args)
|
|||
self.classes = {'mage'}
|
||||
|
||||
self.attack_sensor = Circle(self.x, self.y, 128)
|
||||
self.t:every(2, function()
|
||||
self.t:cooldown(2, function() local enemies = self:get_objects_in_shape(self.attack_sensor, main.current.enemies); return enemies and #enemies > 0 end, function()
|
||||
local closest_enemy = self:get_closest_object_in_shape(self.attack_sensor, main.current.enemies)
|
||||
if closest_enemy then
|
||||
self:shoot(self:angle_to_object(closest_enemy), {wizard = self})
|
||||
self:shoot(self:angle_to_object(closest_enemy))
|
||||
end
|
||||
end, nil, nil, 'shoot')
|
||||
|
||||
|
@ -55,7 +52,7 @@ function Player:init(args)
|
|||
self.classes = {'ranger'}
|
||||
|
||||
self.attack_sensor = Circle(self.x, self.y, 160)
|
||||
self.t:every(2, function()
|
||||
self.t:cooldown(2, function() local enemies = self:get_objects_in_shape(self.attack_sensor, main.current.enemies); return enemies and #enemies > 0 end, function()
|
||||
local closest_enemy = self:get_closest_object_in_shape(self.attack_sensor, main.current.enemies)
|
||||
if closest_enemy then
|
||||
self:shoot(self:angle_to_object(closest_enemy), {pierce = 1000})
|
||||
|
@ -69,7 +66,7 @@ function Player:init(args)
|
|||
self.classes = {'rogue'}
|
||||
|
||||
self.attack_sensor = Circle(self.x, self.y, 64)
|
||||
self.t:every(2, function()
|
||||
self.t:cooldown(2, function() local enemies = self:get_objects_in_shape(self.attack_sensor, main.current.enemies); return enemies and #enemies > 0 end, function()
|
||||
local closest_enemy = self:get_closest_object_in_shape(self.attack_sensor, main.current.enemies)
|
||||
if closest_enemy then
|
||||
self:shoot(self:angle_to_object(closest_enemy), {chain = 3})
|
||||
|
@ -102,7 +99,7 @@ function Player:init(args)
|
|||
self.classes = {'warrior', 'rogue'}
|
||||
|
||||
self.attack_sensor = Circle(self.x, self.y, 96)
|
||||
self.t:every(3, function()
|
||||
self.t:cooldown(3, function() local enemies = self:get_objects_in_shape(self.attack_sensor, main.current.enemies); return enemies and #enemies > 0 end, function()
|
||||
local closest_enemy = self:get_closest_object_in_shape(self.attack_sensor, main.current.enemies)
|
||||
if closest_enemy then
|
||||
self:shoot(self:angle_to_object(closest_enemy))
|
||||
|
@ -116,11 +113,8 @@ function Player:init(args)
|
|||
self.classes = {'warrior', 'rogue'}
|
||||
|
||||
self.attack_sensor = Circle(self.x, self.y, 64)
|
||||
self.t:every(4, function()
|
||||
local enemies = self:get_objects_in_shape(self.attack_sensor, main.current.enemies)
|
||||
if enemies and #enemies > 0 then
|
||||
self:shoot()
|
||||
end
|
||||
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()
|
||||
self:shoot()
|
||||
end, nil, nil, 'shoot')
|
||||
|
||||
elseif self.character == 'elementor' then
|
||||
|
@ -130,7 +124,7 @@ function Player:init(args)
|
|||
self.classes = {'mage', 'nuker'}
|
||||
|
||||
self.attack_sensor = Circle(self.x, self.y, 128)
|
||||
self.t:every(12, function()
|
||||
self.t:cooldown(12, function() local enemies = self:get_objects_in_shape(self.attack_sensor, main.current.enemies); return enemies and #enemies > 0 end, function()
|
||||
local enemy = self:get_random_object_in_shape(self.attack_sensor, main.current.enemies)
|
||||
if enemy then
|
||||
self:attack(128, {x = enemy.x, y = enemy.y})
|
||||
|
@ -157,7 +151,6 @@ function Player:init(args)
|
|||
self.visual_shape = 'rectangle'
|
||||
self.classes = {'enchanter'}
|
||||
|
||||
self.attack_sensor = Circle(self.x, self.y, 96)
|
||||
self.t:every(8, function()
|
||||
stormweaver1:play{pitch = random:float(0.95, 1.05), volume = 0.5}
|
||||
local followers
|
||||
|
@ -169,6 +162,142 @@ function Player:init(args)
|
|||
end
|
||||
end
|
||||
end)
|
||||
|
||||
elseif self.character == 'sage' then
|
||||
self.color = purple[0]
|
||||
self:set_as_rectangle(9, 9, 'dynamic', 'player')
|
||||
self.visual_shape = 'rectangle'
|
||||
self.classes = {'mage', 'nuker'}
|
||||
|
||||
self.attack_sensor = Circle(self.x, self.y, 96)
|
||||
self.t:cooldown(12, function() local enemies = self:get_objects_in_shape(self.attack_sensor, main.current.enemies); return enemies and #enemies > 0 end, function()
|
||||
local closest_enemy = self:get_closest_object_in_shape(self.attack_sensor, main.current.enemies)
|
||||
if closest_enemy then
|
||||
self:shoot(self:angle_to_object(closest_enemy))
|
||||
end
|
||||
end)
|
||||
|
||||
elseif self.character == 'squire' then
|
||||
self.color = green[0]
|
||||
self:set_as_rectangle(9, 9, 'dynamic', 'player')
|
||||
self.visual_shape = 'rectangle'
|
||||
self.classes = {'warrior', 'healer', 'enchanter'}
|
||||
|
||||
self.t:every(8, function()
|
||||
self.applying_buff = true
|
||||
local followers
|
||||
local leader = (self.leader and self) or self.parent
|
||||
if self.leader then followers = self.followers else followers = self.parent.followers end
|
||||
local next_character = followers[self.follower_index + 1]
|
||||
local previous_character = followers[self.follower_index - 1]
|
||||
if next_character then next_character:squire_buff(8) end
|
||||
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
|
||||
end)
|
||||
|
||||
elseif self.character == 'cannoneer' then
|
||||
self.color = yellow[0]
|
||||
self:set_as_rectangle(9, 9, 'dynamic', 'player')
|
||||
self.visual_shape = 'rectangle'
|
||||
self.classes = {'ranger', 'nuker'}
|
||||
|
||||
self.attack_sensor = Circle(self.x, self.y, 128)
|
||||
self.t:cooldown(6, function() local enemies = self:get_objects_in_shape(self.attack_sensor, main.current.enemies); return enemies and #enemies > 0 end, function()
|
||||
local closest_enemy = self:get_closest_object_in_shape(self.attack_sensor, main.current.enemies)
|
||||
if closest_enemy then
|
||||
self:shoot(self:angle_to_object(closest_enemy))
|
||||
end
|
||||
end, nil, nil, 'shoot')
|
||||
|
||||
elseif self.character == 'dual_gunner' then
|
||||
self.color = yellow[0]
|
||||
self:set_as_rectangle(9, 9, 'dynamic', 'player')
|
||||
self.visual_shape = 'rectangle'
|
||||
self.classes = {'ranger', 'rogue'}
|
||||
|
||||
self.attack_sensor = Circle(self.x, self.y, 96)
|
||||
self.t:cooldown(2, function() local enemies = self:get_objects_in_shape(self.attack_sensor, main.current.enemies); return enemies and #enemies > 0 end, function()
|
||||
local closest_enemy = self:get_closest_object_in_shape(self.attack_sensor, main.current.enemies)
|
||||
if closest_enemy then
|
||||
self:shoot(self:angle_to_object(closest_enemy))
|
||||
end
|
||||
end, nil, nil, 'shoot')
|
||||
|
||||
elseif self.character == 'hunter' then
|
||||
self.color = orange[0]
|
||||
self:set_as_rectangle(9, 9, 'dynamic', 'player')
|
||||
self.visual_shape = 'rectangle'
|
||||
self.classes = {'ranger', 'rogue'}
|
||||
|
||||
self.attack_sensor = Circle(self.x, self.y, 160)
|
||||
self.t:cooldown(2, function() local enemies = self:get_objects_in_shape(self.attack_sensor, main.current.enemies); return enemies and #enemies > 0 end, function()
|
||||
local closest_enemy = self:get_closest_object_in_shape(self.attack_sensor, main.current.enemies)
|
||||
if closest_enemy then
|
||||
self:shoot(self:angle_to_object(closest_enemy))
|
||||
end
|
||||
end, nil, nil, 'shoot')
|
||||
|
||||
elseif self.character == 'chronomancer' then
|
||||
self.color = purple[0]
|
||||
self:set_as_rectangle(9, 9, 'dynamic', 'player')
|
||||
self.visual_shape = 'rectangle'
|
||||
self.classes = {'mage', 'enchanter'}
|
||||
|
||||
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
|
||||
local next_character = followers[self.follower_index + 1]
|
||||
local previous_character = followers[self.follower_index - 1]
|
||||
if next_character then next_character:chronomancer_buff(2) end
|
||||
if previous_character then previous_character:chronomancer_buff(2) end
|
||||
end)
|
||||
|
||||
elseif self.character == 'spellblade' then
|
||||
self.color = blue[0]
|
||||
self:set_as_rectangle(9, 9, 'dynamic', 'player')
|
||||
self.visual_shape = 'rectangle'
|
||||
self.classes = {'mage', 'rogue'}
|
||||
|
||||
self.t:every(2, function()
|
||||
self:shoot(random:float(0, 2*math.pi))
|
||||
end, nil, nil, 'shoot')
|
||||
|
||||
elseif self.character == 'psykeeper' then
|
||||
self.color = fg[0]
|
||||
self:set_as_rectangle(9, 9, 'dynamic', 'player')
|
||||
self.visual_shape = 'rectangle'
|
||||
self.classes = {'healer', 'psy'}
|
||||
|
||||
self.psykeeper_heal = 0
|
||||
self.t:every(8, 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 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
|
||||
heal1:play{pitch = random:float(0.95, 1.05), volume = 0.5}
|
||||
self.psykeeper_heal = 0
|
||||
end
|
||||
end)
|
||||
|
||||
elseif self.character == 'engineer' then
|
||||
self.color = orange[0]
|
||||
self:set_as_rectangle(9, 9, 'dynamic', 'player')
|
||||
self.visual_shape = 'rectangle'
|
||||
self.classes = {'conjurer'}
|
||||
|
||||
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}
|
||||
end)
|
||||
end
|
||||
self:calculate_stats(true)
|
||||
|
||||
|
@ -184,13 +313,55 @@ 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
|
||||
local followers
|
||||
local leader = (self.leader and self) or self.parent
|
||||
if self.leader then followers = self.followers else followers = self.parent.followers end
|
||||
local next_character = followers[self.follower_index + 1]
|
||||
local previous_character = followers[self.follower_index - 1]
|
||||
if self.applying_buff then
|
||||
if next_character then
|
||||
next_character.squire_dmg_a = 10
|
||||
next_character.squire_def_a = 25
|
||||
end
|
||||
if previous_character then
|
||||
previous_character.squire_dmg_a = 10
|
||||
previous_character.squire_def_a = 25
|
||||
end
|
||||
else
|
||||
if next_character then
|
||||
next_character.squire_dmg_a = 0
|
||||
next_character.squire_def_a = 0
|
||||
end
|
||||
if previous_character then
|
||||
previous_character.squire_dmg_a = 0
|
||||
previous_character.squire_def_a = 0
|
||||
end
|
||||
end
|
||||
|
||||
elseif self.character == 'chronomancer' then
|
||||
local followers
|
||||
local leader = (self.leader and self) or self.parent
|
||||
if self.leader then followers = self.followers else followers = self.parent.followers end
|
||||
local next_character = followers[self.follower_index + 1]
|
||||
local previous_character = followers[self.follower_index - 1]
|
||||
if next_character then next_character.chronomancer_aspd_m = 1.25 end
|
||||
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
|
||||
self:calculate_stats()
|
||||
|
||||
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)
|
||||
|
||||
|
||||
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
|
||||
|
@ -210,7 +381,7 @@ function Player:update(dt)
|
|||
self:set_angle(self.r)
|
||||
|
||||
else
|
||||
local target_distance = 10.6*self.follower_index
|
||||
local target_distance = 10.4*self.follower_index
|
||||
local distance_sum = 0
|
||||
local p
|
||||
local previous = self.parent
|
||||
|
@ -246,7 +417,6 @@ function Player:draw()
|
|||
graphics.rectangle(self.x, self.y, self.shape.w, self.shape.h, 3, 3, (self.hfx.hit.f or self.hfx.shoot.f) and fg[0] or self.color)
|
||||
end
|
||||
graphics.pop()
|
||||
-- self.attack_sensor:draw(self.color, 2)
|
||||
end
|
||||
|
||||
|
||||
|
@ -292,6 +462,8 @@ function Player:hit(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.character == 'psykeeper' then self.psykeeper_heal = self.psykeeper_heal + actual_damage end
|
||||
|
||||
if self.hp <= 0 then
|
||||
slow(0.25, 1)
|
||||
self.dead = true
|
||||
|
@ -319,6 +491,16 @@ function Player:chain_infuse(duration)
|
|||
end
|
||||
|
||||
|
||||
function Player:squire_buff(duration)
|
||||
self:show_squire(duration or 2)
|
||||
end
|
||||
|
||||
|
||||
function Player:chronomancer_buff(duration)
|
||||
self:show_chronomancer(duration or 2)
|
||||
end
|
||||
|
||||
|
||||
function Player:add_follower(unit)
|
||||
table.insert(self.followers, unit)
|
||||
unit.parent = self
|
||||
|
@ -338,6 +520,7 @@ function Player:shoot(r, mods)
|
|||
Projectile(table.merge(t, mods or {}))
|
||||
r = r + math.pi/8
|
||||
end
|
||||
|
||||
elseif self.character == 'blade' then
|
||||
local enemies = self:get_objects_in_shape(self.attack_sensor, main.current.enemies)
|
||||
if enemies and #enemies > 0 then
|
||||
|
@ -348,20 +531,41 @@ function Player:shoot(r, mods)
|
|||
Projectile(table.merge(t, mods or {}))
|
||||
end
|
||||
end
|
||||
|
||||
elseif self.character == 'sage' 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}
|
||||
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 = 25, r = r, color = self.color, dmg = self.dmg, pierce = 1000, character = 'sage', parent = self}
|
||||
Projectile(table.merge(t, mods or {}))
|
||||
|
||||
elseif self.character == 'dual_gunner' then
|
||||
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}
|
||||
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}
|
||||
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}
|
||||
Projectile(table.merge(t, mods or {}))
|
||||
end
|
||||
|
||||
if self.character == 'vagrant' then
|
||||
if self.character == 'vagrant' or self.character == 'dual_gunner' then
|
||||
shoot1:play{pitch = random:float(0.95, 1.05), volume = 0.3}
|
||||
elseif self.character == 'archer' then
|
||||
elseif self.character == 'archer' or self.character == 'hunter' 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' or self.character == 'outlaw' or self.character == 'blade' then
|
||||
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}
|
||||
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
|
||||
end
|
||||
|
||||
|
@ -393,21 +597,71 @@ function Projectile:init(args)
|
|||
self.chain = args.chain or 0
|
||||
self.chain_enemies_hit = {}
|
||||
self.infused_enemies_hit = {}
|
||||
|
||||
if self.character == 'sage' then
|
||||
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)
|
||||
end)
|
||||
|
||||
self.color_transparent = Color(args.color.r, args.color.g, args.color.b, 0.08)
|
||||
self.t:every(0.08, function()
|
||||
HitParticle{group = main.current.effects, x = self.x, y = self.y, color = self.color}
|
||||
end)
|
||||
self.vr = 0
|
||||
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)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
function Projectile:update(dt)
|
||||
self:update_game_object(dt)
|
||||
|
||||
if self.character == 'spellblade' then
|
||||
self.orbit_r = self.orbit_r + self.orbit_vr*dt
|
||||
end
|
||||
|
||||
self:set_angle(self.r)
|
||||
self:move_along_angle(self.v, self.r)
|
||||
self:move_along_angle(self.v, self.r + (self.orbit_r or 0))
|
||||
|
||||
if self.character == 'sage' then
|
||||
self.pull_sensor:move_to(self.x, self.y)
|
||||
local enemies = self:get_objects_in_shape(self.pull_sensor, main.current.enemies)
|
||||
for _, enemy in ipairs(enemies) do
|
||||
enemy:apply_steering_force(math.remap(self:distance_to_object(enemy), 0, 100, 250, 50), enemy:angle_to_object(self))
|
||||
end
|
||||
self.vr = self.vr + self.dvr*dt
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
function Projectile:draw()
|
||||
graphics.push(self.x, self.y, self.r)
|
||||
graphics.rectangle(self.x, self.y, self.shape.w, self.shape.h, 2, 2, self.color)
|
||||
graphics.pop()
|
||||
if self.character == 'sage' then
|
||||
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.rs + random:float(-1, 1), self.color)
|
||||
graphics.circle(self.x, self.y, self.pull_sensor.rs, self.color_transparent)
|
||||
local lw = math.remap(self.pull_sensor.rs, 32, 256, 2, 4)
|
||||
for i = 1, 4 do graphics.arc('open', self.x, self.y, self.pull_sensor.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()
|
||||
|
||||
else
|
||||
graphics.push(self.x, self.y, self.r + (self.orbit_r or 0))
|
||||
graphics.rectangle(self.x, self.y, self.shape.w, self.shape.h, 2, 2, self.color)
|
||||
graphics.pop()
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
@ -424,6 +678,8 @@ 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*32, color = self.color, dmg = self.parent.area_dmg_m*self.dmg, character = self.character}
|
||||
elseif self.character == 'blade' then
|
||||
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}
|
||||
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 = self.parent.area_dmg_m*self.dmg, character = self.character}
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -438,20 +694,29 @@ function Projectile:on_collision_enter(other, contact)
|
|||
else r = 0 end
|
||||
|
||||
if other:is(Wall) then
|
||||
if self.character == 'archer' then
|
||||
if self.character == 'archer' or self.character == 'hunter' 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' or self.character == 'outlaw' or self.character == 'blade' then
|
||||
elseif self.character == 'scout' or self.character == 'outlaw' or self.character == 'blade' or self.character == 'spellblade' 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)
|
||||
if self.character == 'spellblade' then
|
||||
magic_area1:play{pitch = random:float(0.95, 1.05), volume = 0.075}
|
||||
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}
|
||||
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
|
||||
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
|
||||
self:die(x, y, r, random:int(2, 3))
|
||||
proj_hit_wall1:play{pitch = random:float(0.9, 1.1), volume = 0.2}
|
||||
|
@ -461,6 +726,8 @@ end
|
|||
|
||||
|
||||
function Projectile:on_trigger_enter(other, contact)
|
||||
if self.character == 'sage' then return end
|
||||
|
||||
if table.any(main.current.enemies, function(v) return other:is(v) end) then
|
||||
if self.pierce <= 0 and self.chain <= 0 then
|
||||
self:die(self.x, self.y, nil, random:int(2, 3))
|
||||
|
@ -482,9 +749,11 @@ function Projectile:on_trigger_enter(other, contact)
|
|||
HitParticle{group = main.current.effects, x = self.x, y = self.y, color = other.color}
|
||||
end
|
||||
|
||||
|
||||
if self.character == 'archer' or self.character == 'scout' or self.character == 'outlaw' or self.character == 'blade' then
|
||||
if self.character == 'archer' or self.character == 'scout' or self.character == 'outlaw' or self.character == 'blade' or self.character == 'hunter' or self.character == 'spellblade' or self.character == 'engineer' then
|
||||
hit2:play{pitch = random:float(0.95, 1.05), volume = 0.35}
|
||||
if self.character == 'spellblade' then
|
||||
magic_area1:play{pitch = random:float(0.95, 1.05), volume = 0.15}
|
||||
end
|
||||
elseif self.character == 'wizard' then
|
||||
magic_area1:play{pitch = random:float(0.95, 1.05), volume = 0.15}
|
||||
else
|
||||
|
@ -493,6 +762,14 @@ function Projectile:on_trigger_enter(other, contact)
|
|||
|
||||
other:hit(self.dmg)
|
||||
|
||||
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}
|
||||
end}
|
||||
end)
|
||||
end
|
||||
|
||||
if self.parent.chain_infused then
|
||||
local src = other
|
||||
for i = 1, 2 do
|
||||
|
@ -532,6 +809,8 @@ function Area:init(args)
|
|||
hit2:play{pitch = random:float(0.95, 1.05), volume = 0.2}
|
||||
elseif self.character == 'saboteur' then
|
||||
_G[random:table{'saboteur_hit1', 'saboteur_hit2'}]:play{pitch = random:float(0.95, 1.05), volume = 0.2}
|
||||
elseif self.character == 'cannoneer' then
|
||||
_G[random:table{'saboteur_hit1', 'saboteur_hit2'}]:play{pitch = random:float(0.95, 1.05), volume = 0.075}
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -559,10 +838,11 @@ function Area:draw()
|
|||
local w10 = self.w/10
|
||||
local x1, y1 = self.x - w, self.y - w
|
||||
local x2, y2 = self.x + w, self.y + w
|
||||
graphics.polyline(self.color, 2, x1, y1 + w10, x1, y1, x1 + w10, y1)
|
||||
graphics.polyline(self.color, 2, x2 - w10, y1, x2, y1, x2, y1 + w10)
|
||||
graphics.polyline(self.color, 2, x2 - w10, y2, x2, y2, x2, y2 - w10)
|
||||
graphics.polyline(self.color, 2, x1, y2 - w10, x1, y2, x1 + w10, y2)
|
||||
local lw = math.remap(w, 32, 256, 2, 4)
|
||||
graphics.polyline(self.color, lw, x1, y1 + w10, x1, y1, x1 + w10, y1)
|
||||
graphics.polyline(self.color, lw, x2 - w10, y1, x2, y1, x2, y1 + w10)
|
||||
graphics.polyline(self.color, lw, x2 - w10, y2, x2, y2, x2, y2 - w10)
|
||||
graphics.polyline(self.color, lw, x1, y2 - w10, x1, y2, x1 + w10, y2)
|
||||
graphics.rectangle((x1+x2)/2, (y1+y2)/2, x2-x1, y2-y1, nil, nil, self.color_transparent)
|
||||
graphics.pop()
|
||||
end
|
||||
|
@ -570,6 +850,133 @@ end
|
|||
|
||||
|
||||
|
||||
Turret = Object:extend()
|
||||
Turret:implement(GameObject)
|
||||
Turret:implement(Physics)
|
||||
function Turret:init(args)
|
||||
self:init_game_object(args)
|
||||
self:set_as_rectangle(14, 6, 'static', 'player')
|
||||
self:set_restitution(0.5)
|
||||
self.hfx:add('hit', 1)
|
||||
self.color = orange[0]
|
||||
self.attack_sensor = Circle(self.x, self.y, 96)
|
||||
turret_deploy:play{pitch = 1.2, volume = 0.2}
|
||||
|
||||
self.t:every({3.5, 4.5}, function()
|
||||
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}
|
||||
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()
|
||||
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()
|
||||
self.dead = true
|
||||
end)
|
||||
end
|
||||
|
||||
|
||||
function Turret:update(dt)
|
||||
self:update_game_object(dt)
|
||||
|
||||
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)
|
||||
self.r = self:get_angle()
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
function Turret:draw()
|
||||
graphics.push(self.x, self.y, self.r, self.hfx.hit.x, self.hfx.hit.x)
|
||||
graphics.rectangle(self.x, self.y, self.shape.w, self.shape.h, 3, 3, self.hfx.hit.f and fg[0] or self.color)
|
||||
graphics.pop()
|
||||
end
|
||||
|
||||
|
||||
|
||||
|
||||
Pet = Object:extend()
|
||||
Pet:implement(GameObject)
|
||||
Pet:implement(Physics)
|
||||
function Pet:init(args)
|
||||
self:init_game_object(args)
|
||||
self:set_as_rectangle(8, 8, 'dynamic', 'projectile')
|
||||
self:set_restitution(0.5)
|
||||
self.hfx:add('hit', 1)
|
||||
self.color = orange[0]
|
||||
self.pierce = 6
|
||||
pet1:play{pitch = random:float(0.95, 1.05), volume = 0.35}
|
||||
end
|
||||
|
||||
|
||||
function Pet:update(dt)
|
||||
self:update_game_object(dt)
|
||||
|
||||
self:set_angle(self.r)
|
||||
self:move_along_angle(self.v, self.r)
|
||||
end
|
||||
|
||||
|
||||
function Pet:draw()
|
||||
graphics.push(self.x, self.y, self.r, self.hfx.hit.x, self.hfx.hit.x)
|
||||
graphics.rectangle(self.x, self.y, self.shape.w, self.shape.h, 3, 3, self.hfx.hit.f and fg[0] or self.color)
|
||||
graphics.pop()
|
||||
end
|
||||
|
||||
|
||||
function Pet:on_collision_enter(other, contact)
|
||||
local x, y = contact:getPositions()
|
||||
local nx, ny = contact:getNormal()
|
||||
local r = 0
|
||||
if nx == 0 and ny == -1 then r = -math.pi/2
|
||||
elseif nx == 0 and ny == 1 then r = math.pi/2
|
||||
elseif nx == -1 and ny == 0 then r = math.pi
|
||||
else r = 0 end
|
||||
|
||||
if other:is(Wall) then
|
||||
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}
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
function Pet:on_trigger_enter(other)
|
||||
if table.any(main.current.enemies, function(v) return other:is(v) end) then
|
||||
if self.pierce <= 0 then
|
||||
camera:shake(2, 0.5)
|
||||
other:hit(self.parent.dmg)
|
||||
other:push(35, self:angle_to_object(other))
|
||||
self.dead = true
|
||||
local n = 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()
|
||||
else
|
||||
camera:shake(2, 0.5)
|
||||
other:hit(self.parent.dmg)
|
||||
other:push(35, self:angle_to_object(other))
|
||||
self.pierce = self.pierce - 1
|
||||
end
|
||||
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}
|
||||
HitParticle{group = main.current.effects, x = self.x, y = self.y, color = other.color}
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
|
||||
Saboteur = Object:extend()
|
||||
Saboteur:implement(GameObject)
|
||||
Saboteur:implement(Physics)
|
||||
|
|
12
todo
12
todo
|
@ -9,15 +9,15 @@ Blade: shoots multiple blades at nearby enemies, each dealing AoE damage on cont
|
|||
Elementor: deals massive AoE damage to a random target, long range, AoE has medium range
|
||||
Saboteur: calls on other saboteurs to seek targets and explode on contact, AoE has small range
|
||||
Stormweaver: infuses all projectile attacks with chain lightning, small range
|
||||
Sage: shoots a slow projectile that draws enemies in, medium range, AoE has medium range
|
||||
Sage: shoots a slow projectile that draws enemies in, medium range, AoE has small range
|
||||
Squire: improves damage and defense for adjacent units, as well as healing them periodically
|
||||
Cannoneer: shoots a projectile at any nearby enemy and deals massive AoE damage on contact, long range, AoE has medium range
|
||||
Dual Gunner: shoots two parallel projectiles at any nearby enemy, medium range
|
||||
Hunter: shoots an arrow at any nearby enemy with a chance to summon a pet that will trample through enemies knocking them away, arrow has long range, pet has small range
|
||||
Chronomancer: dramatically improves attack speed for adjacent units
|
||||
Spellblade: knives orbit you and hoam towards nearby enemies, small range
|
||||
Spellblade: knives spiral outwards and pierce enemies
|
||||
Psykeeper: all damage taken is stored and distributed as healing
|
||||
Gambler: drops a sentry that uses random attacks, medium range
|
||||
Engineer: drops a sentry that uses random attacks, medium range
|
||||
|
||||
Ranger: yellow, chance to release a barrage
|
||||
Warrior: orange, increased defense
|
||||
|
@ -39,8 +39,7 @@ Outlaw [rogue, warrior]
|
|||
Blade [warrior, nuker]
|
||||
Elementor [mage, nuker]
|
||||
Saboteur [rogue, conjurer, nuker]
|
||||
|
||||
Linker [enchanter]
|
||||
Stormweaver [enchanter]
|
||||
Sage [mage, nuker]
|
||||
Squire [warrior, healer, enchanter]
|
||||
Cannoneer [ranger, nuker]
|
||||
|
@ -49,7 +48,8 @@ Hunter [ranger, conjurer]
|
|||
Chronomancer [mage, enchanter]
|
||||
Spellblade [mage, rogue]
|
||||
Psykeeper [healer, psy]
|
||||
Gambler [conjurer]
|
||||
|
||||
Engineer [conjurer]
|
||||
|
||||
Ranger [2, 4] (5)
|
||||
Warrior [2, 4] (5)
|
||||
|
|
Loading…
Reference in New Issue