Week 5
20
arena.lua
|
@ -12,6 +12,7 @@ function Arena:on_enter(from, level, units)
|
|||
self.hfx:add('condition2', 1)
|
||||
self.level = level or 1
|
||||
self.units = units
|
||||
self.logo = true
|
||||
|
||||
self.floor = Group()
|
||||
self.main = Group():set_as_physics_world(32, 0, 0, {'player', 'enemy', 'projectile', 'enemy_projectile'})
|
||||
|
@ -59,14 +60,21 @@ function Arena:on_enter(from, level, units)
|
|||
|
||||
for i, unit in ipairs(units) do
|
||||
if i == 1 then
|
||||
self.player = Player{group = self.main, x = gw/2, y = gh/2, leader = true, character = unit.character, level = unit.level}
|
||||
self.player = Player{group = self.main, x = gw/2, y = gh/2 + 16, leader = true, character = unit.character, level = unit.level}
|
||||
else
|
||||
self.player:add_follower(Player{group = self.main, character = unit.character, level = unit.level})
|
||||
end
|
||||
end
|
||||
|
||||
if self.level == 1000 then
|
||||
self.level_1000_text = Text2{group = self.ui, x = gw/2, y = gh/2, lines = {{text = '[fg, wavy_mid]SNKRX', font = fat_font, alignment = 'center'}}}
|
||||
-- self.level_1000_text2 = Text2{group = self.ui, x = gw/2, y = gh/2 + 64, lines = {{text = '[fg, wavy_mid]SNKRX', font = pixul_font, alignment = 'center'}}}
|
||||
-- Wall{group = self.main, vertices = math.to_rectangle_vertices(gw/2 - 0.45*self.level_1000_text.w, gh/2 - 0.3*self.level_1000_text.h, gw/2 + 0.45*self.level_1000_text.w, gh/2 - 3), snkrx = true, color = bg[-1]}
|
||||
|
||||
else
|
||||
-- Set win condition and enemy spawns
|
||||
self.win_condition = random:table{'time', 'enemy_kill', 'wave'}
|
||||
if self.level == 18 and self.trailer then self.win_condition = 'wave' end
|
||||
if self.win_condition == 'wave' then
|
||||
self.level_to_max_waves = {
|
||||
1, 2, random:int(2, 3),
|
||||
|
@ -169,6 +177,13 @@ function Arena:on_enter(from, level, units)
|
|||
self.t:every(function() return #self.main:get_objects_by_classes(self.enemies) <= 0 and self.time_left <= 0 end, function() self.can_quit = true end)
|
||||
end
|
||||
|
||||
if self.level == 18 and self.trailer then
|
||||
Text2{group = self.ui, x = gw/2, y = gh/2 - 24, lines = {{text = '[fg, wavy]SNKRX', font = fat_font, alignment = 'center'}}}
|
||||
Text2{group = self.ui, x = gw/2, y = gh/2, sx = 0.5, sy = 0.5, lines = {{text = '[fg, wavy_mid]try the demo & wishlist!', font = fat_font, alignment = 'center'}}}
|
||||
Text2{group = self.ui, x = gw/2, y = gh/2 + 24, sx = 0.5, sy = 0.5, lines = {{text = '[light_bg, wavy_mid]music: kubbi - ember', font = fat_font, alignment = 'center'}}}
|
||||
end
|
||||
end
|
||||
|
||||
if self.level == 1 then
|
||||
local t1 = Text2{group = self.floor, x = gw/2, y = gh/2 + 2, sx = 0.6, sy = 0.6, lines = {{text = '[light_bg]<- or a -> or d', font = fat_font, alignment = 'center'}}}
|
||||
local t2 = Text2{group = self.floor, x = gw/2, y = gh/2 + 18, lines = {{text = '[light_bg]turn left turn right', font = pixul_font, alignment = 'center'}}}
|
||||
|
@ -298,7 +313,7 @@ function Arena:update(dt)
|
|||
end
|
||||
|
||||
self:update_game_object(dt*slow_amount)
|
||||
cascade_instance.pitch = math.clamp(slow_amount*self.main_slow_amount, 0.05, 1)
|
||||
-- cascade_instance.pitch = math.clamp(slow_amount*self.main_slow_amount, 0.05, 1)
|
||||
|
||||
if input.k.pressed then
|
||||
local enemies = self.main:get_objects_by_classes(self.enemies)
|
||||
|
@ -383,6 +398,7 @@ function Arena:draw()
|
|||
self.main:draw()
|
||||
self.post_main:draw()
|
||||
self.effects:draw()
|
||||
if self.level == 18 and self.trailer then graphics.rectangle(gw/2, gh/2, 2*gw, 2*gh, nil, nil, modal_transparent) end
|
||||
self.ui:draw()
|
||||
|
||||
camera:attach()
|
||||
|
|
After Width: | Height: | Size: 406 B |
After Width: | Height: | Size: 660 B |
After Width: | Height: | Size: 1.5 KiB |
After Width: | Height: | Size: 98 KiB |
After Width: | Height: | Size: 234 B |
After Width: | Height: | Size: 2.6 KiB |
After Width: | Height: | Size: 2.5 KiB |
After Width: | Height: | Size: 2.6 KiB |
After Width: | Height: | Size: 1.7 KiB |
After Width: | Height: | Size: 43 KiB |
After Width: | Height: | Size: 20 KiB |
After Width: | Height: | Size: 39 KiB |
After Width: | Height: | Size: 6.3 KiB |
After Width: | Height: | Size: 8.0 KiB |
After Width: | Height: | Size: 45 KiB |
After Width: | Height: | Size: 23 KiB |
After Width: | Height: | Size: 41 KiB |
After Width: | Height: | Size: 467 B |
|
@ -170,9 +170,9 @@ function BuyScreen:set_party_and_sets()
|
|||
local classes = get_classes(self.units)
|
||||
for i, class in ipairs(classes) do
|
||||
local x, y
|
||||
if #classes <= 8 then x, y = math.index_to_coordinates(i, 2)
|
||||
if #classes <= 6 then x, y = math.index_to_coordinates(i, 2)
|
||||
else x, y = math.index_to_coordinates(i, 3) end
|
||||
table.insert(self.sets, ClassIcon{group = self.main, x = (#classes <= 8 and 319 or 308) + (x-1)*20, y = 45 + (y-1)*56, class = class, units = self.units, parent = self})
|
||||
table.insert(self.sets, ClassIcon{group = self.main, x = (#classes <= 6 and 319 or 308) + (x-1)*20, y = 45 + (y-1)*56, class = class, units = self.units, parent = self})
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -319,6 +319,19 @@ function RerollButton:update(dt)
|
|||
self:update_game_object(dt)
|
||||
|
||||
if self.selected and input.m1.pressed then
|
||||
if gold < 2 then
|
||||
self.spring:pull(0.2, 200, 10)
|
||||
self.selected = true
|
||||
error1:play{pitch = random:float(0.95, 1.05), volume = 0.5}
|
||||
if not self.info_text then
|
||||
self.info_text = InfoText{group = main.current.ui}
|
||||
self.info_text:activate({
|
||||
{text = '[fg]not enough gold', font = pixul_font, alignment = 'center'},
|
||||
}, nil, nil, nil, nil, 16, 4, nil, 2)
|
||||
self.info_text.x, self.info_text.y = gw/2, gh/2 + 10
|
||||
end
|
||||
self.t:after(2, function() self.info_text:deactivate(); self.info_text.dead = true; self.info_text = nil end, 'info_text')
|
||||
else
|
||||
ui_switch2:play{pitch = random:float(0.95, 1.05), volume = 0.5}
|
||||
self.parent:set_cards(random:int(1, 25), true)
|
||||
self.selected = true
|
||||
|
@ -326,6 +339,7 @@ function RerollButton:update(dt)
|
|||
gold = gold - 2
|
||||
self.parent.shop_text:set_text{{text = '[wavy_mid, fg]shop [fg]- [fg, nudge_down]gold: [yellow, nudge_down]' .. gold, font = pixul_font, alignment = 'center'}}
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
|
21
devlog.md
|
@ -413,3 +413,24 @@ Mostly done with balance tuning. Now all that's left are some final details:
|
|||
The demo is finally 100% complete. Now tomorrow I'll spend some time recording gameplay and hopefully finishing the trailer. After that I can start working on the steam page.
|
||||
If I have it done by tomorrow and Valve takes 5 business days to approve the store page I should have everything ready by the 25th. And then I can release the game 14 days after that, which would be the 8th of April.
|
||||
I definitely want to release it around that time, before the 15th because then I will have completed the game in less than 60 days which is my limit, although I should probably aim for 40 days going forward.
|
||||
|
||||
# Week 5 - 17/03/21 to 24/03/21
|
||||
|
||||
This latest week I did everything necessary to get the game into a playable state as well as all the work needed for a Steam page. Most of it was spent making the trailer, but I feel like the more trailers I make
|
||||
the faster I get at making them. This time it took like 3-4 days out of laziness, but I can easily see it being a 1 day job in the future.
|
||||
|
||||
I also tested the demo out with a few people and the results were underwhelming. No one seemed to play it for too much time, which I suspected would happen given that the longer term loop of the game isn't in yet.
|
||||
More interestingly though, all of the feedback I was given about things that needed to be changed were things that I knew needed to be changed/added for the game's release, which means that I have a pretty good idea of where
|
||||
the game is from other people's perspective.
|
||||
|
||||
The web build has a few bugs that I can't fix like sound effects not playing randomly, and since my strategy was doing a web demo coupled with the page's release, I've decided to change it. Both because of these bugs and the
|
||||
underwhelming response I feel like releasing a demo at this point will damage the game more than help it. I have ~3 weeks until release date (13th of April) from now, and that should be enough to add enough things into the game
|
||||
to make it significantly better.
|
||||
|
||||
Going forward I think the Steam page reveal demo strategy probably isn't a good idea for these 1 month games. They're small enough already as they are and releasing them in an ever more crude state is probably a waste of time.
|
||||
The only thing I need to schedule better for next releases is my trailer making timing. If I want to release a game in 1 month I need to have a trailer by day 15 at the latest, which means I need to work on the game for 2 weeks
|
||||
and then take 1 day to make a trailer.
|
||||
|
||||
As for feedback given from the demo:
|
||||
|
||||
|
||||
|
|
|
@ -58,8 +58,8 @@ function Seeker:on_collision_enter(other, contact)
|
|||
|
||||
elseif table.any(main.current.enemies, function(v) return other:is(v) end) then
|
||||
if self.being_pushed and math.length(self:get_velocity()) > 60 then
|
||||
other:hit(math.floor(self.dmg/2))
|
||||
self:hit(self.dmg)
|
||||
other:hit(math.floor(self.dmg/4))
|
||||
self:hit(math.floor(self.dmg/2))
|
||||
other:push(random:float(10, 15), other:angle_to_object(self))
|
||||
HitCircle{group = main.current.effects, x = x, y = y, rs = 6, color = fg[0], duration = 0.1}
|
||||
for i = 1, 2 do HitParticle{group = main.current.effects, x = x, y = y, color = self.color} end
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
call "C:\Program Files\7-Zip\7z.exe" a -r %1.zip -w ..\..\ -xr!engine/love -xr!builds -xr!.git -xr!*.moon
|
||||
rename %1.zip %1.love
|
||||
call love-js -c -m 268435456 -t %1 %2\engine\love\%1.love ..\..\builds\web
|
||||
call love-js -c -m 1073741824 -t %1 %2\engine\love\%1.love ..\..\builds\web
|
||||
del %1.love
|
||||
|
|
6
main.lua
|
@ -1,3 +1,4 @@
|
|||
web = true
|
||||
require 'engine'
|
||||
require 'shared'
|
||||
require 'arena'
|
||||
|
@ -423,7 +424,6 @@ function update(dt)
|
|||
end
|
||||
|
||||
if input.m.pressed then
|
||||
print(music.volume)
|
||||
if music.volume == 0.5 then
|
||||
music.volume = 0
|
||||
elseif music.volume == 0 then
|
||||
|
@ -443,7 +443,7 @@ end
|
|||
function love.run()
|
||||
return engine_run({
|
||||
game_name = 'SNAKRX',
|
||||
window_width = 480*3,
|
||||
window_height = 270*3,
|
||||
window_width = 480*2,
|
||||
window_height = 270*2,
|
||||
})
|
||||
end
|
||||
|
|
|
@ -469,6 +469,9 @@ function Player:on_collision_enter(other, contact)
|
|||
|
||||
if other:is(Wall) then
|
||||
if self.leader then
|
||||
if other.snkrx then
|
||||
main.current.level_1000_text:pull(0.2, 200, 10)
|
||||
end
|
||||
self.hfx:use('hit', 0.5, 200, 10, 0.1)
|
||||
camera:spring_shake(2, math.pi - self.r)
|
||||
self:bounce(contact:getNormal())
|
||||
|
|
2
run.sh
|
@ -1,4 +1,4 @@
|
|||
#!/bin/bash
|
||||
|
||||
cd E:/a327ex/SNAKRX # change to the directory of the current project
|
||||
cd E:/a327ex/SNKRX # change to the directory of the current project
|
||||
engine/love/love.exe --console .
|
||||
|
|
13
shared.lua
|
@ -14,7 +14,7 @@ function shared_init()
|
|||
purple = ColorRamp(Color'#8e559e', 0.025),
|
||||
}
|
||||
for name, color in pairs(colors) do _G[name] = color end
|
||||
modal_transparent = Color(0.1, 0.1, 0.1, 0.5)
|
||||
modal_transparent = Color(0.1, 0.1, 0.1, 0.6)
|
||||
fg_transparent = Color(fg[0].r, fg[0].g, fg[0].b, 0.5)
|
||||
|
||||
graphics.set_background_color(bg[0])
|
||||
|
@ -433,6 +433,7 @@ global_text_tags = {
|
|||
light_bg = TextTag{draw = function(c, i, text) graphics.set_color(bg[5]) end},
|
||||
fg = TextTag{draw = function(c, i, text) graphics.set_color(fg[0]) end},
|
||||
fgm5 = TextTag{draw = function(c, i, text) graphics.set_color(fg[-5]) end},
|
||||
fgm10 = TextTag{draw = function(c, i, text) graphics.set_color(fg[-10]) end},
|
||||
wavy = TextTag{update = function(c, dt, i, text) c.oy = 2*math.sin(4*time + i) end},
|
||||
wavy_mid = TextTag{update = function(c, dt, i, text) c.oy = 0.75*math.sin(3*time + i) end},
|
||||
wavy_mid2 = TextTag{update = function(c, dt, i, text) c.oy = 0.5*math.sin(3*time + i) end},
|
||||
|
@ -474,6 +475,7 @@ Text2:implement(GameObject)
|
|||
function Text2:init(args)
|
||||
self:init_game_object(args)
|
||||
self.text = Text(args.lines, global_text_tags)
|
||||
self.w, self.h = self.text.w, self.text.h
|
||||
end
|
||||
|
||||
|
||||
|
@ -484,7 +486,14 @@ end
|
|||
|
||||
|
||||
function Text2:draw()
|
||||
self.text:draw(self.x, self.y, self.r, self.sx, self.sy)
|
||||
self.text:draw(self.x, self.y, self.r, self.spring.x*self.sx, self.spring.x*self.sy)
|
||||
end
|
||||
|
||||
|
||||
function Text2:pull(...)
|
||||
self.spring:pull(...)
|
||||
self.r = random:table{-math.pi/24, math.pi/24}
|
||||
self.t:tween(0.2, self, {r = 0}, math.linear)
|
||||
end
|
||||
|
||||
|
||||
|
|
44
todo
|
@ -1,3 +1,40 @@
|
|||
Trailer:
|
||||
1
|
||||
01:31
|
||||
03:37
|
||||
05:45
|
||||
06:44
|
||||
07:50
|
||||
14:07
|
||||
2
|
||||
01:01
|
||||
05:21
|
||||
08:02
|
||||
10:40
|
||||
11:55
|
||||
27:00 wall bump
|
||||
27:17
|
||||
3
|
||||
00:23
|
||||
12:12
|
||||
4
|
||||
02:30
|
||||
5
|
||||
01:00
|
||||
03:36
|
||||
07:07
|
||||
6
|
||||
00:14
|
||||
03:45
|
||||
04:29
|
||||
06:19
|
||||
09:40
|
||||
10:27
|
||||
12:51
|
||||
13:51
|
||||
15:12 fast buy
|
||||
|
||||
|
||||
Mini Boss every 3rd level
|
||||
Show a unit DPS list like Underlord's to the right side of the screen
|
||||
About 20-30 passive items that can be collected every 3 levels
|
||||
|
@ -42,3 +79,10 @@ Bugs
|
|||
Sage's pull force doesn't increase with unit level
|
||||
Cleric's healing amount doesn't increase with unit level
|
||||
Squire and Chronomancer's buffs don't increase with unit level
|
||||
|
||||
Engine improvements for after SNKRX release
|
||||
on_hit:
|
||||
on_collision_enter/exit are automatically called and automatically call on_hit/on_leave for each object
|
||||
This enables the definition of on_hit on each object and the question of where the logic should stay is solved/dodged
|
||||
Spurred by Wall needing to have its own on_hit function to do something when the player hits it, without having to change player code for all Walls,
|
||||
Defining on_hit on the Wall creation call for that specific Wall, and thus that specific Wall will have this behavior while other walls won't
|
||||
|
|