Day 7
parent
ec0af70374
commit
57e7b3cab7
11
arena.lua
11
arena.lua
|
@ -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.
31
devlog.md
31
devlog.md
|
@ -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~~
|
||||
|
|
11
main.lua
11
main.lua
|
@ -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')
|
||||
|
|
110
objects.lua
110
objects.lua
|
@ -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)
|
||||
|
|
100
player.lua
100
player.lua
|
@ -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
9
todo
|
@ -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]
|
||||
|
|
Loading…
Reference in New Issue