master
a327ex 2021-02-24 02:47:22 -03:00
parent ec0af70374
commit 57e7b3cab7
16 changed files with 235 additions and 37 deletions

View File

@ -50,13 +50,18 @@ 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 = 'elementor'}
--[[
self.player:add_follower(Player{group = self.main, character = 'archer'})
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:add_follower(Player{group = self.main, character = 'cleric'})
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'})
self.player:add_follower(Player{group = self.main, character = 'blade'})
self.player:add_follower(Player{group = self.main, character = 'elementor'})
]]--
self.win_condition = random:table{'time', 'enemy_kill', 'wave'}

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

@ -120,26 +120,26 @@ Ideaguyed the entire roster for the demo and implemented a few of them.
| Character | Description | Trigger Range | Effect Range |
| --- | --- | --- | --- |
| Vagrant | shoots a projectile | medium | nil |
| Scout | throws a knife that chains 3 times | small | nil |
| Cleric | heals every unit when any one drops below 50% HP | nil | nil |
| Vagrant | shoots a projectile | medium | |
| Scout | throws a knife that chains 3 times | small | |
| Cleric | heals every unit when any one drops below 50% HP | | |
| Swordsman | deals physical damage in an area around the unit | small | medium |
| Archer | shoots an arrow that pierces | very long | nil |
| Archer | shoots an arrow that pierces | very long | |
| Wizard | shoots a projectile that deals AoE damage | long | very small |
| Outlaw | throws a fan of 5 knives | medium | nil |
| Outlaw | throws a fan of 5 knives | medium | |
| Blade | shoots multiple blades that deal AoE damage on contact | small | small |
| Elementor | deals massive AoE damage to a random target | long | medium |
| Ninja | creates clones that roam and shoot shurikens | nil | very small |
| Ninja | creates clones that roam and shoot shurikens | | very small |
| Linker | links nearby enemies together making them share damage taken | medium | small |
| Sage | shoots a slow projectile that draws enemies in | medium | medium |
| Squire | improves damage and defense for adjacent units as well as healing them periodically | nil | nil |
| Squire | improves damage and defense for adjacent units as well as healing them periodically | | |
| Cannoneer | shoots a projectile that deals massive AoE damage | long | medium |
| Dual Gunner | shoots two parallel projectiles | medium | nil |
| Dual Gunner | shoots two parallel projectiles | medium | |
| Hunter | shoots an arrow with a chance to summon a pet | long | small |
| Chronomancer | dramatically improves attack speed for adjacent units | nil | nil |
| Chronomancer | dramatically improves attack speed for adjacent units | | |
| Spellblade | knives orbit you and hoam towards nearby enemies | small | small |
| Psykeeper | all damage taken is stored and distributed as healing | nil | nil |
| Gambler | drops a sentry that uses random attacks | nil | medium |
| Psykeeper | all damage taken is stored and distributed as healing | | |
| Gambler | drops a sentry that uses random attacks | | medium |
### Character Classes
@ -195,3 +195,12 @@ Ideaguyed the entire roster for the demo and implemented a few of them.
| Psy | 1.5 | 1.0 | 1.0 | 1.0 | 1.0 | 0.5 | 1.0 |
I've implemented up to Elementor today and ATM in the process of doing Ninja, but today seems like a particularly low energy day so I'm just going to play some games instead.
# Day 7 - 23/02/21
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~~

View File

@ -41,8 +41,17 @@ function init()
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)
alert1 = Sound('Click.ogg', s)
elementor1 = Sound('Wind Bolt 18.ogg', s)
saboteur_hit1 = Sound('Explosion Flesh_01.ogg', s)
saboteur_hit2 = Sound('Explosion Flesh_02.ogg', s)
saboteur1 = Sound('Male Jump 1.ogg', s)
saboteur2 = Sound('Male Jump 2.ogg', s)
saboteur3 = Sound('Male Jump 3.ogg', s)
spark1 = Sound('Spark 1.ogg', s)
spark2 = Sound('Spark 2.ogg', s)
spark3 = Sound('Spark 3.ogg', s)
stormweaver1 = Sound('Buff 8.ogg', s)
main = Main()
main:add(Arena'arena')

View File

@ -1,3 +1,71 @@
LightningLine = Object:extend()
LightningLine:implement(GameObject)
function LightningLine:init(args)
self:init_game_object(args)
self.lines = {}
table.insert(self.lines, {x1 = self.src.x, y1 = self.src.y, x2 = self.dst.x, y2 = self.dst.y})
self.w = 3
self.generations = 3
self.max_offset = 8
self:generate()
self.t:tween(0.1, self, {w = 1}, math.linear, function() self.dead = true end)
self.color = blue[0]
HitCircle{group = main.current.effects, x = self.src.x, y = self.src.y, rs = 6, color = fg[0], duration = 0.1}
for i = 1, 2 do HitParticle{group = main.current.effects, x = self.src.x, y = self.src.y, color = blue[0]} end
HitCircle{group = main.current.effects, x = self.dst.x, y = self.dst.y, rs = 6, color = fg[0], duration = 0.1}
HitParticle{group = main.current.effects, x = self.dst.x, y = self.dst.y, color = blue[0]}
end
function LightningLine:update(dt)
self:update_game_object(dt)
end
function LightningLine:draw()
graphics.polyline(self.color, self.w, unpack(self.points))
end
function LightningLine:generate()
local offset_amount = self.max_offset
local lines = self.lines
for j = 1, self.generations do
for i = #self.lines, 1, -1 do
local x1, y1 = self.lines[i].x1, self.lines[i].y1
local x2, y2 = self.lines[i].x2, self.lines[i].y2
table.remove(self.lines, i)
local x, y = (x1+x2)/2, (y1+y2)/2
local p = Vector(x2-x1, y2-y1):normalize():perpendicular()
x = x + p.x*random:float(-offset_amount, offset_amount)
y = y + p.y*random:float(-offset_amount, offset_amount)
table.insert(self.lines, {x1 = x1, y1 = y1, x2 = x, y2 = y})
table.insert(self.lines, {x1 = x, y1 = y, x2 = x2, y2 = y2})
end
offset_amount = offset_amount/2
end
self.points = {}
while #self.lines > 0 do
local min_d, min_i = 1000000, 0
for i, line in ipairs(self.lines) do
local d = math.distance(self.src.x, self.src.y, line.x1, line.y1)
if d < min_d then
min_d = d
min_i = i
end
end
local line = table.remove(self.lines, min_i)
table.insert(self.points, line.x1)
table.insert(self.points, line.y1)
end
end
WallKnife = Object:extend()
WallKnife:implement(GameObject)
WallKnife:implement(Physics)
@ -68,6 +136,7 @@ function Unit:init_unit()
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}
end
@ -97,6 +166,12 @@ function Unit:show_heal(n)
end
function Unit:show_infused(n)
self.infused_bar.hidden = false
self.t:after(n, function() self.infused_bar.hidden = true end, 'infused_bar')
end
function Unit:calculate_damage(dmg)
if self.def >= 0 then dmg = dmg*(100/(100+self.def))
else dmg = dmg*(2 - 100/(100+self.def)) end
@ -152,7 +227,8 @@ function Unit:calculate_stats(first_run)
if class == 'warrior' then self.class_dmg_m = self.class_dmg_m*1.1
elseif class == 'ranger' then self.class_dmg_m = self.class_dmg_m*1.2
elseif class == 'rogue' then self.class_dmg_m = self.class_dmg_m*1.3
elseif class == 'mage' then self.class_dmg_m = self.class_dmg_m*1.4 end
elseif class == 'mage' then self.class_dmg_m = self.class_dmg_m*1.4
elseif class == 'ninja_clone' then self.class_dmg_m = self.class_dmg_m*1.5 end
end
self.dmg = (self.base_dmg + self.class_dmg_a + self.buff_dmg_a)*self.class_dmg_m*self.buff_dmg_m
@ -195,7 +271,8 @@ function Unit:calculate_stats(first_run)
elseif class == 'ranger' then self.class_mvspd_m = self.class_mvspd_m*1.2
elseif class == 'rogue' then self.class_mvspd_m = self.class_mvspd_m*1.4
elseif class == 'enchanter' then self.class_mvspd_m = self.class_mvspd_m*1.2
elseif class == 'seeker' then self.class_mvspd_m = self.class_mvspd_m*0.3 end
elseif class == 'seeker' then self.class_mvspd_m = self.class_mvspd_m*0.3
elseif class == 'saboteur' then self.class_mvspd_m = self.class_mvspd_m*1.4 end
end
self.v = (self.base_mvspd + self.class_mvspd_a + self.buff_mvspd_a)*self.class_mvspd_m*self.buff_mvspd_m
end
@ -203,6 +280,35 @@ end
InfusedBar = Object:extend()
InfusedBar:implement(GameObject)
InfusedBar:implement(Parent)
function InfusedBar: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)
end
function InfusedBar:update(dt)
self:update_game_object(dt)
self:follow_parent_exclusively()
end
function InfusedBar: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)

View File

@ -137,18 +137,37 @@ function Player:init(args)
end
end, nil, nil, 'attack')
elseif self.character == 'ninja' then
elseif self.character == 'saboteur' then
self.color = red[0]
self:set_as_rectangle(9, 9, 'dynamic', 'player')
self.visual_shape = 'rectangle'
self.classes = {'rogue', 'conjurer'}
self.classes = {'rogue', 'conjurer', 'nuker'}
self.t:every(8, function()
self.t:every(0.25, function()
SpawnEffect{group = self.effects, x = self.x, y = self.y, action = function(x, y)
NinjaClone{group = self.main, x = x, y = y, parent = self}
SpawnEffect{group = main.current.effects, x = self.x, y = self.y, action = function(x, y)
Saboteur{group = main.current.main, x = x, y = y, parent = self}
end}
end, 3)
end, 4)
end)
elseif self.character == 'stormweaver' then
self.color = blue[0]
self:set_as_rectangle(9, 9, 'dynamic', 'player')
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
local leader = (self.leader and self) or self.parent
if self.leader then followers = self.followers else followers = self.parent.followers end
for _, f in ipairs(followers) do
if f.character ~= 'swordsman' and f.character ~= 'cleric' and f.character ~= 'elementor' and f.character ~= 'saboteur' then
f:chain_infuse(4)
end
end
end)
end
self:calculate_stats(true)
@ -244,7 +263,7 @@ function Player:on_collision_enter(other, contact)
pop1:play{pitch = r, volume = 0.2}
for i, f in ipairs(self.followers) do
trigger:after(i*0.002*self.v, function()
trigger:after(i*(10.6/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}
@ -293,6 +312,13 @@ function Player:heal(amount)
end
function Player:chain_infuse(duration)
self.chain_infused = true
self:show_infused(duration or 2)
self.t:after(duration or 2, function() self.chain_infused = false end, 'chain_infuse')
end
function Player:add_follower(unit)
table.insert(self.followers, unit)
unit.parent = self
@ -341,10 +367,11 @@ end
function Player:attack(area, mods)
mods = mods or {}
camera:shake(2, 0.5)
self.hfx:use('shoot', 0.25)
local t = {group = main.current.effects, x = mods.x or self.x, y = mods.y or self.y, r = self.r, 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))
if self.character == 'swordsman' then
_G[random:table{'swordsman1', 'swordsman2'}]:play{pitch = random:float(0.9, 1.1), volume = 0.75}
@ -365,6 +392,7 @@ function Projectile:init(args)
self.pierce = args.pierce or 0
self.chain = args.chain or 0
self.chain_enemies_hit = {}
self.infused_enemies_hit = {}
end
@ -454,6 +482,7 @@ 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
hit2:play{pitch = random:float(0.95, 1.05), volume = 0.35}
elseif self.character == 'wizard' then
@ -461,7 +490,22 @@ function Projectile:on_trigger_enter(other, contact)
else
hit3:play{pitch = random:float(0.95, 1.05), volume = 0.35}
end
other:hit(self.dmg)
if self.parent.chain_infused then
local src = other
for i = 1, 2 do
_G[random:table{'spark1', 'spark2', 'spark3'}]:play{pitch = random:float(0.9, 1.1), volume = 0.3}
table.insert(self.infused_enemies_hit, src)
local dst = src:get_random_object_in_shape(Circle(src.x, src.y, 64), main.current.enemies, self.infused_enemies_hit)
if dst then
dst:hit(self.dmg/(i+1))
LightningLine{group = main.current.effects, src = src, dst = dst}
src = dst
end
end
end
end
end
@ -486,6 +530,8 @@ function Area:init(args)
elseif self.character == 'blade' then
blade_hit1:play{pitch = random:float(0.9, 1.1), volume = 0.35}
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}
end
end
@ -524,30 +570,54 @@ end
NinjaClone = Object:extend()
NinjaClone:implement(GameObject)
NinjaClone:implement(Physics)
function NinjaClone:init(args)
Saboteur = Object:extend()
Saboteur:implement(GameObject)
Saboteur:implement(Physics)
Saboteur:implement(Unit)
function Saboteur:init(args)
self:init_game_object(args)
self:init_unit()
self:set_as_rectangle(8, 8, 'dynamic', 'enemy')
self:set_as_rectangle(8, 8, 'dynamic', 'player')
self:set_restitution(0.5)
self.color = red[0]
self.classes = {'ninja_clone'}
self.character = 'saboteur'
self.classes = {'saboteur', 'rogue', 'nuker'}
self:calculate_stats(true)
self:set_as_steerable(self.v, 2000, 4*math.pi, 4)
_G[random:table{'saboteur1', 'saboteur2', 'saboteur3'}]:play{pitch = random:float(0.8, 1.2), volume = 0.2}
self.target = random:table(self.group:get_objects_by_classes(main.current.enemies))
end
function NinjaClone:update(dt)
function Saboteur:update(dt)
self:update_game_object(dt)
self:calculate_stats()
if not self.target then self.target = random:table(self.group:get_objects_by_classes(main.current.enemies)) end
if not self.target then return end
if self.target.dead then self.target = random:table(self.group:get_objects_by_classes(main.current.enemies)) end
if not self.target then return end
self:seek_point(self.target.x, self.target.y)
self:rotate_towards_velocity(0.5)
self.r = self:get_angle()
end
function NinjaClone:draw()
function Saboteur: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 Saboteur:on_collision_enter(other, contact)
if table.any(main.current.enemies, function(v) return other:is(v) end) then
camera:shake(4, 0.5)
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, character = self.character}
Area(table.merge(t, mods or {}))
self.dead = true
end
end

9
todo
View File

@ -7,9 +7,8 @@ Wizard: shoots a projectile at any nearby enemy and deals AoE damage on contact,
Outlaw: throws a leque of knives at nearby enemies, medium range
Blade: shoots multiple blades at nearby enemies, each dealing AoE damage on contact, small range, AoE has small range
Elementor: deals massive AoE damage to a random target, long range, AoE has medium range
Ninja: creates clones that roam and shoot projectiles at nearby enemies, very small range
Linker: links nearby enemies together making them share damage taken, 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
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
@ -39,9 +38,9 @@ Wizard [mage]
Outlaw [rogue, warrior]
Blade [warrior, nuker]
Elementor [mage, nuker]
Saboteur [rogue, conjurer, nuker]
Ninja [rogue, conjurer]
Linker [enchanter, nuker]
Linker [enchanter]
Sage [mage, nuker]
Squire [warrior, healer, enchanter]
Cannoneer [ranger, nuker]