From 676f2b23a578af9d7e85be286bda39ac8e315fab Mon Sep 17 00:00:00 2001 From: huulong Date: Thu, 17 Dec 2020 22:23:00 +0100 Subject: [PATCH 01/91] [SPRITE] Fixed top row of some Sonic rotated run sprites missing --- data/data_stage1_runtime.p8 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data/data_stage1_runtime.p8 b/data/data_stage1_runtime.p8 index 36ab09d4..36245ea4 100644 --- a/data/data_stage1_runtime.p8 +++ b/data/data_stage1_runtime.p8 @@ -83,7 +83,7 @@ eeeeeeeeeeeeeeeeeeee7777080eeeeeeeee707e66eeeeeeeeeeee7611ee00eeeeeeeee7c708eeee eeeeeeeeeeeeeeeeeeeee772662622eeeeeee7e222eeeeeeeeeeee226eeeeeeeeeeeeee777880eeeeeeeeee888eeeeeeeeee0717eeeeeeeeeeeeeeeeeeeeeeee eeeeeeeeeeeeeeeeeeeeeee000060eeeeeeeeee066eeeeeeeeeeee66eeeeeeeeeeeeeee87880eeeeeeeeeee277eeeeeeeeee02787eeeeeeeeeeeeeeeeeeeeeee eeeeeeeeeeeeeeeeeeeeeeee2220eeeeeeeeeeee022eeeeeeeeeee02eeeeeeeeeeeeeee0220eeeeeeeeeeeee288eeeeeeeeee02878eeeeeeeeeeeeeeeeeeeeee -eeeecccccceeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee +eeeecccccceeeeeeeeeccccccceeeeeeeeeecccccceeeeeeeeeccccccceeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee eeecccc70cc0eeeeecccccc70cc0eeeeeeecccc70cc0eeeeecccccc70cc0eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee eeccffc7d07feeeeccccffc7d07feeeeeeccffc7d07feeeeccccffc7d07feeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee ecccfcc777ffeeeeeeccfcc777ffeeeeecccfcc777ffeeeeeeccfcc777ffeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee From 4596caaa01563c8e7b36b3d1e4d9e801ade864d6 Mon Sep 17 00:00:00 2001 From: huulong Date: Thu, 17 Dec 2020 22:54:23 +0100 Subject: [PATCH 02/91] [EMERALD] Reduced emerald size by 2px in both directions to free a sprite cell Adjusted emerald pivot, silhouette and placement on HUD --- data/builtin_data_ingame.p8 | 17 ++++++++--------- data/builtin_data_stage_clear.p8 | 16 ++++++++-------- data/builtin_data_titlemenu.p8 | 16 ++++++++-------- data/data_stage1_00.p8 | 16 ++++++++-------- data/data_stage1_01.p8 | 16 ++++++++-------- data/data_stage1_10.p8 | 16 ++++++++-------- data/data_stage1_11.p8 | 16 ++++++++-------- data/data_stage1_20.p8 | 16 ++++++++-------- data/data_stage1_21.p8 | 16 ++++++++-------- data/data_stage1_30.p8 | 16 ++++++++-------- data/data_stage1_31.p8 | 16 ++++++++-------- data/data_stage1_runtime.p8 | 16 ++++++++-------- src/resources/visual_common.lua | 8 ++++---- src/resources/visual_ingame_addon.lua | 4 ++-- src/resources/visual_titlemenu_addon.lua | 2 +- 15 files changed, 103 insertions(+), 104 deletions(-) diff --git a/data/builtin_data_ingame.p8 b/data/builtin_data_ingame.p8 index 0e48c7f1..c15deacd 100644 --- a/data/builtin_data_ingame.p8 +++ b/data/builtin_data_ingame.p8 @@ -127,14 +127,14 @@ eeeea994eeeeeeeeeeed65eeeeee999999999999aa944400d65694cdc0ca9a155194cdc0ca9a656d eeeaaaa94eeeeeeeeeed65eeeee99999999999aa99444000d656dcacac75471551dcacac7547656d3beb3ebe44444494b00bb00bbbbbbbab3333b33beeeeeeee eeaaaaaaa999eeeeeeed65eeee9999999999aa9449444400d6569ac7ca7a7a15519ac7ca7a7a656db3b33eb3449444443b0330b33b3bbbbbb33bee3beeeeeeee eaaaaaaaaaaa999eeeed65eee4949494999a944444444000d6564c000c094715514c000c0947656deebeebee444444440b0330b0eeebbebbbbeeeeeeeeeeeeee -eeeeeeeeeeeeeeeeeeeeeee949494449a99494444444440ed656c0ca7a77701551c0ca7a7770656deee888eeeeeeeeeeeeeeeeeeeeeeebbbbbeeeeeeeeeeeeee -eeeeeeeeeeeeeeeeeeeee99444449a9944444444444440eed65649a04f4977155149a04f4977656de7777788eeeeeeeeeeeeeeeeeeeeeebbbbaeeeeeeeeeeeee -eeeeeeeeeeeeeeeeeeee44449a994444444444994440eeeed6569afaf00a7a15519afaf00a7a656d777777888eeeeeeeeeeeeeeeeeeeeeeebbbbeeeeeeeeeeee -eeeeeeeeeeeeeeeeee4499994444494444944494444eeeeed656dcafaf05071551dcafaf0507656d222888222eeeeeeeeeeeeeeeeeeeeeeeebbbeeeeeeeeeeee -eeeeeeeeeeeeeeeea9994494499449444494444440eeeeeed65690cdf09a5d155190cdf09a5d656de2288822eeeeeeeeeeeeeeeeeeeeeeeeebbbbeeeeeeeeeee -eeeeeeeeeeee99994494444444944444449444444eeeeeeed656dc09d9d9d61551dc09d9d9d6656dee28882eeeeeeeeeeeeeeeeeeeeeeeeeeebbaeeeeeeeeeee -eeeeeeee44994494449444444494444444444400eeeeeeeed60d777777777775577777777777d06deee282eeeeeeeeeeeeeeeeeeeeeeeeeeeeebaeeeeeeeeeee -eeee44994444444444444444444444444440eeeeeeeeeeeeed6000000000000ee0000000000006deeeee2eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee +eeeeeeeeeeeeeeeeeeeeeee949494449a99494444444440ed656c0ca7a77701551c0ca7a7770656de77778eeeeeeeeeeeeeeeeeeeeeeebbbbbeeeeeeeeeeeeee +eeeeeeeeeeeeeeeeeeeee99444449a9944444444444440eed65649a04f4977155149a04f4977656d7777788eeeeeeeeeeeeeeeeeeeeeeebbbbaeeeeeeeeeeeee +eeeeeeeeeeeeeeeeeeee44449a994444444444994440eeeed6569afaf00a7a15519afaf00a7a656d2288822eeeeeeeeeeeeeeeeeeeeeeeeebbbbeeeeeeeeeeee +eeeeeeeeeeeeeeeeee4499994444494444944494444eeeeed656dcafaf05071551dcafaf0507656de28882eeeeeeeeeeeeeeeeeeeeeeeeeeebbbeeeeeeeeeeee +eeeeeeeeeeeeeeeea9994494499449444494444440eeeeeed65690cdf09a5d155190cdf09a5d656dee282eeeeeeeeeeeeeeeeeeeeeeeeeeeebbbbeeeeeeeeeee +eeeeeeeeeeee99994494444444944444449444444eeeeeeed656dc09d9d9d61551dc09d9d9d6656deee2eeeeeeeeeeeeeeeeeeeeeeeeeeeeeebbaeeeeeeeeeee +eeeeeeee44994494449444444494444444444400eeeeeeeed60d777777777775577777777777d06deeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeebaeeeeeeeeeee +eeee44994444444444444444444444444440eeeeeeeeeeeeed6000000000000ee0000000000006deeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee __gff__ 0041414141414141414141414141414141414141414141414141414141410000414141414141414141414141410000004141414141414141414141414141414141414141414141414141616180808000414040414141414141414040414141414545454343434343454541414141414145455149434343434545414141418080 0000000000000000000000000000000000000000000000000000000000000000000000000000000041418080800000000000000000000000414141800080414100000000000000000000008080804141000000000000000000004141808080804040404041410000000080418080800041414141414100000000000000008000 @@ -280,4 +280,3 @@ __music__ 04 39777879 00 32333637 04 34354075 - diff --git a/data/builtin_data_stage_clear.p8 b/data/builtin_data_stage_clear.p8 index 5b857ab5..c15deacd 100644 --- a/data/builtin_data_stage_clear.p8 +++ b/data/builtin_data_stage_clear.p8 @@ -127,14 +127,14 @@ eeeea994eeeeeeeeeeed65eeeeee999999999999aa944400d65694cdc0ca9a155194cdc0ca9a656d eeeaaaa94eeeeeeeeeed65eeeee99999999999aa99444000d656dcacac75471551dcacac7547656d3beb3ebe44444494b00bb00bbbbbbbab3333b33beeeeeeee eeaaaaaaa999eeeeeeed65eeee9999999999aa9449444400d6569ac7ca7a7a15519ac7ca7a7a656db3b33eb3449444443b0330b33b3bbbbbb33bee3beeeeeeee eaaaaaaaaaaa999eeeed65eee4949494999a944444444000d6564c000c094715514c000c0947656deebeebee444444440b0330b0eeebbebbbbeeeeeeeeeeeeee -eeeeeeeeeeeeeeeeeeeeeee949494449a99494444444440ed656c0ca7a77701551c0ca7a7770656deee888eeeeeeeeeeeeeeeeeeeeeeebbbbbeeeeeeeeeeeeee -eeeeeeeeeeeeeeeeeeeee99444449a9944444444444440eed65649a04f4977155149a04f4977656de7777788eeeeeeeeeeeeeeeeeeeeeebbbbaeeeeeeeeeeeee -eeeeeeeeeeeeeeeeeeee44449a994444444444994440eeeed6569afaf00a7a15519afaf00a7a656d777777888eeeeeeeeeeeeeeeeeeeeeeebbbbeeeeeeeeeeee -eeeeeeeeeeeeeeeeee4499994444494444944494444eeeeed656dcafaf05071551dcafaf0507656d222888222eeeeeeeeeeeeeeeeeeeeeeeebbbeeeeeeeeeeee -eeeeeeeeeeeeeeeea9994494499449444494444440eeeeeed65690cdf09a5d155190cdf09a5d656de2288822eeeeeeeeeeeeeeeeeeeeeeeeebbbbeeeeeeeeeee -eeeeeeeeeeee99994494444444944444449444444eeeeeeed656dc09d9d9d61551dc09d9d9d6656dee28882eeeeeeeeeeeeeeeeeeeeeeeeeeebbaeeeeeeeeeee -eeeeeeee44994494449444444494444444444400eeeeeeeed60d777777777775577777777777d06deee282eeeeeeeeeeeeeeeeeeeeeeeeeeeeebaeeeeeeeeeee -eeee44994444444444444444444444444440eeeeeeeeeeeeed6000000000000ee0000000000006deeeee2eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee +eeeeeeeeeeeeeeeeeeeeeee949494449a99494444444440ed656c0ca7a77701551c0ca7a7770656de77778eeeeeeeeeeeeeeeeeeeeeeebbbbbeeeeeeeeeeeeee +eeeeeeeeeeeeeeeeeeeee99444449a9944444444444440eed65649a04f4977155149a04f4977656d7777788eeeeeeeeeeeeeeeeeeeeeeebbbbaeeeeeeeeeeeee +eeeeeeeeeeeeeeeeeeee44449a994444444444994440eeeed6569afaf00a7a15519afaf00a7a656d2288822eeeeeeeeeeeeeeeeeeeeeeeeebbbbeeeeeeeeeeee +eeeeeeeeeeeeeeeeee4499994444494444944494444eeeeed656dcafaf05071551dcafaf0507656de28882eeeeeeeeeeeeeeeeeeeeeeeeeeebbbeeeeeeeeeeee +eeeeeeeeeeeeeeeea9994494499449444494444440eeeeeed65690cdf09a5d155190cdf09a5d656dee282eeeeeeeeeeeeeeeeeeeeeeeeeeeebbbbeeeeeeeeeee +eeeeeeeeeeee99994494444444944444449444444eeeeeeed656dc09d9d9d61551dc09d9d9d6656deee2eeeeeeeeeeeeeeeeeeeeeeeeeeeeeebbaeeeeeeeeeee +eeeeeeee44994494449444444494444444444400eeeeeeeed60d777777777775577777777777d06deeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeebaeeeeeeeeeee +eeee44994444444444444444444444444440eeeeeeeeeeeeed6000000000000ee0000000000006deeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee __gff__ 0041414141414141414141414141414141414141414141414141414141410000414141414141414141414141410000004141414141414141414141414141414141414141414141414141616180808000414040414141414141414040414141414545454343434343454541414141414145455149434343434545414141418080 0000000000000000000000000000000000000000000000000000000000000000000000000000000041418080800000000000000000000000414141800080414100000000000000000000008080804141000000000000000000004141808080804040404041410000000080418080800041414141414100000000000000008000 diff --git a/data/builtin_data_titlemenu.p8 b/data/builtin_data_titlemenu.p8 index aca2fec3..be25e598 100644 --- a/data/builtin_data_titlemenu.p8 +++ b/data/builtin_data_titlemenu.p8 @@ -5,14 +5,14 @@ __lua__ -- this is the built-in data cartridge -- for the titlemenu cartridge __gfx__ -00000000eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee5eeeeeeeee888eeeeeeeeeeee8eeeeeeeeeeeeeeeeee8eeeeeeeeee -00000000ee7eeeeeeeeee7eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee575eeeeee7777788eeeeeeeee272eeeeee2eeeeeeeee272eeeeee2ee -00700700eed7eee87eee7deeeeeeeee0000eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee5775eeee777777888eeeeeee877782eee272e2eeee287778ee2e272e -00077000eee7ee8008ee7eeeeeeeee0222700eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee57775eee222888222eeeeeeee272eeeeee2ee8eeeeee272eee8ee2ee -00077000edd7780970877ddeeeeee028887720eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee577775eee2288822eeeeeeeeee8ee2eeeeee272eee2ee8eee272eeee -00700700eedd78049087ddeeeeeee0888877820eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee57755eeeee28882eeeeeeeeeee2e272eee287778e272e2ee877782ee -00000000eeedde8008eddeeeeeeee0000000000eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee575eeeeeeee282eeeeeeeeeeeeeee2eeeeee272eee2eeeeee272eeee -00000000eeeeeee88eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee5eeeeeeeeee2eeeeeeeeeeeeeeeeeeeeeeee8eeeeeeeeeeee8eeeee +00000000eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee5eeeeeee77778eeeeeeeeeeee8eeeeeeeeeeeeeeeeee8eeeeeeeeee +00000000ee7eeeeeeeeee7eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee575eeeee7777788eeeeeeeeee272eeeeee2eeeeeeeee272eeeeee2ee +00700700eed7eee87eee7deeeeeeeee0000eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee5775eeee2288822eeeeeeeee877782eee272e2eeee287778ee2e272e +00077000eee7ee8008ee7eeeeeeeee0222700eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee57775eeee28882eeeeeeeeeee272eeeeee2ee8eeeeee272eee8ee2ee +00077000edd7780970877ddeeeeee028887720eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee577775eeee282eeeeeeeeeeeee8ee2eeeeee272eee2ee8eee272eeee +00700700eedd78049087ddeeeeeee0888877820eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee57755eeeeee2eeeeeeeeeeeeee2e272eee287778e272e2ee877782ee +00000000eeedde8008eddeeeeeeee0000000000eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee575eeeeeeeeeeeeeeeeeeeeeeeeee2eeeeee272eee2eeeeee272eeee +00000000eeeeeee88eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee5eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee8eeeeeeeeeeee8eeeee eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee diff --git a/data/data_stage1_00.p8 b/data/data_stage1_00.p8 index 6b2e8dde..c4d32f9d 100644 --- a/data/data_stage1_00.p8 +++ b/data/data_stage1_00.p8 @@ -127,14 +127,14 @@ eeeea994eeeeeeeeeeed65eeeeee999999999999aa944400d65694cdc0ca9a155194cdc0ca9a656d eeeaaaa94eeeeeeeeeed65eeeee99999999999aa99444000d656dcacac75471551dcacac7547656d3beb3ebe44444494b00bb00bbbbbbbab3333b33beeeeeeee eeaaaaaaa999eeeeeeed65eeee9999999999aa9449444400d6569ac7ca7a7a15519ac7ca7a7a656db3b33eb3449444443b0330b33b3bbbbbb33bee3beeeeeeee eaaaaaaaaaaa999eeeed65eee4949494999a944444444000d6564c000c094715514c000c0947656deebeebee444444440b0330b0eeebbebbbbeeeeeeeeeeeeee -eeeeeeeeeeeeeeeeeeeeeee949494449a99494444444440ed656c0ca7a77701551c0ca7a7770656deee888eeeeeeeeeeeeeeeeeeeeeeebbbbbeeeeeeeeeeeeee -eeeeeeeeeeeeeeeeeeeee99444449a9944444444444440eed65649a04f4977155149a04f4977656de7777788eeeeeeeeeeeeeeeeeeeeeebbbbaeeeeeeeeeeeee -eeeeeeeeeeeeeeeeeeee44449a994444444444994440eeeed6569afaf00a7a15519afaf00a7a656d777777888eeeeeeeeeeeeeeeeeeeeeeebbbbeeeeeeeeeeee -eeeeeeeeeeeeeeeeee4499994444494444944494444eeeeed656dcafaf05071551dcafaf0507656d222888222eeeeeeeeeeeeeeeeeeeeeeeebbbeeeeeeeeeeee -eeeeeeeeeeeeeeeea9994494499449444494444440eeeeeed65690cdf09a5d155190cdf09a5d656de2288822eeeeeeeeeeeeeeeeeeeeeeeeebbbbeeeeeeeeeee -eeeeeeeeeeee99994494444444944444449444444eeeeeeed656dc09d9d9d61551dc09d9d9d6656dee28882eeeeeeeeeeeeeeeeeeeeeeeeeeebbaeeeeeeeeeee -eeeeeeee44994494449444444494444444444400eeeeeeeed60d777777777775577777777777d06deee282eeeeeeeeeeeeeeeeeeeeeeeeeeeeebaeeeeeeeeeee -eeee44994444444444444444444444444440eeeeeeeeeeeeed6000000000000ee0000000000006deeeee2eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee +eeeeeeeeeeeeeeeeeeeeeee949494449a99494444444440ed656c0ca7a77701551c0ca7a7770656de77778eeeeeeeeeeeeeeeeeeeeeeebbbbbeeeeeeeeeeeeee +eeeeeeeeeeeeeeeeeeeee99444449a9944444444444440eed65649a04f4977155149a04f4977656d7777788eeeeeeeeeeeeeeeeeeeeeeebbbbaeeeeeeeeeeeee +eeeeeeeeeeeeeeeeeeee44449a994444444444994440eeeed6569afaf00a7a15519afaf00a7a656d2288822eeeeeeeeeeeeeeeeeeeeeeeeebbbbeeeeeeeeeeee +eeeeeeeeeeeeeeeeee4499994444494444944494444eeeeed656dcafaf05071551dcafaf0507656de28882eeeeeeeeeeeeeeeeeeeeeeeeeeebbbeeeeeeeeeeee +eeeeeeeeeeeeeeeea9994494499449444494444440eeeeeed65690cdf09a5d155190cdf09a5d656dee282eeeeeeeeeeeeeeeeeeeeeeeeeeeebbbbeeeeeeeeeee +eeeeeeeeeeee99994494444444944444449444444eeeeeeed656dc09d9d9d61551dc09d9d9d6656deee2eeeeeeeeeeeeeeeeeeeeeeeeeeeeeebbaeeeeeeeeeee +eeeeeeee44994494449444444494444444444400eeeeeeeed60d777777777775577777777777d06deeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeebaeeeeeeeeeee +eeee44994444444444444444444444444440eeeeeeeeeeeeed6000000000000ee0000000000006deeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee __gff__ 0041414141414141414141414141414141414141414141414141414141410000414141414141414141414141410000004141414141414141414141414141414141414141414141414141616180808000414040414141414141414040414141414545454343434343454541414141414145455149434343434545414141418080 0000000000000000000000000000000000000000000000000000000000000000000000000000000041418080800000000000000000000000414141800080414100000000000000000000008080804141000000000000000000004141808080804040404041410000000080418080800041414141414100000000000000008000 diff --git a/data/data_stage1_01.p8 b/data/data_stage1_01.p8 index 959a278e..f3a990de 100644 --- a/data/data_stage1_01.p8 +++ b/data/data_stage1_01.p8 @@ -127,14 +127,14 @@ eeeea994eeeeeeeeeeed65eeeeee999999999999aa944400d65694cdc0ca9a155194cdc0ca9a656d eeeaaaa94eeeeeeeeeed65eeeee99999999999aa99444000d656dcacac75471551dcacac7547656d3beb3ebe44444494b00bb00bbbbbbbab3333b33beeeeeeee eeaaaaaaa999eeeeeeed65eeee9999999999aa9449444400d6569ac7ca7a7a15519ac7ca7a7a656db3b33eb3449444443b0330b33b3bbbbbb33bee3beeeeeeee eaaaaaaaaaaa999eeeed65eee4949494999a944444444000d6564c000c094715514c000c0947656deebeebee444444440b0330b0eeebbebbbbeeeeeeeeeeeeee -eeeeeeeeeeeeeeeeeeeeeee949494449a99494444444440ed656c0ca7a77701551c0ca7a7770656deee888eeeeeeeeeeeeeeeeeeeeeeebbbbbeeeeeeeeeeeeee -eeeeeeeeeeeeeeeeeeeee99444449a9944444444444440eed65649a04f4977155149a04f4977656de7777788eeeeeeeeeeeeeeeeeeeeeebbbbaeeeeeeeeeeeee -eeeeeeeeeeeeeeeeeeee44449a994444444444994440eeeed6569afaf00a7a15519afaf00a7a656d777777888eeeeeeeeeeeeeeeeeeeeeeebbbbeeeeeeeeeeee -eeeeeeeeeeeeeeeeee4499994444494444944494444eeeeed656dcafaf05071551dcafaf0507656d222888222eeeeeeeeeeeeeeeeeeeeeeeebbbeeeeeeeeeeee -eeeeeeeeeeeeeeeea9994494499449444494444440eeeeeed65690cdf09a5d155190cdf09a5d656de2288822eeeeeeeeeeeeeeeeeeeeeeeeebbbbeeeeeeeeeee -eeeeeeeeeeee99994494444444944444449444444eeeeeeed656dc09d9d9d61551dc09d9d9d6656dee28882eeeeeeeeeeeeeeeeeeeeeeeeeeebbaeeeeeeeeeee -eeeeeeee44994494449444444494444444444400eeeeeeeed60d777777777775577777777777d06deee282eeeeeeeeeeeeeeeeeeeeeeeeeeeeebaeeeeeeeeeee -eeee44994444444444444444444444444440eeeeeeeeeeeeed6000000000000ee0000000000006deeeee2eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee +eeeeeeeeeeeeeeeeeeeeeee949494449a99494444444440ed656c0ca7a77701551c0ca7a7770656de77778eeeeeeeeeeeeeeeeeeeeeeebbbbbeeeeeeeeeeeeee +eeeeeeeeeeeeeeeeeeeee99444449a9944444444444440eed65649a04f4977155149a04f4977656d7777788eeeeeeeeeeeeeeeeeeeeeeebbbbaeeeeeeeeeeeee +eeeeeeeeeeeeeeeeeeee44449a994444444444994440eeeed6569afaf00a7a15519afaf00a7a656d2288822eeeeeeeeeeeeeeeeeeeeeeeeebbbbeeeeeeeeeeee +eeeeeeeeeeeeeeeeee4499994444494444944494444eeeeed656dcafaf05071551dcafaf0507656de28882eeeeeeeeeeeeeeeeeeeeeeeeeeebbbeeeeeeeeeeee +eeeeeeeeeeeeeeeea9994494499449444494444440eeeeeed65690cdf09a5d155190cdf09a5d656dee282eeeeeeeeeeeeeeeeeeeeeeeeeeeebbbbeeeeeeeeeee +eeeeeeeeeeee99994494444444944444449444444eeeeeeed656dc09d9d9d61551dc09d9d9d6656deee2eeeeeeeeeeeeeeeeeeeeeeeeeeeeeebbaeeeeeeeeeee +eeeeeeee44994494449444444494444444444400eeeeeeeed60d777777777775577777777777d06deeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeebaeeeeeeeeeee +eeee44994444444444444444444444444440eeeeeeeeeeeeed6000000000000ee0000000000006deeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee __gff__ 0041414141414141414141414141414141414141414141414141414141410000414141414141414141414141410000004141414141414141414141414141414141414141414141414141616180808000414040414141414141414040414141414545454343434343454541414141414145455149434343434545414141418080 0000000000000000000000000000000000000000000000000000000000000000000000000000000041418080800000000000000000000000414141800080414100000000000000000000008080804141000000000000000000004141808080804040404041410000000080418080800041414141414100000000000000008000 diff --git a/data/data_stage1_10.p8 b/data/data_stage1_10.p8 index f11e7c77..170ed3f5 100644 --- a/data/data_stage1_10.p8 +++ b/data/data_stage1_10.p8 @@ -127,14 +127,14 @@ eeeea994eeeeeeeeeeed65eeeeee999999999999aa944400d65694cdc0ca9a155194cdc0ca9a656d eeeaaaa94eeeeeeeeeed65eeeee99999999999aa99444000d656dcacac75471551dcacac7547656d3beb3ebe44444494b00bb00bbbbbbbab3333b33beeeeeeee eeaaaaaaa999eeeeeeed65eeee9999999999aa9449444400d6569ac7ca7a7a15519ac7ca7a7a656db3b33eb3449444443b0330b33b3bbbbbb33bee3beeeeeeee eaaaaaaaaaaa999eeeed65eee4949494999a944444444000d6564c000c094715514c000c0947656deebeebee444444440b0330b0eeebbebbbbeeeeeeeeeeeeee -eeeeeeeeeeeeeeeeeeeeeee949494449a99494444444440ed656c0ca7a77701551c0ca7a7770656deee888eeeeeeeeeeeeeeeeeeeeeeebbbbbeeeeeeeeeeeeee -eeeeeeeeeeeeeeeeeeeee99444449a9944444444444440eed65649a04f4977155149a04f4977656de7777788eeeeeeeeeeeeeeeeeeeeeebbbbaeeeeeeeeeeeee -eeeeeeeeeeeeeeeeeeee44449a994444444444994440eeeed6569afaf00a7a15519afaf00a7a656d777777888eeeeeeeeeeeeeeeeeeeeeeebbbbeeeeeeeeeeee -eeeeeeeeeeeeeeeeee4499994444494444944494444eeeeed656dcafaf05071551dcafaf0507656d222888222eeeeeeeeeeeeeeeeeeeeeeeebbbeeeeeeeeeeee -eeeeeeeeeeeeeeeea9994494499449444494444440eeeeeed65690cdf09a5d155190cdf09a5d656de2288822eeeeeeeeeeeeeeeeeeeeeeeeebbbbeeeeeeeeeee -eeeeeeeeeeee99994494444444944444449444444eeeeeeed656dc09d9d9d61551dc09d9d9d6656dee28882eeeeeeeeeeeeeeeeeeeeeeeeeeebbaeeeeeeeeeee -eeeeeeee44994494449444444494444444444400eeeeeeeed60d777777777775577777777777d06deee282eeeeeeeeeeeeeeeeeeeeeeeeeeeeebaeeeeeeeeeee -eeee44994444444444444444444444444440eeeeeeeeeeeeed6000000000000ee0000000000006deeeee2eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee +eeeeeeeeeeeeeeeeeeeeeee949494449a99494444444440ed656c0ca7a77701551c0ca7a7770656de77778eeeeeeeeeeeeeeeeeeeeeeebbbbbeeeeeeeeeeeeee +eeeeeeeeeeeeeeeeeeeee99444449a9944444444444440eed65649a04f4977155149a04f4977656d7777788eeeeeeeeeeeeeeeeeeeeeeebbbbaeeeeeeeeeeeee +eeeeeeeeeeeeeeeeeeee44449a994444444444994440eeeed6569afaf00a7a15519afaf00a7a656d2288822eeeeeeeeeeeeeeeeeeeeeeeeebbbbeeeeeeeeeeee +eeeeeeeeeeeeeeeeee4499994444494444944494444eeeeed656dcafaf05071551dcafaf0507656de28882eeeeeeeeeeeeeeeeeeeeeeeeeeebbbeeeeeeeeeeee +eeeeeeeeeeeeeeeea9994494499449444494444440eeeeeed65690cdf09a5d155190cdf09a5d656dee282eeeeeeeeeeeeeeeeeeeeeeeeeeeebbbbeeeeeeeeeee +eeeeeeeeeeee99994494444444944444449444444eeeeeeed656dc09d9d9d61551dc09d9d9d6656deee2eeeeeeeeeeeeeeeeeeeeeeeeeeeeeebbaeeeeeeeeeee +eeeeeeee44994494449444444494444444444400eeeeeeeed60d777777777775577777777777d06deeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeebaeeeeeeeeeee +eeee44994444444444444444444444444440eeeeeeeeeeeeed6000000000000ee0000000000006deeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee __gff__ 0041414141414141414141414141414141414141414141414141414141410000414141414141414141414141410000004141414141414141414141414141414141414141414141414141616180808000414040414141414141414040414141414545454343434343454541414141414145455149434343434545414141418080 0000000000000000000000000000000000000000000000000000000000000000000000000000000041418080800000000000000000000000414141800080414100000000000000000000008080804141000000000000000000004141808080804040404041410000000080418080800041414141414100000000000000008000 diff --git a/data/data_stage1_11.p8 b/data/data_stage1_11.p8 index 27809ce6..2b03e9a9 100644 --- a/data/data_stage1_11.p8 +++ b/data/data_stage1_11.p8 @@ -127,14 +127,14 @@ eeeea994eeeeeeeeeeed65eeeeee999999999999aa944400d65694cdc0ca9a155194cdc0ca9a656d eeeaaaa94eeeeeeeeeed65eeeee99999999999aa99444000d656dcacac75471551dcacac7547656d3beb3ebe44444494b00bb00bbbbbbbab3333b33beeeeeeee eeaaaaaaa999eeeeeeed65eeee9999999999aa9449444400d6569ac7ca7a7a15519ac7ca7a7a656db3b33eb3449444443b0330b33b3bbbbbb33bee3beeeeeeee eaaaaaaaaaaa999eeeed65eee4949494999a944444444000d6564c000c094715514c000c0947656deebeebee444444440b0330b0eeebbebbbbeeeeeeeeeeeeee -eeeeeeeeeeeeeeeeeeeeeee949494449a99494444444440ed656c0ca7a77701551c0ca7a7770656deee888eeeeeeeeeeeeeeeeeeeeeeebbbbbeeeeeeeeeeeeee -eeeeeeeeeeeeeeeeeeeee99444449a9944444444444440eed65649a04f4977155149a04f4977656de7777788eeeeeeeeeeeeeeeeeeeeeebbbbaeeeeeeeeeeeee -eeeeeeeeeeeeeeeeeeee44449a994444444444994440eeeed6569afaf00a7a15519afaf00a7a656d777777888eeeeeeeeeeeeeeeeeeeeeeebbbbeeeeeeeeeeee -eeeeeeeeeeeeeeeeee4499994444494444944494444eeeeed656dcafaf05071551dcafaf0507656d222888222eeeeeeeeeeeeeeeeeeeeeeeebbbeeeeeeeeeeee -eeeeeeeeeeeeeeeea9994494499449444494444440eeeeeed65690cdf09a5d155190cdf09a5d656de2288822eeeeeeeeeeeeeeeeeeeeeeeeebbbbeeeeeeeeeee -eeeeeeeeeeee99994494444444944444449444444eeeeeeed656dc09d9d9d61551dc09d9d9d6656dee28882eeeeeeeeeeeeeeeeeeeeeeeeeeebbaeeeeeeeeeee -eeeeeeee44994494449444444494444444444400eeeeeeeed60d777777777775577777777777d06deee282eeeeeeeeeeeeeeeeeeeeeeeeeeeeebaeeeeeeeeeee -eeee44994444444444444444444444444440eeeeeeeeeeeeed6000000000000ee0000000000006deeeee2eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee +eeeeeeeeeeeeeeeeeeeeeee949494449a99494444444440ed656c0ca7a77701551c0ca7a7770656de77778eeeeeeeeeeeeeeeeeeeeeeebbbbbeeeeeeeeeeeeee +eeeeeeeeeeeeeeeeeeeee99444449a9944444444444440eed65649a04f4977155149a04f4977656d7777788eeeeeeeeeeeeeeeeeeeeeeebbbbaeeeeeeeeeeeee +eeeeeeeeeeeeeeeeeeee44449a994444444444994440eeeed6569afaf00a7a15519afaf00a7a656d2288822eeeeeeeeeeeeeeeeeeeeeeeeebbbbeeeeeeeeeeee +eeeeeeeeeeeeeeeeee4499994444494444944494444eeeeed656dcafaf05071551dcafaf0507656de28882eeeeeeeeeeeeeeeeeeeeeeeeeeebbbeeeeeeeeeeee +eeeeeeeeeeeeeeeea9994494499449444494444440eeeeeed65690cdf09a5d155190cdf09a5d656dee282eeeeeeeeeeeeeeeeeeeeeeeeeeeebbbbeeeeeeeeeee +eeeeeeeeeeee99994494444444944444449444444eeeeeeed656dc09d9d9d61551dc09d9d9d6656deee2eeeeeeeeeeeeeeeeeeeeeeeeeeeeeebbaeeeeeeeeeee +eeeeeeee44994494449444444494444444444400eeeeeeeed60d777777777775577777777777d06deeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeebaeeeeeeeeeee +eeee44994444444444444444444444444440eeeeeeeeeeeeed6000000000000ee0000000000006deeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee __gff__ 0041414141414141414141414141414141414141414141414141414141410000414141414141414141414141410000004141414141414141414141414141414141414141414141414141616180808000414040414141414141414040414141414545454343434343454541414141414145455149434343434545414141418080 0000000000000000000000000000000000000000000000000000000000000000000000000000000041418080800000000000000000000000414141800080414100000000000000000000008080804141000000000000000000004141808080804040404041410000000080418080800041414141414100000000000000008000 diff --git a/data/data_stage1_20.p8 b/data/data_stage1_20.p8 index d9ba0a86..3f4fecd5 100644 --- a/data/data_stage1_20.p8 +++ b/data/data_stage1_20.p8 @@ -127,14 +127,14 @@ eeeea994eeeeeeeeeeed65eeeeee999999999999aa944400d65694cdc0ca9a155194cdc0ca9a656d eeeaaaa94eeeeeeeeeed65eeeee99999999999aa99444000d656dcacac75471551dcacac7547656d3beb3ebe44444494b00bb00bbbbbbbab3333b33beeeeeeee eeaaaaaaa999eeeeeeed65eeee9999999999aa9449444400d6569ac7ca7a7a15519ac7ca7a7a656db3b33eb3449444443b0330b33b3bbbbbb33bee3beeeeeeee eaaaaaaaaaaa999eeeed65eee4949494999a944444444000d6564c000c094715514c000c0947656deebeebee444444440b0330b0eeebbebbbbeeeeeeeeeeeeee -eeeeeeeeeeeeeeeeeeeeeee949494449a99494444444440ed656c0ca7a77701551c0ca7a7770656deee888eeeeeeeeeeeeeeeeeeeeeeebbbbbeeeeeeeeeeeeee -eeeeeeeeeeeeeeeeeeeee99444449a9944444444444440eed65649a04f4977155149a04f4977656de7777788eeeeeeeeeeeeeeeeeeeeeebbbbaeeeeeeeeeeeee -eeeeeeeeeeeeeeeeeeee44449a994444444444994440eeeed6569afaf00a7a15519afaf00a7a656d777777888eeeeeeeeeeeeeeeeeeeeeeebbbbeeeeeeeeeeee -eeeeeeeeeeeeeeeeee4499994444494444944494444eeeeed656dcafaf05071551dcafaf0507656d222888222eeeeeeeeeeeeeeeeeeeeeeeebbbeeeeeeeeeeee -eeeeeeeeeeeeeeeea9994494499449444494444440eeeeeed65690cdf09a5d155190cdf09a5d656de2288822eeeeeeeeeeeeeeeeeeeeeeeeebbbbeeeeeeeeeee -eeeeeeeeeeee99994494444444944444449444444eeeeeeed656dc09d9d9d61551dc09d9d9d6656dee28882eeeeeeeeeeeeeeeeeeeeeeeeeeebbaeeeeeeeeeee -eeeeeeee44994494449444444494444444444400eeeeeeeed60d777777777775577777777777d06deee282eeeeeeeeeeeeeeeeeeeeeeeeeeeeebaeeeeeeeeeee -eeee44994444444444444444444444444440eeeeeeeeeeeeed6000000000000ee0000000000006deeeee2eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee +eeeeeeeeeeeeeeeeeeeeeee949494449a99494444444440ed656c0ca7a77701551c0ca7a7770656de77778eeeeeeeeeeeeeeeeeeeeeeebbbbbeeeeeeeeeeeeee +eeeeeeeeeeeeeeeeeeeee99444449a9944444444444440eed65649a04f4977155149a04f4977656d7777788eeeeeeeeeeeeeeeeeeeeeeebbbbaeeeeeeeeeeeee +eeeeeeeeeeeeeeeeeeee44449a994444444444994440eeeed6569afaf00a7a15519afaf00a7a656d2288822eeeeeeeeeeeeeeeeeeeeeeeeebbbbeeeeeeeeeeee +eeeeeeeeeeeeeeeeee4499994444494444944494444eeeeed656dcafaf05071551dcafaf0507656de28882eeeeeeeeeeeeeeeeeeeeeeeeeeebbbeeeeeeeeeeee +eeeeeeeeeeeeeeeea9994494499449444494444440eeeeeed65690cdf09a5d155190cdf09a5d656dee282eeeeeeeeeeeeeeeeeeeeeeeeeeeebbbbeeeeeeeeeee +eeeeeeeeeeee99994494444444944444449444444eeeeeeed656dc09d9d9d61551dc09d9d9d6656deee2eeeeeeeeeeeeeeeeeeeeeeeeeeeeeebbaeeeeeeeeeee +eeeeeeee44994494449444444494444444444400eeeeeeeed60d777777777775577777777777d06deeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeebaeeeeeeeeeee +eeee44994444444444444444444444444440eeeeeeeeeeeeed6000000000000ee0000000000006deeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee __gff__ 0041414141414141414141414141414141414141414141414141414141410000414141414141414141414141410000004141414141414141414141414141414141414141414141414141616180808000414040414141414141414040414141414545454343434343454541414141414145455149434343434545414141418080 0000000000000000000000000000000000000000000000000000000000000000000000000000000041418080800000000000000000000000414141800080414100000000000000000000008080804141000000000000000000004141808080804040404041410000000080418080800041414141414100000000000000008000 diff --git a/data/data_stage1_21.p8 b/data/data_stage1_21.p8 index fbb40926..d3612910 100644 --- a/data/data_stage1_21.p8 +++ b/data/data_stage1_21.p8 @@ -127,14 +127,14 @@ eeeea994eeeeeeeeeeed65eeeeee999999999999aa944400d65694cdc0ca9a155194cdc0ca9a656d eeeaaaa94eeeeeeeeeed65eeeee99999999999aa99444000d656dcacac75471551dcacac7547656d3beb3ebe44444494b00bb00bbbbbbbab3333b33beeeeeeee eeaaaaaaa999eeeeeeed65eeee9999999999aa9449444400d6569ac7ca7a7a15519ac7ca7a7a656db3b33eb3449444443b0330b33b3bbbbbb33bee3beeeeeeee eaaaaaaaaaaa999eeeed65eee4949494999a944444444000d6564c000c094715514c000c0947656deebeebee444444440b0330b0eeebbebbbbeeeeeeeeeeeeee -eeeeeeeeeeeeeeeeeeeeeee949494449a99494444444440ed656c0ca7a77701551c0ca7a7770656deee888eeeeeeeeeeeeeeeeeeeeeeebbbbbeeeeeeeeeeeeee -eeeeeeeeeeeeeeeeeeeee99444449a9944444444444440eed65649a04f4977155149a04f4977656de7777788eeeeeeeeeeeeeeeeeeeeeebbbbaeeeeeeeeeeeee -eeeeeeeeeeeeeeeeeeee44449a994444444444994440eeeed6569afaf00a7a15519afaf00a7a656d777777888eeeeeeeeeeeeeeeeeeeeeeebbbbeeeeeeeeeeee -eeeeeeeeeeeeeeeeee4499994444494444944494444eeeeed656dcafaf05071551dcafaf0507656d222888222eeeeeeeeeeeeeeeeeeeeeeeebbbeeeeeeeeeeee -eeeeeeeeeeeeeeeea9994494499449444494444440eeeeeed65690cdf09a5d155190cdf09a5d656de2288822eeeeeeeeeeeeeeeeeeeeeeeeebbbbeeeeeeeeeee -eeeeeeeeeeee99994494444444944444449444444eeeeeeed656dc09d9d9d61551dc09d9d9d6656dee28882eeeeeeeeeeeeeeeeeeeeeeeeeeebbaeeeeeeeeeee -eeeeeeee44994494449444444494444444444400eeeeeeeed60d777777777775577777777777d06deee282eeeeeeeeeeeeeeeeeeeeeeeeeeeeebaeeeeeeeeeee -eeee44994444444444444444444444444440eeeeeeeeeeeeed6000000000000ee0000000000006deeeee2eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee +eeeeeeeeeeeeeeeeeeeeeee949494449a99494444444440ed656c0ca7a77701551c0ca7a7770656de77778eeeeeeeeeeeeeeeeeeeeeeebbbbbeeeeeeeeeeeeee +eeeeeeeeeeeeeeeeeeeee99444449a9944444444444440eed65649a04f4977155149a04f4977656d7777788eeeeeeeeeeeeeeeeeeeeeeebbbbaeeeeeeeeeeeee +eeeeeeeeeeeeeeeeeeee44449a994444444444994440eeeed6569afaf00a7a15519afaf00a7a656d2288822eeeeeeeeeeeeeeeeeeeeeeeeebbbbeeeeeeeeeeee +eeeeeeeeeeeeeeeeee4499994444494444944494444eeeeed656dcafaf05071551dcafaf0507656de28882eeeeeeeeeeeeeeeeeeeeeeeeeeebbbeeeeeeeeeeee +eeeeeeeeeeeeeeeea9994494499449444494444440eeeeeed65690cdf09a5d155190cdf09a5d656dee282eeeeeeeeeeeeeeeeeeeeeeeeeeeebbbbeeeeeeeeeee +eeeeeeeeeeee99994494444444944444449444444eeeeeeed656dc09d9d9d61551dc09d9d9d6656deee2eeeeeeeeeeeeeeeeeeeeeeeeeeeeeebbaeeeeeeeeeee +eeeeeeee44994494449444444494444444444400eeeeeeeed60d777777777775577777777777d06deeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeebaeeeeeeeeeee +eeee44994444444444444444444444444440eeeeeeeeeeeeed6000000000000ee0000000000006deeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee __gff__ 0041414141414141414141414141414141414141414141414141414141410000414141414141414141414141410000004141414141414141414141414141414141414141414141414141616180808000414040414141414141414040414141414545454343434343454541414141414145455149434343434545414141418080 0000000000000000000000000000000000000000000000000000000000000000000000000000000041418080800000000000000000000000414141800080414100000000000000000000008080804141000000000000000000004141808080804040404041410000000080418080800041414141414100000000000000008000 diff --git a/data/data_stage1_30.p8 b/data/data_stage1_30.p8 index ffde5cd9..f94cf49c 100644 --- a/data/data_stage1_30.p8 +++ b/data/data_stage1_30.p8 @@ -127,14 +127,14 @@ eeeea994eeeeeeeeeeed65eeeeee999999999999aa944400d65694cdc0ca9a155194cdc0ca9a656d eeeaaaa94eeeeeeeeeed65eeeee99999999999aa99444000d656dcacac75471551dcacac7547656d3beb3ebe44444494b00bb00bbbbbbbab3333b33beeeeeeee eeaaaaaaa999eeeeeeed65eeee9999999999aa9449444400d6569ac7ca7a7a15519ac7ca7a7a656db3b33eb3449444443b0330b33b3bbbbbb33bee3beeeeeeee eaaaaaaaaaaa999eeeed65eee4949494999a944444444000d6564c000c094715514c000c0947656deebeebee444444440b0330b0eeebbebbbbeeeeeeeeeeeeee -eeeeeeeeeeeeeeeeeeeeeee949494449a99494444444440ed656c0ca7a77701551c0ca7a7770656deee888eeeeeeeeeeeeeeeeeeeeeeebbbbbeeeeeeeeeeeeee -eeeeeeeeeeeeeeeeeeeee99444449a9944444444444440eed65649a04f4977155149a04f4977656de7777788eeeeeeeeeeeeeeeeeeeeeebbbbaeeeeeeeeeeeee -eeeeeeeeeeeeeeeeeeee44449a994444444444994440eeeed6569afaf00a7a15519afaf00a7a656d777777888eeeeeeeeeeeeeeeeeeeeeeebbbbeeeeeeeeeeee -eeeeeeeeeeeeeeeeee4499994444494444944494444eeeeed656dcafaf05071551dcafaf0507656d222888222eeeeeeeeeeeeeeeeeeeeeeeebbbeeeeeeeeeeee -eeeeeeeeeeeeeeeea9994494499449444494444440eeeeeed65690cdf09a5d155190cdf09a5d656de2288822eeeeeeeeeeeeeeeeeeeeeeeeebbbbeeeeeeeeeee -eeeeeeeeeeee99994494444444944444449444444eeeeeeed656dc09d9d9d61551dc09d9d9d6656dee28882eeeeeeeeeeeeeeeeeeeeeeeeeeebbaeeeeeeeeeee -eeeeeeee44994494449444444494444444444400eeeeeeeed60d777777777775577777777777d06deee282eeeeeeeeeeeeeeeeeeeeeeeeeeeeebaeeeeeeeeeee -eeee44994444444444444444444444444440eeeeeeeeeeeeed6000000000000ee0000000000006deeeee2eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee +eeeeeeeeeeeeeeeeeeeeeee949494449a99494444444440ed656c0ca7a77701551c0ca7a7770656de77778eeeeeeeeeeeeeeeeeeeeeeebbbbbeeeeeeeeeeeeee +eeeeeeeeeeeeeeeeeeeee99444449a9944444444444440eed65649a04f4977155149a04f4977656d7777788eeeeeeeeeeeeeeeeeeeeeeebbbbaeeeeeeeeeeeee +eeeeeeeeeeeeeeeeeeee44449a994444444444994440eeeed6569afaf00a7a15519afaf00a7a656d2288822eeeeeeeeeeeeeeeeeeeeeeeeebbbbeeeeeeeeeeee +eeeeeeeeeeeeeeeeee4499994444494444944494444eeeeed656dcafaf05071551dcafaf0507656de28882eeeeeeeeeeeeeeeeeeeeeeeeeeebbbeeeeeeeeeeee +eeeeeeeeeeeeeeeea9994494499449444494444440eeeeeed65690cdf09a5d155190cdf09a5d656dee282eeeeeeeeeeeeeeeeeeeeeeeeeeeebbbbeeeeeeeeeee +eeeeeeeeeeee99994494444444944444449444444eeeeeeed656dc09d9d9d61551dc09d9d9d6656deee2eeeeeeeeeeeeeeeeeeeeeeeeeeeeeebbaeeeeeeeeeee +eeeeeeee44994494449444444494444444444400eeeeeeeed60d777777777775577777777777d06deeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeebaeeeeeeeeeee +eeee44994444444444444444444444444440eeeeeeeeeeeeed6000000000000ee0000000000006deeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee __gff__ 0041414141414141414141414141414141414141414141414141414141410000414141414141414141414141410000004141414141414141414141414141414141414141414141414141616180808000414040414141414141414040414141414545454343434343454541414141414145455149434343434545414141418080 0000000000000000000000000000000000000000000000000000000000000000000000000000000041418080800000000000000000000000414141800080414100000000000000000000008080804141000000000000000000004141808080804040404041410000000080418080800041414141414100000000000000008000 diff --git a/data/data_stage1_31.p8 b/data/data_stage1_31.p8 index a79b918a..ed807528 100644 --- a/data/data_stage1_31.p8 +++ b/data/data_stage1_31.p8 @@ -127,14 +127,14 @@ eeeea994eeeeeeeeeeed65eeeeee999999999999aa944400d65694cdc0ca9a155194cdc0ca9a656d eeeaaaa94eeeeeeeeeed65eeeee99999999999aa99444000d656dcacac75471551dcacac7547656d3beb3ebe44444494b00bb00bbbbbbbab3333b33beeeeeeee eeaaaaaaa999eeeeeeed65eeee9999999999aa9449444400d6569ac7ca7a7a15519ac7ca7a7a656db3b33eb3449444443b0330b33b3bbbbbb33bee3beeeeeeee eaaaaaaaaaaa999eeeed65eee4949494999a944444444000d6564c000c094715514c000c0947656deebeebee444444440b0330b0eeebbebbbbeeeeeeeeeeeeee -eeeeeeeeeeeeeeeeeeeeeee949494449a99494444444440ed656c0ca7a77701551c0ca7a7770656deee888eeeeeeeeeeeeeeeeeeeeeeebbbbbeeeeeeeeeeeeee -eeeeeeeeeeeeeeeeeeeee99444449a9944444444444440eed65649a04f4977155149a04f4977656de7777788eeeeeeeeeeeeeeeeeeeeeebbbbaeeeeeeeeeeeee -eeeeeeeeeeeeeeeeeeee44449a994444444444994440eeeed6569afaf00a7a15519afaf00a7a656d777777888eeeeeeeeeeeeeeeeeeeeeeebbbbeeeeeeeeeeee -eeeeeeeeeeeeeeeeee4499994444494444944494444eeeeed656dcafaf05071551dcafaf0507656d222888222eeeeeeeeeeeeeeeeeeeeeeeebbbeeeeeeeeeeee -eeeeeeeeeeeeeeeea9994494499449444494444440eeeeeed65690cdf09a5d155190cdf09a5d656de2288822eeeeeeeeeeeeeeeeeeeeeeeeebbbbeeeeeeeeeee -eeeeeeeeeeee99994494444444944444449444444eeeeeeed656dc09d9d9d61551dc09d9d9d6656dee28882eeeeeeeeeeeeeeeeeeeeeeeeeeebbaeeeeeeeeeee -eeeeeeee44994494449444444494444444444400eeeeeeeed60d777777777775577777777777d06deee282eeeeeeeeeeeeeeeeeeeeeeeeeeeeebaeeeeeeeeeee -eeee44994444444444444444444444444440eeeeeeeeeeeeed6000000000000ee0000000000006deeeee2eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee +eeeeeeeeeeeeeeeeeeeeeee949494449a99494444444440ed656c0ca7a77701551c0ca7a7770656de77778eeeeeeeeeeeeeeeeeeeeeeebbbbbeeeeeeeeeeeeee +eeeeeeeeeeeeeeeeeeeee99444449a9944444444444440eed65649a04f4977155149a04f4977656d7777788eeeeeeeeeeeeeeeeeeeeeeebbbbaeeeeeeeeeeeee +eeeeeeeeeeeeeeeeeeee44449a994444444444994440eeeed6569afaf00a7a15519afaf00a7a656d2288822eeeeeeeeeeeeeeeeeeeeeeeeebbbbeeeeeeeeeeee +eeeeeeeeeeeeeeeeee4499994444494444944494444eeeeed656dcafaf05071551dcafaf0507656de28882eeeeeeeeeeeeeeeeeeeeeeeeeeebbbeeeeeeeeeeee +eeeeeeeeeeeeeeeea9994494499449444494444440eeeeeed65690cdf09a5d155190cdf09a5d656dee282eeeeeeeeeeeeeeeeeeeeeeeeeeeebbbbeeeeeeeeeee +eeeeeeeeeeee99994494444444944444449444444eeeeeeed656dc09d9d9d61551dc09d9d9d6656deee2eeeeeeeeeeeeeeeeeeeeeeeeeeeeeebbaeeeeeeeeeee +eeeeeeee44994494449444444494444444444400eeeeeeeed60d777777777775577777777777d06deeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeebaeeeeeeeeeee +eeee44994444444444444444444444444440eeeeeeeeeeeeed6000000000000ee0000000000006deeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee __gff__ 0041414141414141414141414141414141414141414141414141414141410000414141414141414141414141410000004141414141414141414141414141414141414141414141414141616180808000414040414141414141414040414141414545454343434343454541414141414145455149434343434545414141418080 0000000000000000000000000000000000000000000000000000000000000000000000000000000041418080800000000000000000000000414141800080414100000000000000000000008080804141000000000000000000004141808080804040404041410000000080418080800041414141414100000000000000008000 diff --git a/data/data_stage1_runtime.p8 b/data/data_stage1_runtime.p8 index 36245ea4..feea5291 100644 --- a/data/data_stage1_runtime.p8 +++ b/data/data_stage1_runtime.p8 @@ -3,14 +3,14 @@ version 29 __lua__ __gfx__ -00000000eeeeebbbeeeeeeeeeeeeeeeeeeed65eeeeeeeeeeeeeeeccccced65eeeeeeeeeee5eeeeeeeee000eeeeeeeeeeee8eeeeeeeeeeeeeeeeee8eeeeeeeeee -00000000eeeebbbeeeeeeeeeed66666666666666666666deed66cccccccc6666666666de575eeeeee00eee00eeeeeeeee272eeeeee2eeeeeeeee272eeeeee2ee -00700700eeebbbb7eeebeeee56000000000000000000006556000cc4ccccc0c0000000655775eeee0eeeeeee0eeeeeee877782eee272e2eeee287778ee2e272e -00077000eeeeeb77eebbbeee56ddd440dddddd4a0dd40d65569999cf4cccccc99999996557775eee0eeeeeee0eeeeeeee272eeeeee2ee8eeeeee272eee8ee2ee -00077000eeebb3b7e777eeee56dd47a0d44404aaa047a06556999ccfccccccc995995965577775eee0eeeee0eeeeeeeeee8ee2eeeeee272eee2ee8eee272eeee -00700700ebb7bb3bb3b7beee56d4aaa047aa4a7aa047a0655699ccccccccccc95759756557755eeeee0eee0eeeeeeeeeee2e272eee287778e272e2ee877782ee -00000000ebb77bbbb777bbee564aa0047aaaa700a04aa065569ccccc77cccc7c57757565575eeeeeeee0e0eeeeeeeeeeeeeee2eeeeee272eee2eeeeee272eeee -00000000eebb777777bbbbee564a0a04a004aa0d704aa06556ccccc7077cc07c95757565e5eeeeeeeeee0eeeeeeeeeeeeeeeeeeeeeeee8eeeeeeeeeeee8eeeee +00000000eeeeebbbeeeeeeeeeeeeeeeeeeed65eeeeeeeeeeeeeeeccccced65eeeeeeeeeee5eeeeeee00000eeeeeeeeeeee8eeeeeeeeeeeeeeeeee8eeeeeeeeee +00000000eeeebbbeeeeeeeeeed66666666666666666666deed66cccccccc6666666666de575eeeee0eeeee0eeeeeeeeee272eeeeee2eeeeeeeee272eeeeee2ee +00700700eeebbbb7eeebeeee56000000000000000000006556000cc4ccccc0c0000000655775eeee0eeeee0eeeeeeeee877782eee272e2eeee287778ee2e272e +00077000eeeeeb77eebbbeee56ddd440dddddd4a0dd40d65569999cf4cccccc99999996557775eeee0eee0eeeeeeeeeee272eeeeee2ee8eeeeee272eee8ee2ee +00077000eeebb3b7e777eeee56dd47a0d44404aaa047a06556999ccfccccccc995995965577775eeee0e0eeeeeeeeeeeee8ee2eeeeee272eee2ee8eee272eeee +00700700ebb7bb3bb3b7beee56d4aaa047aa4a7aa047a0655699ccccccccccc95759756557755eeeeee0eeeeeeeeeeeeee2e272eee287778e272e2ee877782ee +00000000ebb77bbbb777bbee564aa0047aaaa700a04aa065569ccccc77cccc7c57757565575eeeeeeeeeeeeeeeeeeeeeeeeee2eeeeee272eee2eeeeee272eeee +00000000eebb777777bbbbee564a0a04a004aa0d704aa06556ccccc7077cc07c95757565e5eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee8eeeeeeeeeeee8eeeee eeed6deeeb773777777bbeee564a0a70a0d4aaaa707a0d655cccccc70777707c95777565eeeeeeeeeeeeccccceeeeeeeeeeeeeeeeeeeeeeeeeeeeeccccceeeee eee777eebb77bb37777eeeee564a00a0a447aa44ad7a006556999cc7077ff07c99577565eeeeeeeeeeeeeccccceeceeeeeeeccccceeeeeeeeeeeeeeccccccece ee66666eeebbeebb777bb7bb560aaaa0aaaa0a00a0aaa0655699ccffffffff0055757755eeeeeeeeeeeeeecffcccceeeeeeeeccccceeceeeeeeeeeeecffcccfe diff --git a/src/resources/visual_common.lua b/src/resources/visual_common.lua index d1b28818..b2ce92c6 100644 --- a/src/resources/visual_common.lua +++ b/src/resources/visual_common.lua @@ -67,10 +67,10 @@ local sprite_data_t = { --#endif -- ANIMATION SPRITES - emerald_pick_fx1 = sprite_data(sprite_id_location(12, 0), tile_vector(1, 1), vector(4, 4), colors.pink), - emerald_pick_fx2 = sprite_data(sprite_id_location(13, 0), tile_vector(1, 1), vector(4, 4), colors.pink), - emerald_pick_fx3 = sprite_data(sprite_id_location(14, 0), tile_vector(1, 1), vector(4, 4), colors.pink), - emerald_pick_fx4 = sprite_data(sprite_id_location(15, 0), tile_vector(1, 1), vector(4, 4), colors.pink), + emerald_pick_fx1 = sprite_data(sprite_id_location(12, 0), nil, vector(4, 4), colors.pink), + emerald_pick_fx2 = sprite_data(sprite_id_location(13, 0), nil, vector(4, 4), colors.pink), + emerald_pick_fx3 = sprite_data(sprite_id_location(14, 0), nil, vector(4, 4), colors.pink), + emerald_pick_fx4 = sprite_data(sprite_id_location(15, 0), nil, vector(4, 4), colors.pink), } visual.sprite_data_t = sprite_data_t diff --git a/src/resources/visual_ingame_addon.lua b/src/resources/visual_ingame_addon.lua index 45b3660a..ecf9f71e 100644 --- a/src/resources/visual_ingame_addon.lua +++ b/src/resources/visual_ingame_addon.lua @@ -37,7 +37,7 @@ local ingame_sprite_data_t = { -- RUNTIME SPRITES (stage-specific and common runtime) -- below need runtime sprites to be reloaded, overwriting collision masks background_forest_bottom_hole = sprite_data(sprite_id_location(1, 0), tile_vector(2, 3), vector(0, 0), colors.pink), - emerald_silhouette = sprite_data(sprite_id_location(10, 0), tile_vector(2, 1), vector(4, 4), colors.pink), + emerald_silhouette = sprite_data(sprite_id_location(10, 0), nil, vector(3, 2), colors.pink), goal_plate_goal = sprite_data(sprite_id_location(3, 0), tile_vector(3, 2), vector(12, 16), colors.pink), goal_plate_sonic = sprite_data(sprite_id_location(6, 0), tile_vector(3, 2), vector(12, 16), colors.pink), goal_plate_rotating_90 = sprite_data(sprite_id_location(0, 1), tile_vector(1, 2), vector(4, 16), colors.pink), @@ -48,7 +48,7 @@ local ingame_sprite_data_t = { goal_plate_rotating_45_cw = sprite_data(sprite_id_location(8, 14), tile_vector(2, 2), vector(8, 16), colors.pink), -- emerald representation tile (left part) and object sprite (both parts) - emerald = sprite_data(sprite_id_location(10, 15), tile_vector(2, 1), vector(4, 4), colors.pink), + emerald = sprite_data(sprite_id_location(10, 15), nil, vector(3, 2), colors.pink), } -- derived data: the representative sprite of an emerald (the one placed on the tilemap) diff --git a/src/resources/visual_titlemenu_addon.lua b/src/resources/visual_titlemenu_addon.lua index d52ce48a..7a869b32 100644 --- a/src/resources/visual_titlemenu_addon.lua +++ b/src/resources/visual_titlemenu_addon.lua @@ -30,7 +30,7 @@ local titlemenu_sprite_data_t = { title_logo = sprite_data(sprite_id_location(0, 1), tile_vector(14, 10), nil, colors.pink), angel_island_bg = sprite_data(sprite_id_location(0, 11), tile_vector(16, 5), nil, colors.pink), -- true emerald is located where emerald silhouette is in visual_ingame_addon - emerald = sprite_data(sprite_id_location(10, 0), tile_vector(2, 1), vector(4, 4), colors.pink), + emerald = sprite_data(sprite_id_location(10, 0), nil, vector(3, 2), colors.pink), } merge(visual, titlemenu_visual) From b3df146bf57c0555ba7061e647ed0b79cff8e3a0 Mon Sep 17 00:00:00 2001 From: huulong Date: Thu, 17 Dec 2020 22:56:35 +0100 Subject: [PATCH 03/91] [PROJECT] Added "regions" and removed cmd in "edit data: stage 1" to avoid confusion with built-in stage1 data --- sonic-2d-tech-demo.sublime-project | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/sonic-2d-tech-demo.sublime-project b/sonic-2d-tech-demo.sublime-project index cb3bfa8b..4d90856c 100644 --- a/sonic-2d-tech-demo.sublime-project +++ b/sonic-2d-tech-demo.sublime-project @@ -55,9 +55,8 @@ "working_dir": "${project_path}" }, { - "name": "Game: edit data: stage1", + "name": "Game: edit data: stage1 regions", "shell": true, - "cmd": ["pico8 -run data/data_stage1_00.p8"], "working_dir": "${project_path}", "variants": [ From 100c67a36d3198acf81d1609dcc2f766569a06ad Mon Sep 17 00:00:00 2001 From: huulong Date: Fri, 18 Dec 2020 00:21:11 +0100 Subject: [PATCH 04/91] [PHYSICS] Support one-way platforms Added one-way platform visuals to stage (top is one-way collider with new sprite flag, bottom has no collision). Launch ramp edge is also one-way now. Small aesthetic changes to make it work. Known issues: jumping against a one-way platform from the side will hit it as a ceiling --- data/builtin_data_ingame.p8 | 21 ++++---- data/data_stage1_00.p8 | 74 ++++++++++++------------- data/data_stage1_01.p8 | 76 +++++++++++++------------- data/data_stage1_10.p8 | 68 +++++++++++------------ data/data_stage1_11.p8 | 80 ++++++++++++++-------------- data/data_stage1_20.p8 | 72 ++++++++++++------------- data/data_stage1_21.p8 | 72 ++++++++++++------------- data/data_stage1_30.p8 | 68 +++++++++++------------ data/data_stage1_31.p8 | 68 +++++++++++------------ data/data_stage1_runtime.p8 | 19 ++++--- src/data/sprite_flags.lua | 10 ++-- src/ingame/playercharacter.lua | 15 +++--- src/ingame/playercharacter_utest.lua | 6 +-- src/ingame/stage_state.lua | 8 ++- 14 files changed, 335 insertions(+), 322 deletions(-) diff --git a/data/builtin_data_ingame.p8 b/data/builtin_data_ingame.p8 index c15deacd..70cb1de8 100644 --- a/data/builtin_data_ingame.p8 +++ b/data/builtin_data_ingame.p8 @@ -127,17 +127,17 @@ eeeea994eeeeeeeeeeed65eeeeee999999999999aa944400d65694cdc0ca9a155194cdc0ca9a656d eeeaaaa94eeeeeeeeeed65eeeee99999999999aa99444000d656dcacac75471551dcacac7547656d3beb3ebe44444494b00bb00bbbbbbbab3333b33beeeeeeee eeaaaaaaa999eeeeeeed65eeee9999999999aa9449444400d6569ac7ca7a7a15519ac7ca7a7a656db3b33eb3449444443b0330b33b3bbbbbb33bee3beeeeeeee eaaaaaaaaaaa999eeeed65eee4949494999a944444444000d6564c000c094715514c000c0947656deebeebee444444440b0330b0eeebbebbbbeeeeeeeeeeeeee -eeeeeeeeeeeeeeeeeeeeeee949494449a99494444444440ed656c0ca7a77701551c0ca7a7770656de77778eeeeeeeeeeeeeeeeeeeeeeebbbbbeeeeeeeeeeeeee -eeeeeeeeeeeeeeeeeeeee99444449a9944444444444440eed65649a04f4977155149a04f4977656d7777788eeeeeeeeeeeeeeeeeeeeeeebbbbaeeeeeeeeeeeee -eeeeeeeeeeeeeeeeeeee44449a994444444444994440eeeed6569afaf00a7a15519afaf00a7a656d2288822eeeeeeeeeeeeeeeeeeeeeeeeebbbbeeeeeeeeeeee -eeeeeeeeeeeeeeeeee4499994444494444944494444eeeeed656dcafaf05071551dcafaf0507656de28882eeeeeeeeeeeeeeeeeeeeeeeeeeebbbeeeeeeeeeeee -eeeeeeeeeeeeeeeea9994494499449444494444440eeeeeed65690cdf09a5d155190cdf09a5d656dee282eeeeeeeeeeeeeeeeeeeeeeeeeeeebbbbeeeeeeeeeee -eeeeeeeeeeee99994494444444944444449444444eeeeeeed656dc09d9d9d61551dc09d9d9d6656deee2eeeeeeeeeeeeeeeeeeeeeeeeeeeeeebbaeeeeeeeeeee -eeeeeeee44994494449444444494444444444400eeeeeeeed60d777777777775577777777777d06deeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeebaeeeeeeeeeee -eeee44994444444444444444444444444440eeeeeeeeeeeeed6000000000000ee0000000000006deeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee +eeeeeeeeeeeeeeeeeeeeeee949494449a99494444444440ed656c0ca7a77701551c0ca7a7770656de77778ee4444444444444444eeeeebbbbbeeeeeeeeeeeeee +eeeeeeeeeeeeeeeeeeeee99444449a9944444444444440eed65649a04f4977155149a04f4977656d7777788e0449449949944444eeeeeebbbbaeeeeeeeeeeeee +eeeeeeeeeeeeeeeeeeee44449a994444444444994440eeeed6569afaf00a7a15519afaf00a7a656d2288822e0449444994444000eeeeeeeebbbbeeeeeeeeeeee +eeeeeeeeeeeeeeeeee4499994444494444944494444eeeeed656dcafaf05071551dcafaf0507656de28882ee4444444999444044eeeeeeeeebbbeeeeeeeeeeee +eeeeeeeeeeeeeeeea9994494499449444494444440eeeeeed65690cdf09a5d155190cdf09a5d656dee282eee0444499444444444eeeeeeeeebbbbeeeeeeeeeee +eeeeeeeeeeee99994494444444944444449444444eeeeeeed656dc09d9d9d61551dc09d9d9d6656deee2eeee0444444444444440eeeeeeeeeebbaeeeeeeeeeee +eeeeeeee44994494449444444494444444444400eeeeeeeed60d777777777775577777777777d06deeeeeeee004499440e004440eeeeeeeeeeebaeeeeeeeeeee +eeee44994444444444444444444444444440eeeeeeeeeeeeed6000000000000ee0000000000006deeeeeeeeee0049eeeeeee000eeeeeeeeeeeeeeeeeeeeeeeee __gff__ -0041414141414141414141414141414141414141414141414141414141410000414141414141414141414141410000004141414141414141414141414141414141414141414141414141616180808000414040414141414141414040414141414545454343434343454541414141414145455149434343434545414141418080 -0000000000000000000000000000000000000000000000000000000000000000000000000000000041418080800000000000000000000000414141800080414100000000000000000000008080804141000000000000000000004141808080804040404041410000000080418080800041414141414100000000000000008000 +0041414141414141414141414141414141414141414141414141414141410000414141434341414141414141410000004141414141414141414141414141414141414141414141414141616180808000414040414141414141414040414141414545454343434343454541414141414145455149434343434545414141418080 +0000000000000000000000000000000000000000000000000000000000000000000000000000000041418080800000000000000000000000414141800080414100000000000000000000008080804141000000000000000000004141808080804040404043430000000080418080800041414141434300000000004040008000 __map__ 000000000000000000006e6f6e6f6e6f6e6f6f6e6f6e6f6e6f6e6f6e6f6e49466e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f497e7f7e7f7e7f7e7f7e7f7e7f7e7f7e7f7e7f7e7f000000000000000000007c7d7c7d7c7d7c7d1e1f1e1f1e1f1e1f5e5f5e5f5f 000000000000000000006e6f6e6f6e6f6e6f6f585945434443454244455554307e7f7e7f7e7f7e7f7e7f7e7f7e2e6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6f6f6e6f6e6f6e6f6e6f6e6f44417000000000000000000000000000000000000000000000000000000000000000000000000000007c7d5e5f5e5f5e5f6e6f6e6f6f @@ -280,3 +280,4 @@ __music__ 04 39777879 00 32333637 04 34354075 + diff --git a/data/data_stage1_00.p8 b/data/data_stage1_00.p8 index c4d32f9d..de88d4f1 100644 --- a/data/data_stage1_00.p8 +++ b/data/data_stage1_00.p8 @@ -7,30 +7,30 @@ __lua__ -- we only need __map__, but we kept __gfx__ only to visualize tiles when editing -- (and __gff__ because it's not too big) __gfx__ -00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -00000000777700000000000000000000000000000000000000000000000000000000000000000000000000000000777777000000000000000000000000000077 -00700700777777777777777700000000000000000000000000000000000000000000000000000000000000007777777777770000000000000000000000007777 -00077000777777777777777777770000000000000000000000000000000000000000000000000000000077777777777777777700000000000000000000777777 -00077000777777777777777777777777777777770000000000000000000000000000000000000000777777777777777777777777000000000000000077777777 -00700700777777777777777777777777777777777777000000000000000000000000000000007777777777777777777777777777770000000000007777777777 -00000000777777777777777777777777777777777777777777777777000000000000000077777777777777777777777777777777777700000000777777777777 -00000000777777777777777777777777777777777777777777777777777700000000777777777777777777777777777777777777777777000077777777777777 -70000000700000000000000000000000000000070000000700000007777777777777777770000000000000000000000000007777777777770000000000000000 -77000000770000000000000000000000000000770000007700000007077777777777777070000000000000000000000000007777777777770000000000000000 -77700000777700000000000000000000000077770000077700000007077777777777777070000000000077777777777700007777777777770000000000000000 -77770000777770000000000000000000000777770000777700000077007777777777770077000000000077777777777700007777777777770000000000000000 -77777000777777700000000000000000077777770007777700000077000777777777700077000000000077777777777700007777777777770000000000000000 -77777700777777777000000000000007777777770077777700000077000777777777700077000000000077777777777700007777777777770000000000000000 -77777770777777777770000000000777777777770777777700000777000077777777000077700000000077777777777700007777777777770000000000000000 -77777777777777777777700000077777777777777777777700000777000077777777000077700000000077777777777700007777777777770000000000000000 -77777777777777777777700000077777777777777777777700007777000007777770000077770000000000000000007700000000000000000000000000000000 -77777770777777777770000000000777777777770777777700007777000007777770000077770000000000000000777700000000000000000000000000000000 -77777700777777777000000000000007777777770077777700077777000000777700000077777000000000000077777700000000000000000000000000000000 -77777000777777700000000000000000077777770007777700077777000000777700000077777000000000777777777700000000000000000000000000000000 -77770000777770000000000000000000000777770000777700777777000000777700000077777700000077777777777700007777000000000000000000000000 -77700000777700000000000000000000000077770000077707777777000000077000000077777770007777777777777700007777000000000000000000000000 -77000000770000000000000000000000000000770000007707777777000000077000000077777770777777777777777700007777000000000000000000000000 -70000000700000000000000000000000000000070000000777777777000000077000000077777777777777777777777700007777000000000000000000000000 +00000000eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee +00000000eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee +00700700eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee +00077000eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee +00077000eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee +00700700eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee +00000000eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee +00000000eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee +eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee +eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee +eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee +eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee +eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee +eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee +eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee +eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee +eeeeeeeeeeeeeeeeeeeeeeeebbbb3bbbbbbbbbbbeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee +eeeeeeeeeeeeeeeeeeeeeeeebbb303bbbb3bb0bbeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee +eeeeeeeeeeeeeeeeeeeeeeee3b300033b30b3003eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee +eeeeeeeeeeeeeeeeeeeeeeee0300400030030400eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee +eeeeeeeeeeeeeeeeeeeeeeee4004444000000444eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee +eeeeeeeeeeeeeeeeeeeeeeee4444444444004494eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee +eeeeeeeeeeeeeeeeeeeeeeee4449444444044494eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee +eeeeeeeeeeeeeeeeeeeeeeee4449444444444444eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee 44444444eaeaeeeeeeeeaeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeaebeeeaeeeeeeeeeeeeeeeeeeeeeeeeeeeee 44449444babaeabeeaeebeebeeaeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeaeeebaeabbbabbeeeeeeeeeeeeeeeeeeeeeeeeeaeab 44444494bbbababbbababbbbeebeeaeeeeeeeebeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeaeeaeebbabab3babbbeaeeeeeeeeeeeeeeeeeeaeeebbab @@ -127,17 +127,17 @@ eeeea994eeeeeeeeeeed65eeeeee999999999999aa944400d65694cdc0ca9a155194cdc0ca9a656d eeeaaaa94eeeeeeeeeed65eeeee99999999999aa99444000d656dcacac75471551dcacac7547656d3beb3ebe44444494b00bb00bbbbbbbab3333b33beeeeeeee eeaaaaaaa999eeeeeeed65eeee9999999999aa9449444400d6569ac7ca7a7a15519ac7ca7a7a656db3b33eb3449444443b0330b33b3bbbbbb33bee3beeeeeeee eaaaaaaaaaaa999eeeed65eee4949494999a944444444000d6564c000c094715514c000c0947656deebeebee444444440b0330b0eeebbebbbbeeeeeeeeeeeeee -eeeeeeeeeeeeeeeeeeeeeee949494449a99494444444440ed656c0ca7a77701551c0ca7a7770656de77778eeeeeeeeeeeeeeeeeeeeeeebbbbbeeeeeeeeeeeeee -eeeeeeeeeeeeeeeeeeeee99444449a9944444444444440eed65649a04f4977155149a04f4977656d7777788eeeeeeeeeeeeeeeeeeeeeeebbbbaeeeeeeeeeeeee -eeeeeeeeeeeeeeeeeeee44449a994444444444994440eeeed6569afaf00a7a15519afaf00a7a656d2288822eeeeeeeeeeeeeeeeeeeeeeeeebbbbeeeeeeeeeeee -eeeeeeeeeeeeeeeeee4499994444494444944494444eeeeed656dcafaf05071551dcafaf0507656de28882eeeeeeeeeeeeeeeeeeeeeeeeeeebbbeeeeeeeeeeee -eeeeeeeeeeeeeeeea9994494499449444494444440eeeeeed65690cdf09a5d155190cdf09a5d656dee282eeeeeeeeeeeeeeeeeeeeeeeeeeeebbbbeeeeeeeeeee -eeeeeeeeeeee99994494444444944444449444444eeeeeeed656dc09d9d9d61551dc09d9d9d6656deee2eeeeeeeeeeeeeeeeeeeeeeeeeeeeeebbaeeeeeeeeeee -eeeeeeee44994494449444444494444444444400eeeeeeeed60d777777777775577777777777d06deeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeebaeeeeeeeeeee -eeee44994444444444444444444444444440eeeeeeeeeeeeed6000000000000ee0000000000006deeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee +eeeeeeeeeeeeeeeeeeeeeee949494449a99494444444440ed656c0ca7a77701551c0ca7a7770656de77778ee4444444444444444eeeeebbbbbeeeeeeeeeeeeee +eeeeeeeeeeeeeeeeeeeee99444449a9944444444444440eed65649a04f4977155149a04f4977656d7777788e0449449949944444eeeeeebbbbaeeeeeeeeeeeee +eeeeeeeeeeeeeeeeeeee44449a994444444444994440eeeed6569afaf00a7a15519afaf00a7a656d2288822e0449444994444000eeeeeeeebbbbeeeeeeeeeeee +eeeeeeeeeeeeeeeeee4499994444494444944494444eeeeed656dcafaf05071551dcafaf0507656de28882ee4444444999444044eeeeeeeeebbbeeeeeeeeeeee +eeeeeeeeeeeeeeeea9994494499449444494444440eeeeeed65690cdf09a5d155190cdf09a5d656dee282eee0444499444444444eeeeeeeeebbbbeeeeeeeeeee +eeeeeeeeeeee99994494444444944444449444444eeeeeeed656dc09d9d9d61551dc09d9d9d6656deee2eeee0444444444444440eeeeeeeeeebbaeeeeeeeeeee +eeeeeeee44994494449444444494444444444400eeeeeeeed60d777777777775577777777777d06deeeeeeee004499440e004440eeeeeeeeeeebaeeeeeeeeeee +eeee44994444444444444444444444444440eeeeeeeeeeeeed6000000000000ee0000000000006deeeeeeeeee0049eeeeeee000eeeeeeeeeeeeeeeeeeeeeeeee __gff__ -0041414141414141414141414141414141414141414141414141414141410000414141414141414141414141410000004141414141414141414141414141414141414141414141414141616180808000414040414141414141414040414141414545454343434343454541414141414145455149434343434545414141418080 -0000000000000000000000000000000000000000000000000000000000000000000000000000000041418080800000000000000000000000414141800080414100000000000000000000008080804141000000000000000000004141808080804040404041410000000080418080800041414141414100000000000000008000 +0000000000000000000000000000000000000000000000000000000000000000000000434300000000000000000000004141414141414141414141414141414141414141414141414141616180808000414040414141414141414040414141414545454343434343454541414141414145455149434343434545414141418080 +0000000000000000000000000000000000000000000000000000000000000000000000000000000041418080800000000000000000000000414141800080414100000000000000000000008080804141000000000000000000004141808080804040404043430000000080418080800041414141434300000000004040008000 __map__ 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6e6f6e6e6e6f6e6f6e6f6e6f6e6f6e6f6e6e6e6e6e6e6e 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6f6f6f6e6e6e6e6e6e6e6e6e6e6e6e6e6f6e6e6e6e6e6e6e @@ -154,8 +154,8 @@ __map__ 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f58eb575857585354eb5758ebeb7c7d000000000000000000 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006e6f6e6f6e6f6e6f7e7f7e7f7e7f7e7f7e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f535453545354dadb5354eb7c7d0000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006e6f6e6f6e6f6e6f0000000000000000006f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f5e5f5e5f5e5f5e5f5e5f7d00000000000000000000004c4d -0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006e6f6e6f6e6f6e6f0000000000000000006f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f0000000000000000000000515941 -0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006e6f6e6f6e55596f0000000000000000007f57466e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f49466e6f6e6f6e7f0000000000000000000000007c7d +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006e6f6e6f6e6f6e6f0000000000000000006f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f0000000000000000000000512324 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006e6f6e6f6e55596f0000000000000000007f57466e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f49466e6f6e6f6e7f000000000000000000000000fbfc 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006e6f6e6f6eebeb6f000000000000000000007c7d57586e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f7e7f7e7f59557deb59466e6f7e000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006e6f6e6f6e53546f0000000000000000000000007c7d6e6f7e7f7e7f7e7f7e7f7e7f7e7f7e7f7e7f7e7f7e7f7e7f7e7f7e7f7e7f000000007c7d00007c7d7e7f00000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006e6f6e6f6e5e5f6f00000000000000000000000000007e7f0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 @@ -170,4 +170,4 @@ __map__ 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006e6f6e6f6e6f6e6fdadb53545354dadb53545354000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000053545354 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006e6f6e6f6e6f6e6f5e5f5e5f5e5f5e5f5e5f5e5f0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000dadbdadb 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f004d4c4d4c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000dadbdadb -0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e7f5143424342520000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000dadbdadb +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e7f5123242324520000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000dadbdadb diff --git a/data/data_stage1_01.p8 b/data/data_stage1_01.p8 index f3a990de..ea8bbc8e 100644 --- a/data/data_stage1_01.p8 +++ b/data/data_stage1_01.p8 @@ -7,30 +7,30 @@ __lua__ -- we only need __map__, but we kept __gfx__ only to visualize tiles when editing -- (and __gff__ because it's not too big) __gfx__ -00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -00000000777700000000000000000000000000000000000000000000000000000000000000000000000000000000777777000000000000000000000000000077 -00700700777777777777777700000000000000000000000000000000000000000000000000000000000000007777777777770000000000000000000000007777 -00077000777777777777777777770000000000000000000000000000000000000000000000000000000077777777777777777700000000000000000000777777 -00077000777777777777777777777777777777770000000000000000000000000000000000000000777777777777777777777777000000000000000077777777 -00700700777777777777777777777777777777777777000000000000000000000000000000007777777777777777777777777777770000000000007777777777 -00000000777777777777777777777777777777777777777777777777000000000000000077777777777777777777777777777777777700000000777777777777 -00000000777777777777777777777777777777777777777777777777777700000000777777777777777777777777777777777777777777000077777777777777 -70000000700000000000000000000000000000070000000700000007777777777777777770000000000000000000000000007777777777770000000000000000 -77000000770000000000000000000000000000770000007700000007077777777777777070000000000000000000000000007777777777770000000000000000 -77700000777700000000000000000000000077770000077700000007077777777777777070000000000077777777777700007777777777770000000000000000 -77770000777770000000000000000000000777770000777700000077007777777777770077000000000077777777777700007777777777770000000000000000 -77777000777777700000000000000000077777770007777700000077000777777777700077000000000077777777777700007777777777770000000000000000 -77777700777777777000000000000007777777770077777700000077000777777777700077000000000077777777777700007777777777770000000000000000 -77777770777777777770000000000777777777770777777700000777000077777777000077700000000077777777777700007777777777770000000000000000 -77777777777777777777700000077777777777777777777700000777000077777777000077700000000077777777777700007777777777770000000000000000 -77777777777777777777700000077777777777777777777700007777000007777770000077770000000000000000007700000000000000000000000000000000 -77777770777777777770000000000777777777770777777700007777000007777770000077770000000000000000777700000000000000000000000000000000 -77777700777777777000000000000007777777770077777700077777000000777700000077777000000000000077777700000000000000000000000000000000 -77777000777777700000000000000000077777770007777700077777000000777700000077777000000000777777777700000000000000000000000000000000 -77770000777770000000000000000000000777770000777700777777000000777700000077777700000077777777777700007777000000000000000000000000 -77700000777700000000000000000000000077770000077707777777000000077000000077777770007777777777777700007777000000000000000000000000 -77000000770000000000000000000000000000770000007707777777000000077000000077777770777777777777777700007777000000000000000000000000 -70000000700000000000000000000000000000070000000777777777000000077000000077777777777777777777777700007777000000000000000000000000 +00000000eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee +00000000eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee +00700700eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee +00077000eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee +00077000eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee +00700700eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee +00000000eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee +00000000eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee +eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee +eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee +eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee +eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee +eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee +eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee +eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee +eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee +eeeeeeeeeeeeeeeeeeeeeeeebbbb3bbbbbbbbbbbeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee +eeeeeeeeeeeeeeeeeeeeeeeebbb303bbbb3bb0bbeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee +eeeeeeeeeeeeeeeeeeeeeeee3b300033b30b3003eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee +eeeeeeeeeeeeeeeeeeeeeeee0300400030030400eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee +eeeeeeeeeeeeeeeeeeeeeeee4004444000000444eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee +eeeeeeeeeeeeeeeeeeeeeeee4444444444004494eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee +eeeeeeeeeeeeeeeeeeeeeeee4449444444044494eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee +eeeeeeeeeeeeeeeeeeeeeeee4449444444444444eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee 44444444eaeaeeeeeeeeaeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeaebeeeaeeeeeeeeeeeeeeeeeeeeeeeeeeeee 44449444babaeabeeaeebeebeeaeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeaeeebaeabbbabbeeeeeeeeeeeeeeeeeeeeeeeeeaeab 44444494bbbababbbababbbbeebeeaeeeeeeeebeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeaeeaeebbabab3babbbeaeeeeeeeeeeeeeeeeeeaeeebbab @@ -127,26 +127,26 @@ eeeea994eeeeeeeeeeed65eeeeee999999999999aa944400d65694cdc0ca9a155194cdc0ca9a656d eeeaaaa94eeeeeeeeeed65eeeee99999999999aa99444000d656dcacac75471551dcacac7547656d3beb3ebe44444494b00bb00bbbbbbbab3333b33beeeeeeee eeaaaaaaa999eeeeeeed65eeee9999999999aa9449444400d6569ac7ca7a7a15519ac7ca7a7a656db3b33eb3449444443b0330b33b3bbbbbb33bee3beeeeeeee eaaaaaaaaaaa999eeeed65eee4949494999a944444444000d6564c000c094715514c000c0947656deebeebee444444440b0330b0eeebbebbbbeeeeeeeeeeeeee -eeeeeeeeeeeeeeeeeeeeeee949494449a99494444444440ed656c0ca7a77701551c0ca7a7770656de77778eeeeeeeeeeeeeeeeeeeeeeebbbbbeeeeeeeeeeeeee -eeeeeeeeeeeeeeeeeeeee99444449a9944444444444440eed65649a04f4977155149a04f4977656d7777788eeeeeeeeeeeeeeeeeeeeeeebbbbaeeeeeeeeeeeee -eeeeeeeeeeeeeeeeeeee44449a994444444444994440eeeed6569afaf00a7a15519afaf00a7a656d2288822eeeeeeeeeeeeeeeeeeeeeeeeebbbbeeeeeeeeeeee -eeeeeeeeeeeeeeeeee4499994444494444944494444eeeeed656dcafaf05071551dcafaf0507656de28882eeeeeeeeeeeeeeeeeeeeeeeeeeebbbeeeeeeeeeeee -eeeeeeeeeeeeeeeea9994494499449444494444440eeeeeed65690cdf09a5d155190cdf09a5d656dee282eeeeeeeeeeeeeeeeeeeeeeeeeeeebbbbeeeeeeeeeee -eeeeeeeeeeee99994494444444944444449444444eeeeeeed656dc09d9d9d61551dc09d9d9d6656deee2eeeeeeeeeeeeeeeeeeeeeeeeeeeeeebbaeeeeeeeeeee -eeeeeeee44994494449444444494444444444400eeeeeeeed60d777777777775577777777777d06deeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeebaeeeeeeeeeee -eeee44994444444444444444444444444440eeeeeeeeeeeeed6000000000000ee0000000000006deeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee +eeeeeeeeeeeeeeeeeeeeeee949494449a99494444444440ed656c0ca7a77701551c0ca7a7770656de77778ee4444444444444444eeeeebbbbbeeeeeeeeeeeeee +eeeeeeeeeeeeeeeeeeeee99444449a9944444444444440eed65649a04f4977155149a04f4977656d7777788e0449449949944444eeeeeebbbbaeeeeeeeeeeeee +eeeeeeeeeeeeeeeeeeee44449a994444444444994440eeeed6569afaf00a7a15519afaf00a7a656d2288822e0449444994444000eeeeeeeebbbbeeeeeeeeeeee +eeeeeeeeeeeeeeeeee4499994444494444944494444eeeeed656dcafaf05071551dcafaf0507656de28882ee4444444999444044eeeeeeeeebbbeeeeeeeeeeee +eeeeeeeeeeeeeeeea9994494499449444494444440eeeeeed65690cdf09a5d155190cdf09a5d656dee282eee0444499444444444eeeeeeeeebbbbeeeeeeeeeee +eeeeeeeeeeee99994494444444944444449444444eeeeeeed656dc09d9d9d61551dc09d9d9d6656deee2eeee0444444444444440eeeeeeeeeebbaeeeeeeeeeee +eeeeeeee44994494449444444494444444444400eeeeeeeed60d777777777775577777777777d06deeeeeeee004499440e004440eeeeeeeeeeebaeeeeeeeeeee +eeee44994444444444444444444444444440eeeeeeeeeeeeed6000000000000ee0000000000006deeeeeeeeee0049eeeeeee000eeeeeeeeeeeeeeeeeeeeeeeee __gff__ -0041414141414141414141414141414141414141414141414141414141410000414141414141414141414141410000004141414141414141414141414141414141414141414141414141616180808000414040414141414141414040414141414545454343434343454541414141414145455149434343434545414141418080 -0000000000000000000000000000000000000000000000000000000000000000000000000000000041418080800000000000000000000000414141800080414100000000000000000000008080804141000000000000000000004141808080804040404041410000000080418080800041414141414100000000000000008000 +0000000000000000000000000000000000000000000000000000000000000000000000434300000000000000000000004141414141414141414141414141414141414141414141414141616180808000414040414141414141414040414141414545454343434343454541414141414145455149434343434545414141418080 +0000000000000000000000000000000000000000000000000000000000000000000000000000000041418080800000000000000000000000414141800080414100000000000000000000008080804141000000000000000000004141808080804040404043430000000080418080800041414141434300000000004040008000 __map__ -0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e7f00007c7d7c7d0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007cdbdadb +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e7f0000fbfcfbfc0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007cdbdadb 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f7e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007c7d7c 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006f6e6f6e6f6e6f6e6f6e6f495758466e7f00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004a4b0000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007f7e7f7e7f7e7f7e7f7e7f7c7d7c7d7e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ec0000000000000000000000 -00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000aaabac000000004c4d4d000000 -00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004a4b00000000000000000000000000000000000000000000000000000000000000bb0000000051424341520000 -0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000bb00000000007c7d7d000000 +00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000aaabac000000004d4c4d000000 +00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004a4b00000000000000000000000000000000000000000000000000000000000000bb0000000051232424520000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000bb0000000000fbfcfc000000 000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ec00000000000000000000000000000000000000000000000000000000000000bb0000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000aaabac000000000000000000000000000000000000000000000000000000000000bb0000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000bb00000000000000000000000000000000000000000000000000000000000000bb0000000000000000000000 diff --git a/data/data_stage1_10.p8 b/data/data_stage1_10.p8 index 170ed3f5..20e20093 100644 --- a/data/data_stage1_10.p8 +++ b/data/data_stage1_10.p8 @@ -7,30 +7,30 @@ __lua__ -- we only need __map__, but we kept __gfx__ only to visualize tiles when editing -- (and __gff__ because it's not too big) __gfx__ -00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -00000000777700000000000000000000000000000000000000000000000000000000000000000000000000000000777777000000000000000000000000000077 -00700700777777777777777700000000000000000000000000000000000000000000000000000000000000007777777777770000000000000000000000007777 -00077000777777777777777777770000000000000000000000000000000000000000000000000000000077777777777777777700000000000000000000777777 -00077000777777777777777777777777777777770000000000000000000000000000000000000000777777777777777777777777000000000000000077777777 -00700700777777777777777777777777777777777777000000000000000000000000000000007777777777777777777777777777770000000000007777777777 -00000000777777777777777777777777777777777777777777777777000000000000000077777777777777777777777777777777777700000000777777777777 -00000000777777777777777777777777777777777777777777777777777700000000777777777777777777777777777777777777777777000077777777777777 -70000000700000000000000000000000000000070000000700000007777777777777777770000000000000000000000000007777777777770000000000000000 -77000000770000000000000000000000000000770000007700000007077777777777777070000000000000000000000000007777777777770000000000000000 -77700000777700000000000000000000000077770000077700000007077777777777777070000000000077777777777700007777777777770000000000000000 -77770000777770000000000000000000000777770000777700000077007777777777770077000000000077777777777700007777777777770000000000000000 -77777000777777700000000000000000077777770007777700000077000777777777700077000000000077777777777700007777777777770000000000000000 -77777700777777777000000000000007777777770077777700000077000777777777700077000000000077777777777700007777777777770000000000000000 -77777770777777777770000000000777777777770777777700000777000077777777000077700000000077777777777700007777777777770000000000000000 -77777777777777777777700000077777777777777777777700000777000077777777000077700000000077777777777700007777777777770000000000000000 -77777777777777777777700000077777777777777777777700007777000007777770000077770000000000000000007700000000000000000000000000000000 -77777770777777777770000000000777777777770777777700007777000007777770000077770000000000000000777700000000000000000000000000000000 -77777700777777777000000000000007777777770077777700077777000000777700000077777000000000000077777700000000000000000000000000000000 -77777000777777700000000000000000077777770007777700077777000000777700000077777000000000777777777700000000000000000000000000000000 -77770000777770000000000000000000000777770000777700777777000000777700000077777700000077777777777700007777000000000000000000000000 -77700000777700000000000000000000000077770000077707777777000000077000000077777770007777777777777700007777000000000000000000000000 -77000000770000000000000000000000000000770000007707777777000000077000000077777770777777777777777700007777000000000000000000000000 -70000000700000000000000000000000000000070000000777777777000000077000000077777777777777777777777700007777000000000000000000000000 +00000000eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee +00000000eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee +00700700eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee +00077000eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee +00077000eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee +00700700eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee +00000000eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee +00000000eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee +eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee +eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee +eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee +eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee +eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee +eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee +eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee +eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee +eeeeeeeeeeeeeeeeeeeeeeeebbbb3bbbbbbbbbbbeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee +eeeeeeeeeeeeeeeeeeeeeeeebbb303bbbb3bb0bbeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee +eeeeeeeeeeeeeeeeeeeeeeee3b300033b30b3003eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee +eeeeeeeeeeeeeeeeeeeeeeee0300400030030400eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee +eeeeeeeeeeeeeeeeeeeeeeee4004444000000444eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee +eeeeeeeeeeeeeeeeeeeeeeee4444444444004494eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee +eeeeeeeeeeeeeeeeeeeeeeee4449444444044494eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee +eeeeeeeeeeeeeeeeeeeeeeee4449444444444444eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee 44444444eaeaeeeeeeeeaeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeaebeeeaeeeeeeeeeeeeeeeeeeeeeeeeeeeee 44449444babaeabeeaeebeebeeaeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeaeeebaeabbbabbeeeeeeeeeeeeeeeeeeeeeeeeeaeab 44444494bbbababbbababbbbeebeeaeeeeeeeebeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeaeeaeebbabab3babbbeaeeeeeeeeeeeeeeeeeeaeeebbab @@ -127,17 +127,17 @@ eeeea994eeeeeeeeeeed65eeeeee999999999999aa944400d65694cdc0ca9a155194cdc0ca9a656d eeeaaaa94eeeeeeeeeed65eeeee99999999999aa99444000d656dcacac75471551dcacac7547656d3beb3ebe44444494b00bb00bbbbbbbab3333b33beeeeeeee eeaaaaaaa999eeeeeeed65eeee9999999999aa9449444400d6569ac7ca7a7a15519ac7ca7a7a656db3b33eb3449444443b0330b33b3bbbbbb33bee3beeeeeeee eaaaaaaaaaaa999eeeed65eee4949494999a944444444000d6564c000c094715514c000c0947656deebeebee444444440b0330b0eeebbebbbbeeeeeeeeeeeeee -eeeeeeeeeeeeeeeeeeeeeee949494449a99494444444440ed656c0ca7a77701551c0ca7a7770656de77778eeeeeeeeeeeeeeeeeeeeeeebbbbbeeeeeeeeeeeeee -eeeeeeeeeeeeeeeeeeeee99444449a9944444444444440eed65649a04f4977155149a04f4977656d7777788eeeeeeeeeeeeeeeeeeeeeeebbbbaeeeeeeeeeeeee -eeeeeeeeeeeeeeeeeeee44449a994444444444994440eeeed6569afaf00a7a15519afaf00a7a656d2288822eeeeeeeeeeeeeeeeeeeeeeeeebbbbeeeeeeeeeeee -eeeeeeeeeeeeeeeeee4499994444494444944494444eeeeed656dcafaf05071551dcafaf0507656de28882eeeeeeeeeeeeeeeeeeeeeeeeeeebbbeeeeeeeeeeee -eeeeeeeeeeeeeeeea9994494499449444494444440eeeeeed65690cdf09a5d155190cdf09a5d656dee282eeeeeeeeeeeeeeeeeeeeeeeeeeeebbbbeeeeeeeeeee -eeeeeeeeeeee99994494444444944444449444444eeeeeeed656dc09d9d9d61551dc09d9d9d6656deee2eeeeeeeeeeeeeeeeeeeeeeeeeeeeeebbaeeeeeeeeeee -eeeeeeee44994494449444444494444444444400eeeeeeeed60d777777777775577777777777d06deeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeebaeeeeeeeeeee -eeee44994444444444444444444444444440eeeeeeeeeeeeed6000000000000ee0000000000006deeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee +eeeeeeeeeeeeeeeeeeeeeee949494449a99494444444440ed656c0ca7a77701551c0ca7a7770656de77778ee4444444444444444eeeeebbbbbeeeeeeeeeeeeee +eeeeeeeeeeeeeeeeeeeee99444449a9944444444444440eed65649a04f4977155149a04f4977656d7777788e0449449949944444eeeeeebbbbaeeeeeeeeeeeee +eeeeeeeeeeeeeeeeeeee44449a994444444444994440eeeed6569afaf00a7a15519afaf00a7a656d2288822e0449444994444000eeeeeeeebbbbeeeeeeeeeeee +eeeeeeeeeeeeeeeeee4499994444494444944494444eeeeed656dcafaf05071551dcafaf0507656de28882ee4444444999444044eeeeeeeeebbbeeeeeeeeeeee +eeeeeeeeeeeeeeeea9994494499449444494444440eeeeeed65690cdf09a5d155190cdf09a5d656dee282eee0444499444444444eeeeeeeeebbbbeeeeeeeeeee +eeeeeeeeeeee99994494444444944444449444444eeeeeeed656dc09d9d9d61551dc09d9d9d6656deee2eeee0444444444444440eeeeeeeeeebbaeeeeeeeeeee +eeeeeeee44994494449444444494444444444400eeeeeeeed60d777777777775577777777777d06deeeeeeee004499440e004440eeeeeeeeeeebaeeeeeeeeeee +eeee44994444444444444444444444444440eeeeeeeeeeeeed6000000000000ee0000000000006deeeeeeeeee0049eeeeeee000eeeeeeeeeeeeeeeeeeeeeeeee __gff__ -0041414141414141414141414141414141414141414141414141414141410000414141414141414141414141410000004141414141414141414141414141414141414141414141414141616180808000414040414141414141414040414141414545454343434343454541414141414145455149434343434545414141418080 -0000000000000000000000000000000000000000000000000000000000000000000000000000000041418080800000000000000000000000414141800080414100000000000000000000008080804141000000000000000000004141808080804040404041410000000080418080800041414141414100000000000000008000 +0000000000000000000000000000000000000000000000000000000000000000000000434300000000000000000000004141414141414141414141414141414141414141414141414141616180808000414040414141414141414040414141414545454343434343454541414141414145455149434343434545414141418080 +0000000000000000000000000000000000000000000000000000000000000000000000000000000041418080800000000000000000000000414141800080414100000000000000000000008080804141000000000000000000004141808080804040404043430000000080418080800041414141434300000000004040008000 __map__ 6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e 6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6f6f6e6f6e6f6e6f6e6f6e6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e diff --git a/data/data_stage1_11.p8 b/data/data_stage1_11.p8 index 2b03e9a9..67ba3fdb 100644 --- a/data/data_stage1_11.p8 +++ b/data/data_stage1_11.p8 @@ -7,30 +7,30 @@ __lua__ -- we only need __map__, but we kept __gfx__ only to visualize tiles when editing -- (and __gff__ because it's not too big) __gfx__ -00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -00000000777700000000000000000000000000000000000000000000000000000000000000000000000000000000777777000000000000000000000000000077 -00700700777777777777777700000000000000000000000000000000000000000000000000000000000000007777777777770000000000000000000000007777 -00077000777777777777777777770000000000000000000000000000000000000000000000000000000077777777777777777700000000000000000000777777 -00077000777777777777777777777777777777770000000000000000000000000000000000000000777777777777777777777777000000000000000077777777 -00700700777777777777777777777777777777777777000000000000000000000000000000007777777777777777777777777777770000000000007777777777 -00000000777777777777777777777777777777777777777777777777000000000000000077777777777777777777777777777777777700000000777777777777 -00000000777777777777777777777777777777777777777777777777777700000000777777777777777777777777777777777777777777000077777777777777 -70000000700000000000000000000000000000070000000700000007777777777777777770000000000000000000000000007777777777770000000000000000 -77000000770000000000000000000000000000770000007700000007077777777777777070000000000000000000000000007777777777770000000000000000 -77700000777700000000000000000000000077770000077700000007077777777777777070000000000077777777777700007777777777770000000000000000 -77770000777770000000000000000000000777770000777700000077007777777777770077000000000077777777777700007777777777770000000000000000 -77777000777777700000000000000000077777770007777700000077000777777777700077000000000077777777777700007777777777770000000000000000 -77777700777777777000000000000007777777770077777700000077000777777777700077000000000077777777777700007777777777770000000000000000 -77777770777777777770000000000777777777770777777700000777000077777777000077700000000077777777777700007777777777770000000000000000 -77777777777777777777700000077777777777777777777700000777000077777777000077700000000077777777777700007777777777770000000000000000 -77777777777777777777700000077777777777777777777700007777000007777770000077770000000000000000007700000000000000000000000000000000 -77777770777777777770000000000777777777770777777700007777000007777770000077770000000000000000777700000000000000000000000000000000 -77777700777777777000000000000007777777770077777700077777000000777700000077777000000000000077777700000000000000000000000000000000 -77777000777777700000000000000000077777770007777700077777000000777700000077777000000000777777777700000000000000000000000000000000 -77770000777770000000000000000000000777770000777700777777000000777700000077777700000077777777777700007777000000000000000000000000 -77700000777700000000000000000000000077770000077707777777000000077000000077777770007777777777777700007777000000000000000000000000 -77000000770000000000000000000000000000770000007707777777000000077000000077777770777777777777777700007777000000000000000000000000 -70000000700000000000000000000000000000070000000777777777000000077000000077777777777777777777777700007777000000000000000000000000 +00000000eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee +00000000eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee +00700700eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee +00077000eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee +00077000eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee +00700700eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee +00000000eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee +00000000eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee +eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee +eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee +eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee +eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee +eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee +eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee +eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee +eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee +eeeeeeeeeeeeeeeeeeeeeeeebbbb3bbbbbbbbbbbeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee +eeeeeeeeeeeeeeeeeeeeeeeebbb303bbbb3bb0bbeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee +eeeeeeeeeeeeeeeeeeeeeeee3b300033b30b3003eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee +eeeeeeeeeeeeeeeeeeeeeeee0300400030030400eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee +eeeeeeeeeeeeeeeeeeeeeeee4004444000000444eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee +eeeeeeeeeeeeeeeeeeeeeeee4444444444004494eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee +eeeeeeeeeeeeeeeeeeeeeeee4449444444044494eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee +eeeeeeeeeeeeeeeeeeeeeeee4449444444444444eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee 44444444eaeaeeeeeeeeaeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeaebeeeaeeeeeeeeeeeeeeeeeeeeeeeeeeeee 44449444babaeabeeaeebeebeeaeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeaeeebaeabbbabbeeeeeeeeeeeeeeeeeeeeeeeeeaeab 44444494bbbababbbababbbbeebeeaeeeeeeeebeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeaeeaeebbabab3babbbeaeeeeeeeeeeeeeeeeeeaeeebbab @@ -127,17 +127,17 @@ eeeea994eeeeeeeeeeed65eeeeee999999999999aa944400d65694cdc0ca9a155194cdc0ca9a656d eeeaaaa94eeeeeeeeeed65eeeee99999999999aa99444000d656dcacac75471551dcacac7547656d3beb3ebe44444494b00bb00bbbbbbbab3333b33beeeeeeee eeaaaaaaa999eeeeeeed65eeee9999999999aa9449444400d6569ac7ca7a7a15519ac7ca7a7a656db3b33eb3449444443b0330b33b3bbbbbb33bee3beeeeeeee eaaaaaaaaaaa999eeeed65eee4949494999a944444444000d6564c000c094715514c000c0947656deebeebee444444440b0330b0eeebbebbbbeeeeeeeeeeeeee -eeeeeeeeeeeeeeeeeeeeeee949494449a99494444444440ed656c0ca7a77701551c0ca7a7770656de77778eeeeeeeeeeeeeeeeeeeeeeebbbbbeeeeeeeeeeeeee -eeeeeeeeeeeeeeeeeeeee99444449a9944444444444440eed65649a04f4977155149a04f4977656d7777788eeeeeeeeeeeeeeeeeeeeeeebbbbaeeeeeeeeeeeee -eeeeeeeeeeeeeeeeeeee44449a994444444444994440eeeed6569afaf00a7a15519afaf00a7a656d2288822eeeeeeeeeeeeeeeeeeeeeeeeebbbbeeeeeeeeeeee -eeeeeeeeeeeeeeeeee4499994444494444944494444eeeeed656dcafaf05071551dcafaf0507656de28882eeeeeeeeeeeeeeeeeeeeeeeeeeebbbeeeeeeeeeeee -eeeeeeeeeeeeeeeea9994494499449444494444440eeeeeed65690cdf09a5d155190cdf09a5d656dee282eeeeeeeeeeeeeeeeeeeeeeeeeeeebbbbeeeeeeeeeee -eeeeeeeeeeee99994494444444944444449444444eeeeeeed656dc09d9d9d61551dc09d9d9d6656deee2eeeeeeeeeeeeeeeeeeeeeeeeeeeeeebbaeeeeeeeeeee -eeeeeeee44994494449444444494444444444400eeeeeeeed60d777777777775577777777777d06deeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeebaeeeeeeeeeee -eeee44994444444444444444444444444440eeeeeeeeeeeeed6000000000000ee0000000000006deeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee +eeeeeeeeeeeeeeeeeeeeeee949494449a99494444444440ed656c0ca7a77701551c0ca7a7770656de77778ee4444444444444444eeeeebbbbbeeeeeeeeeeeeee +eeeeeeeeeeeeeeeeeeeee99444449a9944444444444440eed65649a04f4977155149a04f4977656d7777788e0449449949944444eeeeeebbbbaeeeeeeeeeeeee +eeeeeeeeeeeeeeeeeeee44449a994444444444994440eeeed6569afaf00a7a15519afaf00a7a656d2288822e0449444994444000eeeeeeeebbbbeeeeeeeeeeee +eeeeeeeeeeeeeeeeee4499994444494444944494444eeeeed656dcafaf05071551dcafaf0507656de28882ee4444444999444044eeeeeeeeebbbeeeeeeeeeeee +eeeeeeeeeeeeeeeea9994494499449444494444440eeeeeed65690cdf09a5d155190cdf09a5d656dee282eee0444499444444444eeeeeeeeebbbbeeeeeeeeeee +eeeeeeeeeeee99994494444444944444449444444eeeeeeed656dc09d9d9d61551dc09d9d9d6656deee2eeee0444444444444440eeeeeeeeeebbaeeeeeeeeeee +eeeeeeee44994494449444444494444444444400eeeeeeeed60d777777777775577777777777d06deeeeeeee004499440e004440eeeeeeeeeeebaeeeeeeeeeee +eeee44994444444444444444444444444440eeeeeeeeeeeeed6000000000000ee0000000000006deeeeeeeeee0049eeeeeee000eeeeeeeeeeeeeeeeeeeeeeeee __gff__ -0041414141414141414141414141414141414141414141414141414141410000414141414141414141414141410000004141414141414141414141414141414141414141414141414141616180808000414040414141414141414040414141414545454343434343454541414141414145455149434343434545414141418080 -0000000000000000000000000000000000000000000000000000000000000000000000000000000041418080800000000000000000000000414141800080414100000000000000000000008080804141000000000000000000004141808080804040404041410000000080418080800041414141414100000000000000008000 +0000000000000000000000000000000000000000000000000000000000000000000000434300000000000000000000004141414141414141414141414141414141414141414141414141616180808000414040414141414141414040414141414545454343434343454541414141414145455149434343434545414141418080 +0000000000000000000000000000000000000000000000000000000000000000000000000000000041418080800000000000000000000000414141800080414100000000000000000000008080804141000000000000000000004141808080804040404043430000000080418080800041414141434300000000004040008000 __map__ 6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6f6e6f6e6f6e6f6e6f6e6f6e6f6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f7f7e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f7f7e7f7e7f7e7f7e7f7e7f7e7f7e7f7e7f7e7f7e7f7e7f7e7f7e7f7e7f7e7f7e7f7e7f7e7f7e7f7e0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 @@ -148,9 +148,9 @@ __map__ 000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a85c5d0000b86c6d0000b9cecf0000005354535457444446313335374e4c4e4e4db86c6d4c4d4e4d4e4ca9be 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004e4d4db86c6d4c4ca9bebf4c4cba7a7b4d4e4d5e5f5e5f535453545744444747494643454346464245454246494549 0000000000000000000000000000000000000000000000000000000000000000000000000000000000004c4d4e4d4e0000000000000000000000000000000000000000004c4d4e4d4e0000000000000051424346494346464346474847484748474748486e6f6e6f5e5f5e5f5354eb5958eb5354535453545354535453545354 -000000000000a85c5d000000000000a85c5d00000000000000000000000000004c4d4e00000000000051414243424352000000000000004c4d4e4d4e0000000000000051414243424352000000000000007c7debeb7c7d7c7d5357466e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f5e5f535453545e5f5e5f5e5f5e5f5e5f5e5f5e5f -4c4e4e4d4e4cb86c6d4e4c4d4e4e4cb86c6d4c4c4c4e00000000000000000051414243520000000000007c7d7c7d7d0000000000000051414243424352000000000000007c7d7c7d7d000000000000000000007c7d000000007c7d7d59466e6f6e6f6e6f6e6f6e6f6e6f6e7f6e6f5e5f5e5f6e6f6e6f6e6f6e6f6e6f6e6f6e6f -47484847484847484748484748474847484942434546313335370000000000007c7d7d00000000000000000000000000000000000000007c7d7c7d7d00000000000000000000000000000000000000000000000000000000000000007c7d595556574246464246466e6f6e007e7f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6e +000000000000a85c5d000000000000a85c5d00000000000000000000000000004c4d4e00000000000051232423242352000000000000004c4d4e4d4e0000000000000051232423242352000000000000007c7debeb7c7d7c7d5357466e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f5e5f535453545e5f5e5f5e5f5e5f5e5f5e5f5e5f +4c4e4e4d4e4cb86c6d4e4c4d4e4e4cb86c6d4c4c4c4e0000000000000000005123242352000000000000fbfcfbfcfc000000000000005123242324235200000000000000fbfcfbfcfc000000000000000000007c7d000000007c7d7d59466e6f6e6f6e6f6e6f6e6f6e6f6e7f6e6f5e5f5e5f6e6f6e6f6e6f6e6f6e6f6e6f6e6f +4748484748484748474848474847484748494243454631333537000000000000fbfcfc0000000000000000000000000000000000000000fbfcfbfcfc00000000000000000000000000000000000000000000000000000000000000007c7d595556574246464246466e6f6e007e7f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6e 434344555643464455564346494241465853545354305743464252000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006740404040404040404059557e0000007e7f7e6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e 5354535453545353545354535453545354dadbdadb407c7d7c7d0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000774040407172000073744040690000000000006e6f6e6f6e6f6e6f6e6f6e6f6e6f6e 5e5f5e5f5e5f5e5e5f5e5f5e5f5e5f5e5f5e5f5e5f50000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004040700000000000007540790000000000006e6f6e6f6e6f6e6f6e6f6e6f6e6f6e @@ -158,8 +158,8 @@ __map__ 6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f7e7f7e7f00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000407800000000000000007740000000000000000000007e7f7e7f6e6f6e6f6e7e7f 6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e7f000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040000000000000000000004000000000000000000000000000006e6f6e6f6e0000 6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e000000000000000000000000000000000000004a4b000000004c4d4e00000000000000000000000000004d4c4d4c4e4c4c4e4c4d4d4c4d4d4d4c4e4d4c4e4c4c000000000000000000000000000040000000000000000000004000000000000000000000000000007e7f7e7f7e0000 -6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f7e000000000000000000000000000000000000000000000000514142435200000000000000000000000051434455434445494546444549424146444444464646463133353700000000000000000000406900000000000000006668000000000000000000000000000000000000000000 -6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e7f000000000000000000000000000000000000000000ec000000007c7d7d0000000000000000000000000000535453545354535453545354535453545354535453545744444731333537000000000000757900000000000000007678000000000000000000000000000000000000000000 +6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f7e000000000000000000000000000000000000000000000000512324235200000000000000000000000051434455434445494546444549424146444444464646463133353700000000000000000000406900000000000000006668000000000000000000000000000000000000000000 +6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e7f000000000000000000000000000000000000000000ec00000000fbfcfc0000000000000000000000000000535453545354535453545354535453545354535453545744444731333537000000000000757900000000000000007678000000000000000000000000000000000000000000 6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e000000000000000000000000000000000000000000aaabac00000000000000000000000000000000000000005e5f5e5f5e5f5e5f5e5f5e5f5e5f5e5f5e5f5e5f5e5f5354eb595744444731333537000000673c3d0000000000656800000000000000000000000000000000000000000000 6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e00000000000000000000000000000000000000000000bb000000000000000000000000004d4e4e4d4c4e4d4d6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f5e5f53545354eb59574444473c3d4d7759463c3d4d6364407800000000000000000000000000000000000000000000 7e7feaeaeaeaeaeaeaeaeaeaeaeaea00000000000000000000000000000000000000000000bb0000000000000000000000005143444547484855566e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f5e5f5e5f53545354eb595744474955594241465853543c3d000000000000000000000000000000000000000000 @@ -168,6 +168,6 @@ __map__ 4c4eeaeaeaeaeaeaeaeaeaeaeaeaea4c4e4c4d4e4d4c4d0000000000000000000000000000bb00000000003e3f4749585354305e5f5e5f5e5f5e5f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f5e5f5e5f5e5f5e5f5e5f5e5f5354535459483c3d00000000000000000000e0e1000000 46454541444946454344494545494546454344454341423133353700000000000000000000bb0000003e3f48455553545e5f506e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f5e5fdadbeb5744463c3d00000000000000e3e4e5000000 53545354535453545354535453545354535453545354eb57434642313335374c4e4e4d4e4dcb4c3e3f47495853545e5f6e6f6e6f6e6e6f6f6e6f6e6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f5e5f5354535457463c3d4d4ef0f1f2f3f4f5000000 -5e5f5e5f5e5f5e5f5e5f5e5f5e5f5e5f5e5f5e5f5e5f53545354eb57434646474848474848474848455553545e5f6e6f6e6f6e6f6e6f6e6f6e6f6e6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f5edbdadb535457424358535453547800000000 +5e5f5e5f5e5f5e5f5e5f5e5f5e5f5e5f5e5f5e5f5e5f53545354eb57434646474848474848474848455553545e5f6e6f6e6f6e6f6e6f6e6f6e6f6e6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f5edbdadb535457424358535453545200000000 6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f5e5f5e5f535453543059434455564346555853545e5f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e5f5e5fdadb5354dadbdadbdadb4c4e4d4d4c 6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f5e5f5e5f505354535453545354305e5f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f5e5f5e5f5e5f5e5f5e5f5944415946 diff --git a/data/data_stage1_20.p8 b/data/data_stage1_20.p8 index 3f4fecd5..8e567105 100644 --- a/data/data_stage1_20.p8 +++ b/data/data_stage1_20.p8 @@ -7,30 +7,30 @@ __lua__ -- we only need __map__, but we kept __gfx__ only to visualize tiles when editing -- (and __gff__ because it's not too big) __gfx__ -00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -00000000777700000000000000000000000000000000000000000000000000000000000000000000000000000000777777000000000000000000000000000077 -00700700777777777777777700000000000000000000000000000000000000000000000000000000000000007777777777770000000000000000000000007777 -00077000777777777777777777770000000000000000000000000000000000000000000000000000000077777777777777777700000000000000000000777777 -00077000777777777777777777777777777777770000000000000000000000000000000000000000777777777777777777777777000000000000000077777777 -00700700777777777777777777777777777777777777000000000000000000000000000000007777777777777777777777777777770000000000007777777777 -00000000777777777777777777777777777777777777777777777777000000000000000077777777777777777777777777777777777700000000777777777777 -00000000777777777777777777777777777777777777777777777777777700000000777777777777777777777777777777777777777777000077777777777777 -70000000700000000000000000000000000000070000000700000007777777777777777770000000000000000000000000007777777777770000000000000000 -77000000770000000000000000000000000000770000007700000007077777777777777070000000000000000000000000007777777777770000000000000000 -77700000777700000000000000000000000077770000077700000007077777777777777070000000000077777777777700007777777777770000000000000000 -77770000777770000000000000000000000777770000777700000077007777777777770077000000000077777777777700007777777777770000000000000000 -77777000777777700000000000000000077777770007777700000077000777777777700077000000000077777777777700007777777777770000000000000000 -77777700777777777000000000000007777777770077777700000077000777777777700077000000000077777777777700007777777777770000000000000000 -77777770777777777770000000000777777777770777777700000777000077777777000077700000000077777777777700007777777777770000000000000000 -77777777777777777777700000077777777777777777777700000777000077777777000077700000000077777777777700007777777777770000000000000000 -77777777777777777777700000077777777777777777777700007777000007777770000077770000000000000000007700000000000000000000000000000000 -77777770777777777770000000000777777777770777777700007777000007777770000077770000000000000000777700000000000000000000000000000000 -77777700777777777000000000000007777777770077777700077777000000777700000077777000000000000077777700000000000000000000000000000000 -77777000777777700000000000000000077777770007777700077777000000777700000077777000000000777777777700000000000000000000000000000000 -77770000777770000000000000000000000777770000777700777777000000777700000077777700000077777777777700007777000000000000000000000000 -77700000777700000000000000000000000077770000077707777777000000077000000077777770007777777777777700007777000000000000000000000000 -77000000770000000000000000000000000000770000007707777777000000077000000077777770777777777777777700007777000000000000000000000000 -70000000700000000000000000000000000000070000000777777777000000077000000077777777777777777777777700007777000000000000000000000000 +00000000eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee +00000000eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee +00700700eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee +00077000eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee +00077000eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee +00700700eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee +00000000eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee +00000000eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee +eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee +eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee +eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee +eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee +eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee +eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee +eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee +eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee +eeeeeeeeeeeeeeeeeeeeeeeebbbb3bbbbbbbbbbbeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee +eeeeeeeeeeeeeeeeeeeeeeeebbb303bbbb3bb0bbeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee +eeeeeeeeeeeeeeeeeeeeeeee3b300033b30b3003eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee +eeeeeeeeeeeeeeeeeeeeeeee0300400030030400eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee +eeeeeeeeeeeeeeeeeeeeeeee4004444000000444eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee +eeeeeeeeeeeeeeeeeeeeeeee4444444444004494eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee +eeeeeeeeeeeeeeeeeeeeeeee4449444444044494eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee +eeeeeeeeeeeeeeeeeeeeeeee4449444444444444eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee 44444444eaeaeeeeeeeeaeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeaebeeeaeeeeeeeeeeeeeeeeeeeeeeeeeeeee 44449444babaeabeeaeebeebeeaeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeaeeebaeabbbabbeeeeeeeeeeeeeeeeeeeeeeeeeaeab 44444494bbbababbbababbbbeebeeaeeeeeeeebeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeaeeaeebbabab3babbbeaeeeeeeeeeeeeeeeeeeaeeebbab @@ -127,17 +127,17 @@ eeeea994eeeeeeeeeeed65eeeeee999999999999aa944400d65694cdc0ca9a155194cdc0ca9a656d eeeaaaa94eeeeeeeeeed65eeeee99999999999aa99444000d656dcacac75471551dcacac7547656d3beb3ebe44444494b00bb00bbbbbbbab3333b33beeeeeeee eeaaaaaaa999eeeeeeed65eeee9999999999aa9449444400d6569ac7ca7a7a15519ac7ca7a7a656db3b33eb3449444443b0330b33b3bbbbbb33bee3beeeeeeee eaaaaaaaaaaa999eeeed65eee4949494999a944444444000d6564c000c094715514c000c0947656deebeebee444444440b0330b0eeebbebbbbeeeeeeeeeeeeee -eeeeeeeeeeeeeeeeeeeeeee949494449a99494444444440ed656c0ca7a77701551c0ca7a7770656de77778eeeeeeeeeeeeeeeeeeeeeeebbbbbeeeeeeeeeeeeee -eeeeeeeeeeeeeeeeeeeee99444449a9944444444444440eed65649a04f4977155149a04f4977656d7777788eeeeeeeeeeeeeeeeeeeeeeebbbbaeeeeeeeeeeeee -eeeeeeeeeeeeeeeeeeee44449a994444444444994440eeeed6569afaf00a7a15519afaf00a7a656d2288822eeeeeeeeeeeeeeeeeeeeeeeeebbbbeeeeeeeeeeee -eeeeeeeeeeeeeeeeee4499994444494444944494444eeeeed656dcafaf05071551dcafaf0507656de28882eeeeeeeeeeeeeeeeeeeeeeeeeeebbbeeeeeeeeeeee -eeeeeeeeeeeeeeeea9994494499449444494444440eeeeeed65690cdf09a5d155190cdf09a5d656dee282eeeeeeeeeeeeeeeeeeeeeeeeeeeebbbbeeeeeeeeeee -eeeeeeeeeeee99994494444444944444449444444eeeeeeed656dc09d9d9d61551dc09d9d9d6656deee2eeeeeeeeeeeeeeeeeeeeeeeeeeeeeebbaeeeeeeeeeee -eeeeeeee44994494449444444494444444444400eeeeeeeed60d777777777775577777777777d06deeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeebaeeeeeeeeeee -eeee44994444444444444444444444444440eeeeeeeeeeeeed6000000000000ee0000000000006deeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee +eeeeeeeeeeeeeeeeeeeeeee949494449a99494444444440ed656c0ca7a77701551c0ca7a7770656de77778ee4444444444444444eeeeebbbbbeeeeeeeeeeeeee +eeeeeeeeeeeeeeeeeeeee99444449a9944444444444440eed65649a04f4977155149a04f4977656d7777788e0449449949944444eeeeeebbbbaeeeeeeeeeeeee +eeeeeeeeeeeeeeeeeeee44449a994444444444994440eeeed6569afaf00a7a15519afaf00a7a656d2288822e0449444994444000eeeeeeeebbbbeeeeeeeeeeee +eeeeeeeeeeeeeeeeee4499994444494444944494444eeeeed656dcafaf05071551dcafaf0507656de28882ee4444444999444044eeeeeeeeebbbeeeeeeeeeeee +eeeeeeeeeeeeeeeea9994494499449444494444440eeeeeed65690cdf09a5d155190cdf09a5d656dee282eee0444499444444444eeeeeeeeebbbbeeeeeeeeeee +eeeeeeeeeeee99994494444444944444449444444eeeeeeed656dc09d9d9d61551dc09d9d9d6656deee2eeee0444444444444440eeeeeeeeeebbaeeeeeeeeeee +eeeeeeee44994494449444444494444444444400eeeeeeeed60d777777777775577777777777d06deeeeeeee004499440e004440eeeeeeeeeeebaeeeeeeeeeee +eeee44994444444444444444444444444440eeeeeeeeeeeeed6000000000000ee0000000000006deeeeeeeeee0049eeeeeee000eeeeeeeeeeeeeeeeeeeeeeeee __gff__ -0041414141414141414141414141414141414141414141414141414141410000414141414141414141414141410000004141414141414141414141414141414141414141414141414141616180808000414040414141414141414040414141414545454343434343454541414141414145455149434343434545414141418080 -0000000000000000000000000000000000000000000000000000000000000000000000000000000041418080800000000000000000000000414141800080414100000000000000000000008080804141000000000000000000004141808080804040404041410000000080418080800041414141414100000000000000008000 +0000000000000000000000000000000000000000000000000000000000000000000000434300000000000000000000004141414141414141414141414141414141414141414141414141616180808000414040414141414141414040414141414545454343434343454541414141414145455149434343434545414141418080 +0000000000000000000000000000000000000000000000000000000000000000000000000000000041418080800000000000000000000000414141800080414100000000000000000000008080804141000000000000000000004141808080804040404043430000000080418080800041414141434300000000004040008000 __map__ 6e6f000000000000000000000000000000000000000000006e6f6e6f6e6f6e6f6e6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6e6f6e6f 6e6f000000000000000000000000000000000000000000006e6f6e6f6e6f6e6e6e6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6e6f6e6f @@ -162,8 +162,8 @@ __map__ 000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004c4c4e4c4d4c4e4c4d4c4d0000000000000000000000000000000000006730594342424646425530300000000000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005144454648474847484748475200000000000000000000000000000000007740403071720000737440406900000000000000000000000000000000000000000000000000000000000000000000 00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000535453596e6f6e6f6e6f6e00000000000000000000000000000000000000403070000000000000754079000000004c4e4d4e0000000000000000000000000000000000000000000000000000 -00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000dadbdadb466f6e6f6e6f6e0000000000000000000000000000000000000030680000000000000000674000000051464446445200000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007cebdadbda596e6f7e7e7f00000000000000000000000000000000000000307800000000000000007740000000007c7d7c7d000000000000000000eaeaea0000000000000000000000000000 +00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000dadbdadb466f6e6f6e6f6e0000000000000000000000000000000000000030680000000000000000674000000051232423245200000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007cebdadbda596e6f7e7e7f0000000000000000000000000000000000000030780000000000000000774000000000fbfcfbfc000000000000000000eaeaea0000000000000000000000000000 00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007c7d7d7d7d6e7f0000000000000000000000000000000000000000000040000000000000000000004000000000000000000000000000eaeaeaeaeafaeaeaeaea0000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000000000004a4b000000000000000000000000007e00000000000000000000000000000000000000000000004000000000000000000000400000000000000000000000eaeaeaeaeaeaeaeaeaeaeaea0000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040690000000000000000666800000000000000000000514244474847484748474849435200000000000000000000 diff --git a/data/data_stage1_21.p8 b/data/data_stage1_21.p8 index d3612910..f2207a2d 100644 --- a/data/data_stage1_21.p8 +++ b/data/data_stage1_21.p8 @@ -7,30 +7,30 @@ __lua__ -- we only need __map__, but we kept __gfx__ only to visualize tiles when editing -- (and __gff__ because it's not too big) __gfx__ -00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -00000000777700000000000000000000000000000000000000000000000000000000000000000000000000000000777777000000000000000000000000000077 -00700700777777777777777700000000000000000000000000000000000000000000000000000000000000007777777777770000000000000000000000007777 -00077000777777777777777777770000000000000000000000000000000000000000000000000000000077777777777777777700000000000000000000777777 -00077000777777777777777777777777777777770000000000000000000000000000000000000000777777777777777777777777000000000000000077777777 -00700700777777777777777777777777777777777777000000000000000000000000000000007777777777777777777777777777770000000000007777777777 -00000000777777777777777777777777777777777777777777777777000000000000000077777777777777777777777777777777777700000000777777777777 -00000000777777777777777777777777777777777777777777777777777700000000777777777777777777777777777777777777777777000077777777777777 -70000000700000000000000000000000000000070000000700000007777777777777777770000000000000000000000000007777777777770000000000000000 -77000000770000000000000000000000000000770000007700000007077777777777777070000000000000000000000000007777777777770000000000000000 -77700000777700000000000000000000000077770000077700000007077777777777777070000000000077777777777700007777777777770000000000000000 -77770000777770000000000000000000000777770000777700000077007777777777770077000000000077777777777700007777777777770000000000000000 -77777000777777700000000000000000077777770007777700000077000777777777700077000000000077777777777700007777777777770000000000000000 -77777700777777777000000000000007777777770077777700000077000777777777700077000000000077777777777700007777777777770000000000000000 -77777770777777777770000000000777777777770777777700000777000077777777000077700000000077777777777700007777777777770000000000000000 -77777777777777777777700000077777777777777777777700000777000077777777000077700000000077777777777700007777777777770000000000000000 -77777777777777777777700000077777777777777777777700007777000007777770000077770000000000000000007700000000000000000000000000000000 -77777770777777777770000000000777777777770777777700007777000007777770000077770000000000000000777700000000000000000000000000000000 -77777700777777777000000000000007777777770077777700077777000000777700000077777000000000000077777700000000000000000000000000000000 -77777000777777700000000000000000077777770007777700077777000000777700000077777000000000777777777700000000000000000000000000000000 -77770000777770000000000000000000000777770000777700777777000000777700000077777700000077777777777700007777000000000000000000000000 -77700000777700000000000000000000000077770000077707777777000000077000000077777770007777777777777700007777000000000000000000000000 -77000000770000000000000000000000000000770000007707777777000000077000000077777770777777777777777700007777000000000000000000000000 -70000000700000000000000000000000000000070000000777777777000000077000000077777777777777777777777700007777000000000000000000000000 +00000000eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee +00000000eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee +00700700eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee +00077000eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee +00077000eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee +00700700eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee +00000000eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee +00000000eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee +eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee +eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee +eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee +eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee +eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee +eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee +eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee +eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee +eeeeeeeeeeeeeeeeeeeeeeeebbbb3bbbbbbbbbbbeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee +eeeeeeeeeeeeeeeeeeeeeeeebbb303bbbb3bb0bbeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee +eeeeeeeeeeeeeeeeeeeeeeee3b300033b30b3003eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee +eeeeeeeeeeeeeeeeeeeeeeee0300400030030400eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee +eeeeeeeeeeeeeeeeeeeeeeee4004444000000444eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee +eeeeeeeeeeeeeeeeeeeeeeee4444444444004494eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee +eeeeeeeeeeeeeeeeeeeeeeee4449444444044494eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee +eeeeeeeeeeeeeeeeeeeeeeee4449444444444444eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee 44444444eaeaeeeeeeeeaeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeaebeeeaeeeeeeeeeeeeeeeeeeeeeeeeeeeee 44449444babaeabeeaeebeebeeaeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeaeeebaeabbbabbeeeeeeeeeeeeeeeeeeeeeeeeeaeab 44444494bbbababbbababbbbeebeeaeeeeeeeebeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeaeeaeebbabab3babbbeaeeeeeeeeeeeeeeeeeeaeeebbab @@ -127,23 +127,23 @@ eeeea994eeeeeeeeeeed65eeeeee999999999999aa944400d65694cdc0ca9a155194cdc0ca9a656d eeeaaaa94eeeeeeeeeed65eeeee99999999999aa99444000d656dcacac75471551dcacac7547656d3beb3ebe44444494b00bb00bbbbbbbab3333b33beeeeeeee eeaaaaaaa999eeeeeeed65eeee9999999999aa9449444400d6569ac7ca7a7a15519ac7ca7a7a656db3b33eb3449444443b0330b33b3bbbbbb33bee3beeeeeeee eaaaaaaaaaaa999eeeed65eee4949494999a944444444000d6564c000c094715514c000c0947656deebeebee444444440b0330b0eeebbebbbbeeeeeeeeeeeeee -eeeeeeeeeeeeeeeeeeeeeee949494449a99494444444440ed656c0ca7a77701551c0ca7a7770656de77778eeeeeeeeeeeeeeeeeeeeeeebbbbbeeeeeeeeeeeeee -eeeeeeeeeeeeeeeeeeeee99444449a9944444444444440eed65649a04f4977155149a04f4977656d7777788eeeeeeeeeeeeeeeeeeeeeeebbbbaeeeeeeeeeeeee -eeeeeeeeeeeeeeeeeeee44449a994444444444994440eeeed6569afaf00a7a15519afaf00a7a656d2288822eeeeeeeeeeeeeeeeeeeeeeeeebbbbeeeeeeeeeeee -eeeeeeeeeeeeeeeeee4499994444494444944494444eeeeed656dcafaf05071551dcafaf0507656de28882eeeeeeeeeeeeeeeeeeeeeeeeeeebbbeeeeeeeeeeee -eeeeeeeeeeeeeeeea9994494499449444494444440eeeeeed65690cdf09a5d155190cdf09a5d656dee282eeeeeeeeeeeeeeeeeeeeeeeeeeeebbbbeeeeeeeeeee -eeeeeeeeeeee99994494444444944444449444444eeeeeeed656dc09d9d9d61551dc09d9d9d6656deee2eeeeeeeeeeeeeeeeeeeeeeeeeeeeeebbaeeeeeeeeeee -eeeeeeee44994494449444444494444444444400eeeeeeeed60d777777777775577777777777d06deeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeebaeeeeeeeeeee -eeee44994444444444444444444444444440eeeeeeeeeeeeed6000000000000ee0000000000006deeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee +eeeeeeeeeeeeeeeeeeeeeee949494449a99494444444440ed656c0ca7a77701551c0ca7a7770656de77778ee4444444444444444eeeeebbbbbeeeeeeeeeeeeee +eeeeeeeeeeeeeeeeeeeee99444449a9944444444444440eed65649a04f4977155149a04f4977656d7777788e0449449949944444eeeeeebbbbaeeeeeeeeeeeee +eeeeeeeeeeeeeeeeeeee44449a994444444444994440eeeed6569afaf00a7a15519afaf00a7a656d2288822e0449444994444000eeeeeeeebbbbeeeeeeeeeeee +eeeeeeeeeeeeeeeeee4499994444494444944494444eeeeed656dcafaf05071551dcafaf0507656de28882ee4444444999444044eeeeeeeeebbbeeeeeeeeeeee +eeeeeeeeeeeeeeeea9994494499449444494444440eeeeeed65690cdf09a5d155190cdf09a5d656dee282eee0444499444444444eeeeeeeeebbbbeeeeeeeeeee +eeeeeeeeeeee99994494444444944444449444444eeeeeeed656dc09d9d9d61551dc09d9d9d6656deee2eeee0444444444444440eeeeeeeeeebbaeeeeeeeeeee +eeeeeeee44994494449444444494444444444400eeeeeeeed60d777777777775577777777777d06deeeeeeee004499440e004440eeeeeeeeeeebaeeeeeeeeeee +eeee44994444444444444444444444444440eeeeeeeeeeeeed6000000000000ee0000000000006deeeeeeeeee0049eeeeeee000eeeeeeeeeeeeeeeeeeeeeeeee __gff__ -0041414141414141414141414141414141414141414141414141414141410000414141414141414141414141410000004141414141414141414141414141414141414141414141414141616180808000414040414141414141414040414141414545454343434343454541414141414145455149434343434545414141418080 -0000000000000000000000000000000000000000000000000000000000000000000000000000000041418080800000000000000000000000414141800080414100000000000000000000008080804141000000000000000000004141808080804040404041410000000080418080800041414141414100000000000000008000 +0000000000000000000000000000000000000000000000000000000000000000000000434300000000000000000000004141414141414141414141414141414141414141414141414141616180808000414040414141414141414040414141414545454343434343454541414141414145455149434343434545414141418080 +0000000000000000000000000000000000000000000000000000000000000000000000000000000041418080800000000000000000000000414141800080414100000000000000000000008080804141000000000000000000004141808080804040404043430000000080418080800041414141434300000000004040008000 __map__ 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000bb00000000000000000000004e4d393a3335374c4d4e4c4e5e5f5e5f5e5f5e5f5e5f535453545354535453545354535457463c3d0000000000000000000040780000000000000000774000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000bb0000000000000000003e3f4544444445454244444246446e6f6e6f6e6f6e6f6e6f5e5f5e5f5e5f5e5f5e5f5e5f5e5fdadb59463c3d000000000000000040000000000000000000004000000000000000000000 00000000000000000000000000004c4d4e000000000000000000000000000000000000000000000000000000bb000000000000003e3f45585354535453545354535453546e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f5e5f535457463c3d00000000000040690000000000000000666800000000000000000000 -0000000000000000000000000051414243520000000000000000000000000000000000000000000000000000bb00000000003e3f49585354dadb5e5f5e5f5e5f5e5f5e5f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6e6e6e5e5f535457463c3d0000000075790000000000000000767800000000000000000000 -5d000000000000000000000000007c7d7d0000000000000000004d4c4d4d4c4d4e4d4e4a4b4c4e4d4c4e4c4dcb4e4d4e3e3f45585354dadb5e5f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6e6e6f6e6f5e5f535457443c3d000000673c3d000000000065680000000000000000000000 +0000000000000000000000000051232423520000000000000000000000000000000000000000000000000000bb00000000003e3f49585354dadb5e5f5e5f5e5f5e5f5e5f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6e6e6e5e5f535457463c3d0000000075790000000000000000767800000000000000000000 +5d00000000000000000000000000fbfcfc0000000000000000004d4c4d4d4c4d4e4d4e4a4b4c4e4d4c4e4c4dcb4e4d4e3e3f45585354dadb5e5f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6e6e6f6e6f5e5f535457443c3d000000673c3d000000000065680000000000000000000000 6d00000000000000000000000000000000000000000038393a3b4945434542444943424246494945424145434649494649585354dadb5e5f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6e6e6f6e6f6e6f5e5f535457463c3d4e7759463c3d4d636440780000000000000000000000 bf4e4e4c4e4c0000000000000000000000000000005141424358ebeb53545354535453545354535453545354535453545354dadb5e5f6e6e6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6e6e6f6e6f6e6f6e6f5e5f5354574446494543425559585354000000000000000000000000 454243424346313335370000000000000000000000007c7d7c7d7c7ddadb5e5f5e5f5e5f5e5f5e5f5e5f5e5f5e5f5e5f5e5f5e5f6e6f6e6f6e6f6e6f6e6f6f6e6f6e6f6e6f6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6e6e6f6e6f6e6f6e6f6e6f5e5f5354535453545354dadbdadbeaea3c3d0000000000000000 diff --git a/data/data_stage1_30.p8 b/data/data_stage1_30.p8 index f94cf49c..c900d10d 100644 --- a/data/data_stage1_30.p8 +++ b/data/data_stage1_30.p8 @@ -7,30 +7,30 @@ __lua__ -- we only need __map__, but we kept __gfx__ only to visualize tiles when editing -- (and __gff__ because it's not too big) __gfx__ -00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -00000000777700000000000000000000000000000000000000000000000000000000000000000000000000000000777777000000000000000000000000000077 -00700700777777777777777700000000000000000000000000000000000000000000000000000000000000007777777777770000000000000000000000007777 -00077000777777777777777777770000000000000000000000000000000000000000000000000000000077777777777777777700000000000000000000777777 -00077000777777777777777777777777777777770000000000000000000000000000000000000000777777777777777777777777000000000000000077777777 -00700700777777777777777777777777777777777777000000000000000000000000000000007777777777777777777777777777770000000000007777777777 -00000000777777777777777777777777777777777777777777777777000000000000000077777777777777777777777777777777777700000000777777777777 -00000000777777777777777777777777777777777777777777777777777700000000777777777777777777777777777777777777777777000077777777777777 -70000000700000000000000000000000000000070000000700000007777777777777777770000000000000000000000000007777777777770000000000000000 -77000000770000000000000000000000000000770000007700000007077777777777777070000000000000000000000000007777777777770000000000000000 -77700000777700000000000000000000000077770000077700000007077777777777777070000000000077777777777700007777777777770000000000000000 -77770000777770000000000000000000000777770000777700000077007777777777770077000000000077777777777700007777777777770000000000000000 -77777000777777700000000000000000077777770007777700000077000777777777700077000000000077777777777700007777777777770000000000000000 -77777700777777777000000000000007777777770077777700000077000777777777700077000000000077777777777700007777777777770000000000000000 -77777770777777777770000000000777777777770777777700000777000077777777000077700000000077777777777700007777777777770000000000000000 -77777777777777777777700000077777777777777777777700000777000077777777000077700000000077777777777700007777777777770000000000000000 -77777777777777777777700000077777777777777777777700007777000007777770000077770000000000000000007700000000000000000000000000000000 -77777770777777777770000000000777777777770777777700007777000007777770000077770000000000000000777700000000000000000000000000000000 -77777700777777777000000000000007777777770077777700077777000000777700000077777000000000000077777700000000000000000000000000000000 -77777000777777700000000000000000077777770007777700077777000000777700000077777000000000777777777700000000000000000000000000000000 -77770000777770000000000000000000000777770000777700777777000000777700000077777700000077777777777700007777000000000000000000000000 -77700000777700000000000000000000000077770000077707777777000000077000000077777770007777777777777700007777000000000000000000000000 -77000000770000000000000000000000000000770000007707777777000000077000000077777770777777777777777700007777000000000000000000000000 -70000000700000000000000000000000000000070000000777777777000000077000000077777777777777777777777700007777000000000000000000000000 +00000000eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee +00000000eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee +00700700eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee +00077000eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee +00077000eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee +00700700eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee +00000000eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee +00000000eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee +eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee +eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee +eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee +eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee +eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee +eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee +eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee +eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee +eeeeeeeeeeeeeeeeeeeeeeeebbbb3bbbbbbbbbbbeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee +eeeeeeeeeeeeeeeeeeeeeeeebbb303bbbb3bb0bbeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee +eeeeeeeeeeeeeeeeeeeeeeee3b300033b30b3003eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee +eeeeeeeeeeeeeeeeeeeeeeee0300400030030400eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee +eeeeeeeeeeeeeeeeeeeeeeee4004444000000444eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee +eeeeeeeeeeeeeeeeeeeeeeee4444444444004494eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee +eeeeeeeeeeeeeeeeeeeeeeee4449444444044494eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee +eeeeeeeeeeeeeeeeeeeeeeee4449444444444444eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee 44444444eaeaeeeeeeeeaeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeaebeeeaeeeeeeeeeeeeeeeeeeeeeeeeeeeee 44449444babaeabeeaeebeebeeaeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeaeeebaeabbbabbeeeeeeeeeeeeeeeeeeeeeeeeeaeab 44444494bbbababbbababbbbeebeeaeeeeeeeebeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeaeeaeebbabab3babbbeaeeeeeeeeeeeeeeeeeeaeeebbab @@ -127,17 +127,17 @@ eeeea994eeeeeeeeeeed65eeeeee999999999999aa944400d65694cdc0ca9a155194cdc0ca9a656d eeeaaaa94eeeeeeeeeed65eeeee99999999999aa99444000d656dcacac75471551dcacac7547656d3beb3ebe44444494b00bb00bbbbbbbab3333b33beeeeeeee eeaaaaaaa999eeeeeeed65eeee9999999999aa9449444400d6569ac7ca7a7a15519ac7ca7a7a656db3b33eb3449444443b0330b33b3bbbbbb33bee3beeeeeeee eaaaaaaaaaaa999eeeed65eee4949494999a944444444000d6564c000c094715514c000c0947656deebeebee444444440b0330b0eeebbebbbbeeeeeeeeeeeeee -eeeeeeeeeeeeeeeeeeeeeee949494449a99494444444440ed656c0ca7a77701551c0ca7a7770656de77778eeeeeeeeeeeeeeeeeeeeeeebbbbbeeeeeeeeeeeeee -eeeeeeeeeeeeeeeeeeeee99444449a9944444444444440eed65649a04f4977155149a04f4977656d7777788eeeeeeeeeeeeeeeeeeeeeeebbbbaeeeeeeeeeeeee -eeeeeeeeeeeeeeeeeeee44449a994444444444994440eeeed6569afaf00a7a15519afaf00a7a656d2288822eeeeeeeeeeeeeeeeeeeeeeeeebbbbeeeeeeeeeeee -eeeeeeeeeeeeeeeeee4499994444494444944494444eeeeed656dcafaf05071551dcafaf0507656de28882eeeeeeeeeeeeeeeeeeeeeeeeeeebbbeeeeeeeeeeee -eeeeeeeeeeeeeeeea9994494499449444494444440eeeeeed65690cdf09a5d155190cdf09a5d656dee282eeeeeeeeeeeeeeeeeeeeeeeeeeeebbbbeeeeeeeeeee -eeeeeeeeeeee99994494444444944444449444444eeeeeeed656dc09d9d9d61551dc09d9d9d6656deee2eeeeeeeeeeeeeeeeeeeeeeeeeeeeeebbaeeeeeeeeeee -eeeeeeee44994494449444444494444444444400eeeeeeeed60d777777777775577777777777d06deeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeebaeeeeeeeeeee -eeee44994444444444444444444444444440eeeeeeeeeeeeed6000000000000ee0000000000006deeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee +eeeeeeeeeeeeeeeeeeeeeee949494449a99494444444440ed656c0ca7a77701551c0ca7a7770656de77778ee4444444444444444eeeeebbbbbeeeeeeeeeeeeee +eeeeeeeeeeeeeeeeeeeee99444449a9944444444444440eed65649a04f4977155149a04f4977656d7777788e0449449949944444eeeeeebbbbaeeeeeeeeeeeee +eeeeeeeeeeeeeeeeeeee44449a994444444444994440eeeed6569afaf00a7a15519afaf00a7a656d2288822e0449444994444000eeeeeeeebbbbeeeeeeeeeeee +eeeeeeeeeeeeeeeeee4499994444494444944494444eeeeed656dcafaf05071551dcafaf0507656de28882ee4444444999444044eeeeeeeeebbbeeeeeeeeeeee +eeeeeeeeeeeeeeeea9994494499449444494444440eeeeeed65690cdf09a5d155190cdf09a5d656dee282eee0444499444444444eeeeeeeeebbbbeeeeeeeeeee +eeeeeeeeeeee99994494444444944444449444444eeeeeeed656dc09d9d9d61551dc09d9d9d6656deee2eeee0444444444444440eeeeeeeeeebbaeeeeeeeeeee +eeeeeeee44994494449444444494444444444400eeeeeeeed60d777777777775577777777777d06deeeeeeee004499440e004440eeeeeeeeeeebaeeeeeeeeeee +eeee44994444444444444444444444444440eeeeeeeeeeeeed6000000000000ee0000000000006deeeeeeeeee0049eeeeeee000eeeeeeeeeeeeeeeeeeeeeeeee __gff__ -0041414141414141414141414141414141414141414141414141414141410000414141414141414141414141410000004141414141414141414141414141414141414141414141414141616180808000414040414141414141414040414141414545454343434343454541414141414145455149434343434545414141418080 -0000000000000000000000000000000000000000000000000000000000000000000000000000000041418080800000000000000000000000414141800080414100000000000000000000008080804141000000000000000000004141808080804040404041410000000080418080800041414141414100000000000000008000 +0000000000000000000000000000000000000000000000000000000000000000000000434300000000000000000000004141414141414141414141414141414141414141414141414141616180808000414040414141414141414040414141414545454343434343454541414141414145455149434343434545414141418080 +0000000000000000000000000000000000000000000000000000000000000000000000000000000041418080800000000000000000000000414141800080414100000000000000000000008080804141000000000000000000004141808080804040404043430000000080418080800041414141434300000000004040008000 __map__ 6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 diff --git a/data/data_stage1_31.p8 b/data/data_stage1_31.p8 index ed807528..b6cd2da2 100644 --- a/data/data_stage1_31.p8 +++ b/data/data_stage1_31.p8 @@ -7,30 +7,30 @@ __lua__ -- we only need __map__, but we kept __gfx__ only to visualize tiles when editing -- (and __gff__ because it's not too big) __gfx__ -00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -00000000777700000000000000000000000000000000000000000000000000000000000000000000000000000000777777000000000000000000000000000077 -00700700777777777777777700000000000000000000000000000000000000000000000000000000000000007777777777770000000000000000000000007777 -00077000777777777777777777770000000000000000000000000000000000000000000000000000000077777777777777777700000000000000000000777777 -00077000777777777777777777777777777777770000000000000000000000000000000000000000777777777777777777777777000000000000000077777777 -00700700777777777777777777777777777777777777000000000000000000000000000000007777777777777777777777777777770000000000007777777777 -00000000777777777777777777777777777777777777777777777777000000000000000077777777777777777777777777777777777700000000777777777777 -00000000777777777777777777777777777777777777777777777777777700000000777777777777777777777777777777777777777777000077777777777777 -70000000700000000000000000000000000000070000000700000007777777777777777770000000000000000000000000007777777777770000000000000000 -77000000770000000000000000000000000000770000007700000007077777777777777070000000000000000000000000007777777777770000000000000000 -77700000777700000000000000000000000077770000077700000007077777777777777070000000000077777777777700007777777777770000000000000000 -77770000777770000000000000000000000777770000777700000077007777777777770077000000000077777777777700007777777777770000000000000000 -77777000777777700000000000000000077777770007777700000077000777777777700077000000000077777777777700007777777777770000000000000000 -77777700777777777000000000000007777777770077777700000077000777777777700077000000000077777777777700007777777777770000000000000000 -77777770777777777770000000000777777777770777777700000777000077777777000077700000000077777777777700007777777777770000000000000000 -77777777777777777777700000077777777777777777777700000777000077777777000077700000000077777777777700007777777777770000000000000000 -77777777777777777777700000077777777777777777777700007777000007777770000077770000000000000000007700000000000000000000000000000000 -77777770777777777770000000000777777777770777777700007777000007777770000077770000000000000000777700000000000000000000000000000000 -77777700777777777000000000000007777777770077777700077777000000777700000077777000000000000077777700000000000000000000000000000000 -77777000777777700000000000000000077777770007777700077777000000777700000077777000000000777777777700000000000000000000000000000000 -77770000777770000000000000000000000777770000777700777777000000777700000077777700000077777777777700007777000000000000000000000000 -77700000777700000000000000000000000077770000077707777777000000077000000077777770007777777777777700007777000000000000000000000000 -77000000770000000000000000000000000000770000007707777777000000077000000077777770777777777777777700007777000000000000000000000000 -70000000700000000000000000000000000000070000000777777777000000077000000077777777777777777777777700007777000000000000000000000000 +00000000eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee +00000000eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee +00700700eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee +00077000eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee +00077000eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee +00700700eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee +00000000eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee +00000000eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee +eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee +eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee +eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee +eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee +eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee +eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee +eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee +eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee +eeeeeeeeeeeeeeeeeeeeeeeebbbb3bbbbbbbbbbbeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee +eeeeeeeeeeeeeeeeeeeeeeeebbb303bbbb3bb0bbeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee +eeeeeeeeeeeeeeeeeeeeeeee3b300033b30b3003eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee +eeeeeeeeeeeeeeeeeeeeeeee0300400030030400eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee +eeeeeeeeeeeeeeeeeeeeeeee4004444000000444eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee +eeeeeeeeeeeeeeeeeeeeeeee4444444444004494eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee +eeeeeeeeeeeeeeeeeeeeeeee4449444444044494eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee +eeeeeeeeeeeeeeeeeeeeeeee4449444444444444eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee 44444444eaeaeeeeeeeeaeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeaebeeeaeeeeeeeeeeeeeeeeeeeeeeeeeeeee 44449444babaeabeeaeebeebeeaeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeaeeebaeabbbabbeeeeeeeeeeeeeeeeeeeeeeeeeaeab 44444494bbbababbbababbbbeebeeaeeeeeeeebeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeaeeaeebbabab3babbbeaeeeeeeeeeeeeeeeeeeaeeebbab @@ -127,17 +127,17 @@ eeeea994eeeeeeeeeeed65eeeeee999999999999aa944400d65694cdc0ca9a155194cdc0ca9a656d eeeaaaa94eeeeeeeeeed65eeeee99999999999aa99444000d656dcacac75471551dcacac7547656d3beb3ebe44444494b00bb00bbbbbbbab3333b33beeeeeeee eeaaaaaaa999eeeeeeed65eeee9999999999aa9449444400d6569ac7ca7a7a15519ac7ca7a7a656db3b33eb3449444443b0330b33b3bbbbbb33bee3beeeeeeee eaaaaaaaaaaa999eeeed65eee4949494999a944444444000d6564c000c094715514c000c0947656deebeebee444444440b0330b0eeebbebbbbeeeeeeeeeeeeee -eeeeeeeeeeeeeeeeeeeeeee949494449a99494444444440ed656c0ca7a77701551c0ca7a7770656de77778eeeeeeeeeeeeeeeeeeeeeeebbbbbeeeeeeeeeeeeee -eeeeeeeeeeeeeeeeeeeee99444449a9944444444444440eed65649a04f4977155149a04f4977656d7777788eeeeeeeeeeeeeeeeeeeeeeebbbbaeeeeeeeeeeeee -eeeeeeeeeeeeeeeeeeee44449a994444444444994440eeeed6569afaf00a7a15519afaf00a7a656d2288822eeeeeeeeeeeeeeeeeeeeeeeeebbbbeeeeeeeeeeee -eeeeeeeeeeeeeeeeee4499994444494444944494444eeeeed656dcafaf05071551dcafaf0507656de28882eeeeeeeeeeeeeeeeeeeeeeeeeeebbbeeeeeeeeeeee -eeeeeeeeeeeeeeeea9994494499449444494444440eeeeeed65690cdf09a5d155190cdf09a5d656dee282eeeeeeeeeeeeeeeeeeeeeeeeeeeebbbbeeeeeeeeeee -eeeeeeeeeeee99994494444444944444449444444eeeeeeed656dc09d9d9d61551dc09d9d9d6656deee2eeeeeeeeeeeeeeeeeeeeeeeeeeeeeebbaeeeeeeeeeee -eeeeeeee44994494449444444494444444444400eeeeeeeed60d777777777775577777777777d06deeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeebaeeeeeeeeeee -eeee44994444444444444444444444444440eeeeeeeeeeeeed6000000000000ee0000000000006deeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee +eeeeeeeeeeeeeeeeeeeeeee949494449a99494444444440ed656c0ca7a77701551c0ca7a7770656de77778ee4444444444444444eeeeebbbbbeeeeeeeeeeeeee +eeeeeeeeeeeeeeeeeeeee99444449a9944444444444440eed65649a04f4977155149a04f4977656d7777788e0449449949944444eeeeeebbbbaeeeeeeeeeeeee +eeeeeeeeeeeeeeeeeeee44449a994444444444994440eeeed6569afaf00a7a15519afaf00a7a656d2288822e0449444994444000eeeeeeeebbbbeeeeeeeeeeee +eeeeeeeeeeeeeeeeee4499994444494444944494444eeeeed656dcafaf05071551dcafaf0507656de28882ee4444444999444044eeeeeeeeebbbeeeeeeeeeeee +eeeeeeeeeeeeeeeea9994494499449444494444440eeeeeed65690cdf09a5d155190cdf09a5d656dee282eee0444499444444444eeeeeeeeebbbbeeeeeeeeeee +eeeeeeeeeeee99994494444444944444449444444eeeeeeed656dc09d9d9d61551dc09d9d9d6656deee2eeee0444444444444440eeeeeeeeeebbaeeeeeeeeeee +eeeeeeee44994494449444444494444444444400eeeeeeeed60d777777777775577777777777d06deeeeeeee004499440e004440eeeeeeeeeeebaeeeeeeeeeee +eeee44994444444444444444444444444440eeeeeeeeeeeeed6000000000000ee0000000000006deeeeeeeeee0049eeeeeee000eeeeeeeeeeeeeeeeeeeeeeeee __gff__ -0041414141414141414141414141414141414141414141414141414141410000414141414141414141414141410000004141414141414141414141414141414141414141414141414141616180808000414040414141414141414040414141414545454343434343454541414141414145455149434343434545414141418080 -0000000000000000000000000000000000000000000000000000000000000000000000000000000041418080800000000000000000000000414141800080414100000000000000000000008080804141000000000000000000004141808080804040404041410000000080418080800041414141414100000000000000008000 +0000000000000000000000000000000000000000000000000000000000000000000000434300000000000000000000004141414141414141414141414141414141414141414141414141616180808000414040414141414141414040414141414545454343434343454541414141414145455149434343434545414141418080 +0000000000000000000000000000000000000000000000000000000000000000000000000000000041418080800000000000000000000000414141800080414100000000000000000000008080804141000000000000000000004141808080804040404043430000000080418080800041414141434300000000004040008000 __map__ 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 diff --git a/data/data_stage1_runtime.p8 b/data/data_stage1_runtime.p8 index feea5291..1c5ba4db 100644 --- a/data/data_stage1_runtime.p8 +++ b/data/data_stage1_runtime.p8 @@ -19,14 +19,14 @@ eed666deeeba77777777777756dd00a0d00d0a0da0dddd65569cccc9fffff99957566655eeeeeeee eed666deebba77777777b77b56dddd0dddddd0dd00dddd65569cc9999999999995666655eeeeeeee77eeecccc770ceeeeeecccccc770cceeeeeeeeecccc7707c eed666deebbb77777777bb775d77777777777777777777d55d7c777777777777775555d5eeeeeeeee77eeeccff77f0ee77eeecccc770ceeeeeeeeeffccff77f0 eed666debbbba7777777bbbbe0000000000000000000000ee0000000000000000000000eeeeeeeee777ffccccfffeeeee77eeeccff77f0eeeee77fcccccfffee -eed666debbbb7a7777777777eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeefccfeeeeeeeee7ffccccfffeeeeee7707eccffce67e -eed666de77b77777777777bbeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee1e1cffeeeeeeee7e1efcffeeeeeeeeee77cccccf11607 -eed666deebba777777777ebbeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee11cf1eeeeeeeeeee11cff11eeeeeeeeccceee611ee06 -eed666deebbba7777777eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeecc11ee8800eeeeeecccc1ee880ee77eeee227eeeee -eed666dee7b7777777777bbeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeecc1688800eeeeeeeeec6e8880e882eeee2887eeee -eed666debbbb77777b3377bbeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeec7872000eeeeeeeeec77820008772eeee268eeee -ee06660eebbe77b777bbbbebeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee788200eeeeeeeeee788820e07882eeee288eeee -eee000eeeebee77bebebeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee22000eeeeeeeeeee88200ee0888eeeee8eeeee +eed666debbbb7a7777777777bbbb3bbbbbbbbbbbeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeefccfeeeeeeeee7ffccccfffeeeeee7707eccffce67e +eed666de77b77777777777bbbbb303bbbb3bb0bbeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee1e1cffeeeeeeee7e1efcffeeeeeeeeee77cccccf11607 +eed666deebba777777777ebb3b300033b30b3003eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee11cf1eeeeeeeeeee11cff11eeeeeeeeccceee611ee06 +eed666deebbba7777777eeee0300400030030400eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeecc11ee8800eeeeeecccc1ee880ee77eeee227eeeee +eed666dee7b7777777777bbe4004444000000444eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeecc1688800eeeeeeeeec6e8880e882eeee2887eeee +eed666debbbb77777b3377bb4444444444004494eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeec7872000eeeeeeeeec77820008772eeee268eeee +ee06660eebbe77b777bbbbeb4449444444044494eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee788200eeeeeeeeee788820e07882eeee288eeee +eee000eeeebee77bebebeeee4449444444444444eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee22000eeeeeeeeeee88200ee0888eeeee8eeeee eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee @@ -131,3 +131,6 @@ eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee +__gff__ +0000000000000000000000000000000000000000000000000000000000000000000000030300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 diff --git a/src/data/sprite_flags.lua b/src/data/sprite_flags.lua index 2abaa684..133123d3 100644 --- a/src/data/sprite_flags.lua +++ b/src/data/sprite_flags.lua @@ -1,12 +1,12 @@ sprite_flags = { - collision = 0, -- collision flag set on VISIBLE sprite (and MASK sprite for testing with proto tiles) - unused1 = 1, + collision = 0, -- collision flag set on VISUAL sprite (and MASK sprite for testing with proto tiles) + oneway = 1, -- one-way collision flag set on VISUAL sprite unused2 = 2, unused3 = 3, unused4 = 4, - spring = 5, -- spring - midground = 6, -- midground sprite (should be drawn after programmatical background) - foreground = 7, -- foreground sprite (should be drawn last) + spring = 5, -- spring + midground = 6, -- midground sprite (should be drawn after programmatical background) + foreground = 7, -- foreground sprite (should be drawn last) } sprite_masks = { diff --git a/src/ingame/playercharacter.lua b/src/ingame/playercharacter.lua index 20c32a4a..0ff8cc9c 100644 --- a/src/ingame/playercharacter.lua +++ b/src/ingame/playercharacter.lua @@ -493,7 +493,7 @@ function player_char:get_ground_sensor_position_from(center_position, quadrant_h return qx_floored_bottom_center + offset_qx_vector end --- helper method for compute_closest_ground_query_info and _is_blocked_by_ceiling_at +-- helper method for compute_closest_ground_query_info and is_blocked_by_ceiling_at -- for given player character pc, it iterates over tiles from start to last (defined via offset from sensor position), providing distance from sensor_position_base + sensor_offset_qy along q-down (foot or head) -- to q-column q-top (with reverse tile support) to custom callbacks which should return ground query info to closest ground/ceiling in quadrant direction -- pass it a quadrant of interest (direction used to check collisions), iteration start and last tile locations @@ -560,10 +560,14 @@ local function iterate_over_collision_tiles(pc, collision_check_quadrant, start_ local tile_region_loc = curr_stage_state:global_to_region_location(curr_global_tile_loc) local visual_tile_id = mget(tile_region_loc.i, tile_region_loc.j) - -- we now check for ignored tiles, ramp or loop + -- we now check for ignored tiles: + -- a. ramps just after launching + -- b. loops on inactive layer from PC's point-of-view + -- c. one-way platforms unless we check collision downward if pc.ignore_launch_ramp_timer > 0 and visual_tile_id == visual.launch_ramp_last_tile_id or pc.active_loop_layer == 1 and curr_stage_state:is_tile_in_loop_exit(curr_global_tile_loc) or - pc.active_loop_layer == 2 and curr_stage_state:is_tile_in_loop_entrance(curr_global_tile_loc) then + pc.active_loop_layer == 2 and curr_stage_state:is_tile_in_loop_entrance(curr_global_tile_loc) or + collision_check_quadrant ~= directions.down and fget(visual_tile_id, sprite_flags.oneway) then ignore_tile = true end @@ -1394,7 +1398,7 @@ function player_char:next_ground_step(quadrant_horizontal_dir, ref_motion_result -- position is inside ground, check if we can step up during this step -- (note that we kept the name max_ground_escape_height but in quadrant left and right, -- the escape is done on the X axis so technically we escape row width) - -- refactor: code is similar to _check_escape_from_ground and above all next_air_step + -- refactor: code is similar to check_escape_from_ground and above all next_air_step if - signed_distance_to_closest_ground <= pc_data.max_ground_escape_height then -- step up or step flat next_position_candidate:add_inplace(vector_to_closest_ground) @@ -1876,8 +1880,7 @@ function player_char:next_air_step(direction, ref_motion_result) -- I used to check direction == directions.down only, and indeed if you step 1px down, -- the penetration distance will be no more than 1 and you will always snap to ground. -- But this didn't work when direction left/right hit the slope. - -- refactor: code is similar to _check_escape_from_ground and above all next_ground_step - -- if - signed_distance_to_closest_ground <= pc_data.max_ground_escape_height then + -- refactor: code is similar to check_escape_from_ground and above all next_ground_step if - signed_distance_to_closest_ground <= pc_data.max_ground_escape_height then next_position_candidate.y = next_position_candidate.y + signed_distance_to_closest_ground -- landing: the character has just set foot on ground, flag it and initialize slope angle diff --git a/src/ingame/playercharacter_utest.lua b/src/ingame/playercharacter_utest.lua index e3435cab..8edaca69 100644 --- a/src/ingame/playercharacter_utest.lua +++ b/src/ingame/playercharacter_utest.lua @@ -1148,7 +1148,7 @@ describe('player_char', function () describe('compute_ground_sensors_query_info', function () - -- interface tests are mostly redundant with _compute_closest_ground_query_info + -- interface tests are mostly redundant with compute_closest_ground_query_info -- so we prefer implementation tests, checking that it calls the later with both sensor positions describe('with stubs', function () @@ -5382,7 +5382,7 @@ describe('player_char', function () mock_mset(1, 0, tile_repr.full_tile_id) -- full tile (wall without ground below) end) - -- it will fail until _compute_closest_ground_query_info + -- it will fail until compute_closest_ground_query_info -- detects upper-level tiles as suggested in the note it('when stepping right on the ground and hitting the non-supported wall, preserve x and block', function () local motion_result = motion.ground_motion_result( @@ -5418,7 +5418,7 @@ describe('player_char', function () mock_mset(1, 0, tile_repr.full_tile_id) -- full tile (head wall) end) - -- it will fail until _compute_closest_ground_query_info + -- it will fail until compute_closest_ground_query_info -- detects upper-level tiles as suggested in the note it('when stepping right on the half-tile and hitting the head wall, preserve x and block', function () local motion_result = motion.ground_motion_result( diff --git a/src/ingame/stage_state.lua b/src/ingame/stage_state.lua index 8593df9e..f9bdd498 100644 --- a/src/ingame/stage_state.lua +++ b/src/ingame/stage_state.lua @@ -114,6 +114,12 @@ function stage_state:reload_runtime_data() -- we need to copy 3 rows of 16 sprites, 32 = 0x20 bytes per sprite, -- so 512 = 0x200 bytes per row, -- so 1536 = 0x600 bytes + -- NOTE: we are *not* reloading sprite flags (could do by copying 0x100 bytes from 0x3000-0x30ff) + -- which means our builtin spritesheet *must* contain any new flags brought by runtime extra tiles + -- (located in the 3 top rows of the spritesheet). Those are rare (only one-way platform tiles) + -- but without the flags, they won't behave properly. This means you must place flags on the mask tiles + -- in the built-in spritesheet. Later, you can move all mask tiles to another spritesheet to reload + -- on start instead. local runtime_data_path = "data_stage"..self.curr_stage_id.."_runtime.p8" reload(0x0, 0x0, 0x600, runtime_data_path) @@ -1094,7 +1100,7 @@ function stage_state:render_hud() -- draw emeralds obtained at top-left of screen, in order from left to right, -- with the right color for i = 1, #self.spawned_emerald_locations do - local draw_position = vector(-4 + 10 * i, 6) + local draw_position = vector(-4 + 8 * i, 3) if self.picked_emerald_numbers_set[i] then emerald.draw(i, draw_position) else From b1a3e55d86c2615c0f876fc1af0c5dea6f2aac92 Mon Sep 17 00:00:00 2001 From: huulong Date: Fri, 18 Dec 2020 01:36:18 +0100 Subject: [PATCH 05/91] [PHYSICS] Fix one-way platform: no collision 1px or more below its surface This also fixes character hitting one-way like a wall/ceiling from the side --- src/data/sprite_flags.lua | 2 +- src/ingame/playercharacter.lua | 24 +++++- src/ingame/playercharacter_utest.lua | 92 ++++++++++++++++++++-- src/ingame/stage_state_utest.lua | 6 +- src/itests/ingame/itestplayercharacter.lua | 16 ++++ src/test_data/tile_representation.lua | 2 + src/test_data/tile_test_data.lua | 13 ++- 7 files changed, 136 insertions(+), 19 deletions(-) diff --git a/src/data/sprite_flags.lua b/src/data/sprite_flags.lua index 133123d3..804ba533 100644 --- a/src/data/sprite_flags.lua +++ b/src/data/sprite_flags.lua @@ -11,7 +11,7 @@ sprite_flags = { sprite_masks = { collision = 1, -- 1 << 0 - unused1 = 2, -- 1 << 1 + oneway = 2, -- 1 << 1 unused2 = 4, -- 1 << 2 unused3 = 8, -- 1 << 3 unused4 = 16, -- 1 << 4 diff --git a/src/ingame/playercharacter.lua b/src/ingame/playercharacter.lua index 0ff8cc9c..a26b2b61 100644 --- a/src/ingame/playercharacter.lua +++ b/src/ingame/playercharacter.lua @@ -559,6 +559,7 @@ local function iterate_over_collision_tiles(pc, collision_check_quadrant, start_ -- convert to region location before using mget local tile_region_loc = curr_stage_state:global_to_region_location(curr_global_tile_loc) local visual_tile_id = mget(tile_region_loc.i, tile_region_loc.j) + local is_oneway = fget(visual_tile_id, sprite_flags.oneway) -- we now check for ignored tiles: -- a. ramps just after launching @@ -567,7 +568,7 @@ local function iterate_over_collision_tiles(pc, collision_check_quadrant, start_ if pc.ignore_launch_ramp_timer > 0 and visual_tile_id == visual.launch_ramp_last_tile_id or pc.active_loop_layer == 1 and curr_stage_state:is_tile_in_loop_exit(curr_global_tile_loc) or pc.active_loop_layer == 2 and curr_stage_state:is_tile_in_loop_entrance(curr_global_tile_loc) or - collision_check_quadrant ~= directions.down and fget(visual_tile_id, sprite_flags.oneway) then + is_oneway and collision_check_quadrant ~= directions.down then ignore_tile = true end @@ -608,6 +609,13 @@ local function iterate_over_collision_tiles(pc, collision_check_quadrant, start_ -- then subtract qcolumn_height and you get the signed distance to the current ground q-column local signed_distance_to_closest_collider = world.sub_qy(current_tile_qbottom, world.get_quadrant_y_coord(sensor_position, collision_check_quadrant), collision_check_quadrant) - qcolumn_height + -- even when checking downward, we cannot detect one-way platforms from below their surface (signed distance < 0) + -- this way, we don't step up or get blocked by them as ceiling inadvertently, but can still just land on them + if is_oneway and signed_distance_to_closest_collider < -1 then + printh("signed_distance_to_closest_collider: "..nice_dump(signed_distance_to_closest_collider)) + signed_distance_to_closest_collider = pc_data.max_ground_snap_height + 1 + end + -- let caller decide how to handle the presence of collider local result = collider_distance_callback(curr_global_tile_loc, signed_distance_to_closest_collider, slope_angle) @@ -627,8 +635,10 @@ local function iterate_over_collision_tiles(pc, collision_check_quadrant, start_ -- else (can only happen in compute_closest_ground_query_info): ground has been found, but it is too far below character's q-feet -- to snap q-down. This can only happen on the last tile we iterate on -- (since it was computed to be at the snap q-down limit), - -- which means we will enter the "end of iteration" block below - assert(curr_global_tile_loc == last_global_tile_loc) + -- *unless* we are ignore a one-way platform from below (we can't check signed_distance_to_closest_collider < 0 + -- because signed_distance_to_closest_collider changed already, but we could by storing a backup var if #assert only), + -- which means we will enter the "end of iteration" block below (if because on one-way, we'll continue iteration as normal) + assert(curr_global_tile_loc == last_global_tile_loc or is_oneway) end -- check fo end of iteration (reached last tile) @@ -1854,6 +1864,7 @@ function player_char:next_air_step(direction, ref_motion_result) log("step_vec: "..step_vec, "trace2") log("next_position_candidate: "..next_position_candidate, "trace2") + -- we can only hit walls or the ground when stepping left, right or down -- (horizontal step of diagonal upward motion is OK) if direction ~= directions.up then @@ -1870,7 +1881,12 @@ function player_char:next_air_step(direction, ref_motion_result) -- allow jump from an ascending sheer angle directly onto a platform. This includes moving horizontally. -- This must be combined with a step up (snap to ground top, but directly from the air) to really work if self.velocity.y > 0 or abs(self.velocity.x) > abs(self.velocity.y) then - -- check if we are touching or entering ground + -- check if we are entering ground + -- NOTE: for solid ground we could also consider *touching* as landing, by checking <= 0, + -- then we'd need to move signed_distance_to_closest_ground definition outside direction ~= directions.up block + -- and in the bottom block of this method, check if not ref_motion_result:is_blocked_along(direction) or + -- signed_distance_to_closest_ground == 0, instead of just is_blocked_along, since when landing + -- we are technically blocked along q-down, but must still update position to avoid getting stuck above ground if signed_distance_to_closest_ground < 0 then -- Just like during ground step, check the step height: if too high, we hit a wall and stay airborne -- else, we land diff --git a/src/ingame/playercharacter_utest.lua b/src/ingame/playercharacter_utest.lua index 8edaca69..9c84437f 100644 --- a/src/ingame/playercharacter_utest.lua +++ b/src/ingame/playercharacter_utest.lua @@ -1440,13 +1440,13 @@ describe('player_char', function () assert.are_same(ground_query_info(nil, pc_data.max_ground_snap_height + 1, nil), pc:compute_closest_ground_query_info(vector(0, 12))) end) - it('(right wall) should return ground_query_info(location(1, 1), 2, 0) if 2 pixels from the wall', function () + it('(right wall) should return ground_query_info(location(1, 1), 2, 0.25) if 2 pixels from the wall', function () pc.quadrant = directions.right assert.are_same(ground_query_info(location(1, 1), 2, 0.25), pc:compute_closest_ground_query_info(vector(6, 12))) end) - it('(right wall) should return ground_query_info(location(1, 1), -2, 0) if 2 pixels inside the wall', function () + it('(right wall) should return ground_query_info(location(1, 1), -2, 0.25) if 2 pixels inside the wall', function () pc.quadrant = directions.right assert.are_same(ground_query_info(location(1, 1), -2, 0.25), pc:compute_closest_ground_query_info(vector(10, 12))) @@ -1466,13 +1466,13 @@ describe('player_char', function () assert.are_same(ground_query_info(nil, pc_data.max_ground_snap_height + 1, nil), pc:compute_closest_ground_query_info(vector(12, 24))) end) - it('(ceiling) should return ground_query_info(location(1, 1), 2, 0) if 2 pixels from the wall', function () + it('(ceiling) should return ground_query_info(location(1, 1), 2, 0.5) if 2 pixels from the wall', function () pc.quadrant = directions.up assert.are_same(ground_query_info(location(1, 1), 2, 0.5), pc:compute_closest_ground_query_info(vector(12, 18))) end) - it('(ceiling) should return ground_query_info(location(1, 1), -2, 0) if 2 pixels inside the wall', function () + it('(ceiling) should return ground_query_info(location(1, 1), -2, 0.5) if 2 pixels inside the wall', function () pc.quadrant = directions.up assert.are_same(ground_query_info(location(1, 1), -2, 0.5), pc:compute_closest_ground_query_info(vector(12, 14))) @@ -1492,13 +1492,13 @@ describe('player_char', function () assert.are_same(ground_query_info(nil, pc_data.max_ground_snap_height + 1, nil), pc:compute_closest_ground_query_info(vector(24, 12))) end) - it('(left wall) should return ground_query_info(location(1, 1), 2, 0) if 2 pixels from the wall', function () + it('(left wall) should return ground_query_info(location(1, 1), 2, 0.75) if 2 pixels from the wall', function () pc.quadrant = directions.left assert.are_same(ground_query_info(location(1, 1), 2, 0.75), pc:compute_closest_ground_query_info(vector(18, 12))) end) - it('(left wall) should return ground_query_info(location(1, 1), -2, 0) if 2 pixels inside the wall', function () + it('(left wall) should return ground_query_info(location(1, 1), -2, 0.75) if 2 pixels inside the wall', function () pc.quadrant = directions.left assert.are_same(ground_query_info(location(1, 1), -2, 0.75), pc:compute_closest_ground_query_info(vector(14, 12))) @@ -1538,6 +1538,78 @@ describe('player_char', function () end) + describe('with one-way tile', function () + + before_each(function () + mock_mset(1, 1, tile_repr.oneway_platform_left) + end) + + -- QUADRANT DOWN + + -- above + + it('should return ground_query_info(location(1, 1), max_ground_snap_height, 0) if above the tile by max_ground_snap_height', function () + assert.are_same(ground_query_info(location(1, 1), pc_data.max_ground_snap_height, 0), pc:compute_closest_ground_query_info(vector(12, 8 - pc_data.max_ground_snap_height))) + end) + + -- on top + + it('should return ground_query_info(location(1, 1), 0, 0) if just at the top of tile, in the middle', function () + assert.are_same(ground_query_info(location(1, 1), 0, 0), pc:compute_closest_ground_query_info(vector(12, 8))) + end) + + -- just below the top by up to 1px (still recognize ground to allow step up on low one-way slopes) + + it('should return ground_query_info(location(1, 1), -0.0625, 0) if 0.0625 inside the top-left pixel', function () + assert.are_same(ground_query_info(location(1, 1), -0.0625, 0), pc:compute_closest_ground_query_info(vector(8, 8 + 0.0625))) + end) + + it('should return ground_query_info(location(1, 1), -1, 0) if 1 (<= max_ground_escape_height) inside vertically', function () + assert.are_same(ground_query_info(location(1, 1), -1, 0), pc:compute_closest_ground_query_info(vector(12, 8 + 1))) + end) + + -- below the top by more than 1px (ignoring it) + + it('should return ground_query_info(nil, max_ground_snap_height + 1, nil) if 1.1 (<= max_ground_escape_height) inside vertically', function () + assert.are_same(ground_query_info(nil, pc_data.max_ground_snap_height + 1, nil), pc:compute_closest_ground_query_info(vector(12, 8 + 1.1))) + end) + + it('should return ground_query_info(nil, max_ground_snap_height + 1, nil) if 3 (<= max_ground_escape_height) inside vertically', function () + assert.are_same(ground_query_info(nil, pc_data.max_ground_snap_height + 1, nil), pc:compute_closest_ground_query_info(vector(12, 8 + 3))) + end) + + -- below by more than step up distance (still ignoring it) + + it('should return ground_query_info(nil, max_ground_snap_height + 1, nil) if max_ground_escape_height + 1 below the bottom', function () + assert.are_same(ground_query_info(nil, pc_data.max_ground_snap_height + 1, nil), pc:compute_closest_ground_query_info(vector(15, 16 + pc_data.max_ground_escape_height + 1))) + end) + + -- QUADRANT RIGHT (always ignore) + + it('(right wall) should return ground_query_info(nil, pc_data.max_ground_snap_height + 1, nil) if 2 pixels from the left (not relevant)', function () + pc.quadrant = directions.right + + assert.are_same(ground_query_info(nil, pc_data.max_ground_snap_height + 1, nil), pc:compute_closest_ground_query_info(vector(6, 12))) + end) + + -- QUADRANT UP (always ignore) + + it('(ceiling) should return ground_query_info(nil, pc_data.max_ground_snap_height + 1, nil) if 2 pixels below the surface', function () + pc.quadrant = directions.up + + assert.are_same(ground_query_info(nil, pc_data.max_ground_snap_height + 1, nil), pc:compute_closest_ground_query_info(vector(12, 10))) + end) + + -- QUADRANT LEFT (always ignore) + + it('(left wall) should return ground_query_info(nil, pc_data.max_ground_snap_height + 1, nil) if 2 pixels from the surface right (not relevant)', function () + pc.quadrant = directions.left + + assert.are_same(ground_query_info(nil, pc_data.max_ground_snap_height + 1, nil), pc:compute_closest_ground_query_info(vector(18, 12))) + end) + + end) + describe('with 2 full flat tiles', function () before_each(function () @@ -1934,7 +2006,13 @@ describe('player_char', function () it('(not ignoring ramp) position on ramp should return actual ground_query_info() as it would be detected', function () pc.ignore_launch_ramp_timer = 0 -- same shape as tile_repr.visual_loop_bottomright, so expect same signed distance - assert.are_same(ground_query_info(location(0, 0), -2, atan2(8, -5)), pc:compute_closest_ground_query_info(vector(4, 4))) + assert.are_same(ground_query_info(location(0, 0), 0, atan2(8, -5)), pc:compute_closest_ground_query_info(vector(4, 2))) + end) + + it('(ignoring ramp) position below ramp by more than 1px should return ground_query_info(nil, pc_data.max_ground_snap_height + 1, nil) because it is one-way', function () + pc.ignore_launch_ramp_timer = 0 + -- same shape as tile_repr.visual_loop_bottomright, so expect same signed distance + assert.are_same(ground_query_info(nil, pc_data.max_ground_snap_height + 1, nil), pc:compute_closest_ground_query_info(vector(4, 4))) end) it('(ignoring ramp) position on entrance should return ground_query_info(nil, pc_data.max_ground_snap_height + 1, nil) as if there were nothing', function () diff --git a/src/ingame/stage_state_utest.lua b/src/ingame/stage_state_utest.lua index e00b7eed..957f6287 100644 --- a/src/ingame/stage_state_utest.lua +++ b/src/ingame/stage_state_utest.lua @@ -1750,10 +1750,10 @@ describe('stage_state', function () state:render_hud() assert.spy(emerald.draw).was_called(3) - assert.spy(emerald.draw).was_called_with(1, vector(6, 6)) + assert.spy(emerald.draw).was_called_with(1, vector(4, 3)) -- silhouette only - assert.spy(emerald.draw).was_called_with(-1, vector(16, 6)) - assert.spy(emerald.draw).was_called_with(3, vector(26, 6)) + assert.spy(emerald.draw).was_called_with(-1, vector(12, 3)) + assert.spy(emerald.draw).was_called_with(3, vector(20, 3)) end) it('should debug render character info (#debug_character only)', function () diff --git a/src/itests/ingame/itestplayercharacter.lua b/src/itests/ingame/itestplayercharacter.lua index 21520a1f..bb392cfd 100644 --- a/src/itests/ingame/itestplayercharacter.lua +++ b/src/itests/ingame/itestplayercharacter.lua @@ -910,6 +910,22 @@ expect pc_velocity 0 -5 --]=] +--#if busted +itest_dsl_parser.register( + '#solo stand on one-way', [[ +@stage # +. +o + +warp 4 7 +wait 10 + +expect pc_bottom_pos 4 8 +expect pc_motion_state grounded +expect pc_velocity 0 0 +]]) +--#endif + --[=[ -- human tests: let human check rendering (until I find a way to automate this) diff --git a/src/test_data/tile_representation.lua b/src/test_data/tile_representation.lua index be3dc9b9..7036eaa4 100644 --- a/src/test_data/tile_representation.lua +++ b/src/test_data/tile_representation.lua @@ -34,6 +34,7 @@ local tile_repr = { visual_loop_bottomright_steepest = 102, spring_left_id = 74, -- add 1 to get right, must match value in visual grass_top_decoration1 = 76, -- no collider, just to test foreground + oneway_platform_left = 35, -- left side of one-way platform top part } -- symbol mapping for itests @@ -55,6 +56,7 @@ tile_repr.tile_symbol_to_ids = { ['i'] = tile_repr.visual_loop_bottomright_steepest, ['s'] = tile_repr.spring_left_id, ['S'] = tile_repr.spring_left_id + 1, + ['o'] = tile_repr.oneway_platform_left, } return tile_repr diff --git a/src/test_data/tile_test_data.lua b/src/test_data/tile_test_data.lua index 2ed59963..2e434807 100644 --- a/src/test_data/tile_test_data.lua +++ b/src/test_data/tile_test_data.lua @@ -44,6 +44,7 @@ local mock_raw_tile_collision_data = { [tile_repr.spring_left_id] = {tile_repr.flat_high_tile_left_id, {0, 0, 0, 0, 6, 6, 6, 6}, {0, 0, 4, 4, 4, 4, 4, 4}, atan2(8, 0)}, -- copied from flat_high_tile_left_id [tile_repr.spring_left_id + 1] = {tile_repr.flat_high_tile_id, {6, 6, 6, 6, 6, 6, 6, 6}, {0, 0, 8, 8, 8, 8, 8, 8}, atan2(8, 0)}, -- copied from flat_high_tile_id [visual.launch_ramp_last_tile_id] = {tile_repr.mask_loop_bottomright, {3, 4, 4, 5, 6, 6, 7, 8}, {1, 2, 4, 5, 7, 8, 8, 8}, atan2(8, -5)}, -- copied from visual_loop_bottomright + [tile_repr.oneway_platform_left] = {tile_repr.oneway_platform_left, {8, 8, 8, 8, 8, 8, 8, 8}, {8, 8, 8, 8, 8, 8, 8, 8}, atan2(8, 0)}, } -- process data above to generate interior_v/h automatically, so we don't have to add them manually @@ -80,7 +81,7 @@ function tile_test_data.setup() fset(tile_repr.visual_loop_topleft, sprite_masks.collision + sprite_masks.midground) - -- mask also have collision falg, but only useful to test + -- mask also have collision flag, but only useful to test -- a non-loop proto curve tile with the same shaped fset(tile_repr.mask_loop_topleft, sprite_masks.collision + sprite_masks.midground) @@ -98,13 +99,17 @@ function tile_test_data.setup() fset(tile_repr.visual_loop_bottomright_steepest, sprite_masks.collision + sprite_masks.midground) - -- visual sprites + -- spring fset(tile_repr.spring_left_id, sprite_masks.collision + sprite_masks.spring + sprite_masks.midground) fset(tile_repr.spring_left_id + 1, sprite_masks.collision + sprite_masks.spring + sprite_masks.midground) - -- ramp - fset(visual.launch_ramp_last_tile_id, sprite_masks.collision + sprite_masks.midground) + -- ramp (last tile is one-way) + fset(visual.launch_ramp_last_tile_id, sprite_masks.collision + sprite_masks.oneway + sprite_masks.midground) + -- one-way platform + fset(tile_repr.oneway_platform_left, sprite_masks.collision + sprite_masks.oneway + sprite_masks.midground) + + -- grass fset(tile_repr.grass_top_decoration1, sprite_masks.foreground) -- mock height array init so it doesn't have to dig in sprite data, inaccessible from busted From e263060ec8c674c067c5daec1d9591e085f8fa73 Mon Sep 17 00:00:00 2001 From: huulong Date: Fri, 18 Dec 2020 01:44:51 +0100 Subject: [PATCH 06/91] [CHANGELOG] Added Unreleased changes --- CHANGELOG.md | 8 ++++++++ README.md | 2 +- pico-boots | 2 +- 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a1bc40b8..2283ba53 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,14 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [Unreleased] +### Added +- Physics: added one-way platform system. Added one-way platform tiles and integrated them in stage + +### Changed +- Sprite: fixed top row of some Sonic rotated run sprites missing +- Sprite: reduced emerald size by 2px in both directions, adjusted HUD + ## [5.1] - 2020-12-17 ### Changed - Audio: fixed Travis release for GitHub forgetting to upload BGM cartridge (a glitchy version of Green Hill Zone was played instead of Angel Island) diff --git a/README.md b/README.md index 403dd453..9bb0197f 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,7 @@ It is currently under development. ## Compatibility -Works with PICO-8 0.2.0i and 0.2.1b. +Works with PICO-8 0.2.0i ~ 0.2.1b. ## Screenshots diff --git a/pico-boots b/pico-boots index c8d59229..0fac6e7c 160000 --- a/pico-boots +++ b/pico-boots @@ -1 +1 @@ -Subproject commit c8d59229901b13af1c6e4d7979f4473c673a1266 +Subproject commit 0fac6e7c2de44c7bfe8318ef4ae0b9be8b644685 From 51177a3b504a66023620e193aed8aa45c8ed08ce Mon Sep 17 00:00:00 2001 From: huulong Date: Sun, 20 Dec 2020 21:16:31 +0100 Subject: [PATCH 07/91] [RETRY] Add retry screen at the end of stage clear sequence Player can: - retry keeping emeralds - retry stage from zero - go back to title Missed emeralds are also shown below "Try again?" message Adapted spritesheet so stage_clear cartridge built-in spritesheet uses runtime spritesheet top rows directly, which now includes menu cursor. Menu cursor is located differently than in titlemenu spritesheet, so added dedicated visual_stage_clear_addon Stage clear state now stores picked emerald info in members, and uses them to show missed emeralds Stage state now restores any picked emerald info from stage clear retry (keep emeralds) action, removing picked emerald objects, so player doesn't have to pick them again --- data/builtin_data_stage_clear.p8 | 65 ++++---- data/data_stage1_runtime.p8 | 28 ++-- src/common_stage_clear.lua | 4 +- src/common_titlemenu.lua | 2 +- src/data/stage_clear_data.lua | 12 ++ src/data/stage_data.lua | 8 - src/ingame/stage_state.lua | 30 +++- src/ingame/stage_state_utest.lua | 51 ++++++- src/itest_main_stage_clear.lua | 1 + src/main_stage_clear.lua | 1 + src/menu/titlemenu.lua | 3 + src/resources/visual_ingame_addon.lua | 2 +- src/resources/visual_stage_clear_addon.lua | 15 ++ src/stage_clear/stage_clear_state.lua | 160 +++++++++++++++----- src/stage_clear/stage_clear_state_utest.lua | 52 +++---- src/test/bustedhelper_stage_clear.lua | 2 + src/utest_main.lua | 2 +- 17 files changed, 305 insertions(+), 133 deletions(-) create mode 100644 src/data/stage_clear_data.lua create mode 100644 src/resources/visual_stage_clear_addon.lua diff --git a/data/builtin_data_stage_clear.p8 b/data/builtin_data_stage_clear.p8 index c15deacd..78147202 100644 --- a/data/builtin_data_stage_clear.p8 +++ b/data/builtin_data_stage_clear.p8 @@ -7,30 +7,30 @@ __lua__ -- the collision masks at the top will be overwritten by runtime sprites -- via reload __gfx__ -00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -00000000777700000000000000000000000000000000000000000000000000000000000000000000000000000000777777000000000000000000000000000077 -00700700777777777777777700000000000000000000000000000000000000000000000000000000000000007777777777770000000000000000000000007777 -00077000777777777777777777770000000000000000000000000000000000000000000000000000000077777777777777777700000000000000000000777777 -00077000777777777777777777777777777777770000000000000000000000000000000000000000777777777777777777777777000000000000000077777777 -00700700777777777777777777777777777777777777000000000000000000000000000000007777777777777777777777777777770000000000007777777777 -00000000777777777777777777777777777777777777777777777777000000000000000077777777777777777777777777777777777700000000777777777777 -00000000777777777777777777777777777777777777777777777777777700000000777777777777777777777777777777777777777777000077777777777777 -70000000700000000000000000000000000000070000000700000007777777777777777770000000000000000000000000007777777777770000000000000000 -77000000770000000000000000000000000000770000007700000007077777777777777070000000000000000000000000007777777777770000000000000000 -77700000777700000000000000000000000077770000077700000007077777777777777070000000000077777777777700007777777777770000000000000000 -77770000777770000000000000000000000777770000777700000077007777777777770077000000000077777777777700007777777777770000000000000000 -77777000777777700000000000000000077777770007777700000077000777777777700077000000000077777777777700007777777777770000000000000000 -77777700777777777000000000000007777777770077777700000077000777777777700077000000000077777777777700007777777777770000000000000000 -77777770777777777770000000000777777777770777777700000777000077777777000077700000000077777777777700007777777777770000000000000000 -77777777777777777777700000077777777777777777777700000777000077777777000077700000000077777777777700007777777777770000000000000000 -77777777777777777777700000077777777777777777777700007777000007777770000077770000000000000000007700000000000000000000000000000000 -77777770777777777770000000000777777777770777777700007777000007777770000077770000000000000000777700000000000000000000000000000000 -77777700777777777000000000000007777777770077777700077777000000777700000077777000000000000077777700000000000000000000000000000000 -77777000777777700000000000000000077777770007777700077777000000777700000077777000000000777777777700000000000000000000000000000000 -77770000777770000000000000000000000777770000777700777777000000777700000077777700000077777777777700007777000000000000000000000000 -77700000777700000000000000000000000077770000077707777777000000077000000077777770007777777777777700007777000000000000000000000000 -77000000770000000000000000000000000000770000007707777777000000077000000077777770777777777777777700007777000000000000000000000000 -70000000700000000000000000000000000000070000000777777777000000077000000077777777777777777777777700007777000000000000000000000000 +00000000eeeeebbbeeeeeeeeeeeeeeeeeeed65eeeeeeeeeeeeeeeccccced65eeeeeeeeeee5eeeeeeeeeeeeeeeeeeeeeeee8eeeeeeeeeeeeeeeeee8eeeeeeeeee +00000000eeeebbbeeeeeeeeeed66666666666666666666deed66cccccccc6666666666de575eeeeeee7eeeeeeeeee7eee272eeeeee2eeeeeeeee272eeeeee2ee +00700700eeebbbb7eeebeeee56000000000000000000006556000cc4ccccc0c0000000655775eeeeeed7eee87eee7dee877782eee272e2eeee287778ee2e272e +00077000eeeeeb77eebbbeee56ddd440dddddd4a0dd40d65569999cf4cccccc99999996557775eeeeee7ee8008ee7eeee272eeeeee2ee8eeeeee272eee8ee2ee +00077000eeebb3b7e777eeee56dd47a0d44404aaa047a06556999ccfccccccc995995965577775eeedd7780970877ddeee8ee2eeeeee272eee2ee8eee272eeee +00700700ebb7bb3bb3b7beee56d4aaa047aa4a7aa047a0655699ccccccccccc95759756557755eeeeedd78049087ddeeee2e272eee287778e272e2ee877782ee +00000000ebb77bbbb777bbee564aa0047aaaa700a04aa065569ccccc77cccc7c57757565575eeeeeeeedde8008eddeeeeeeee2eeeeee272eee2eeeeee272eeee +00000000eebb777777bbbbee564a0a04a004aa0d704aa06556ccccc7077cc07c95757565e5eeeeeeeeeeeee88eeeeeeeeeeeeeeeeeeee8eeeeeeeeeeee8eeeee +eeed6deeeb773777777bbeee564a0a70a0d4aaaa707a0d655cccccc70777707c95777565e00000eeeeeeccccceeeeeeeeeeeeeeeeeeeeeeeeeeeeeccccceeeee +eee777eebb77bb37777eeeee564a00a0a447aa44ad7a006556999cc7077ff07c995775650eeeee0eeeeeeccccceeceeeeeeeccccceeeeeeeeeeeeeeccccccece +ee66666eeebbeebb777bb7bb560aaaa0aaaa0a00a0aaa0655699ccffffffff00557577550eeeee0eeeeeeecffcccceeeeeeeeccccceeceeeeeeeeeeecffcccfe +eed666deeeeeee77b777b7b756d0a0a00aa00a0da00000655699cccfffffff9957757755e0eee0eeeeeecccfcc7ccceeeeeeeecffcccceeeeeeeeecccfcc7ccc +eed666deeeba77777777777756dd00a0d00d0a0da0dddd65569cccc9fffff99957566655ee0e0eeeeeecccccc770cceeeeeecccfcc7ccceeeeeeecccccc770cc +eed666deebba77777777b77b56dddd0dddddd0dd00dddd65569cc9999999999995666655eee0eeee77eeecccc770ceeeeeecccccc770cceeeeeeeeecccc7707c +eed666deebbb77777777bb775d77777777777777777777d55d7c777777777777775555d5eeeeeeeee77eeeccff77f0ee77eeecccc770ceeeeeeeeeffccff77f0 +eed666debbbba7777777bbbbe0000000000000000000000ee0000000000000000000000eeeeeeeee777ffccccfffeeeee77eeeccff77f0eeeee77fcccccfffee +eed666debbbb7a7777777777bbbb3bbbbbbbbbbbeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeefccfeeeeeeeee7ffccccfffeeeeee7707eccffce67e +eed666de77b77777777777bbbbb303bbbb3bb0bbeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee1e1cffeeeeeeee7e1efcffeeeeeeeeee77cccccf11607 +eed666deebba777777777ebb3b300033b30b3003eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee11cf1eeeeeeeeeee11cff11eeeeeeeeccceee611ee06 +eed666deebbba7777777eeee0300400030030400eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeecc11ee8800eeeeeecccc1ee880ee77eeee227eeeee +eed666dee7b7777777777bbe4004444000000444eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeecc1688800eeeeeeeeec6e8880e882eeee2887eeee +eed666debbbb77777b3377bb4444444444004494eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeec7872000eeeeeeeeec77820008772eeee268eeee +ee06660eebbe77b777bbbbeb4449444444044494eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee788200eeeeeeeeee788820e07882eeee288eeee +eee000eeeebee77bebebeeee4449444444444444eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee22000eeeeeeeeeee88200ee0888eeeee8eeeee 44444444eaeaeeeeeeeeaeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeaebeeeaeeeeeeeeeeeeeeeeeeeeeeeeeeeee 44449444babaeabeeaeebeebeeaeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeaeeebaeabbbabbeeeeeeeeeeeeeeeeeeeeeeeeeaeab 44444494bbbababbbababbbbeebeeaeeeeeeeebeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeaeeaeebbabab3babbbeaeeeeeeeeeeeeeeeeeeaeeebbab @@ -127,14 +127,14 @@ eeeea994eeeeeeeeeeed65eeeeee999999999999aa944400d65694cdc0ca9a155194cdc0ca9a656d eeeaaaa94eeeeeeeeeed65eeeee99999999999aa99444000d656dcacac75471551dcacac7547656d3beb3ebe44444494b00bb00bbbbbbbab3333b33beeeeeeee eeaaaaaaa999eeeeeeed65eeee9999999999aa9449444400d6569ac7ca7a7a15519ac7ca7a7a656db3b33eb3449444443b0330b33b3bbbbbb33bee3beeeeeeee eaaaaaaaaaaa999eeeed65eee4949494999a944444444000d6564c000c094715514c000c0947656deebeebee444444440b0330b0eeebbebbbbeeeeeeeeeeeeee -eeeeeeeeeeeeeeeeeeeeeee949494449a99494444444440ed656c0ca7a77701551c0ca7a7770656de77778eeeeeeeeeeeeeeeeeeeeeeebbbbbeeeeeeeeeeeeee -eeeeeeeeeeeeeeeeeeeee99444449a9944444444444440eed65649a04f4977155149a04f4977656d7777788eeeeeeeeeeeeeeeeeeeeeeebbbbaeeeeeeeeeeeee -eeeeeeeeeeeeeeeeeeee44449a994444444444994440eeeed6569afaf00a7a15519afaf00a7a656d2288822eeeeeeeeeeeeeeeeeeeeeeeeebbbbeeeeeeeeeeee -eeeeeeeeeeeeeeeeee4499994444494444944494444eeeeed656dcafaf05071551dcafaf0507656de28882eeeeeeeeeeeeeeeeeeeeeeeeeeebbbeeeeeeeeeeee -eeeeeeeeeeeeeeeea9994494499449444494444440eeeeeed65690cdf09a5d155190cdf09a5d656dee282eeeeeeeeeeeeeeeeeeeeeeeeeeeebbbbeeeeeeeeeee -eeeeeeeeeeee99994494444444944444449444444eeeeeeed656dc09d9d9d61551dc09d9d9d6656deee2eeeeeeeeeeeeeeeeeeeeeeeeeeeeeebbaeeeeeeeeeee -eeeeeeee44994494449444444494444444444400eeeeeeeed60d777777777775577777777777d06deeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeebaeeeeeeeeeee -eeee44994444444444444444444444444440eeeeeeeeeeeeed6000000000000ee0000000000006deeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee +eeeeeeeeeeeeeeeeeeeeeee949494449a99494444444440ed656c0ca7a77701551c0ca7a7770656de77778ee4444444444444444eeeeebbbbbeeeeeeeeeeeeee +eeeeeeeeeeeeeeeeeeeee99444449a9944444444444440eed65649a04f4977155149a04f4977656d7777788e0449449949944444eeeeeebbbbaeeeeeeeeeeeee +eeeeeeeeeeeeeeeeeeee44449a994444444444994440eeeed6569afaf00a7a15519afaf00a7a656d2288822e0449444994444000eeeeeeeebbbbeeeeeeeeeeee +eeeeeeeeeeeeeeeeee4499994444494444944494444eeeeed656dcafaf05071551dcafaf0507656de28882ee4444444999444044eeeeeeeeebbbeeeeeeeeeeee +eeeeeeeeeeeeeeeea9994494499449444494444440eeeeeed65690cdf09a5d155190cdf09a5d656dee282eee0444499444444444eeeeeeeeebbbbeeeeeeeeeee +eeeeeeeeeeee99994494444444944444449444444eeeeeeed656dc09d9d9d61551dc09d9d9d6656deee2eeee0444444444444440eeeeeeeeeebbaeeeeeeeeeee +eeeeeeee44994494449444444494444444444400eeeeeeeed60d777777777775577777777777d06deeeeeeee004499440e004440eeeeeeeeeeebaeeeeeeeeeee +eeee44994444444444444444444444444440eeeeeeeeeeeeed6000000000000ee0000000000006deeeeeeeeee0049eeeeeee000eeeeeeeeeeeeeeeeeeeeeeeee __gff__ 0041414141414141414141414141414141414141414141414141414141410000414141414141414141414141410000004141414141414141414141414141414141414141414141414141616180808000414040414141414141414040414141414545454343434343454541414141414145455149434343434545414141418080 0000000000000000000000000000000000000000000000000000000000000000000000000000000041418080800000000000000000000000414141800080414100000000000000000000008080804141000000000000000000004141808080804040404041410000000080418080800041414141414100000000000000008000 @@ -280,3 +280,4 @@ __music__ 04 39777879 00 32333637 04 34354075 + diff --git a/data/data_stage1_runtime.p8 b/data/data_stage1_runtime.p8 index 1c5ba4db..54500466 100644 --- a/data/data_stage1_runtime.p8 +++ b/data/data_stage1_runtime.p8 @@ -3,20 +3,20 @@ version 29 __lua__ __gfx__ -00000000eeeeebbbeeeeeeeeeeeeeeeeeeed65eeeeeeeeeeeeeeeccccced65eeeeeeeeeee5eeeeeee00000eeeeeeeeeeee8eeeeeeeeeeeeeeeeee8eeeeeeeeee -00000000eeeebbbeeeeeeeeeed66666666666666666666deed66cccccccc6666666666de575eeeee0eeeee0eeeeeeeeee272eeeeee2eeeeeeeee272eeeeee2ee -00700700eeebbbb7eeebeeee56000000000000000000006556000cc4ccccc0c0000000655775eeee0eeeee0eeeeeeeee877782eee272e2eeee287778ee2e272e -00077000eeeeeb77eebbbeee56ddd440dddddd4a0dd40d65569999cf4cccccc99999996557775eeee0eee0eeeeeeeeeee272eeeeee2ee8eeeeee272eee8ee2ee -00077000eeebb3b7e777eeee56dd47a0d44404aaa047a06556999ccfccccccc995995965577775eeee0e0eeeeeeeeeeeee8ee2eeeeee272eee2ee8eee272eeee -00700700ebb7bb3bb3b7beee56d4aaa047aa4a7aa047a0655699ccccccccccc95759756557755eeeeee0eeeeeeeeeeeeee2e272eee287778e272e2ee877782ee -00000000ebb77bbbb777bbee564aa0047aaaa700a04aa065569ccccc77cccc7c57757565575eeeeeeeeeeeeeeeeeeeeeeeeee2eeeeee272eee2eeeeee272eeee -00000000eebb777777bbbbee564a0a04a004aa0d704aa06556ccccc7077cc07c95757565e5eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee8eeeeeeeeeeee8eeeee -eeed6deeeb773777777bbeee564a0a70a0d4aaaa707a0d655cccccc70777707c95777565eeeeeeeeeeeeccccceeeeeeeeeeeeeeeeeeeeeeeeeeeeeccccceeeee -eee777eebb77bb37777eeeee564a00a0a447aa44ad7a006556999cc7077ff07c99577565eeeeeeeeeeeeeccccceeceeeeeeeccccceeeeeeeeeeeeeeccccccece -ee66666eeebbeebb777bb7bb560aaaa0aaaa0a00a0aaa0655699ccffffffff0055757755eeeeeeeeeeeeeecffcccceeeeeeeeccccceeceeeeeeeeeeecffcccfe -eed666deeeeeee77b777b7b756d0a0a00aa00a0da00000655699cccfffffff9957757755eeeeeeeeeeeecccfcc7ccceeeeeeeecffcccceeeeeeeeecccfcc7ccc -eed666deeeba77777777777756dd00a0d00d0a0da0dddd65569cccc9fffff99957566655eeeeeeeeeeecccccc770cceeeeeecccfcc7ccceeeeeeecccccc770cc -eed666deebba77777777b77b56dddd0dddddd0dd00dddd65569cc9999999999995666655eeeeeeee77eeecccc770ceeeeeecccccc770cceeeeeeeeecccc7707c +00000000eeeeebbbeeeeeeeeeeeeeeeeeeed65eeeeeeeeeeeeeeeccccced65eeeeeeeeeee5eeeeeeeeeeeeeeeeeeeeeeee8eeeeeeeeeeeeeeeeee8eeeeeeeeee +00000000eeeebbbeeeeeeeeeed66666666666666666666deed66cccccccc6666666666de575eeeeeee7eeeeeeeeee7eee272eeeeee2eeeeeeeee272eeeeee2ee +00700700eeebbbb7eeebeeee56000000000000000000006556000cc4ccccc0c0000000655775eeeeeed7eee87eee7dee877782eee272e2eeee287778ee2e272e +00077000eeeeeb77eebbbeee56ddd440dddddd4a0dd40d65569999cf4cccccc99999996557775eeeeee7ee8008ee7eeee272eeeeee2ee8eeeeee272eee8ee2ee +00077000eeebb3b7e777eeee56dd47a0d44404aaa047a06556999ccfccccccc995995965577775eeedd7780970877ddeee8ee2eeeeee272eee2ee8eee272eeee +00700700ebb7bb3bb3b7beee56d4aaa047aa4a7aa047a0655699ccccccccccc95759756557755eeeeedd78049087ddeeee2e272eee287778e272e2ee877782ee +00000000ebb77bbbb777bbee564aa0047aaaa700a04aa065569ccccc77cccc7c57757565575eeeeeeeedde8008eddeeeeeeee2eeeeee272eee2eeeeee272eeee +00000000eebb777777bbbbee564a0a04a004aa0d704aa06556ccccc7077cc07c95757565e5eeeeeeeeeeeee88eeeeeeeeeeeeeeeeeeee8eeeeeeeeeeee8eeeee +eeed6deeeb773777777bbeee564a0a70a0d4aaaa707a0d655cccccc70777707c95777565e00000eeeeeeccccceeeeeeeeeeeeeeeeeeeeeeeeeeeeeccccceeeee +eee777eebb77bb37777eeeee564a00a0a447aa44ad7a006556999cc7077ff07c995775650eeeee0eeeeeeccccceeceeeeeeeccccceeeeeeeeeeeeeeccccccece +ee66666eeebbeebb777bb7bb560aaaa0aaaa0a00a0aaa0655699ccffffffff00557577550eeeee0eeeeeeecffcccceeeeeeeeccccceeceeeeeeeeeeecffcccfe +eed666deeeeeee77b777b7b756d0a0a00aa00a0da00000655699cccfffffff9957757755e0eee0eeeeeecccfcc7ccceeeeeeeecffcccceeeeeeeeecccfcc7ccc +eed666deeeba77777777777756dd00a0d00d0a0da0dddd65569cccc9fffff99957566655ee0e0eeeeeecccccc770cceeeeeecccfcc7ccceeeeeeecccccc770cc +eed666deebba77777777b77b56dddd0dddddd0dd00dddd65569cc9999999999995666655eee0eeee77eeecccc770ceeeeeecccccc770cceeeeeeeeecccc7707c eed666deebbb77777777bb775d77777777777777777777d55d7c777777777777775555d5eeeeeeeee77eeeccff77f0ee77eeecccc770ceeeeeeeeeffccff77f0 eed666debbbba7777777bbbbe0000000000000000000000ee0000000000000000000000eeeeeeeee777ffccccfffeeeee77eeeccff77f0eeeee77fcccccfffee eed666debbbb7a7777777777bbbb3bbbbbbbbbbbeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeefccfeeeeeeeee7ffccccfffeeeeee7707eccffce67e diff --git a/src/common_stage_clear.lua b/src/common_stage_clear.lua index ffcd30fe..23c19a3a 100644 --- a/src/common_stage_clear.lua +++ b/src/common_stage_clear.lua @@ -4,8 +4,8 @@ -- Usage: add require("common_stage_clear") at the top of each of your stage_clear main scripts -- (along with "engine/common") and in bustedhelper_stage_clear --- we need merge to add the visual_ingame_addon to visual module -require("engine/core/table_helper") +require("engine/core/fun_helper") -- unpacking +require("engine/core/table_helper") -- merge (to add the visual_ingame_addon and visual_menu_addon) -- we need sprite flags to draw grass on top of the rest require("data/sprite_flags") diff --git a/src/common_titlemenu.lua b/src/common_titlemenu.lua index f05af829..1c72916a 100644 --- a/src/common_titlemenu.lua +++ b/src/common_titlemenu.lua @@ -4,7 +4,7 @@ -- Usage: add require("common_titlemenu") at the top of each of your titlemenu main scripts -- (along with "engine/common") and in bustedhelper_titlemenu -require("engine/core/fun_helper") +require("engine/core/fun_helper") -- unpacking require("engine/core/table_helper") --#if minify_level3 diff --git a/src/data/stage_clear_data.lua b/src/data/stage_clear_data.lua new file mode 100644 index 00000000..69d03eff --- /dev/null +++ b/src/data/stage_clear_data.lua @@ -0,0 +1,12 @@ +return { + + -- stage clear sequence timing + + -- duration of stage clear jingle (frames) + -- (actual notes length is 357, added one note = 7 frames to reach half of 3rd column) + stage_clear_duration = 364, + + -- delay between emerald assessment animation has ended, and fade out to retry screen starts (s) + show_emerald_assessment_duration = 1.0, + +} diff --git a/src/data/stage_data.lua b/src/data/stage_data.lua index 0dafd743..3a3465af 100644 --- a/src/data/stage_data.lua +++ b/src/data/stage_data.lua @@ -1,7 +1,4 @@ local location_rect = require("engine/core/location_rect") -local sprite_data = require("engine/render/sprite_data") - -local audio = require("resources/audio") return { @@ -17,11 +14,6 @@ return { show_stage_splash_delay = 0.2, -- duration of goal plate rotating before stage clear (results sub-state) starts (frames) goal_rotating_anim_duration = 120, - -- duration of stage clear jingle (frames) - -- (actual notes length is 357, added one note = 7 frames to reach half of 3rd column) - stage_clear_duration = 364, - -- delay between reaching goal and going back to title menu (s) - back_to_titlemenu_delay = 1.0, -- duration of bgm fade out after reaching goal (s) bgm_fade_out_duration = 1.0, diff --git a/src/ingame/stage_state.lua b/src/ingame/stage_state.lua index f9bdd498..1a081123 100644 --- a/src/ingame/stage_state.lua +++ b/src/ingame/stage_state.lua @@ -72,12 +72,16 @@ function stage_state:on_enter() -- as it's slow and will add considerable overhead on test start if self.enable_spawn_objects then self:spawn_objects_in_all_map_regions() + self:restore_picked_emerald_data() end --#endif +-- ! Make sure to duplicate content of block above in #pico8 block below ! + --[[#pico8 --#ifn itest self:spawn_objects_in_all_map_regions() + self:restore_picked_emerald_data() --#endif --#pico8]] @@ -841,6 +845,29 @@ function stage_state:on_reached_goal_async() load('picosonic_stage_clear.p8') end +function stage_state:restore_picked_emerald_data() + -- Retrieve and store picked emeralds set information from memory stored in stage_clear + -- before retry. If you come directly from the titlemenu, this should do nothing. + -- Similar to stage_clear_state:restore_picked_emerald_data, but we also + -- remove emerald objects from the stage with a "silent pick" + -- (so this method must be called after object spawning) + local picked_emerald_byte = peek(0x4300) + + -- read bitset low-endian, from highest bit (emerald 8) to lowest bit (emerald 1) + -- the only reason we iterate from the end is because del() will remove elements + -- from self.emeralds sequence, rearranging them to fill gaps + -- by iterating backward, we don't have to worry about their index changing + for i = 8, 1, -1 do + if band(picked_emerald_byte, shl(1, i - 1)) ~= 0 then + -- add emerald number to picked set + self.picked_emerald_numbers_set[i] = true + + -- remove emerald from sequence (backward iteration ensures correct index) + del(self.emeralds, self.emeralds[i]) + end + end +end + function stage_state:store_picked_emerald_data() -- general memory is a good fit to store data across cartridges, -- although this behavior is undocumented @@ -849,8 +876,7 @@ function stage_state:store_picked_emerald_data() -- note that Sonic is not visible so we don't mind overwriting the memory at 0x4300 -- which during ingame contains rotated and non-rotated sprite variants -- convert set of picked emeralds to bitset (1 if emerald was picked, low-endian) - -- there are 8 emeralds so we need 1 bytes, but we can combine them in one - -- 2-byte value and store it at once with length 2 + -- there are 8 emeralds so we need 1 byte local picked_emerald_bytes = 0 for i = 1, 8 do if self.picked_emerald_numbers_set[i] then diff --git a/src/ingame/stage_state_utest.lua b/src/ingame/stage_state_utest.lua index 957f6287..899f62b7 100644 --- a/src/ingame/stage_state_utest.lua +++ b/src/ingame/stage_state_utest.lua @@ -95,6 +95,7 @@ describe('stage_state', function () stub(stage_state, "play_bgm") stub(stage_state, "reload_bgm") stub(stage_state, "spawn_objects_in_all_map_regions") + stub(stage_state, "restore_picked_emerald_data") stub(camera_class, "setup_for_stage") stub(stage_state, "check_reload_map_region") stub(stage_state, "reload_runtime_data") @@ -106,6 +107,7 @@ describe('stage_state', function () stage_state.play_bgm:revert() stage_state.reload_bgm:revert() stage_state.spawn_objects_in_all_map_regions:revert() + stage_state.restore_picked_emerald_data:revert() camera_class.setup_for_stage:revert() stage_state.check_reload_map_region:revert() stage_state.reload_runtime_data:revert() @@ -117,6 +119,7 @@ describe('stage_state', function () stage_state.play_bgm:clear() stage_state.reload_bgm:clear() stage_state.spawn_objects_in_all_map_regions:clear() + stage_state.restore_picked_emerald_data:clear() camera_class.setup_for_stage:clear() stage_state.check_reload_map_region:clear() stage_state.reload_runtime_data:clear() @@ -131,6 +134,11 @@ describe('stage_state', function () assert.spy(state.spawn_objects_in_all_map_regions).was_called_with(match.ref(state)) end) + it('should call restore_picked_emerald_data', function () + assert.spy(state.restore_picked_emerald_data).was_called(1) + assert.spy(state.restore_picked_emerald_data).was_called_with(match.ref(state)) + end) + it('should call setup_for_stage on camera with current stage data', function () assert.spy(camera_class.setup_for_stage).was_called(1) assert.spy(camera_class.setup_for_stage).was_called_with(match.ref(state.camera), state.curr_stage_data) @@ -831,6 +839,7 @@ describe('stage_state', function () end) + -- we stub spawn_objects_in_all_map_regions in (stage state entered) region, so test it outside describe('spawn_objects_in_all_map_regions', function () setup(function () @@ -870,6 +879,38 @@ describe('stage_state', function () end) + -- we stub restore_picked_emerald_data in (stage state entered) region, so test it outside + describe('restore_picked_emerald_data', function () + + before_each(function () + -- 0b01001001 -> 73 (low-endian, so lowest bit is for emerald 1) + poke(0x4300, 73) + end) + + after_each(function () + poke(0x4300, 0) + end) + + it('should read 1 byte in general memory representing picked emeralds bitset', function () + state:restore_picked_emerald_data() + + assert.are_same({ + [1] = true, + [4] = true, + [7] = true, + }, state.picked_emerald_numbers_set) + end) + + it('should delete emerald object for every picked emerald', function () + state.emeralds = {"dummy1", "dummy2", "dummy3", "dummy4", "dummy5", "dummy6", "dummy7", "dummy8"} + + state:restore_picked_emerald_data() + + assert.are_same({"dummy2", "dummy3", "dummy5", "dummy6", "dummy8"}, state.emeralds) + end) + + end) + describe('(stage states added)', function () before_each(function () @@ -887,15 +928,21 @@ describe('stage_state', function () -- but we do not want to spend several seconds finding all of them -- in before_each every time due to on_enter just for tests, -- so we stub this - stub(stage_state, "spawn_objects_in_all_map_regions") + stub(stage_state, "spawn_objects_in_all_map_regions") + + -- restore_picked_emerald_data relies on peek which will find nil memory if not set + -- so stub it + stub(stage_state, "restore_picked_emerald_data") end) teardown(function () stage_state.spawn_objects_in_all_map_regions:revert() + stage_state.restore_picked_emerald_data:revert() end) after_each(function () stage_state.spawn_objects_in_all_map_regions:clear() + stage_state.restore_picked_emerald_data:clear() end) before_each(function () @@ -1427,7 +1474,7 @@ describe('stage_state', function () describe('store_picked_emerald_data', function () - it('should store 2 bytes in general memory representing picked emeralds bitset', function () + it('should store 1 byte in general memory representing picked emeralds bitset', function () state.picked_emerald_numbers_set = { [1] = true, [4] = true, diff --git a/src/itest_main_stage_clear.lua b/src/itest_main_stage_clear.lua index 2e05560a..a5f5a999 100644 --- a/src/itest_main_stage_clear.lua +++ b/src/itest_main_stage_clear.lua @@ -8,6 +8,7 @@ require("common_stage_clear") -- require visual add-on for ingame (also used for stage_clear), so any require visual_common -- in this cartridge will get both common data and ingame data require("resources/visual_ingame_addon") +require("resources/visual_stage_clear_addon") local itest_manager = require("engine/test/itest_manager") diff --git a/src/main_stage_clear.lua b/src/main_stage_clear.lua index e60d17a0..c720d3d6 100644 --- a/src/main_stage_clear.lua +++ b/src/main_stage_clear.lua @@ -9,6 +9,7 @@ require("common_stage_clear") -- require ingame visual add-on for stage clear since we still show the stage -- any require visual_common in this cartridge will get both common data and ingame data require("resources/visual_ingame_addon") +require("resources/visual_stage_clear_addon") -- we also require codetuner so any file can used tuned() -- if tuner symbol is defined, then we also initialize it in init diff --git a/src/menu/titlemenu.lua b/src/menu/titlemenu.lua index d8025e18..1fd0aee0 100644 --- a/src/menu/titlemenu.lua +++ b/src/menu/titlemenu.lua @@ -25,6 +25,9 @@ titlemenu.items = transform({ end}, }, unpacking(menu_item)) +-- attributes: +-- menu menu title menu showing items (only created when it must be shown) + function titlemenu:on_enter() self.app:start_coroutine(self.opening_sequence_async, self) end diff --git a/src/resources/visual_ingame_addon.lua b/src/resources/visual_ingame_addon.lua index ecf9f71e..ca3bee57 100644 --- a/src/resources/visual_ingame_addon.lua +++ b/src/resources/visual_ingame_addon.lua @@ -37,7 +37,7 @@ local ingame_sprite_data_t = { -- RUNTIME SPRITES (stage-specific and common runtime) -- below need runtime sprites to be reloaded, overwriting collision masks background_forest_bottom_hole = sprite_data(sprite_id_location(1, 0), tile_vector(2, 3), vector(0, 0), colors.pink), - emerald_silhouette = sprite_data(sprite_id_location(10, 0), nil, vector(3, 2), colors.pink), + emerald_silhouette = sprite_data(sprite_id_location(9, 1), nil, vector(3, 2), colors.pink), goal_plate_goal = sprite_data(sprite_id_location(3, 0), tile_vector(3, 2), vector(12, 16), colors.pink), goal_plate_sonic = sprite_data(sprite_id_location(6, 0), tile_vector(3, 2), vector(12, 16), colors.pink), goal_plate_rotating_90 = sprite_data(sprite_id_location(0, 1), tile_vector(1, 2), vector(4, 16), colors.pink), diff --git a/src/resources/visual_stage_clear_addon.lua b/src/resources/visual_stage_clear_addon.lua new file mode 100644 index 00000000..761fe8c9 --- /dev/null +++ b/src/resources/visual_stage_clear_addon.lua @@ -0,0 +1,15 @@ +local visual = require("resources/visual_common") + +local sprite_data = require("engine/render/sprite_data") + +-- visuals for stage_clear only +-- it uses the add-on system, which means you only need to require it along with visual_common, +-- but only get the return value of visual_common named `visual` here +-- it will automatically add extra information to `visual` +local menu_sprite_data_t = { + -- stage clear spritesheet is pretty busy as it must contain stage tiles, so we had to move menu cursor + -- sprites to a different location than in titlemenu, that we define in this stage_clear-specific add-on + menu_cursor = sprite_data(sprite_id_location(10, 0), tile_vector(2, 1), vector(8, 5), colors.pink), +} + +merge(visual.sprite_data_t, menu_sprite_data_t) diff --git a/src/stage_clear/stage_clear_state.lua b/src/stage_clear/stage_clear_state.lua index e79df2b4..d82e4b36 100644 --- a/src/stage_clear/stage_clear_state.lua +++ b/src/stage_clear/stage_clear_state.lua @@ -1,10 +1,13 @@ local gamestate = require("engine/application/gamestate") local label = require("engine/ui/label") local overlay = require("engine/ui/overlay") +local rectangle = require("engine/ui/rectangle") +local stage_clear_data = require("data/stage_clear_data") local emerald = require("ingame/emerald") local goal_plate = require("ingame/goal_plate") -local stage_data = require("data/stage_data") +local menu_item = require("menu/menu_item") +local menu = require("menu/menu_with_sfx") local audio = require("resources/audio") local ui_animation = require("ui/ui_animation") local visual = require("resources/visual_common") -- we should require ingameadd-on in main @@ -14,39 +17,59 @@ local stage_clear_state = derived_class(gamestate) stage_clear_state.type = ':stage_clear' +-- sequence of menu items to display, with their target states +stage_clear_state.retry_items = transform({ + {"retry (keep emeralds)", function(app) + -- load stage cartridge without clearing picked emerald data in general memory + stage_clear_state.retry_stage() + end}, + {"retry from zero", function(app) + stage_clear_state.retry_from_zero() + end}, + {"back to title", function(app) + stage_clear_state.back_to_titlemenu() + end}, + }, unpacking(menu_item)) + function stage_clear_state:init() -- gamestate.init(self) -- kept for expliciteness, but does nothing -- result (stage clear) overlay self.result_overlay = overlay() - -- emerald cross variables for result UI animation + -- emerald variables for result UI animation + self.picked_emerald_numbers_set = {} + self.picked_emerald_count = 0 self.result_show_emerald_cross_base = false self.result_emerald_cross_palette_swap_table = {} -- for emerald cross bright animation self.result_show_emerald_set_by_number = {} -- [number] = nil means don't show it self.result_emerald_brightness_levels = {} -- for emerald bright animation (nil means 0) + + -- self.retry_menu starts nil, only created when it must be shown end function stage_clear_state:on_enter() - -- simplified compared to stage_state, just reload runtime spritesheet - -- (for goal plate in particular) then reload map region hardcoded to where goal is, + -- simplified compared to stage_state + -- we don't even need to reload runtime spritesheet since the stage_clear builtin spritesheet + -- now integrates the runtime spritesheet top rows from the start (and later, ingame may do the same, + -- since ultimately we only need the tile masks in the top rows for initial collision data loading, + -- and we could quick reload on stage start just for that) + -- we need the runtime sprites for goal plate and menu cursor in particular + + -- first, restore picked emerald data set in ingame, just before loading this cartridge + self:restore_picked_emerald_data() + + -- we still need to reload map region hardcoded to where goal is, -- and spawn objects just there (basically just spawn the goal plate) - self:reload_runtime_data() self:reload_map_region() self:scan_current_region_to_spawn_objects() self.app:start_coroutine(self.play_stage_clear_sequence_async, self) end --- reload background, HUD and character sprites from runtime data --- also store non-rotated and rotated sprites into general memory for swapping later -function stage_clear_state:reload_runtime_data() - -- reload runtime background+HUD sprites by copying spritesheet top from background data - -- cartridge. see stage_state:reload_runtime_data - -- hardcode "1" since we only have 1 stage, and we only need runtime goal sprites - reload(0x0, 0x0, 0x600, "data_stage1_runtime.p8") -end - +-- good to know what on_exit should do, but never called since stage_clear cartridge only contains stage_clear state +-- and we directly load other cartridges without ever exiting this state; so strip it +--[[ function stage_clear_state:on_exit() -- clear all coroutines (we normally let app handle them, but in this context -- we know that all coroutines belong to the stage state, so no risk clearing them from here) @@ -58,8 +81,12 @@ function stage_clear_state:on_exit() -- reinit camera offset for other states camera() end +--]] function stage_clear_state:update() + if self.retry_menu then + self.retry_menu:update() + end end function stage_clear_state:render() @@ -67,7 +94,13 @@ function stage_clear_state:render() visual_stage.render_background(vector(3392, 328)) self:render_stage_elements() self:render_overlay() + + -- draw either picked or missed emeralds self:render_emerald_cross() + + if self.retry_menu then + self.retry_menu:draw(29, 90) + end end function stage_clear_state:spawn_goal_plate_at(global_loc) @@ -136,21 +169,49 @@ function stage_clear_state:play_stage_clear_sequence_async() -- stop BGM and play stage clear jingle music(audio.jingle_ids.stage_clear) - yield_delay(stage_data.stage_clear_duration) + yield_delay(stage_clear_data.stage_clear_duration) -- play result UI "calculation" (we don't have score so it's just checking -- if we have all the emeralds) self:assess_result_async() - -- wait a moment and go back to titlemenu - self.app:yield_delay_s(stage_data.back_to_titlemenu_delay) - self:back_to_titlemenu() + -- fade out and show retry screen + self:zigzag_fade_out_async() + self:show_retry_screen() +end + +function stage_clear_state.retry_stage() + load('picosonic_ingame.p8') end -function stage_clear_state:back_to_titlemenu() +function stage_clear_state.retry_from_zero() + -- clear picked emeralds data (see stage_state:store_picked_emerald_data) in general memory + poke(0x4300, 0) + stage_clear_state.retry_stage() +end + +function stage_clear_state.back_to_titlemenu() + -- remember to clear picked emerald data, so if we start again from titlemenu we'll also restart from zero + poke(0x4300, 0) load('picosonic_titlemenu.p8') end +function stage_clear_state:restore_picked_emerald_data() + -- retrieve and store picked emeralds set information from memory stored in ingame before stage clear + -- cartridge was loaded + -- similar to stage_state:restore_picked_emerald_data, but we don't remove emerald objects + -- and cache the picked count for assessment + local picked_emerald_byte = peek(0x4300) + + -- read bitset low-endian, from lowest bit (emerald 1) to highest bit (emerald 8) + for i = 1, 8 do + if band(picked_emerald_byte, shl(1, i - 1)) ~= 0 then + self.picked_emerald_numbers_set[i] = true + self.picked_emerald_count = self.picked_emerald_count + 1 + end + end +end + function stage_clear_state:show_result_async() -- "sonic got through": 17 characters, so 17*4 = 68 px wide -- so to enter from left, offset by -68 (we even get an extra margin pixel) @@ -183,24 +244,9 @@ function stage_clear_state:show_result_async() end function stage_clear_state:assess_result_async() - -- retrieve picked emeralds set information from memory stored in ingame before stage clear - -- cartridge was loaded - local picked_emerald_numbers_set = {} - local picked_emerald_count = 0 - - local picked_emerald_byte = peek(0x4300) - - -- read bitset low-endian, from lowest bit (emerald 1) to highest bit (emerald 8) - for i = 1, 8 do - if band(picked_emerald_byte, shl(1, i - 1)) ~= 0 then - picked_emerald_numbers_set[i] = true - picked_emerald_count = picked_emerald_count + 1 - end - end - for num = 1, 8 do -- only display and yield wait for picked emeralds - if picked_emerald_numbers_set[num] then + if self.picked_emerald_numbers_set[num] then self.result_show_emerald_set_by_number[num] = true for step = 1, 2 do -- instead of setting self.result_emerald_palette_swap_table_by_number[num] = visual.bright_to_normal_palette_swap_by_original_color_sequence[step] @@ -237,8 +283,8 @@ function stage_clear_state:assess_result_async() -- it comes from the left again, so offset negatively on start -- "got ..." is 6 chars after the string start so 24px after , -88+24=-64 -- hardcoded since we don't have access to spawned_emerald_locations anymore - if picked_emerald_count < 8 then - emerald_text = "got "..picked_emerald_count.." emeralds" + if self.picked_emerald_count < 8 then + emerald_text = "got "..self.picked_emerald_count.." emeralds" else emerald_text = "got all emeralds" end @@ -246,8 +292,42 @@ function stage_clear_state:assess_result_async() local emerald_label = label(emerald_text, vector(-64, 14), colors.white, colors.black) self.result_overlay:add_drawable("emerald", emerald_label) - -- move text from left to right + -- move text from left to right and give some time to player to read ui_animation.move_drawables_on_coord_async("x", {sonic_label, emerald_label}, {0, 24}, -88, 20, 20) + self.app:yield_delay_s(stage_clear_data.show_emerald_assessment_duration) +end + +function stage_clear_state:zigzag_fade_out_async() + -- todo + + -- at the end of the zigzag, clear the emerald assessment widgets which are now completely hidden + self.result_overlay:clear_drawables() + + -- only clear members that draw custom items, except for actual emeralds as we'll draw the missing emeralds + -- soon anyway + self.result_show_emerald_cross_base = false + + -- just keep the full black screen rectangle as background for retry screen + local bg = rectangle(vector(0, 0), 128, 128, colors.black) + self.result_overlay:add_drawable("bg", bg) + + for num = 1, 8 do + -- only display missed emeralds + -- not nil is true, and not true is false, so we are effectively filling the set, + -- just setting false for picked emeralds instead of the usual nil, but works the same + self.result_show_emerald_set_by_number[num] = not self.picked_emerald_numbers_set[num] + end +end + +function stage_clear_state:show_retry_screen() + -- at the end of the zigzag, clear the emerald assessment widgets which are now completely hidden, + -- but keep the full black screen rectangle as background for retry screen + local try_again_label = label("try again?", vector(41, 34), colors.white) + self.result_overlay:add_drawable("try again", try_again_label) + + printh("visual.sprite_data_t.menu_cursor: "..nice_dump(visual.sprite_data_t.menu_cursor)) + self.retry_menu = menu(self.app, alignments.left, 1, colors.white, visual.sprite_data_t.menu_cursor, 7) + self.retry_menu:show_items(stage_clear_state.retry_items) end @@ -366,8 +446,8 @@ function stage_clear_state:draw_emeralds_around_cross(x, y) -- but here we obviously only defined 8 relative positions, -- so just iterate to 8 (but if you happen to only place 7, you'll need to update that) for num = 1, 8 do - -- result_show_emerald_set_by_number[num] is only set to true when - -- we have picked emerald, so no need to check picked_emerald_numbers_set again + -- self.result_show_emerald_set_by_number[num] is only set to true when + -- we have picked emerald, so no need to check self.picked_emerald_numbers_set again if self.result_show_emerald_set_by_number[num] then local draw_position = vector(x, y) + emerald_relative_positions[num] emerald.draw(num, draw_position, self.result_emerald_brightness_levels[num]) diff --git a/src/stage_clear/stage_clear_state_utest.lua b/src/stage_clear/stage_clear_state_utest.lua index bbabc6b6..b3da6127 100644 --- a/src/stage_clear/stage_clear_state_utest.lua +++ b/src/stage_clear/stage_clear_state_utest.lua @@ -5,6 +5,7 @@ require("test/bustedhelper_stage_clear") -- whether complex tests done via busted but done in dedicated files, or simulation tests) require("common_titlemenu") require("resources/visual_ingame_addon") +require("resources/visual_stage_clear_addon") local stage_clear_state = require("stage_clear/stage_clear_state") @@ -72,21 +73,21 @@ describe('stage_clear_state', function () describe('on_enter', function () setup(function () - stub(stage_clear_state, "reload_runtime_data") + stub(stage_clear_state, "restore_picked_emerald_data") stub(stage_clear_state, "reload_map_region") stub(stage_clear_state, "scan_current_region_to_spawn_objects") stub(picosonic_app, "start_coroutine") end) teardown(function () - stage_clear_state.reload_runtime_data:revert() + stage_clear_state.restore_picked_emerald_data:revert() stage_clear_state.reload_map_region:revert() stage_clear_state.scan_current_region_to_spawn_objects:revert() picosonic_app.start_coroutine:revert() end) after_each(function () - stage_clear_state.reload_runtime_data:clear() + stage_clear_state.restore_picked_emerald_data:clear() stage_clear_state.reload_map_region:clear() stage_clear_state.scan_current_region_to_spawn_objects:clear() picosonic_app.start_coroutine:clear() @@ -96,9 +97,12 @@ describe('stage_clear_state', function () state:on_enter() end) - it('should call reload_runtime_data, reload_map_region, scan_current_region_to_spawn_objects', function () - assert.spy(state.reload_runtime_data).was_called(1) - assert.spy(state.reload_runtime_data).was_called_with(match.ref(state)) + it('should call restore_picked_emerald_data', function () + assert.spy(state.restore_picked_emerald_data).was_called(1) + assert.spy(state.restore_picked_emerald_data).was_called_with(match.ref(state)) + end) + + it('should call reload_map_region, scan_current_region_to_spawn_objects', function () assert.spy(state.reload_map_region).was_called(1) assert.spy(state.reload_map_region).was_called_with(match.ref(state)) assert.spy(state.scan_current_region_to_spawn_objects).was_called(1) @@ -113,29 +117,6 @@ describe('stage_clear_state', function () end) - describe('reload_runtime_data', function () - - setup(function () - stub(_G, "reload") - end) - - teardown(function () - reload:revert() - end) - - after_each(function () - reload:clear() - end) - - it('should reload stage runtime data into spritesheet top', function () - state:reload_runtime_data() - - assert.spy(reload).was_called(1) - assert.spy(reload).was_called_with(0x0, 0x0, 0x600, "data_stage1_runtime.p8") - end) - - end) - describe('spawn_goal_plate_at', function () setup(function () @@ -268,19 +249,25 @@ describe('stage_clear_state', function () describe('(stage state entered)', function () setup(function () + -- restore_picked_emerald_data relies on peek which will find nil memory if not set + -- so stub it + stub(stage_clear_state, "restore_picked_emerald_data") + -- we don't really mind spying on scan_current_region_to_spawn_objects -- but we do not want to spend several seconds finding all of them -- in before_each every time due to on_enter just for tests, -- so we stub this - stub(stage_clear_state, "scan_current_region_to_spawn_objects") + stub(stage_clear_state, "scan_current_region_to_spawn_objects") end) teardown(function () stage_clear_state.scan_current_region_to_spawn_objects:revert() + stage_clear_state.restore_picked_emerald_data:revert() end) after_each(function () stage_clear_state.scan_current_region_to_spawn_objects:clear() + stage_clear_state.restore_picked_emerald_data:clear() end) before_each(function () @@ -636,6 +623,10 @@ describe('stage_clear_state', function () end) -- on exit stage state to enter titlemenu state -- unlike above, we test on_exit method itself here + + -- COMMENTED OUT to strip characters since we just load new cartridges and never exit + -- the stage_clear state + --[[ describe('on_exit', function () setup(function () @@ -679,6 +670,7 @@ describe('stage_clear_state', function () end) end) + --]] end) -- (stage state entered) diff --git a/src/test/bustedhelper_stage_clear.lua b/src/test/bustedhelper_stage_clear.lua index 59b35971..8ff1baba 100644 --- a/src/test/bustedhelper_stage_clear.lua +++ b/src/test/bustedhelper_stage_clear.lua @@ -12,3 +12,5 @@ require("engine/test/bustedhelper") require("common_stage_clear") -- we still need to draw goal plate and emeralds, so keep using ingame visuals during stage_clear require("resources/visual_ingame_addon") +-- we also need menu sprite data, but located at a different place than in titlemenu, hence use a unique addon +require("resources/visual_stage_clear_addon") diff --git a/src/utest_main.lua b/src/utest_main.lua index bdbbf97a..4d5af240 100644 --- a/src/utest_main.lua +++ b/src/utest_main.lua @@ -12,7 +12,7 @@ require("common_ingame") -- same, utest_main is neutral, but right now we're testing ingame tiles -- so if we have to use visual at some point, requiring the ingame add-on --- is probably more useful than the titlemenu add-on +-- is probably more useful than the titlemenu/stage_clear add-on require("resources/visual_ingame_addon") local p8utest = require("engine/test/p8utest") From db124f2e86918aaa502754ca40c0a167f61594cd Mon Sep 17 00:00:00 2001 From: huulong Date: Tue, 22 Dec 2020 19:28:20 +0100 Subject: [PATCH 08/91] [TITLE] Fixed early bgm fade out after coming back from credits due to opening coroutine not being stopped on titlemenu state exit --- src/menu/titlemenu.lua | 4 ++++ src/menu/titlemenu_utest.lua | 24 +++++++++++++++++++++--- 2 files changed, 25 insertions(+), 3 deletions(-) diff --git a/src/menu/titlemenu.lua b/src/menu/titlemenu.lua index 1fd0aee0..96a9c9b4 100644 --- a/src/menu/titlemenu.lua +++ b/src/menu/titlemenu.lua @@ -64,6 +64,10 @@ end function titlemenu:on_exit() -- clear menu completely (will call GC, but fine) self.menu = nil + + -- stop all coroutines, this is important to prevent opening_sequence_async from continuing in the background + -- while reading credits, and fading out music earlier than expected after coming back to title + self.app:stop_all_coroutines() end function titlemenu:update() diff --git a/src/menu/titlemenu_utest.lua b/src/menu/titlemenu_utest.lua index c6588e3e..55103b50 100644 --- a/src/menu/titlemenu_utest.lua +++ b/src/menu/titlemenu_utest.lua @@ -51,9 +51,8 @@ describe('titlemenu', function () it('should call start_coroutine_method on opening_sequence_async', function () tm:on_enter() - local s = assert.spy(picosonic_app.start_coroutine) - s.was_called(1) - s.was_called_with(match.ref(tm.app), titlemenu.opening_sequence_async, match.ref(tm)) + assert.spy(picosonic_app.start_coroutine).was_called(1) + assert.spy(picosonic_app.start_coroutine).was_called_with(match.ref(tm.app), titlemenu.opening_sequence_async, match.ref(tm)) end) end) @@ -81,6 +80,18 @@ describe('titlemenu', function () describe('on_exit', function () + setup(function () + stub(picosonic_app, "stop_all_coroutines") + end) + + teardown(function () + picosonic_app.stop_all_coroutines:revert() + end) + + after_each(function () + picosonic_app.stop_all_coroutines:clear() + end) + it('should clear menu reference', function () tm.menu = {"dummy"} @@ -89,6 +100,13 @@ describe('titlemenu', function () assert.is_nil(tm.menu) end) + it('should call stop_all_coroutines', function () + tm:on_exit() + + assert.spy(picosonic_app.stop_all_coroutines).was_called(1) + assert.spy(picosonic_app.stop_all_coroutines).was_called_with(match.ref(tm.app)) + end) + end) describe('update', function () From fb77876ebbf6b5c2fbcc38c2f83f29a02e42d895 Mon Sep 17 00:00:00 2001 From: huulong Date: Tue, 22 Dec 2020 19:30:55 +0100 Subject: [PATCH 09/91] [AUDIO] Fixed stage clear menu select SFX playing pick emerald jingle instead By cleaning up all SFX and music on builtin data stage_clear, rearranging stage clear jingle and tracks to the beginning (sfx 8+, music 0+) and adding menu confirm SFX from title menu builtin data at SFX 51 (cannot be heard due to cartridge loading starting immediately) Also cleaned up builtin data ingame (removed unused Green Hill Zone BGM and unused pick emerald jingle alt version) --- data/builtin_data_ingame.p8 | 194 +++++++++++++++---------------- data/builtin_data_stage_clear.p8 | 145 ++++++++--------------- src/ingame/stage_state.lua | 5 +- src/resources/audio.lua | 5 +- 4 files changed, 148 insertions(+), 201 deletions(-) diff --git a/data/builtin_data_ingame.p8 b/data/builtin_data_ingame.p8 index 70cb1de8..d945d3a9 100644 --- a/data/builtin_data_ingame.p8 +++ b/data/builtin_data_ingame.p8 @@ -172,63 +172,63 @@ __map__ 6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f5e5f5e5f5e5f6e6f6e6f6e6f6e6f6e6f6e6e6f6e6f3536373839495857555859434143444946444748474848474845555354506f6e6f6e6f6e6f6e6f6e6f6e6f5e5f1e1f4d4c7a4e4d3e3f4749445530405e5f5e5f5e5f5e5f5e5f5e5f5e5f504e7a4d3e3f505e5f5e5f 6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6e6f6e6e6e48474558535453545354535453545354305745555657445853541e1f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f5e5f56464748474845555f5e5f50506e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f47484849466e6f6e6f __sfx__ -011000003005032050300503205030050320503005032050300503205030050320503005032050300503205030050320503005032050300503205030050320503005032050300503205030050320503005032050 +011000003005032050300503205030050320503005032050300503205030050320503005032050300503205030050320503005032050300503205030050320503005032050300503205000000000000000000000 010c00001835018350183501835018350183501835018350183501835018350183501835018350183501835018350183501835018350183501835018350183501835018350183501835018350183501835018350 -000c0000213301d330213301d330233301f330233301f3302433021330243302133026330233302633023330303302b3202f330303202d330303202b3302d320303302b3202f330303202d330303202b3302d320 -010c00000000000000306250000030625000003062500000306250000030625000003062500000306250000030625000002334023345000000000030625000002134021345000000000030625000002334023345 -010c00003062500000213402134030625000003062500000233402334530625000002134021345306250000024340243403062500000306250000023340233403062500000306250000021340213403062500000 -010c00000c3400c3450c3400c3450c3400c3450c3400c3450c3400c3450c3400c3450c3400c3450c3400c3450c3400c3450c3400c3450c3400c3450c3400c3450c3400c3450c3400c3450c3400c3450c3400c345 -010c0000303302b3202f330303202d330303202b3302d320303302b3202f330303202d330303202b3302d320303302b3202f330303202d330303202b3302d320303302b3202f330303202d330303202b3302d320 -010c00000000000000306250000021340213450000000000306250000000000000003062500000000000000030625000002434024345000000000030625000002334023345000000000030625000002134021340 -010c00003062500000306250000030625000003062500000306250000030625000003062500000306250000021340213403062500000306250000023340233403062500000306250000024340243403062500000 -010c00000c3400c3450c3400c345093400934509340093450a3400a3450a3400a3450b3400b3450b3400b3450c3400c3450c3400c3450c3400c3450c3400c3450c3400c3450c3400c3450c3400c3450c3400c345 -010c00002134021340213402134021340213402134021340213402134021340213450000000000000000000030625000002134021345000000000030625000002334023345000000000030625000002434024345 -010c00002134021340306250000030625000002334023340306250000030625000002434024340306250000024340243403062500000306250000023340233403062500000306250000030625000003062500000 -010c00003062500000213402134500000000003062500000233402334500000000003062500000243402434530625000002434024340243402434530625000002334023340233402334023340233402334023340 -010c00003062500000306250000030625000003062500000306250000030625000003062500000306250000030625000003062500000306250000030625000002434024345213402134030625000002434024345 -010c00000c3400c3450c3400c3450c3400c3450c3400c3450c3400c3450c3400c3450e3400e3451034010345113401134511340113451d3401d3451134011345113401134511340113451d3401d3451134011345 -010c0000303302b3202f330303202d330303202b3302d320303302b3202f330303202d330303202b3302d32000000000000000000000000000000000000000002433024335213302133021330213352433024335 -010c00002334023340233402334023340233402334023340233402334023340233450000000000000000000000000000000000000000000000000000000000003062500000306250000021340213453062500000 -010c0000233402334030625000002434024345233402334030625000001f3401f3403062500000306250000030625000003062500000306250000021340213452834028345263402634030625000002434024345 -010c0000103401034510340103451c3401c345103401034510340103450c3400c3450e3400e3451034010345113401134511340113451d3401d3451134011345113401134511340113451d3401d3451134011345 -010c0000233302333023330233352433024335233302333023330233352b3302d33030330303352d3302d33500000000000000000000000000000021330213352833028335263302633026330263352433024335 -010c00003062500000233402334530625000003062500000233402334530625000001f3401f3401f3401f34500000000000000000000000000000030625000003062500000306250000026340263453062500000 -010c0000233402334030625000002434024345233402334030625000001f3401f3403062500000306250000030625000003062500000306250000030625000002434024345213402134030625000002434024345 -010c0000233302333023330233352433024335233302333023330233352b3302d3303033030335343303433500000000000000000000000000000000000000002433024335213302133021330213352433024335 -010c00003062500000233402334530625000003062500000233402334530625000001f3401f3401f3401f34500000000000000000000000000000000000000003062500000306250000021340213453062500000 -010c0000233402334030625000002434024345233402334030625000001f3401f34030625000003062500000306250000030625000003062500000213402134521340213451d3401d34030625000002134021345 -010c0000103401034510340103451c3401c3451034010345103401034510340103451c3401c34510340103450e3400e3450e3400e3451a3401a3450e3400e3450e3400e3450e3400e3451a3401a3450e3400e345 -010c0000233302333023330233352433024335233302333023330233352b3302d33030330303352d3302d335000000000000000000000000000000213302133521330213351d3301d3301d3301d3352133021335 -010c00003062500000233402334530625000003062500000233402334530625000001f3401f3401f3401f3450000000000000000000000000000003062500000306250000030625000001d3401d3453062500000 -010c00001f3401f340306250000021340213451f3401f340306250000018340183403062500000306250000030625000003062500000306250000030625000002434024345213402134030625000002434024345 -010c00000c3400c3450c3400c34518340183450c3400c3450c3400c3450c3400c3450e3400e3451034010345113401134511340113451d3401d3451134011345113401134511340113451d3401d3451134011345 -010c00001f3301f3301f3301f33521330213351f3301f3301f3301f33518330183301833018330183301833530330303352d330303202933030320263302932030330303352d3303032029330303202633029320 -010c000030625000001f3401f345306250000030625000001f3401f34530625000001834018340183401834500000000000000000000000000000000000000003062500000306250000021340213453062500000 -010c00002f3302f3352b3302f320323302b3202f330323202f3302f3352b3302f3202b3302f320283302b32030330303352d330303202933030320263302932030330303352d3303032029330303202633029320 -010c00002f3302f3352b3302f320323302b3202f330323202f3302f3352b3302f3202b3302f320283302b32030330303352d330303202933030320263302932030330303352d3303032029330303203433034330 -010c00001f3401f340306250000021340213451f3401f3403062500000183401834030625000001c3401c3451a3401a3401a3401a3401a3401a34030625000001534015340153401534530625000001334013340 -010c00000c3400c3450c3400c34518340183450c3400c3450c3400c3450c3400c3450c3400c3450c3400c34516340163403062500000306250000015340153403062500000306250000013340133403062500000 -010c00003062500000303303033534320343252d3302d33530330303352d3302d335303303033534330343352e3302e335293302e32032330293202e330323202e3302e335293302e32032330293202e33032320 -010c000034330343351f3401f345306250000030625000001f3401f3453062500000183401834530625000003062500000163401634016340163451a3401a3401a3401a3401a3401a3401a3401a3401a3401a340 -010c00001334013345306250000011340113401134011345306250000018340183451a3401a3451c3401c34030625000000934009340093400934530625000000b3400b3400b3400b34530625000000c3400c340 -010c00003062500000113401134030625000003062500000103401034030625000000e3400e34030625000000934009340306250000030625000000b3400b340306250000030625000000c3400c3403062500000 -010c00002e3302e335293302e32032330293202e330323202e3302e335293302e32032330293202e330323202d3302d335283302d32030330283202d330303202d3302d335283302d32030330283202d33030320 -010c00001a3401a3401a3401a3401a3401a3401a3401a3401a3401a345103401034530625000000e3400e3451c3401c3401c3401c3401c3401c3401c3401c3401c3401c3401c3401c3401c3401c3401c3401c340 -010c00000c3400c34530625000000e3400e3400e3400e3453062500000183401834521340213451b3401b34030625000001434014340143401434530625000001334013340133401334530625000001134011340 -010c000030625000000e3400e34030625000003062500000103401034030625000001534015340306250000014340143403062500000306250000013340133403062500000306250000011340113403062500000 -010c00002d3302d335283302d32030330283202d330303202d3302d335283302d32030330283202d330303202c3302c335273302c32030330273202c330303202c3302c335273302c32030330273202c33030320 -010c00001c3401c3401c3401c3401c3401c3401c3401c3401c3401c3451034010345306250000015340153451b3401b3401b3401b3401b3401b3401b3401b3401b3401b3401b3401b3401b3401b3401b3401b340 -010c0000113401134530625000000f3400f3400f3400f345306250000018340183451b3401b3451a3401a34030625000001334013340133401334530625000000e3400e3400e3400e34530625000002834028340 -010c000030625000000f3400f340306250000030625000000e3400e34030625000000c3400c34030625000001334013340306250000030625000000e3400e3403062500000306250000013340133403062500000 -010c00002c3302c335273302c32030330273202c330303202c3302c335273302c32030330273202c3303032030330303352d33030320343302d3202d3303432030330303352d33030320343302d3202d33034320 -010c00001b3401b3401b3401b3401b3401b3401b3401b3401b3401b3450e3400e34530625000000c3400c3451a3401a3401a3401a3401a3401a3401a3401a3401a3401a3401a3401a3401a3401a3451334013345 -010e00001a1351a1351a1351a135000001a135000001c135000001c135000001c135000001c1301c1301c1351f1301f1350000021130211350000024130241350000026130261302613026130261302613026130 -010e0000131351313513135131350000013135000001513500000151350000015135000001513015130151351813018135000001a1301a135000001d1301d135000001f1301f1301f1301f1301f1301f1301f130 -010e00002613026130261302613026130261302613026130261302613026130261302613026130261302613026130261302613500000000000000000000000000000000000000000000000000000000000000000 -010e00001f1301f1301f1301f1301f1301f1301f1301f1301f1301f1301f1301f1301f1301f1301f1301f1301f1301f1301f13500000000000000000000000000000000000000000000000000000000000000000 -0107000015645156050c6450c6050c6450c60515645156050c6450c6050c6450c60515645156050c6450c60515645156050c6450c60515645156050c6450c6050c6450c6050c6450c605156450c645156450c645 -010e00000000000000000000000000000000000000000000000000000000000000000000000000000000000015635116351763515635116351763515635116351763515635156001560015600156001560015605 -01100000247502b75030750347503475030750307503775037750327503275023750217501f7501d75023750217501f7501d75023750217501f7501d7501a7501a7501a7501a7501a75000700007000070000700 +001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 0110000024755287552b75530755307002d7550000032755327000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 010900003f5733f5633f5433f5233f5133f5233f5433f5633f5733f5633f5433f5233f5133f503025030250302503025030250301503025030250302503025030050300503005030050300503005030050300503 010300001354013540145401555016550185501b5601e560235712a56100501005010050100501005010050100501005010050100501005010050100501005010050100501005010050100000000000000000000 @@ -237,47 +237,45 @@ __sfx__ 010400002433024330243002433024330243302433024330243302433024330243302433022300223000830008300083000830008300083000830008300083000830008300083000830008300083000830008300 011000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 __music__ -01 00010203 -00 04050607 -00 0809060a -00 0b05060c -00 0d0e0f10 -00 11121314 -00 15121617 -00 18191a1b -00 1c1d1e1f -00 11122014 -00 15122017 -00 1819211b -00 22232425 -00 26272829 -00 2a2b2c2d -00 2e2f3031 -00 32333435 -00 11121314 -00 15121617 -00 18191a1b -00 1c1d1e1f -00 11122014 -00 15122017 -00 1819211b -00 22232425 -00 26272829 -00 2a2b2c2d -00 2e2f3031 -00 32333435 -00 11121314 -00 15121617 -00 18191a1b -00 1c1d1e1f -00 11122014 -00 15122017 -00 1819211b -00 22232425 -00 26272829 -00 2a2b2c2d -00 2e2f3031 +00 41424344 +00 41424344 +00 41424344 +00 41424344 +00 41424344 +00 41424344 +00 41424344 +00 41424344 +00 41424344 +00 41424344 +00 41424344 +00 41424344 +00 41424344 +00 41424344 +00 41424344 +00 41424344 +00 41424344 +00 41424344 +00 41424344 +00 41424344 +00 41424344 +00 41424344 +00 41424344 +00 41424344 +00 41424344 +00 41424344 +00 41424344 +00 41424344 +00 41424344 +00 41424344 +00 41424344 +00 41424344 +00 41424344 +00 41424344 +00 41424344 +00 41424344 +00 41424344 +00 41424344 +00 41424344 +00 41424344 04 39777879 -00 32333637 -04 34354075 diff --git a/data/builtin_data_stage_clear.p8 b/data/builtin_data_stage_clear.p8 index 78147202..cc03db75 100644 --- a/data/builtin_data_stage_clear.p8 +++ b/data/builtin_data_stage_clear.p8 @@ -174,110 +174,57 @@ __map__ __sfx__ 011000003005032050300503205030050320503005032050300503205030050320503005032050300503205030050320503005032050300503205030050320503005032050300503205030050320503005032050 010c00001835018350183501835018350183501835018350183501835018350183501835018350183501835018350183501835018350183501835018350183501835018350183501835018350183501835018350 -000c0000213301d330213301d330233301f330233301f3302433021330243302133026330233302633023330303302b3202f330303202d330303202b3302d320303302b3202f330303202d330303202b3302d320 -010c00000000000000306250000030625000003062500000306250000030625000003062500000306250000030625000002334023345000000000030625000002134021345000000000030625000002334023345 -010c00003062500000213402134030625000003062500000233402334530625000002134021345306250000024340243403062500000306250000023340233403062500000306250000021340213403062500000 -010c00000c3400c3450c3400c3450c3400c3450c3400c3450c3400c3450c3400c3450c3400c3450c3400c3450c3400c3450c3400c3450c3400c3450c3400c3450c3400c3450c3400c3450c3400c3450c3400c345 -010c0000303302b3202f330303202d330303202b3302d320303302b3202f330303202d330303202b3302d320303302b3202f330303202d330303202b3302d320303302b3202f330303202d330303202b3302d320 -010c00000000000000306250000021340213450000000000306250000000000000003062500000000000000030625000002434024345000000000030625000002334023345000000000030625000002134021340 -010c00003062500000306250000030625000003062500000306250000030625000003062500000306250000021340213403062500000306250000023340233403062500000306250000024340243403062500000 -010c00000c3400c3450c3400c345093400934509340093450a3400a3450a3400a3450b3400b3450b3400b3450c3400c3450c3400c3450c3400c3450c3400c3450c3400c3450c3400c3450c3400c3450c3400c345 -010c00002134021340213402134021340213402134021340213402134021340213450000000000000000000030625000002134021345000000000030625000002334023345000000000030625000002434024345 -010c00002134021340306250000030625000002334023340306250000030625000002434024340306250000024340243403062500000306250000023340233403062500000306250000030625000003062500000 -010c00003062500000213402134500000000003062500000233402334500000000003062500000243402434530625000002434024340243402434530625000002334023340233402334023340233402334023340 -010c00003062500000306250000030625000003062500000306250000030625000003062500000306250000030625000003062500000306250000030625000002434024345213402134030625000002434024345 -010c00000c3400c3450c3400c3450c3400c3450c3400c3450c3400c3450c3400c3450e3400e3451034010345113401134511340113451d3401d3451134011345113401134511340113451d3401d3451134011345 -010c0000303302b3202f330303202d330303202b3302d320303302b3202f330303202d330303202b3302d32000000000000000000000000000000000000000002433024335213302133021330213352433024335 -010c00002334023340233402334023340233402334023340233402334023340233450000000000000000000000000000000000000000000000000000000000003062500000306250000021340213453062500000 -010c0000233402334030625000002434024345233402334030625000001f3401f3403062500000306250000030625000003062500000306250000021340213452834028345263402634030625000002434024345 -010c0000103401034510340103451c3401c345103401034510340103450c3400c3450e3400e3451034010345113401134511340113451d3401d3451134011345113401134511340113451d3401d3451134011345 -010c0000233302333023330233352433024335233302333023330233352b3302d33030330303352d3302d33500000000000000000000000000000021330213352833028335263302633026330263352433024335 -010c00003062500000233402334530625000003062500000233402334530625000001f3401f3401f3401f34500000000000000000000000000000030625000003062500000306250000026340263453062500000 -010c0000233402334030625000002434024345233402334030625000001f3401f3403062500000306250000030625000003062500000306250000030625000002434024345213402134030625000002434024345 -010c0000233302333023330233352433024335233302333023330233352b3302d3303033030335343303433500000000000000000000000000000000000000002433024335213302133021330213352433024335 -010c00003062500000233402334530625000003062500000233402334530625000001f3401f3401f3401f34500000000000000000000000000000000000000003062500000306250000021340213453062500000 -010c0000233402334030625000002434024345233402334030625000001f3401f34030625000003062500000306250000030625000003062500000213402134521340213451d3401d34030625000002134021345 -010c0000103401034510340103451c3401c3451034010345103401034510340103451c3401c34510340103450e3400e3450e3400e3451a3401a3450e3400e3450e3400e3450e3400e3451a3401a3450e3400e345 -010c0000233302333023330233352433024335233302333023330233352b3302d33030330303352d3302d335000000000000000000000000000000213302133521330213351d3301d3301d3301d3352133021335 -010c00003062500000233402334530625000003062500000233402334530625000001f3401f3401f3401f3450000000000000000000000000000003062500000306250000030625000001d3401d3453062500000 -010c00001f3401f340306250000021340213451f3401f340306250000018340183403062500000306250000030625000003062500000306250000030625000002434024345213402134030625000002434024345 -010c00000c3400c3450c3400c34518340183450c3400c3450c3400c3450c3400c3450e3400e3451034010345113401134511340113451d3401d3451134011345113401134511340113451d3401d3451134011345 -010c00001f3301f3301f3301f33521330213351f3301f3301f3301f33518330183301833018330183301833530330303352d330303202933030320263302932030330303352d3303032029330303202633029320 -010c000030625000001f3401f345306250000030625000001f3401f34530625000001834018340183401834500000000000000000000000000000000000000003062500000306250000021340213453062500000 -010c00002f3302f3352b3302f320323302b3202f330323202f3302f3352b3302f3202b3302f320283302b32030330303352d330303202933030320263302932030330303352d3303032029330303202633029320 -010c00002f3302f3352b3302f320323302b3202f330323202f3302f3352b3302f3202b3302f320283302b32030330303352d330303202933030320263302932030330303352d3303032029330303203433034330 -010c00001f3401f340306250000021340213451f3401f3403062500000183401834030625000001c3401c3451a3401a3401a3401a3401a3401a34030625000001534015340153401534530625000001334013340 -010c00000c3400c3450c3400c34518340183450c3400c3450c3400c3450c3400c3450c3400c3450c3400c34516340163403062500000306250000015340153403062500000306250000013340133403062500000 -010c00003062500000303303033534320343252d3302d33530330303352d3302d335303303033534330343352e3302e335293302e32032330293202e330323202e3302e335293302e32032330293202e33032320 -010c000034330343351f3401f345306250000030625000001f3401f3453062500000183401834530625000003062500000163401634016340163451a3401a3401a3401a3401a3401a3401a3401a3401a3401a340 -010c00001334013345306250000011340113401134011345306250000018340183451a3401a3451c3401c34030625000000934009340093400934530625000000b3400b3400b3400b34530625000000c3400c340 -010c00003062500000113401134030625000003062500000103401034030625000000e3400e34030625000000934009340306250000030625000000b3400b340306250000030625000000c3400c3403062500000 -010c00002e3302e335293302e32032330293202e330323202e3302e335293302e32032330293202e330323202d3302d335283302d32030330283202d330303202d3302d335283302d32030330283202d33030320 -010c00001a3401a3401a3401a3401a3401a3401a3401a3401a3401a345103401034530625000000e3400e3451c3401c3401c3401c3401c3401c3401c3401c3401c3401c3401c3401c3401c3401c3401c3401c340 -010c00000c3400c34530625000000e3400e3400e3400e3453062500000183401834521340213451b3401b34030625000001434014340143401434530625000001334013340133401334530625000001134011340 -010c000030625000000e3400e34030625000003062500000103401034030625000001534015340306250000014340143403062500000306250000013340133403062500000306250000011340113403062500000 -010c00002d3302d335283302d32030330283202d330303202d3302d335283302d32030330283202d330303202c3302c335273302c32030330273202c330303202c3302c335273302c32030330273202c33030320 -010c00001c3401c3401c3401c3401c3401c3401c3401c3401c3401c3451034010345306250000015340153451b3401b3401b3401b3401b3401b3401b3401b3401b3401b3401b3401b3401b3401b3401b3401b340 -010c0000113401134530625000000f3400f3400f3400f345306250000018340183451b3401b3451a3401a34030625000001334013340133401334530625000000e3400e3400e3400e34530625000002834028340 -010c000030625000000f3400f340306250000030625000000e3400e34030625000000c3400c34030625000001334013340306250000030625000000e3400e3403062500000306250000013340133403062500000 -010c00002c3302c335273302c32030330273202c330303202c3302c335273302c32030330273202c3303032030330303352d33030320343302d3202d3303432030330303352d33030320343302d3202d33034320 -010c00001b3401b3401b3401b3401b3401b3401b3401b3401b3401b3450e3400e34530625000000c3400c3451a3401a3401a3401a3401a3401a3401a3401a3401a3401a3401a3401a3401a3401a3451334013345 +001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 010e00001a1351a1351a1351a135000001a135000001c135000001c135000001c135000001c1301c1301c1351f1301f1350000021130211350000024130241350000026130261302613026130261302613026130 010e0000131351313513135131350000013135000001513500000151350000015135000001513015130151351813018135000001a1301a135000001d1301d135000001f1301f1301f1301f1301f1301f1301f130 010e00002613026130261302613026130261302613026130261302613026130261302613026130261302613026130261302613500000000000000000000000000000000000000000000000000000000000000000 010e00001f1301f1301f1301f1301f1301f1301f1301f1301f1301f1301f1301f1301f1301f1301f1301f1301f1301f1301f13500000000000000000000000000000000000000000000000000000000000000000 0107000015645156050c6450c6050c6450c60515645156050c6450c6050c6450c60515645156050c6450c60515645156050c6450c60515645156050c6450c6050c6450c6050c6450c605156450c645156450c645 010e00000000000000000000000000000000000000000000000000000000000000000000000000000000000015635116351763515635116351763515635116351763515635156001560015600156001560015605 -01100000247502b75030750347503475030750307503775037750327503275023750217501f7501d75023750217501f7501d75023750217501f7501d7501a7501a7501a7501a7501a75000700007000070000700 -0110000024755287552b75530755307002d7550000032755327000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -010900003f5733f5633f5433f5233f5133f5233f5433f5633f5733f5633f5433f5233f5133f503025030250302503025030250301503025030250302503025030050300503005030050300503005030050300503 -010300001354013540145401555016550185501b5601e560235712a56100501005010050100501005010050100501005010050100501005010050100501005010050100501005010050100000000000000000000 -01030000144721447215462164621a4621d4522144222432234222342224422244222442225412254122541225412254121640216402164021640216402164021640216402164021640216402164021640216402 -01070000285512c5512d5512d5512d5512d5412d5412d5312d5212d5212d5112d5112d5112d5112d5112d5002a5012a5012a5012a5012a5012c5012c5012c5012c5012d5012d5012d5011e5011e5011e5011e501 -010400002433024330243002433024330243302433024330243302433024330243302433022300223000830008300083000830008300083000830008300083000830008300083000830008300083000830008300 -011000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +010400002d8502d800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 __music__ -01 00010203 -00 04050607 -00 0809060a -00 0b05060c -00 0d0e0f10 -00 11121314 -00 15121617 -00 18191a1b -00 1c1d1e1f -00 11122014 -00 15122017 -00 1819211b -00 22232425 -00 26272829 -00 2a2b2c2d -00 2e2f3031 -00 32333435 -00 11121314 -00 15121617 -00 18191a1b -00 1c1d1e1f -00 11122014 -00 15122017 -00 1819211b -00 22232425 -00 26272829 -00 2a2b2c2d -00 2e2f3031 -00 32333435 -00 11121314 -00 15121617 -00 18191a1b -00 1c1d1e1f -00 11122014 -00 15122017 -00 1819211b -00 22232425 -00 26272829 -00 2a2b2c2d -00 2e2f3031 -04 39777879 -00 32333637 -04 34354075 +00 08090c0d +04 0a0b4040 diff --git a/src/ingame/stage_state.lua b/src/ingame/stage_state.lua index 1a081123..ec4760d2 100644 --- a/src/ingame/stage_state.lua +++ b/src/ingame/stage_state.lua @@ -1162,7 +1162,8 @@ end function stage_state:reload_bgm_tracks() -- reload sfx from bgm cartridge memory - -- we guarantee that the music sfx will take maximum 50 entries (out of 64) + -- we guarantee that the music sfx will take maximum 50 entries (out of 64), + -- potentially 0-7 for custom instruments and 8-49 for music tracks -- => 50 * 68 = 3400 = 0xd48 bytes -- the bgm sfx should start at index 0 on both source and -- current cartridge, so use copy memory from the start of sfx section @@ -1171,7 +1172,7 @@ end function stage_state:play_bgm() -- only 4 channels at a time in PICO-8 - -- Angel Island BGM currently uses only 3 channels so t's pretty safe + -- Angel Island BGM currently uses only 3 channels so it's pretty safe -- as there is always a channel left for SFX, but in case we add a 4th one -- (or we try to play 2 SFX at once), protect the 3 channels by passing priority mask music(self.curr_stage_data.bgm_id, 0, shl(1, 0) + shl(1, 1) + shl(1, 2)) diff --git a/src/resources/audio.lua b/src/resources/audio.lua index c56d8c81..19f82b7d 100644 --- a/src/resources/audio.lua +++ b/src/resources/audio.lua @@ -1,7 +1,7 @@ local audio = {} audio.sfx_ids = { - -- builtin_data_titlemenu only + -- builtin_data_titlemenu and builtin_data_stage_clear only menu_select = 50, menu_confirm = 51, @@ -17,7 +17,8 @@ audio.sfx_ids = { } audio.jingle_ids = { - stage_clear = 41, + -- builtin_data_stage_clear only (overlaps stage bgm in data_bgm1.p8) + stage_clear = 0, } audio.music_ids = { From 6c999cb3de3b4de5878d5a0dad1e2162f5049eb7 Mon Sep 17 00:00:00 2001 From: huulong Date: Tue, 22 Dec 2020 19:40:51 +0100 Subject: [PATCH 10/91] [CLEANUP] Updated pico-boots to fix nil crash on itest DSL utest Removed printh in playercharacter --- pico-boots | 2 +- src/ingame/playercharacter.lua | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/pico-boots b/pico-boots index 0fac6e7c..72f419ad 160000 --- a/pico-boots +++ b/pico-boots @@ -1 +1 @@ -Subproject commit 0fac6e7c2de44c7bfe8318ef4ae0b9be8b644685 +Subproject commit 72f419ade5be58f1fadfeb1fe370a47cce607b73 diff --git a/src/ingame/playercharacter.lua b/src/ingame/playercharacter.lua index a26b2b61..81c49d54 100644 --- a/src/ingame/playercharacter.lua +++ b/src/ingame/playercharacter.lua @@ -612,7 +612,6 @@ local function iterate_over_collision_tiles(pc, collision_check_quadrant, start_ -- even when checking downward, we cannot detect one-way platforms from below their surface (signed distance < 0) -- this way, we don't step up or get blocked by them as ceiling inadvertently, but can still just land on them if is_oneway and signed_distance_to_closest_collider < -1 then - printh("signed_distance_to_closest_collider: "..nice_dump(signed_distance_to_closest_collider)) signed_distance_to_closest_collider = pc_data.max_ground_snap_height + 1 end From 9f97a96619acd55e81bfe326ca07744270fc14c7 Mon Sep 17 00:00:00 2001 From: huulong Date: Tue, 22 Dec 2020 20:31:01 +0100 Subject: [PATCH 11/91] [STAGE CLEAR] Much shorter itest as very slow with render and 100 frames enough to cover stage clear sequence for now --- src/itests/stage_clear/iteststage_clear.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/itests/stage_clear/iteststage_clear.lua b/src/itests/stage_clear/iteststage_clear.lua index 5a93dba3..69aa3e6b 100644 --- a/src/itests/stage_clear/iteststage_clear.lua +++ b/src/itests/stage_clear/iteststage_clear.lua @@ -23,7 +23,7 @@ itest_manager:register_itest('player waits', end) -- let stage clear sequence play and see if nothing crashes - wait(20.0) + wait(100, true) -- we should still be in stage clear (because even if we load() titlemenu cartridge in headless, -- it won't do anything) From 21e4d6e3f9f961c0e2ef0ac24ce2576b7838f3a4 Mon Sep 17 00:00:00 2001 From: huulong Date: Tue, 22 Dec 2020 20:32:45 +0100 Subject: [PATCH 12/91] [UI ANIMATION] Moved yield after position set so even if initial position of drawable is not matching a, we won't see drawable at initial position for just 1 frame, then warping to a (visual glitch), as it will be moved to a before first rendering. This will offset all frames by 1 in the future, but not perceptible --- src/ui/ui_animation.lua | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/ui/ui_animation.lua b/src/ui/ui_animation.lua index 0b5507f1..81587a01 100644 --- a/src/ui/ui_animation.lua +++ b/src/ui/ui_animation.lua @@ -1,17 +1,16 @@ local ui_animation = {} --- helper: move drawable (position member, draw method) linearly along coord ("x" or "y") +-- helper: move drawable (position attribute, draw method) linearly along coord ("x" or "y") -- from a to b over n frames -- coord_offsets allow to offset drawables relatively to a and b while keeping drawable motions in sync -- (coord_offsets list is indexed by index of drawable in drawables) function ui_animation.move_drawables_on_coord_async(coord, drawables, coord_offsets, a, b, n) for frame = 1, n do - yield() local alpha = frame / n - for i, dr in ipairs(drawables) do dr.position:set(coord, (1 - alpha) * a + alpha * b + coord_offsets[i]) end + yield() end end From dd23d042e30c28305eefb933f3b8e7a737529baa Mon Sep 17 00:00:00 2001 From: huulong Date: Tue, 22 Dec 2020 20:33:20 +0100 Subject: [PATCH 13/91] [STAGE CLEAR] Added fadeout with zigzag and full background rectangle Rearranged methods order to put stage/camera methods together --- src/resources/visual_stage_clear_addon.lua | 5 + src/stage_clear/stage_clear_state.lua | 109 ++++++++++++--------- 2 files changed, 70 insertions(+), 44 deletions(-) diff --git a/src/resources/visual_stage_clear_addon.lua b/src/resources/visual_stage_clear_addon.lua index 761fe8c9..df29327d 100644 --- a/src/resources/visual_stage_clear_addon.lua +++ b/src/resources/visual_stage_clear_addon.lua @@ -2,6 +2,10 @@ local visual = require("resources/visual_common") local sprite_data = require("engine/render/sprite_data") +local stage_clear_visual = { + fadeout_zigzag_width = 11 -- doesn't include pixel 0, so actually count +1 pixel in total +} + -- visuals for stage_clear only -- it uses the add-on system, which means you only need to require it along with visual_common, -- but only get the return value of visual_common named `visual` here @@ -12,4 +16,5 @@ local menu_sprite_data_t = { menu_cursor = sprite_data(sprite_id_location(10, 0), tile_vector(2, 1), vector(8, 5), colors.pink), } +merge(visual, stage_clear_visual) merge(visual.sprite_data_t, menu_sprite_data_t) diff --git a/src/stage_clear/stage_clear_state.lua b/src/stage_clear/stage_clear_state.lua index d82e4b36..40bb6729 100644 --- a/src/stage_clear/stage_clear_state.lua +++ b/src/stage_clear/stage_clear_state.lua @@ -103,6 +103,9 @@ function stage_clear_state:render() end end + +-- stage-related methods, simplified versions of stage_state equivalents + function stage_clear_state:spawn_goal_plate_at(global_loc) -- remember where we found palm tree leaves core tile, to draw extension sprites around later assert(self.goal_plate == nil, "stage_clear_state:spawn_goal_plate_at: goal plate already spawned!") @@ -148,9 +151,6 @@ function stage_clear_state:scan_current_region_to_spawn_objects() end end - --- extended map system: see stage_state - -- return map filename for current stage and given region coordinates (u: int, v: int) -- do not try this with transitional regions, instead we'll patch them from individual regions function stage_clear_state:get_map_region_filename(u, v) @@ -163,6 +163,42 @@ function stage_clear_state:reload_map_region() reload(0x2000, 0x2000, 0x1000, self:get_map_region_filename(3, 1)) end + +-- camera methods, also simplified versions of stage_stage equivalent + +-- hardcoded version of stage_state:set_camera_with_origin +function stage_clear_state:set_camera_with_origin(origin) + origin = origin or vector.zero() + -- hardcoded version: we printed the following value during ingame just before loading stage_clear: + -- self.camera.position.x = 3392 + -- self.camera.position.y = 328 + -- self.camera.position.x - screen_width / 2 = 3328 + -- self.camera.position.y - screen_height / 2 = 264 + -- and reinjected the values below (correspond to camera approx. centered on goal plate) + camera(3328 - origin.x, 264 - origin.y) +end + +-- same as stage_state:set_camera_with_region_origin but short enough to copy +function stage_clear_state:set_camera_with_region_origin() + local region_topleft_loc = self:get_region_topleft_location() + self:set_camera_with_origin(vector(tile_size * region_topleft_loc.i, tile_size * region_topleft_loc.j)) +end + +-- same as stage_state:region_to_global_location but short enough to copy +function stage_clear_state:region_to_global_location(region_loc) + return region_loc + self:get_region_topleft_location() +end + +-- return current region topleft as location (convert uv to ij) +-- hardcoded version of stage_state:get_region_topleft_location +-- for stage_clear: goal is in region (3, 1) for pico island +function stage_clear_state:get_region_topleft_location() + return location(map_region_tile_width * 3, map_region_tile_height * 1) +end + + +-- actual stage clear sequence + function stage_clear_state:play_stage_clear_sequence_async() -- show result UI self:show_result_async() @@ -297,19 +333,38 @@ function stage_clear_state:assess_result_async() self.app:yield_delay_s(stage_clear_data.show_emerald_assessment_duration) end +-- drawable for the right part of the fade-out layer (the body will be filled with a separate rectangle) +-- there is only one, so don't bother creating a struct just for that +local zigzag_drawable = { + position = vector(0, 0) +} + +function zigzag_drawable:draw() + for j = 0, 127 do + -- zigzag can be represented by a periodical abs function (length is 0 when line contains 1 px) + local length = abs((j - 8) % (2 * visual.fadeout_zigzag_width) - visual.fadeout_zigzag_width) + line(self.position.x, j, self.position.x + length, j, colors.black) + end +end + function stage_clear_state:zigzag_fade_out_async() - -- todo + local bg_rect = rectangle(vector(0, 0), 128, 128, colors.black) + self.result_overlay:add_drawable("bg_rect", bg_rect) + self.result_overlay:add_drawable("zigzag", zigzag_drawable) + + -- make rectangle with zigzag edge enter the screen from the left + -- note that we finish at 128 and not 127 so the zigzag fully goes out of the screen to the right, + -- and the bg_rect fully covers the screen, ready to be used as background + ui_animation.move_drawables_on_coord_async("x", {bg_rect, zigzag_drawable}, {-128, 0}, - visual.fadeout_zigzag_width, 128, 36) -- at the end of the zigzag, clear the emerald assessment widgets which are now completely hidden + -- as well as members that draw custom items, except for actual emeralds as we'll draw the missing emeralds more below self.result_overlay:clear_drawables() - - -- only clear members that draw custom items, except for actual emeralds as we'll draw the missing emeralds - -- soon anyway self.result_show_emerald_cross_base = false - -- just keep the full black screen rectangle as background for retry screen - local bg = rectangle(vector(0, 0), 128, 128, colors.black) - self.result_overlay:add_drawable("bg", bg) + -- just re-add the black rectangle as background for the retry menu + -- (no need for zigzag edge itself, since the rectangle body now covers the whole screen) + self.result_overlay:add_drawable("bg_rect", bg_rect) for num = 1, 8 do -- only display missed emeralds @@ -325,45 +380,11 @@ function stage_clear_state:show_retry_screen() local try_again_label = label("try again?", vector(41, 34), colors.white) self.result_overlay:add_drawable("try again", try_again_label) - printh("visual.sprite_data_t.menu_cursor: "..nice_dump(visual.sprite_data_t.menu_cursor)) self.retry_menu = menu(self.app, alignments.left, 1, colors.white, visual.sprite_data_t.menu_cursor, 7) self.retry_menu:show_items(stage_clear_state.retry_items) end --- camera - --- hardcoded version of stage_state:set_camera_with_origin -function stage_clear_state:set_camera_with_origin(origin) - origin = origin or vector.zero() - -- hardcoded version: we printed the following value during ingame just before loading stage_clear: - -- self.camera.position.x = 3392 - -- self.camera.position.y = 328 - -- self.camera.position.x - screen_width / 2 = 3328 - -- self.camera.position.y - screen_height / 2 = 264 - -- and reinjected the values below (correspond to camera approx. centered on goal plate) - camera(3328 - origin.x, 264 - origin.y) -end - --- same as stage_state:set_camera_with_region_origin but short enough to copy -function stage_clear_state:set_camera_with_region_origin() - local region_topleft_loc = self:get_region_topleft_location() - self:set_camera_with_origin(vector(tile_size * region_topleft_loc.i, tile_size * region_topleft_loc.j)) -end - --- same as stage_state:region_to_global_location but short enough to copy -function stage_clear_state:region_to_global_location(region_loc) - return region_loc + self:get_region_topleft_location() -end - --- return current region topleft as location (convert uv to ij) --- hardcoded version of stage_state:get_region_topleft_location --- for stage_clear: goal is in region (3, 1) for pico island -function stage_clear_state:get_region_topleft_location() - return location(map_region_tile_width * 3, map_region_tile_height * 1) -end - - -- render -- render the stage elements with the main camera: From 1d0f590647f557d54da5d714e9921437efa4ff4c Mon Sep 17 00:00:00 2001 From: huulong Date: Tue, 22 Dec 2020 21:06:37 +0100 Subject: [PATCH 14/91] [STAGE CLEAR] Improve stage clear sequence and fix visual issues - render retry menu background on top of emerald cross - adjust position of emerald result label if shorter to center on screen X - make first sentence exit screen instead of being removed instantly - draw emeralds on circle instead of diamond - reuse sonic label for optimisation --- pico-boots | 2 +- src/resources/visual_stage_clear_addon.lua | 3 +- src/stage_clear/stage_clear_state.lua | 82 ++++++++++++---------- 3 files changed, 46 insertions(+), 41 deletions(-) diff --git a/pico-boots b/pico-boots index 72f419ad..f841b663 160000 --- a/pico-boots +++ b/pico-boots @@ -1 +1 @@ -Subproject commit 72f419ade5be58f1fadfeb1fe370a47cce607b73 +Subproject commit f841b6635624c91f7c8dffd51b7e405b0e7d9f69 diff --git a/src/resources/visual_stage_clear_addon.lua b/src/resources/visual_stage_clear_addon.lua index df29327d..204d91f8 100644 --- a/src/resources/visual_stage_clear_addon.lua +++ b/src/resources/visual_stage_clear_addon.lua @@ -3,7 +3,8 @@ local visual = require("resources/visual_common") local sprite_data = require("engine/render/sprite_data") local stage_clear_visual = { - fadeout_zigzag_width = 11 -- doesn't include pixel 0, so actually count +1 pixel in total + fadeout_zigzag_width = 11, -- doesn't include pixel 0, so actually count +1 pixel in total + missed_emeralds_radius = 10 } -- visuals for stage_clear only diff --git a/src/stage_clear/stage_clear_state.lua b/src/stage_clear/stage_clear_state.lua index 40bb6729..f03508ed 100644 --- a/src/stage_clear/stage_clear_state.lua +++ b/src/stage_clear/stage_clear_state.lua @@ -93,11 +93,15 @@ function stage_clear_state:render() -- see set_camera_with_origin for value explanation (we must pass camera position) visual_stage.render_background(vector(3392, 328)) self:render_stage_elements() - self:render_overlay() -- draw either picked or missed emeralds self:render_emerald_cross() + -- draw overlay on top to hide result widgets + self:render_overlay() + + -- exceptionally draw menu above overlay, because this overlay is used as background + -- for retry menu if self.retry_menu then self.retry_menu:draw(29, 90) end @@ -249,23 +253,22 @@ function stage_clear_state:restore_picked_emerald_data() end function stage_clear_state:show_result_async() - -- "sonic got through": 17 characters, so 17*4 = 68 px wide - -- so to enter from left, offset by -68 (we even get an extra margin pixel) - local sonic_label = label("sonic", vector(-68, 14), colors.dark_blue, colors.orange) + -- create "sonic" label separately just for different color + local sonic_label = label("sonic", vector(0, 14), colors.dark_blue, colors.orange) self.result_overlay:add_drawable("sonic", sonic_label) - -- "got through" is 6 chars after the string start so 24px after , -68+24=-44 - local through_label = label("got through", vector(-44, 14), colors.white, colors.black) + local through_label = label("got through", vector(0, 14), colors.white, colors.black) self.result_overlay:add_drawable("through", through_label) - -- move text from left to right + -- "sonic got through": 17 characters, so 17*4 = 68 px wide + -- make text enter from left to right (starts on screen edge, so -68 with even an extra margin pixel after last char) + -- "got through" is 6 chars after the string start so 24px after "sonic" ui_animation.move_drawables_on_coord_async("x", {sonic_label, through_label}, {0, 24}, -68, 30, 20) - -- enter from screen right so offset is 128 - local result_label = label("angel island", vector(128, 26), colors.white, colors.black) - self.result_overlay:add_drawable("stage", result_label) + local stage_label = label("angel island", vector(0, 26), colors.white, colors.black) + self.result_overlay:add_drawable("stage", stage_label) - -- move text from right to left - ui_animation.move_drawables_on_coord_async("x", {result_label}, {0}, 128, 40, 20) + -- make text enter screen from right to left (starts on screen edge, so 128) + ui_animation.move_drawables_on_coord_async("x", {stage_label}, {0}, 128, 40, 20) -- show emerald cross self.result_show_emerald_cross_base = true @@ -303,33 +306,48 @@ function stage_clear_state:assess_result_async() yield_delay(30) - self.result_overlay:remove_drawable("sonic") + -- retrieve labels from overlay (as we didn't store references as state members) + local sonic_label = self.result_overlay.drawables_map["sonic"] + local through_label = self.result_overlay.drawables_map["through"] + local stage_label = self.result_overlay.drawables_map["stage"] + + -- make text exit to the left (faster) + ui_animation.move_drawables_on_coord_async("x", {sonic_label, through_label}, {0, 24}, 30, -68, 10) + + -- make text exit to the right (faster) + ui_animation.move_drawables_on_coord_async("x", {stage_label}, {0}, 40, 128, 10) + + -- clean up labels outside screen, except "sonic" that we will reuse + -- "sonic" label is already outside screen so it won't bother us until we use it again self.result_overlay:remove_drawable("through") self.result_overlay:remove_drawable("stage") yield_delay(30) - -- create another sonic label (previous one was also local var, so can't access it from here) - local sonic_label = label("sonic", vector(-88, 14), colors.dark_blue, colors.orange) - self.result_overlay:add_drawable("sonic", sonic_label) local emerald_text -- show how many emeralds player got - -- "sonic got all emeralds" (the longest sentence) has 22 chars so is 22*4 88 px wide - -- it comes from the left again, so offset negatively on start - -- "got ..." is 6 chars after the string start so 24px after , -88+24=-64 - -- hardcoded since we don't have access to spawned_emerald_locations anymore + -- "[number]" is has1 character but "all" has 3 characters, and label doesn't support centered text, + -- so adjust manually shorter label to be a little more to the right to center it on screen + local x_offset = 0 if self.picked_emerald_count < 8 then emerald_text = "got "..self.picked_emerald_count.." emeralds" + x_offset = 6 else emerald_text = "got all emeralds" end - local emerald_label = label(emerald_text, vector(-64, 14), colors.white, colors.black) + -- don't mind initial x, move_drawables_on_coord_async now sets it before first render + local emerald_label = label(emerald_text, vector(0, 14), colors.white, colors.black) self.result_overlay:add_drawable("emerald", emerald_label) - -- move text from left to right and give some time to player to read - ui_animation.move_drawables_on_coord_async("x", {sonic_label, emerald_label}, {0, 24}, -88, 20, 20) + -- move text "sonic got X emeralds" (reusing "sonic" label) from left to right and give some time to player to read + -- "got ..." is 6 chars after the full string start so 24px after "sonic" -> second offset is 24 + -- "sonic got all emeralds" (the longest sentence) has 22 chars so is 22*4 88 px wide + -- it comes from the left again, so offset negatively on start -> a = -88 + -- apply offset for shorter label to start and end x + -- animation takes 20 frames + ui_animation.move_drawables_on_coord_async("x", {sonic_label, emerald_label}, {0, 24}, -88 + x_offset, 20 + x_offset, 20) self.app:yield_delay_s(stage_clear_data.show_emerald_assessment_duration) end @@ -447,21 +465,6 @@ end -- render the emerald cross base and every picked emeralds -- (x, y) is at cross center function stage_clear_state:draw_emeralds_around_cross(x, y) - -- indexed by emerald number - -- numbers would be more consistent (0, 11, 20 everywhere) - -- if pivot was at (4, 3) instead of (4, 4) - -- but we need to make this work with the stage too - local emerald_relative_positions = { - vector(0, -19), - vector(11, -10), - vector(20, 1), - vector(11, 12), - vector(0, 21), - vector(-11, 12), - vector(-20, 1), - vector(-11, -10) - } - -- draw emeralds around the cross, from top, CW -- usually we iterate from 1 to #self.spawned_emerald_locations -- but here we obviously only defined 8 relative positions, @@ -470,7 +473,8 @@ function stage_clear_state:draw_emeralds_around_cross(x, y) -- self.result_show_emerald_set_by_number[num] is only set to true when -- we have picked emerald, so no need to check self.picked_emerald_numbers_set again if self.result_show_emerald_set_by_number[num] then - local draw_position = vector(x, y) + emerald_relative_positions[num] + local draw_position = vector(x + visual.missed_emeralds_radius * cos(0.25 - (num - 1) / 8), + y + visual.missed_emeralds_radius * sin(0.25 - (num - 1) / 8)) emerald.draw(num, draw_position, self.result_emerald_brightness_levels[num]) end end From 850c7220a0010107f2c3dd2abfd35396cf081cde Mon Sep 17 00:00:00 2001 From: huulong Date: Tue, 22 Dec 2020 21:58:17 +0100 Subject: [PATCH 15/91] [STAGE CLEAR] Extended itest duration to support jingle and new animation --- src/itests/stage_clear/iteststage_clear.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/itests/stage_clear/iteststage_clear.lua b/src/itests/stage_clear/iteststage_clear.lua index 69aa3e6b..6ee5abce 100644 --- a/src/itests/stage_clear/iteststage_clear.lua +++ b/src/itests/stage_clear/iteststage_clear.lua @@ -23,7 +23,7 @@ itest_manager:register_itest('player waits', end) -- let stage clear sequence play and see if nothing crashes - wait(100, true) + wait(750, true) -- we should still be in stage clear (because even if we load() titlemenu cartridge in headless, -- it won't do anything) From 1fa65571d01b097b182f0cacc3cb74938c140c7a Mon Sep 17 00:00:00 2001 From: huulong Date: Tue, 22 Dec 2020 21:59:55 +0100 Subject: [PATCH 16/91] [PROJECT] Added script to export single cartridge with all data cartridges as so far it only did it for ingame, but other cartridges need bgm/spritesheet updates, and even tilemap update for stage_clear Also added build command for stage_clear with tuner --- build_and_install_all_cartridges.sh | 2 +- ..._and_install_single_cartridge_with_data.sh | 8 +- install_all_cartridges.sh | 18 ++- install_single_cartridge.sh | 23 ++- install_single_cartridge_with_data.sh | 55 +++++++ sonic-2d-tech-demo.sublime-project | 146 ++++++++++-------- 6 files changed, 159 insertions(+), 93 deletions(-) rename build_and_install_single_cartridge.sh => build_and_install_single_cartridge_with_data.sh (82%) create mode 100755 install_single_cartridge_with_data.sh diff --git a/build_and_install_all_cartridges.sh b/build_and_install_all_cartridges.sh index 9f255825..9aa6ec86 100755 --- a/build_and_install_all_cartridges.sh +++ b/build_and_install_all_cartridges.sh @@ -1,6 +1,6 @@ #!/bin/bash -# Build all game cartridges and install them in PICO-8 carts folder. +# Build all game cartridges, including data cartridges and install them in PICO-8 carts folder. # Currently only supported on Linux. diff --git a/build_and_install_single_cartridge.sh b/build_and_install_single_cartridge_with_data.sh similarity index 82% rename from build_and_install_single_cartridge.sh rename to build_and_install_single_cartridge_with_data.sh index 68975cf5..acd64970 100755 --- a/build_and_install_single_cartridge.sh +++ b/build_and_install_single_cartridge_with_data.sh @@ -1,7 +1,7 @@ #!/bin/bash -# Build a specific cartridge for the game and install it in PICO-8 carts folder -# to allow playing with multiple cartridges +# Build a specific cartridge for the game and install it with data cartridges in PICO-8 carts folder +# to allow playing with multiple cartridges and newest data. # Currently only supported on Linux @@ -14,7 +14,7 @@ help() { } usage() { - echo "Usage: build_and_install_single_cartridge.sh CARTRIDGE_SUFFIX [CONFIG] + echo "Usage: build_and_install_single_cartridge_with_data.sh CARTRIDGE_SUFFIX [CONFIG] ARGUMENTS CARTRIDGE_SUFFIX Cartridge to build for the multi-cartridge game @@ -74,4 +74,4 @@ if [[ $? -ne 0 ]]; then exit 1 fi -"$game_scripts_path/install_single_cartridge.sh" "$cartridge_suffix" "$config" +"$game_scripts_path/install_single_cartridge_with_data.sh" "$cartridge_suffix" "$config" diff --git a/install_all_cartridges.sh b/install_all_cartridges.sh index d24c0a7b..d271d768 100755 --- a/install_all_cartridges.sh +++ b/install_all_cartridges.sh @@ -1,12 +1,12 @@ #!/bin/bash -# Install all cartridges for the game to the default +# Install all cartridges for the game, including data cartridges, to the default # PICO-8 carts location # This is required if you need to play with multiple carts, # as other carts will only be loaded in PICO-8 carts location # Usage: install_all_cartridges.sh config [png] # config build config (e.g. 'debug' or 'release') -# png if passed, the .png cartridges are installed +# png if passed, the .png cartridges are installed # Currently only supported on Linux @@ -29,16 +29,18 @@ config="$1"; shift # option "png" will export the png cartridge if [[ $1 = "png" ]] ; then - suffix=".png" + suffix=".png" else - suffix="" + suffix="" fi -# note that we don't add the data/data_stage*.p8 cartridges because -# install_single_cartridge.sh for ingame will install all data cartridges anyway -# (and said script is really meant for built cartridges as it refers to build path) cartridge_list="titlemenu ingame stage_clear" for cartridge in $cartridge_list; do - "$game_scripts_path/install_single_cartridge.sh" "$cartridge" "$config" "$suffix" + "$game_scripts_path/install_single_cartridge.sh" "$cartridge" "$config" "$suffix" done + +# Also copy data cartridges +echo "Copying data cartridges data/data_*.p8 in ${install_dirpath} ..." +# trailing slash just to make sure we copy to a directory +cp data/data_*.p8 "${install_dirpath}/" diff --git a/install_single_cartridge.sh b/install_single_cartridge.sh index 8ff91aa4..a8d82dd1 100755 --- a/install_single_cartridge.sh +++ b/install_single_cartridge.sh @@ -3,11 +3,14 @@ # PICO-8 carts location # This is required if you need to play with multiple carts, # as other carts will only be loaded in PICO-8 carts location +# ! This does not install data and is not useful on its own, +# make sure to use install_single_cartridge_with_data.sh or +# to manually copy data cartridges after this. # Usage: install_single_cartridge.sh cartridge_suffix config [png] # cartridge_suffix 'titlemenu', 'ingame' or 'stage_clear' -# config build config (e.g. 'debug' or 'release') -# png if passed, the .png cartridge is installed +# config build config (e.g. 'debug' or 'release'. Default: 'debug') +# png if passed, the .png cartridge is installed # Currently only supported on Linux @@ -19,7 +22,7 @@ if ! [[ $# -ge 1 && $# -le 3 ]] ; then echo "build.sh takes 1 to 2 params, provided $#: \$1: cartridge_suffix ('titlemenu', 'ingame' or 'stage_clear') - \$2: config ('debug', 'release', etc.) + \$2: config ('debug', 'release', etc. Default: 'debug') \$3: optional suffix ('png' for .png cartridge install)" exit 1 fi @@ -32,9 +35,9 @@ config="$1"; shift # option "png" will export the png cartridge if [[ $1 = "png" ]] ; then - suffix=".png" + suffix=".png" else - suffix="" + suffix="" fi output_path="build/v${version}_${config}" @@ -43,8 +46,8 @@ carts_dirpath="$HOME/.lexaloffle/pico-8/carts" install_dirpath="${carts_dirpath}/picosonic/v${version}_${config}" if [[ ! -f "${cartridge_filepath}" ]]; then - echo "File ${cartridge_filepath} could not be found, cannot install. Make sure you built it first." - exit 1 + echo "File ${cartridge_filepath} could not be found, cannot install. Make sure you built it first." + exit 1 fi mkdir -p "${install_dirpath}" @@ -53,10 +56,4 @@ echo "Installing ${cartridge_filepath} in ${install_dirpath} ..." # trailing slash just to make sure we copy to a directory cp "${cartridge_filepath}" "${install_dirpath}/" -# Also copy data stage and bgm cartridges for extended map, when installing ingame cartridge -if [[ "$cartridge_suffix" == "ingame" ]]; then - echo "Copying data cartridges data/data_*.p8 in ${install_dirpath} ..." - cp data/data_*.p8 "${install_dirpath}/" -fi - echo "Done." diff --git a/install_single_cartridge_with_data.sh b/install_single_cartridge_with_data.sh new file mode 100755 index 00000000..4944a446 --- /dev/null +++ b/install_single_cartridge_with_data.sh @@ -0,0 +1,55 @@ +#!/bin/bash +# Install a specific cartridge and data cartridges to the default +# PICO-8 carts location +# This is required if you changed data or build for the first time for a given config, +# as install_single_cartridge.sh will not copy data along and is not reliable alone. + +# Usage: install_single_cartridge_with_data.sh config [png] +# cartridge_suffix 'titlemenu', 'ingame' or 'stage_clear' +# config build config (e.g. 'debug' or 'release'. Default: 'debug') +# png if passed, the .png cartridge is installed + +# Currently only supported on Linux + +# png option is legacy for p8tool. It works in theory but in practice, +# since p8tool fails to build .p8.png properly, png will be directly +# saved from PICO-8 with export_cartridge_release.p8 into PICO-8 carts folder + +# Configuration: paths +game_scripts_path="$(dirname "$0")" + +# check that source and output paths have been provided +if ! [[ $# -ge 1 && $# -le 3 ]] ; then + echo "build.sh takes 1 or 2 params, provided $#: + \$1: cartridge_suffix ('titlemenu', 'ingame' or 'stage_clear') + \$2: config ('debug', 'release', etc. Default: 'debug') + \$3: optional suffix ('png' for .png cartridge install)" + exit 1 +fi + +# Configuration: cartridge +version="5.1" +cartridge_suffix="$1"; shift +config="$1"; shift + +# option "png" will export the png cartridge +if [[ $1 = "png" ]] ; then + suffix=".png" +else + suffix="" +fi + +# note that we don't add the data/data_stage*.p8 cartridges because +# install_single_cartridge.sh for ingame will install all data cartridges anyway +# (and said script is really meant for built cartridges as it refers to build path) +"$game_scripts_path/install_single_cartridge.sh" "$cartridge_suffix" "$config" "$suffix" + +# recompute same install dirpath as used in install_single_cartridge.sh +# (no need to mkdir -p "${install_dirpath}", it must have been created in said script) +carts_dirpath="$HOME/.lexaloffle/pico-8/carts" +install_dirpath="${carts_dirpath}/picosonic/v${version}_${config}" + +# Also copy data cartridges +echo "Copying data cartridges data/data_*.p8 in ${install_dirpath} ..." +# trailing slash just to make sure we copy to a directory +cp data/data_*.p8 "${install_dirpath}/" diff --git a/sonic-2d-tech-demo.sublime-project b/sonic-2d-tech-demo.sublime-project index 4d90856c..cf56ac6c 100644 --- a/sonic-2d-tech-demo.sublime-project +++ b/sonic-2d-tech-demo.sublime-project @@ -104,22 +104,22 @@ "name": "Game: build cartridge titlemenu", "working_dir": "${project_path}", "shell": true, - "cmd": ["./build_and_install_single_cartridge.sh titlemenu debug"], + "cmd": ["./build_and_install_single_cartridge_with_data.sh titlemenu debug"], "file_regex": "(.*\\.sh)(?:\\: line )([\\d]+)(?:\\:)([\\d]+)? (.*)", "syntax": "Packages/Python/Python.sublime-syntax", "variants": [ { "name": "debug", - "cmd": ["./build_and_install_single_cartridge.sh titlemenu debug"] + "cmd": ["./build_and_install_single_cartridge_with_data.sh titlemenu debug"] }, { "name": "cheat", - "cmd": ["./build_and_install_single_cartridge.sh titlemenu cheat"] + "cmd": ["./build_and_install_single_cartridge_with_data.sh titlemenu cheat"] }, { "name": "release", - "cmd": ["./build_and_install_single_cartridge.sh titlemenu release"] + "cmd": ["./build_and_install_single_cartridge_with_data.sh titlemenu release"] } ] }, @@ -127,50 +127,50 @@ "name": "Game: build cartridge ingame", "working_dir": "${project_path}", "shell": true, - "cmd": ["./build_and_install_single_cartridge.sh ingame debug"], + "cmd": ["./build_and_install_single_cartridge_with_data.sh ingame debug"], "file_regex": "(.*\\.sh)(?:\\: line )([\\d]+)(?:\\:)([\\d]+)? (.*)", "syntax": "Packages/Python/Python.sublime-syntax", "variants": [ { "name": "debug", - "cmd": ["./build_and_install_single_cartridge.sh ingame debug"] + "cmd": ["./build_and_install_single_cartridge_with_data.sh ingame debug"] }, { "name": "debug-ultrafast", - "cmd": ["./build_and_install_single_cartridge.sh ingame debug-ultrafast"] + "cmd": ["./build_and_install_single_cartridge_with_data.sh ingame debug-ultrafast"] }, { "name": "cheat", - "cmd": ["./build_and_install_single_cartridge.sh ingame cheat"] + "cmd": ["./build_and_install_single_cartridge_with_data.sh ingame cheat"] }, { "name": "tuner", - "cmd": ["./build_and_install_single_cartridge.sh ingame tuner"] + "cmd": ["./build_and_install_single_cartridge_with_data.sh ingame tuner"] }, { "name": "ultrafast", - "cmd": ["./build_and_install_single_cartridge.sh ingame ultrafast"] + "cmd": ["./build_and_install_single_cartridge_with_data.sh ingame ultrafast"] }, { "name": "cheat-ultrafast", - "cmd": ["./build_and_install_single_cartridge.sh ingame cheat-ultrafast"] + "cmd": ["./build_and_install_single_cartridge_with_data.sh ingame cheat-ultrafast"] }, { "name": "sandbox", - "cmd": ["./build_and_install_single_cartridge.sh ingame sandbox"] + "cmd": ["./build_and_install_single_cartridge_with_data.sh ingame sandbox"] }, { "name": "assert", - "cmd": ["./build_and_install_single_cartridge.sh ingame assert"] + "cmd": ["./build_and_install_single_cartridge_with_data.sh ingame assert"] }, { "name": "profiler", - "cmd": ["./build_and_install_single_cartridge.sh ingame profiler"] + "cmd": ["./build_and_install_single_cartridge_with_data.sh ingame profiler"] }, { "name": "release", - "cmd": ["./build_and_install_single_cartridge.sh ingame release"] + "cmd": ["./build_and_install_single_cartridge_with_data.sh ingame release"] } ] }, @@ -178,22 +178,22 @@ "name": "Game: build cartridge stage_clear", "working_dir": "${project_path}", "shell": true, - "cmd": ["./build_and_install_single_cartridge.sh stage_clear debug"], + "cmd": ["./build_and_install_single_cartridge_with_data.sh stage_clear debug"], "file_regex": "(.*\\.sh)(?:\\: line )([\\d]+)(?:\\:)([\\d]+)? (.*)", "syntax": "Packages/Python/Python.sublime-syntax", "variants": [ { "name": "debug", - "cmd": ["./build_and_install_single_cartridge.sh stage_clear debug"] + "cmd": ["./build_and_install_single_cartridge_with_data.sh stage_clear debug"] }, { "name": "cheat", - "cmd": ["./build_and_install_single_cartridge.sh stage_clear cheat"] + "cmd": ["./build_and_install_single_cartridge_with_data.sh stage_clear cheat"] }, { "name": "release", - "cmd": ["./build_and_install_single_cartridge.sh stage_clear release"] + "cmd": ["./build_and_install_single_cartridge_with_data.sh stage_clear release"] } ] }, @@ -256,26 +256,26 @@ "name": "Game: build and run titlemenu", "working_dir": "${project_path}", "shell": true, - "cmd": ["./build_and_install_single_cartridge.sh titlemenu debug && (./pico-boots/scripts/reload.sh || ./run_cartridge.sh titlemenu debug)"], + "cmd": ["./build_and_install_single_cartridge_with_data.sh titlemenu debug && (./pico-boots/scripts/reload.sh || ./run_cartridge.sh titlemenu debug)"], "file_regex": "(.*\\.sh)(?:\\: line )([\\d]+)(?:\\:)([\\d]+)? (.*)", "syntax": "Packages/Python/Python.sublime-syntax", "variants": [ { "name": "debug", - "cmd": ["./build_and_install_single_cartridge.sh titlemenu debug && (./pico-boots/scripts/reload.sh || ./run_cartridge.sh titlemenu debug)"] + "cmd": ["./build_and_install_single_cartridge_with_data.sh titlemenu debug && (./pico-boots/scripts/reload.sh || ./run_cartridge.sh titlemenu debug)"] }, { "name": "cheat", - "cmd": ["./build_and_install_single_cartridge.sh titlemenu cheat && (./pico-boots/scripts/reload.sh || ./run_cartridge.sh titlemenu cheat)"] + "cmd": ["./build_and_install_single_cartridge_with_data.sh titlemenu cheat && (./pico-boots/scripts/reload.sh || ./run_cartridge.sh titlemenu cheat)"] }, { "name": "tuner", - "cmd": ["./build_and_install_single_cartridge.sh titlemenu tuner && (./pico-boots/scripts/reload.sh || ./run_cartridge.sh titlemenu tuner)"] + "cmd": ["./build_and_install_single_cartridge_with_data.sh titlemenu tuner && (./pico-boots/scripts/reload.sh || ./run_cartridge.sh titlemenu tuner)"] }, { "name": "release", - "cmd": ["./build_and_install_single_cartridge.sh titlemenu release && (./pico-boots/scripts/reload.sh || ./run_cartridge.sh titlemenu release)"] + "cmd": ["./build_and_install_single_cartridge_with_data.sh titlemenu release && (./pico-boots/scripts/reload.sh || ./run_cartridge.sh titlemenu release)"] }, ] }, @@ -283,50 +283,50 @@ "name": "Game: build and run ingame", "working_dir": "${project_path}", "shell": true, - "cmd": ["./build_and_install_single_cartridge.sh ingame debug && (./pico-boots/scripts/reload.sh || ./run_cartridge.sh ingame debug)"], + "cmd": ["./build_and_install_single_cartridge_with_data.sh ingame debug && (./pico-boots/scripts/reload.sh || ./run_cartridge.sh ingame debug)"], "file_regex": "(.*\\.sh)(?:\\: line )([\\d]+)(?:\\:)([\\d]+)? (.*)", "syntax": "Packages/Python/Python.sublime-syntax", "variants": [ { "name": "debug", - "cmd": ["./build_and_install_single_cartridge.sh ingame debug && (./pico-boots/scripts/reload.sh || ./run_cartridge.sh ingame debug)"] + "cmd": ["./build_and_install_single_cartridge_with_data.sh ingame debug && (./pico-boots/scripts/reload.sh || ./run_cartridge.sh ingame debug)"] }, { "name": "debug-ultrafast", - "cmd": ["./build_and_install_single_cartridge.sh ingame debug-ultrafast && (./pico-boots/scripts/reload.sh || ./run_cartridge.sh ingame debug-ultrafast)"] + "cmd": ["./build_and_install_single_cartridge_with_data.sh ingame debug-ultrafast && (./pico-boots/scripts/reload.sh || ./run_cartridge.sh ingame debug-ultrafast)"] }, { "name": "cheat", - "cmd": ["./build_and_install_single_cartridge.sh ingame cheat && (./pico-boots/scripts/reload.sh || ./run_cartridge.sh ingame cheat)"] + "cmd": ["./build_and_install_single_cartridge_with_data.sh ingame cheat && (./pico-boots/scripts/reload.sh || ./run_cartridge.sh ingame cheat)"] }, { "name": "tuner", - "cmd": ["./build_and_install_single_cartridge.sh ingame tuner && (./pico-boots/scripts/reload.sh || ./run_cartridge.sh ingame tuner)"] + "cmd": ["./build_and_install_single_cartridge_with_data.sh ingame tuner && (./pico-boots/scripts/reload.sh || ./run_cartridge.sh ingame tuner)"] }, { "name": "ultrafast", - "cmd": ["./build_and_install_single_cartridge.sh ingame ultrafast && (./pico-boots/scripts/reload.sh || ./run_cartridge.sh ingame ultrafast)"] + "cmd": ["./build_and_install_single_cartridge_with_data.sh ingame ultrafast && (./pico-boots/scripts/reload.sh || ./run_cartridge.sh ingame ultrafast)"] }, { "name": "cheat-ultrafast", - "cmd": ["./build_and_install_single_cartridge.sh ingame cheat-ultrafast && (./pico-boots/scripts/reload.sh || ./run_cartridge.sh ingame cheat-ultrafast)"] + "cmd": ["./build_and_install_single_cartridge_with_data.sh ingame cheat-ultrafast && (./pico-boots/scripts/reload.sh || ./run_cartridge.sh ingame cheat-ultrafast)"] }, { "name": "sandbox", - "cmd": ["./build_and_install_single_cartridge.sh ingame sandbox && (./pico-boots/scripts/reload.sh || ./run_cartridge.sh ingame sandbox)"] + "cmd": ["./build_and_install_single_cartridge_with_data.sh ingame sandbox && (./pico-boots/scripts/reload.sh || ./run_cartridge.sh ingame sandbox)"] }, { "name": "assert", - "cmd": ["./build_and_install_single_cartridge.sh ingame assert && (./pico-boots/scripts/reload.sh || ./run_cartridge.sh ingame assert)"] + "cmd": ["./build_and_install_single_cartridge_with_data.sh ingame assert && (./pico-boots/scripts/reload.sh || ./run_cartridge.sh ingame assert)"] }, { "name": "profiler", - "cmd": ["./build_and_install_single_cartridge.sh ingame profiler && (./pico-boots/scripts/reload.sh || ./run_cartridge.sh ingame profiler)"] + "cmd": ["./build_and_install_single_cartridge_with_data.sh ingame profiler && (./pico-boots/scripts/reload.sh || ./run_cartridge.sh ingame profiler)"] }, { "name": "release", - "cmd": ["./build_and_install_single_cartridge.sh ingame release && (./pico-boots/scripts/reload.sh || ./run_cartridge.sh ingame release)"] + "cmd": ["./build_and_install_single_cartridge_with_data.sh ingame release && (./pico-boots/scripts/reload.sh || ./run_cartridge.sh ingame release)"] }, ] }, @@ -334,22 +334,26 @@ "name": "Game: build and run stage_clear", "working_dir": "${project_path}", "shell": true, - "cmd": ["./build_and_install_single_cartridge.sh stage_clear debug && (./pico-boots/scripts/reload.sh || ./run_cartridge.sh stage_clear debug)"], + "cmd": ["./build_and_install_single_cartridge_with_data.sh stage_clear debug && (./pico-boots/scripts/reload.sh || ./run_cartridge.sh stage_clear debug)"], "file_regex": "(.*\\.sh)(?:\\: line )([\\d]+)(?:\\:)([\\d]+)? (.*)", "syntax": "Packages/Python/Python.sublime-syntax", "variants": [ { "name": "debug", - "cmd": ["./build_and_install_single_cartridge.sh stage_clear debug && (./pico-boots/scripts/reload.sh || ./run_cartridge.sh stage_clear debug)"] + "cmd": ["./build_and_install_single_cartridge_with_data.sh stage_clear debug && (./pico-boots/scripts/reload.sh || ./run_cartridge.sh stage_clear debug)"] }, { "name": "cheat", - "cmd": ["./build_and_install_single_cartridge.sh stage_clear cheat && (./pico-boots/scripts/reload.sh || ./run_cartridge.sh stage_clear cheat)"] + "cmd": ["./build_and_install_single_cartridge_with_data.sh stage_clear cheat && (./pico-boots/scripts/reload.sh || ./run_cartridge.sh stage_clear cheat)"] + }, + { + "name": "tuner", + "cmd": ["./build_and_install_single_cartridge_with_data.sh stage_clear tuner && (./pico-boots/scripts/reload.sh || ./run_cartridge.sh stage_clear tuner)"] }, { "name": "release", - "cmd": ["./build_and_install_single_cartridge.sh stage_clear release && (./pico-boots/scripts/reload.sh || ./run_cartridge.sh stage_clear release)"] + "cmd": ["./build_and_install_single_cartridge_with_data.sh stage_clear release && (./pico-boots/scripts/reload.sh || ./run_cartridge.sh stage_clear release)"] }, ] }, @@ -370,6 +374,10 @@ "name": "cheat", "cmd": ["./build_and_install_all_cartridges.sh cheat && (./pico-boots/scripts/reload.sh || ./run_cartridge.sh titlemenu cheat)"] }, + { + "name": "tuner", + "cmd": ["./build_and_install_all_cartridges.sh tuner && (./pico-boots/scripts/reload.sh || ./run_cartridge.sh titlemenu tuner)"] + }, { "name": "release", "cmd": ["./build_and_install_all_cartridges.sh release && (./pico-boots/scripts/reload.sh || ./run_cartridge.sh titlemenu release)"] @@ -476,6 +484,10 @@ "name": "stage_clear cheat", "cmd": ["./run_cartridge.sh stage_clear cheat"] }, + { + "name": "stage_clear tuner", + "cmd": ["./run_cartridge.sh stage_clear tuner"] + }, { "name": "stage_clear release", "cmd": ["./run_cartridge.sh stage_clear release"] @@ -495,120 +507,120 @@ ] }, { - "name": "Game: install cartridge titlemenu", + "name": "Game: install cartridge titlemenu (with data)", "working_dir": "${project_path}", "shell": true, - "cmd": ["./install_single_cartridge.sh titlemenu debug"], + "cmd": ["./install_single_cartridge_with_data.sh titlemenu debug"], "syntax": "Packages/Python/Python.sublime-syntax", "variants": [ { "name": "debug", - "cmd": ["./install_single_cartridge.sh titlemenu debug"] + "cmd": ["./install_single_cartridge_with_data.sh titlemenu debug"] }, { "name": "cheat", - "cmd": ["./install_single_cartridge.sh titlemenu cheat"] + "cmd": ["./install_single_cartridge_with_data.sh titlemenu cheat"] }, { "name": "release", - "cmd": ["./install_single_cartridge.sh titlemenu release"] + "cmd": ["./install_single_cartridge_with_data.sh titlemenu release"] }, { "name": "debug (png)", - "cmd": ["./install_single_cartridge.sh titlemenu debug png"] + "cmd": ["./install_single_cartridge_with_data.sh titlemenu debug png"] }, { "name": "release (png)", - "cmd": ["./install_single_cartridge.sh titlemenu release png"] + "cmd": ["./install_single_cartridge_with_data.sh titlemenu release png"] } ] }, { - "name": "Game: install cartridge ingame (with stage maps and bgm)", + "name": "Game: install cartridge ingame (with data)", "working_dir": "${project_path}", "shell": true, - "cmd": ["./install_single_cartridge.sh ingame debug"], + "cmd": ["./install_single_cartridge_with_data.sh ingame debug"], "syntax": "Packages/Python/Python.sublime-syntax", "variants": [ { "name": "debug", - "cmd": ["./install_single_cartridge.sh ingame debug"] + "cmd": ["./install_single_cartridge_with_data.sh ingame debug"] }, { "name": "debug-ultrafast", - "cmd": ["./install_single_cartridge.sh ingame debug-ultrafast"] + "cmd": ["./install_single_cartridge_with_data.sh ingame debug-ultrafast"] }, { "name": "cheat", - "cmd": ["./install_single_cartridge.sh ingame cheat"] + "cmd": ["./install_single_cartridge_with_data.sh ingame cheat"] }, { "name": "tuner", - "cmd": ["./install_single_cartridge.sh ingame tuner"] + "cmd": ["./install_single_cartridge_with_data.sh ingame tuner"] }, { "name": "ultrafast", - "cmd": ["./install_single_cartridge.sh ingame ultrafast"] + "cmd": ["./install_single_cartridge_with_data.sh ingame ultrafast"] }, { "name": "cheat-ultrafast", - "cmd": ["./install_single_cartridge.sh ingame cheat-ultrafast"] + "cmd": ["./install_single_cartridge_with_data.sh ingame cheat-ultrafast"] }, { "name": "sandbox", - "cmd": ["./install_single_cartridge.sh ingame sandbox"] + "cmd": ["./install_single_cartridge_with_data.sh ingame sandbox"] }, { "name": "assert", - "cmd": ["./install_single_cartridge.sh ingame assert"] + "cmd": ["./install_single_cartridge_with_data.sh ingame assert"] }, { "name": "profiler", - "cmd": ["./install_single_cartridge.sh ingame profiler"] + "cmd": ["./install_single_cartridge_with_data.sh ingame profiler"] }, { "name": "release", - "cmd": ["./install_single_cartridge.sh ingame release"] + "cmd": ["./install_single_cartridge_with_data.sh ingame release"] }, { "name": "cheat (png)", - "cmd": ["./install_single_cartridge.sh ingame cheat png"] + "cmd": ["./install_single_cartridge_with_data.sh ingame cheat png"] }, { "name": "release (png)", - "cmd": ["./install_single_cartridge.sh ingame release png"] + "cmd": ["./install_single_cartridge_with_data.sh ingame release png"] } ] }, { - "name": "Game: install cartridge stage_clear", + "name": "Game: install cartridge stage_clear (with data)", "working_dir": "${project_path}", "shell": true, - "cmd": ["./install_single_cartridge.sh stage_clear debug"], + "cmd": ["./install_single_cartridge_with_data.sh stage_clear debug"], "syntax": "Packages/Python/Python.sublime-syntax", "variants": [ { "name": "debug", - "cmd": ["./install_single_cartridge.sh stage_clear debug"] + "cmd": ["./install_single_cartridge_with_data.sh stage_clear debug"] }, { "name": "cheat", - "cmd": ["./install_single_cartridge.sh stage_clear cheat"] + "cmd": ["./install_single_cartridge_with_data.sh stage_clear cheat"] }, { "name": "release", - "cmd": ["./install_single_cartridge.sh stage_clear release"] + "cmd": ["./install_single_cartridge_with_data.sh stage_clear release"] }, { "name": "cheat (png)", - "cmd": ["./install_single_cartridge.sh stage_clear cheat png"] + "cmd": ["./install_single_cartridge_with_data.sh stage_clear cheat png"] }, { "name": "release (png)", - "cmd": ["./install_single_cartridge.sh stage_clear release png"] + "cmd": ["./install_single_cartridge_with_data.sh stage_clear release png"] } ] }, From ba4cd0ca2cbe686ccfcc6241b575aacfda245665 Mon Sep 17 00:00:00 2001 From: huulong Date: Tue, 22 Dec 2020 22:01:14 +0100 Subject: [PATCH 17/91] [STAGE CLEAR] Fixed missed emeralds being drawn below overlay by adding phase system to swap layers Increased missed emeralds radius Known issue: currently used for picked emeralds too, they overlap emerald cross --- src/resources/visual_stage_clear_addon.lua | 2 +- src/stage_clear/stage_clear_state.lua | 34 +++++++++++++++++----- 2 files changed, 27 insertions(+), 9 deletions(-) diff --git a/src/resources/visual_stage_clear_addon.lua b/src/resources/visual_stage_clear_addon.lua index 204d91f8..deefbe3c 100644 --- a/src/resources/visual_stage_clear_addon.lua +++ b/src/resources/visual_stage_clear_addon.lua @@ -4,7 +4,7 @@ local sprite_data = require("engine/render/sprite_data") local stage_clear_visual = { fadeout_zigzag_width = 11, -- doesn't include pixel 0, so actually count +1 pixel in total - missed_emeralds_radius = 10 + missed_emeralds_radius = 15 } -- visuals for stage_clear only diff --git a/src/stage_clear/stage_clear_state.lua b/src/stage_clear/stage_clear_state.lua index f03508ed..eec095aa 100644 --- a/src/stage_clear/stage_clear_state.lua +++ b/src/stage_clear/stage_clear_state.lua @@ -45,6 +45,10 @@ function stage_clear_state:init() self.result_show_emerald_set_by_number = {} -- [number] = nil means don't show it self.result_emerald_brightness_levels = {} -- for emerald bright animation (nil means 0) + -- phase 0: stage result + -- phase 1: retry menu + self.phase = 0 + -- self.retry_menu starts nil, only created when it must be shown end @@ -94,11 +98,21 @@ function stage_clear_state:render() visual_stage.render_background(vector(3392, 328)) self:render_stage_elements() - -- draw either picked or missed emeralds - self:render_emerald_cross() + -- hack to draw fade-out on top of picked emeralds during phase 0 (stage result) + -- then missed emeralds on top of missed emeralds during phase 1 (retry menu) + if self.phase == 0 then + -- draw either picked or missed emeralds + self:render_emerald_cross() - -- draw overlay on top to hide result widgets - self:render_overlay() + -- draw overlay on top to hide result widgets + self:render_overlay() + else + -- draw overlay on top to hide result widgets + self:render_overlay() + + -- draw either picked or missed emeralds + self:render_emerald_cross() + end -- exceptionally draw menu above overlay, because this overlay is used as background -- for retry menu @@ -393,6 +407,9 @@ function stage_clear_state:zigzag_fade_out_async() end function stage_clear_state:show_retry_screen() + -- enter phase 1: retry menu + self.phase = 1 + -- at the end of the zigzag, clear the emerald assessment widgets which are now completely hidden, -- but keep the full black screen rectangle as background for retry screen local try_again_label = label("try again?", vector(41, 34), colors.white) @@ -437,9 +454,9 @@ function stage_clear_state:render_environment_foreground() map(0, 0, 0, 0, map_region_tile_width, map_region_tile_height, sprite_masks.foreground) end --- render the goal plate upper body (similar to stage_state equivalent, but don't check for goal_plate) +-- render the goal plate upper body (similar to stage_state equivalent) function stage_clear_state:render_goal_plate() - assert(self.goal_plate) + assert(self.goal_plate, "stage_clear_state:render_goal_plate: no goal plate spawned in stage") self:set_camera_with_origin() self.goal_plate:render() @@ -473,8 +490,9 @@ function stage_clear_state:draw_emeralds_around_cross(x, y) -- self.result_show_emerald_set_by_number[num] is only set to true when -- we have picked emerald, so no need to check self.picked_emerald_numbers_set again if self.result_show_emerald_set_by_number[num] then - local draw_position = vector(x + visual.missed_emeralds_radius * cos(0.25 - (num - 1) / 8), - y + visual.missed_emeralds_radius * sin(0.25 - (num - 1) / 8)) + local radius = visual.missed_emeralds_radius + local draw_position = vector(x + radius * cos(0.25 - (num - 1) / 8), + y + radius * sin(0.25 - (num - 1) / 8)) emerald.draw(num, draw_position, self.result_emerald_brightness_levels[num]) end end From ef0f7d7640a737cbf2a0e890a40c7398415c273f Mon Sep 17 00:00:00 2001 From: huulong Date: Tue, 22 Dec 2020 22:22:35 +0100 Subject: [PATCH 18/91] [BUILD] Fixed install_all_cartridges script by adding variables for path --- install_all_cartridges.sh | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/install_all_cartridges.sh b/install_all_cartridges.sh index d271d768..c46109b6 100755 --- a/install_all_cartridges.sh +++ b/install_all_cartridges.sh @@ -25,6 +25,8 @@ if ! [[ $# -ge 1 && $# -le 2 ]] ; then exit 1 fi +# Configuration: cartridge +version="5.1" config="$1"; shift # option "png" will export the png cartridge @@ -40,6 +42,11 @@ for cartridge in $cartridge_list; do "$game_scripts_path/install_single_cartridge.sh" "$cartridge" "$config" "$suffix" done +# recompute same install dirpath as used in install_single_cartridge.sh +# (no need to mkdir -p "${install_dirpath}", it must have been created in said script) +carts_dirpath="$HOME/.lexaloffle/pico-8/carts" +install_dirpath="${carts_dirpath}/picosonic/v${version}_${config}" + # Also copy data cartridges echo "Copying data cartridges data/data_*.p8 in ${install_dirpath} ..." # trailing slash just to make sure we copy to a directory From cf180a7b4911e659999aa9cc3341f487c3081486 Mon Sep 17 00:00:00 2001 From: huulong Date: Tue, 22 Dec 2020 22:26:34 +0100 Subject: [PATCH 19/91] [STAGE CLEAR] Removed emerald cross entirely It was not too pretty and overlapped emeralds arranged on circle Besides, it was in visual_common even after stage_clear split, which means it was consuming many character in ingame cartridge Chars / Compressed chars for ingame release -> 48946, 17112 (109%) --- src/resources/visual_common.lua | 117 -------------------- src/stage_clear/stage_clear_state.lua | 35 ++---- src/stage_clear/stage_clear_state_utest.lua | 20 ++-- 3 files changed, 16 insertions(+), 156 deletions(-) diff --git a/src/resources/visual_common.lua b/src/resources/visual_common.lua index b2ce92c6..7a97d341 100644 --- a/src/resources/visual_common.lua +++ b/src/resources/visual_common.lua @@ -30,36 +30,9 @@ local visual = { {colors.green, colors.dark_green}, {colors.yellow, colors.orange}, {colors.orange, colors.brown}, - }, - - -- for flash appearance animations, we need a palette that maps colors to their bright equivalent - -- since we need an animation we define one per step - -- we only use 2 steps: very bright and bright (use nor color swap to revert to original colors) - bright_to_normal_palette_swap_sequence_by_original_color = { - [colors.dark_gray] = {colors.white, colors.light_gray}, - [colors.light_gray] = {colors.white, colors.white}, - [colors.red] = {colors.white, colors.white}, - [colors.peach] = {colors.white, colors.white}, - [colors.pink] = {colors.white, colors.white}, - [colors.indigo] = {colors.white, colors.white}, - [colors.blue] = {colors.white, colors.white}, - [colors.green] = {colors.white, colors.white}, - [colors.yellow] = {colors.white, colors.white}, - [colors.orange] = {colors.white, colors.white}, - -- we don't define mappings for emerald darker colors, as we want custom - -- dark to bright mapping ware of the original emerald color (see assess_result_async) } } --- transform bright_to_normal_palette_swap_sequence_by_original_color, a table of sequence of new color per original color --- into a sequence (over steps 1, 2) of color palette swap (usable with pal) --- by extracting new color i for each original color, for each step -visual.bright_to_normal_palette_swap_by_original_color_sequence = transform({1, 2}, function (step) - return transform(visual.bright_to_normal_palette_swap_sequence_by_original_color, function (color_sequence) - return color_sequence[step] - end) -end) - local sprite_data_t = { -- COMMON INITIAL SPRITES --#if mouse @@ -95,94 +68,4 @@ visual.animated_sprite_data_t = { }, } - --- drawing helpers (titlemenu will use them too) - --- render a cross at center (x, y) with branches of base length from center (excluding spike) --- branch_base_length, half-width branch_half_width (width 2 * branch_half_width + 1 adding center pixel), --- spiky ends and color c -function visual.draw_cross(x, y, branch_base_length, branch_half_width, c) - -- horizontal white rectangle - rectfill(x - branch_base_length, y - branch_half_width, x + branch_base_length, y + branch_half_width, c) - -- vertical white rectangle - rectfill(x - branch_half_width, y - branch_base_length, x + branch_half_width, y + branch_base_length, c) - -- left and right white triangle spike (branch_half_width lines, last one being a dot) - for sign = -1, 1, 2 do - for i = 1, branch_half_width do - line(x + sign * (branch_base_length + i), y - branch_half_width + i, - x + sign * (branch_base_length + i), y + branch_half_width - i, c) - end - end - -- up and down white triangle spike (branch_half_width lines, last one being a dot) - for sign = -1, 1, 2 do - for i = 1, branch_half_width do - line(x - branch_half_width + i, y + sign * (branch_base_length + i), - x + branch_half_width - i, y + sign * (branch_base_length + i), c) - end - end -end - --- render the emerald cross base and every picked emeralds --- (x, y) is at cross center -function visual.draw_emerald_cross_base(x, y, palette_swap_table) - -- we prefer using pal() to manually assign palette_swap_table[c] to internal colors - -- because when we want implicit default colors and the swap table is empty, - -- the former will preserve colors, while the latter will interpret nil as 0 - -- also, it allows us to swap other colors than gray too - pal(palette_swap_table) - - local internal_color1 = colors.dark_gray - local internal_color2 = colors.light_gray - - visual.draw_cross(x, y, 11, 3, colors.white) - -- dark and light gray crosses have the same base length! - visual.draw_cross(x, y, 10, 2, internal_color1) - visual.draw_cross(x, y, 10, 1, internal_color2) - - -- dark gray marks - -- horizontal line (will have holes) - line(x - 10, y, x + 10, y, internal_color1) - -- vertical - line(x, y - 10, x, y + 10, internal_color1) - - -- diagonals (faster to draw 4 diagonals than 4 mini orthogonal segments + - -- 4 dots in the diagonal directions to make cross smooth near the center) - -- from top-left one, CW - line(x - 5, y - 1, x - 1, y - 5, internal_color1) - line(x + 1, y - 5, x + 5, y - 1, internal_color1) - line(x + 5, y + 1, x + 1, y + 5, internal_color1) - line(x - 1, y + 5, x - 5, y + 1, internal_color1) - - -- light gray holes - -- again, the trick is to use diagonals rather than dots to cover 3 pixels at once - -- (or only 2 when overlapping) - -- from top-left, then CW - -- compared to above, get closer to center just by remove -1/+1 - line(x - 5, y , x , y - 5, internal_color2) - line(x , y - 5, x + 5, y , internal_color2) - line(x + 5, y , x , y + 5, internal_color2) - line(x , y + 5, x - 5, y , internal_color2) - - -- we left 4 dots placed on square corners in the middle - -- cover them all with a square at once - rectfill(x - 2, y - 2, x + 2, y + 2, internal_color2) - - -- make a roundy square rotated by 45 degrees with a circle - circ(x, y, 2, internal_color1) - - -- draw the PICO-8 logo inside - -- white square, will be covered by colors to make a cross - rectfill(x - 1, y - 1, x + 1, y + 1, colors.white) - pset(x , y - 2, colors.red) - pset(x + 1, y - 1, colors.peach) - pset(x + 2, y , colors.pink) - pset(x + 1, y + 1, colors.indigo) - pset(x , y + 2, colors.blue) - pset(x - 1, y + 1, colors.green) - pset(x - 2, y , colors.yellow) - pset(x - 1, y - 1, colors.orange) - - pal() -end - return visual diff --git a/src/stage_clear/stage_clear_state.lua b/src/stage_clear/stage_clear_state.lua index eec095aa..b7cf7b40 100644 --- a/src/stage_clear/stage_clear_state.lua +++ b/src/stage_clear/stage_clear_state.lua @@ -40,8 +40,6 @@ function stage_clear_state:init() -- emerald variables for result UI animation self.picked_emerald_numbers_set = {} self.picked_emerald_count = 0 - self.result_show_emerald_cross_base = false - self.result_emerald_cross_palette_swap_table = {} -- for emerald cross bright animation self.result_show_emerald_set_by_number = {} -- [number] = nil means don't show it self.result_emerald_brightness_levels = {} -- for emerald bright animation (nil means 0) @@ -102,7 +100,7 @@ function stage_clear_state:render() -- then missed emeralds on top of missed emeralds during phase 1 (retry menu) if self.phase == 0 then -- draw either picked or missed emeralds - self:render_emerald_cross() + self:render_emeralds() -- draw overlay on top to hide result widgets self:render_overlay() @@ -111,7 +109,7 @@ function stage_clear_state:render() self:render_overlay() -- draw either picked or missed emeralds - self:render_emerald_cross() + self:render_emeralds() end -- exceptionally draw menu above overlay, because this overlay is used as background @@ -283,17 +281,6 @@ function stage_clear_state:show_result_async() -- make text enter screen from right to left (starts on screen edge, so 128) ui_animation.move_drawables_on_coord_async("x", {stage_label}, {0}, 128, 40, 20) - - -- show emerald cross - self.result_show_emerald_cross_base = true - - for step = 1, 2 do - self.result_emerald_cross_palette_swap_table = visual.bright_to_normal_palette_swap_by_original_color_sequence[step] - yield_delay(10) -- duration of a step - end - - -- finish with normal colors - clear_table(self.result_emerald_cross_palette_swap_table) end function stage_clear_state:assess_result_async() @@ -392,7 +379,6 @@ function stage_clear_state:zigzag_fade_out_async() -- at the end of the zigzag, clear the emerald assessment widgets which are now completely hidden -- as well as members that draw custom items, except for actual emeralds as we'll draw the missing emeralds more below self.result_overlay:clear_drawables() - self.result_show_emerald_cross_base = false -- just re-add the black rectangle as background for the retry menu -- (no need for zigzag edge itself, since the rectangle body now covers the whole screen) @@ -468,21 +454,16 @@ function stage_clear_state:render_overlay() self.result_overlay:draw() end --- render the emerald cross base and every picked emeralds -function stage_clear_state:render_emerald_cross() +-- render every picked/missed emeralds at fixed screen position +function stage_clear_state:render_emeralds() camera() - if self.result_show_emerald_cross_base then - visual.draw_emerald_cross_base(64, 64, self.result_emerald_cross_palette_swap_table) - end - - self:draw_emeralds_around_cross(64, 64) + self:draw_emeralds(64, 64) end --- render the emerald cross base and every picked emeralds --- (x, y) is at cross center -function stage_clear_state:draw_emeralds_around_cross(x, y) - -- draw emeralds around the cross, from top, CW +-- draw picked/missed emeralds on an invisible circle centered on (x, y) +function stage_clear_state:draw_emeralds(x, y) + -- draw emeralds around the clock, from top, CW -- usually we iterate from 1 to #self.spawned_emerald_locations -- but here we obviously only defined 8 relative positions, -- so just iterate to 8 (but if you happen to only place 7, you'll need to update that) diff --git a/src/stage_clear/stage_clear_state_utest.lua b/src/stage_clear/stage_clear_state_utest.lua index b3da6127..e8a3b478 100644 --- a/src/stage_clear/stage_clear_state_utest.lua +++ b/src/stage_clear/stage_clear_state_utest.lua @@ -54,8 +54,6 @@ describe('stage_clear_state', function () assert.are_same({ ':stage_clear', overlay(), - false, - {}, {}, {}, }, @@ -63,8 +61,6 @@ describe('stage_clear_state', function () state.type, -- result UI animation only (async methods changing this won't be fully tested) state.result_overlay, - state.result_show_emerald_cross_base, - state.result_emerald_cross_palette_swap_table, state.result_show_emerald_set_by_number, state.result_emerald_brightness_levels }) @@ -300,24 +296,24 @@ describe('stage_clear_state', function () stub(visual_stage, "render_background") stub(stage_clear_state, "render_stage_elements") stub(stage_clear_state, "render_overlay") - stub(stage_clear_state, "render_emerald_cross") + stub(stage_clear_state, "render_emeralds") end) teardown(function () visual_stage.render_background:revert() stage_clear_state.render_stage_elements:revert() stage_clear_state.render_overlay:revert() - stage_clear_state.render_emerald_cross:revert() + stage_clear_state.render_emeralds:revert() end) after_each(function () visual_stage.render_background:clear() stage_clear_state.render_stage_elements:clear() stage_clear_state.render_overlay:clear() - stage_clear_state.render_emerald_cross:clear() + stage_clear_state.render_emeralds:clear() end) - it('should call render_background, render_stage_elements, render_overlay, render_emerald_cross', function () + it('should call render_background, render_stage_elements, render_overlay, render_emeralds', function () state:render() assert.spy(visual_stage.render_background).was_called(1) assert.spy(visual_stage.render_background).was_called_with(vector(3392, 328)) @@ -325,8 +321,8 @@ describe('stage_clear_state', function () assert.spy(stage_clear_state.render_stage_elements).was_called_with(match.ref(state)) assert.spy(stage_clear_state.render_overlay).was_called(1) assert.spy(stage_clear_state.render_overlay).was_called_with(match.ref(state)) - assert.spy(stage_clear_state.render_emerald_cross).was_called(1) - assert.spy(stage_clear_state.render_emerald_cross).was_called_with(match.ref(state)) + assert.spy(stage_clear_state.render_emeralds).was_called(1) + assert.spy(stage_clear_state.render_emeralds).was_called_with(match.ref(state)) end) end) -- state.render @@ -598,8 +594,8 @@ describe('stage_clear_state', function () describe('extra render methods (no-crash only)', function () - it('render_emerald_cross should not crash', function () - state:render_emerald_cross() + it('render_emeralds should not crash', function () + state:render_emeralds() end) end) From c614c277750292e84d60cbec7b446dd71bedac47 Mon Sep 17 00:00:00 2001 From: huulong Date: Tue, 22 Dec 2020 22:44:14 +0100 Subject: [PATCH 20/91] [STAGE CLEAR] Added delay before showing retry screen content --- src/data/stage_clear_data.lua | 2 ++ src/stage_clear/stage_clear_state.lua | 4 +++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/data/stage_clear_data.lua b/src/data/stage_clear_data.lua index 69d03eff..464072eb 100644 --- a/src/data/stage_clear_data.lua +++ b/src/data/stage_clear_data.lua @@ -9,4 +9,6 @@ return { -- delay between emerald assessment animation has ended, and fade out to retry screen starts (s) show_emerald_assessment_duration = 1.0, + -- delay after zigzag fade out, before showing retry screen content + delay_after_zigzag_fadeout = 1.0, } diff --git a/src/stage_clear/stage_clear_state.lua b/src/stage_clear/stage_clear_state.lua index b7cf7b40..b780ef61 100644 --- a/src/stage_clear/stage_clear_state.lua +++ b/src/stage_clear/stage_clear_state.lua @@ -226,9 +226,12 @@ function stage_clear_state:play_stage_clear_sequence_async() -- play result UI "calculation" (we don't have score so it's just checking -- if we have all the emeralds) self:assess_result_async() + self.app:yield_delay_s(stage_clear_data.show_emerald_assessment_duration) -- fade out and show retry screen self:zigzag_fade_out_async() + self.app:yield_delay_s(stage_clear_data.delay_after_zigzag_fadeout) + self:show_retry_screen() end @@ -349,7 +352,6 @@ function stage_clear_state:assess_result_async() -- apply offset for shorter label to start and end x -- animation takes 20 frames ui_animation.move_drawables_on_coord_async("x", {sonic_label, emerald_label}, {0, 24}, -88 + x_offset, 20 + x_offset, 20) - self.app:yield_delay_s(stage_clear_data.show_emerald_assessment_duration) end -- drawable for the right part of the fade-out layer (the body will be filled with a separate rectangle) From e26ed459f20d5dca4fc3ca72796e5e9f3a97f6e4 Mon Sep 17 00:00:00 2001 From: huulong Date: Tue, 22 Dec 2020 22:47:56 +0100 Subject: [PATCH 21/91] [STAGE CLEAR] More time to see X emeralds got, faster zigzag fade-out --- src/data/stage_clear_data.lua | 7 +++++-- src/stage_clear/stage_clear_state.lua | 2 +- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/data/stage_clear_data.lua b/src/data/stage_clear_data.lua index 464072eb..3724f48a 100644 --- a/src/data/stage_clear_data.lua +++ b/src/data/stage_clear_data.lua @@ -7,8 +7,11 @@ return { stage_clear_duration = 364, -- delay between emerald assessment animation has ended, and fade out to retry screen starts (s) - show_emerald_assessment_duration = 1.0, + show_emerald_assessment_duration = 2.0, - -- delay after zigzag fade out, before showing retry screen content + -- duration of zigzag fadeout (frames) + zigzag_fadeout_duration = 18, + + -- delay after zigzag fade out, before showing retry screen content (s) delay_after_zigzag_fadeout = 1.0, } diff --git a/src/stage_clear/stage_clear_state.lua b/src/stage_clear/stage_clear_state.lua index b780ef61..16ac679c 100644 --- a/src/stage_clear/stage_clear_state.lua +++ b/src/stage_clear/stage_clear_state.lua @@ -376,7 +376,7 @@ function stage_clear_state:zigzag_fade_out_async() -- make rectangle with zigzag edge enter the screen from the left -- note that we finish at 128 and not 127 so the zigzag fully goes out of the screen to the right, -- and the bg_rect fully covers the screen, ready to be used as background - ui_animation.move_drawables_on_coord_async("x", {bg_rect, zigzag_drawable}, {-128, 0}, - visual.fadeout_zigzag_width, 128, 36) + ui_animation.move_drawables_on_coord_async("x", {bg_rect, zigzag_drawable}, {-128, 0}, - visual.fadeout_zigzag_width, 128, stage_clear_data.zigzag_fadeout_duration) -- at the end of the zigzag, clear the emerald assessment widgets which are now completely hidden -- as well as members that draw custom items, except for actual emeralds as we'll draw the missing emeralds more below From 21f3342a3bfe7a14504450b1988c0987a03ab52d Mon Sep 17 00:00:00 2001 From: huulong Date: Tue, 22 Dec 2020 23:38:35 +0100 Subject: [PATCH 22/91] [STAGE CLEAR] Fade in whole screen by color darkness --- src/resources/visual_stage_clear_addon.lua | 24 +++++- src/stage_clear/stage_clear_state.lua | 91 +++++++++++++--------- 2 files changed, 78 insertions(+), 37 deletions(-) diff --git a/src/resources/visual_stage_clear_addon.lua b/src/resources/visual_stage_clear_addon.lua index deefbe3c..7d8a924e 100644 --- a/src/resources/visual_stage_clear_addon.lua +++ b/src/resources/visual_stage_clear_addon.lua @@ -4,7 +4,29 @@ local sprite_data = require("engine/render/sprite_data") local stage_clear_visual = { fadeout_zigzag_width = 11, -- doesn't include pixel 0, so actually count +1 pixel in total - missed_emeralds_radius = 15 + missed_emeralds_radius = 15, + + -- use this table with pal() to recolor things darker (level 1-2, don't use at level 0) + -- we skip black which doesn't get darker, so we can start at index 1 and we don't need + -- explicit keys at all + swap_palette_by_darkness = { + -- [colors.black] = {colors.black, colors.black}, + --[[ [colors.dark_blue] = ]] {colors.black, colors.black}, + --[[ [colors.dark_purple] = ]] {colors.black, colors.black}, + --[[ [colors.dark_green] = ]] {colors.black, colors.black}, + --[[ [colors.brown] = ]] {colors.black, colors.black}, + --[[ [colors.dark_gray] = ]] {colors.black, colors.black}, + --[[ [colors.light_gray] = ]] {colors.dark_gray, colors.black}, + --[[ [colors.white] = ]] {colors.light_gray, colors.dark_gray}, + --[[ [colors.red] = ]] {colors.dark_purple, colors.black}, + --[[ [colors.orange] = ]] {colors.brown, colors.black}, + --[[ [colors.yellow] = ]] {colors.orange, colors.brown}, + --[[ [colors.green] = ]] {colors.dark_green, colors.black}, + --[[ [colors.blue] = ]] {colors.dark_blue, colors.black}, + --[[ [colors.indigo] = ]] {colors.dark_gray, colors.black}, + --[[ [colors.pink] = ]] {colors.dark_purple, colors.black}, + --[[ [colors.peach] = ]] {colors.orange, colors.brown} + } } -- visuals for stage_clear only diff --git a/src/stage_clear/stage_clear_state.lua b/src/stage_clear/stage_clear_state.lua index 16ac679c..4d0f8623 100644 --- a/src/stage_clear/stage_clear_state.lua +++ b/src/stage_clear/stage_clear_state.lua @@ -34,6 +34,13 @@ stage_clear_state.retry_items = transform({ function stage_clear_state:init() -- gamestate.init(self) -- kept for expliciteness, but does nothing + -- phase 0: stage result + -- phase 1: retry menu + self.phase = 0 + + -- global darkness affect post-draw screen palette + self.global_darkness = 0 + -- result (stage clear) overlay self.result_overlay = overlay() @@ -43,10 +50,6 @@ function stage_clear_state:init() self.result_show_emerald_set_by_number = {} -- [number] = nil means don't show it self.result_emerald_brightness_levels = {} -- for emerald bright animation (nil means 0) - -- phase 0: stage result - -- phase 1: retry menu - self.phase = 0 - -- self.retry_menu starts nil, only created when it must be shown end @@ -69,6 +72,31 @@ function stage_clear_state:on_enter() self.app:start_coroutine(self.play_stage_clear_sequence_async, self) end + +-- play overall stage clear sequence (coroutine) +function stage_clear_state:play_stage_clear_sequence_async() + -- show result UI + self:show_result_async() + + -- stop BGM and play stage clear jingle + music(audio.jingle_ids.stage_clear) + yield_delay(stage_clear_data.stage_clear_duration) + + -- play result UI "calculation" (we don't have score so it's just checking + -- if we have all the emeralds) + self:assess_result_async() + self.app:yield_delay_s(stage_clear_data.show_emerald_assessment_duration) + + -- fade out and show retry screen + self:zigzag_fade_out_async() + self.app:yield_delay_s(stage_clear_data.delay_after_zigzag_fadeout) + + -- enter phase 1: retry menu + self.phase = 1 + + self:show_retry_screen_async() +end + -- good to know what on_exit should do, but never called since stage_clear cartridge only contains stage_clear state -- and we directly load other cartridges without ever exiting this state; so strip it --[[ @@ -117,6 +145,13 @@ function stage_clear_state:render() if self.retry_menu then self.retry_menu:draw(29, 90) end + + if self.global_darkness > 0 then + -- black can't get darker, just check the other 15 colors + for c = 1, 15 do + pal(c, visual.swap_palette_by_darkness[c][self.global_darkness], 1) + end + end end @@ -213,27 +248,7 @@ function stage_clear_state:get_region_topleft_location() end --- actual stage clear sequence - -function stage_clear_state:play_stage_clear_sequence_async() - -- show result UI - self:show_result_async() - - -- stop BGM and play stage clear jingle - music(audio.jingle_ids.stage_clear) - yield_delay(stage_clear_data.stage_clear_duration) - - -- play result UI "calculation" (we don't have score so it's just checking - -- if we have all the emeralds) - self:assess_result_async() - self.app:yield_delay_s(stage_clear_data.show_emerald_assessment_duration) - - -- fade out and show retry screen - self:zigzag_fade_out_async() - self.app:yield_delay_s(stage_clear_data.delay_after_zigzag_fadeout) - - self:show_retry_screen() -end +-- actual stage clear sequence functions function stage_clear_state.retry_stage() load('picosonic_ingame.p8') @@ -385,26 +400,30 @@ function stage_clear_state:zigzag_fade_out_async() -- just re-add the black rectangle as background for the retry menu -- (no need for zigzag edge itself, since the rectangle body now covers the whole screen) self.result_overlay:add_drawable("bg_rect", bg_rect) - - for num = 1, 8 do - -- only display missed emeralds - -- not nil is true, and not true is false, so we are effectively filling the set, - -- just setting false for picked emeralds instead of the usual nil, but works the same - self.result_show_emerald_set_by_number[num] = not self.picked_emerald_numbers_set[num] - end end -function stage_clear_state:show_retry_screen() - -- enter phase 1: retry menu - self.phase = 1 - +function stage_clear_state:show_retry_screen_async() -- at the end of the zigzag, clear the emerald assessment widgets which are now completely hidden, -- but keep the full black screen rectangle as background for retry screen local try_again_label = label("try again?", vector(41, 34), colors.white) self.result_overlay:add_drawable("try again", try_again_label) + -- display missed emeralds + for num = 1, 8 do + -- not nil is true, and not true is false, so we are effectively filling the set, + -- just setting false for picked emeralds instead of the usual nil, but works the same + self.result_show_emerald_set_by_number[num] = not self.picked_emerald_numbers_set[num] + end + self.retry_menu = menu(self.app, alignments.left, 1, colors.white, visual.sprite_data_t.menu_cursor, 7) self.retry_menu:show_items(stage_clear_state.retry_items) + + -- color fade in + self.global_darkness = 2 + yield_delay(8) + self.global_darkness = 1 + yield_delay(8) + self.global_darkness = 0 end From 6f4961864eaff42e03af2ebe7c1433f17442afe8 Mon Sep 17 00:00:00 2001 From: huulong Date: Tue, 22 Dec 2020 23:49:46 +0100 Subject: [PATCH 23/91] [STAGE CLEAR] Wait 5 frames before menu confirm so player can hear confirm SFX --- src/stage_clear/stage_clear_state.lua | 46 ++++++++++++++++----------- 1 file changed, 27 insertions(+), 19 deletions(-) diff --git a/src/stage_clear/stage_clear_state.lua b/src/stage_clear/stage_clear_state.lua index 4d0f8623..a4747850 100644 --- a/src/stage_clear/stage_clear_state.lua +++ b/src/stage_clear/stage_clear_state.lua @@ -21,16 +21,40 @@ stage_clear_state.type = ':stage_clear' stage_clear_state.retry_items = transform({ {"retry (keep emeralds)", function(app) -- load stage cartridge without clearing picked emerald data in general memory - stage_clear_state.retry_stage() + app:start_coroutine(stage_clear_state.retry_stage_async) end}, {"retry from zero", function(app) - stage_clear_state.retry_from_zero() + app:start_coroutine(stage_clear_state.retry_from_zero_async) end}, {"back to title", function(app) - stage_clear_state.back_to_titlemenu() + app:start_coroutine(stage_clear_state.back_to_titlemenu_async) end}, }, unpacking(menu_item)) +-- menu callbacks + +function stage_clear_state.retry_stage_async() + -- wait just a little before loading so player can hear confirm SFX + -- (1 frame is enough since sound is played on another thread) + yield_delay(5) + load('picosonic_ingame.p8') +end + +function stage_clear_state.retry_from_zero_async() + -- clear picked emeralds data (see stage_state:store_picked_emerald_data) in general memory + poke(0x4300, 0) + stage_clear_state.retry_stage_async() +end + +function stage_clear_state.back_to_titlemenu_async() + -- remember to clear picked emerald data, so if we start again from titlemenu we'll also restart from zero + poke(0x4300, 0) + + -- wait just a little before loading so player can hear confirm SFX + yield_delay(5) + load('picosonic_titlemenu.p8') +end + function stage_clear_state:init() -- gamestate.init(self) -- kept for expliciteness, but does nothing @@ -250,22 +274,6 @@ end -- actual stage clear sequence functions -function stage_clear_state.retry_stage() - load('picosonic_ingame.p8') -end - -function stage_clear_state.retry_from_zero() - -- clear picked emeralds data (see stage_state:store_picked_emerald_data) in general memory - poke(0x4300, 0) - stage_clear_state.retry_stage() -end - -function stage_clear_state.back_to_titlemenu() - -- remember to clear picked emerald data, so if we start again from titlemenu we'll also restart from zero - poke(0x4300, 0) - load('picosonic_titlemenu.p8') -end - function stage_clear_state:restore_picked_emerald_data() -- retrieve and store picked emeralds set information from memory stored in ingame before stage clear -- cartridge was loaded From 4b73faa387268c681f0c8890f189b14cfa974bc0 Mon Sep 17 00:00:00 2001 From: huulong Date: Wed, 23 Dec 2020 00:01:57 +0100 Subject: [PATCH 24/91] [STAGE CLEAR] Better item placement --- src/stage_clear/stage_clear_state.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/stage_clear/stage_clear_state.lua b/src/stage_clear/stage_clear_state.lua index a4747850..3aa55b8b 100644 --- a/src/stage_clear/stage_clear_state.lua +++ b/src/stage_clear/stage_clear_state.lua @@ -167,7 +167,7 @@ function stage_clear_state:render() -- exceptionally draw menu above overlay, because this overlay is used as background -- for retry menu if self.retry_menu then - self.retry_menu:draw(29, 90) + self.retry_menu:draw(29, 95) end if self.global_darkness > 0 then @@ -413,7 +413,7 @@ end function stage_clear_state:show_retry_screen_async() -- at the end of the zigzag, clear the emerald assessment widgets which are now completely hidden, -- but keep the full black screen rectangle as background for retry screen - local try_again_label = label("try again?", vector(41, 34), colors.white) + local try_again_label = label("try again?", vector(45, 30), colors.white) self.result_overlay:add_drawable("try again", try_again_label) -- display missed emeralds From 69b5786f2320ac855011ff72d24155747b684c63 Mon Sep 17 00:00:00 2001 From: huulong Date: Wed, 23 Dec 2020 00:16:07 +0100 Subject: [PATCH 25/91] [STAGE CLEAR] Replace full rectangle bg with cls() to prepare fadeout on top of retry menu --- src/stage_clear/stage_clear_state.lua | 64 ++++++++++++++------------- 1 file changed, 33 insertions(+), 31 deletions(-) diff --git a/src/stage_clear/stage_clear_state.lua b/src/stage_clear/stage_clear_state.lua index 3aa55b8b..db18f95f 100644 --- a/src/stage_clear/stage_clear_state.lua +++ b/src/stage_clear/stage_clear_state.lua @@ -113,11 +113,12 @@ function stage_clear_state:play_stage_clear_sequence_async() -- fade out and show retry screen self:zigzag_fade_out_async() - self.app:yield_delay_s(stage_clear_data.delay_after_zigzag_fadeout) - -- enter phase 1: retry menu + -- enter phase 1: retry menu immediately so we can clear screen self.phase = 1 + self.app:yield_delay_s(stage_clear_data.delay_after_zigzag_fadeout) + self:show_retry_screen_async() end @@ -144,36 +145,39 @@ function stage_clear_state:update() end function stage_clear_state:render() - -- see set_camera_with_origin for value explanation (we must pass camera position) - visual_stage.render_background(vector(3392, 328)) - self:render_stage_elements() - - -- hack to draw fade-out on top of picked emeralds during phase 0 (stage result) - -- then missed emeralds on top of missed emeralds during phase 1 (retry menu) if self.phase == 0 then - -- draw either picked or missed emeralds + -- phase 0: stage result + + -- see set_camera_with_origin for value explanation (we must pass camera position) + visual_stage.render_background(vector(3392, 328)) + self:render_stage_elements() + + -- draw picked emeralds self:render_emeralds() -- draw overlay on top to hide result widgets self:render_overlay() else - -- draw overlay on top to hide result widgets - self:render_overlay() + -- phase 1: retry menu + cls() - -- draw either picked or missed emeralds + -- draw missed emeralds self:render_emeralds() - end - -- exceptionally draw menu above overlay, because this overlay is used as background - -- for retry menu - if self.retry_menu then - self.retry_menu:draw(29, 95) - end + -- for retry menu + if self.retry_menu then + self.retry_menu:draw(29, 95) + end - if self.global_darkness > 0 then - -- black can't get darker, just check the other 15 colors - for c = 1, 15 do - pal(c, visual.swap_palette_by_darkness[c][self.global_darkness], 1) + if self.global_darkness > 0 then + -- black can't get darker, just check the other 15 colors + for c = 1, 15 do + pal(c, visual.swap_palette_by_darkness[c][self.global_darkness], 1) + end + else + -- post-render palette swap seems to persist even after cls() + -- so, although render_emeralds indirectly calls pal(), it's safer to manually reset + pal() end end end @@ -392,22 +396,20 @@ function zigzag_drawable:draw() end function stage_clear_state:zigzag_fade_out_async() - local bg_rect = rectangle(vector(0, 0), 128, 128, colors.black) - self.result_overlay:add_drawable("bg_rect", bg_rect) + local fadeout_rect = rectangle(vector(0, 0), 128, 128, colors.black) + self.result_overlay:add_drawable("fadeout_rect", fadeout_rect) self.result_overlay:add_drawable("zigzag", zigzag_drawable) -- make rectangle with zigzag edge enter the screen from the left -- note that we finish at 128 and not 127 so the zigzag fully goes out of the screen to the right, - -- and the bg_rect fully covers the screen, ready to be used as background - ui_animation.move_drawables_on_coord_async("x", {bg_rect, zigzag_drawable}, {-128, 0}, - visual.fadeout_zigzag_width, 128, stage_clear_data.zigzag_fadeout_duration) + -- and the fadeout_rect fully covers the screen, ready to be used as background + ui_animation.move_drawables_on_coord_async("x", {fadeout_rect, zigzag_drawable}, {-128, 0}, - visual.fadeout_zigzag_width, 128, stage_clear_data.zigzag_fadeout_duration) -- at the end of the zigzag, clear the emerald assessment widgets which are now completely hidden - -- as well as members that draw custom items, except for actual emeralds as we'll draw the missing emeralds more below + -- no need to clear result_show_emerald_set_by_number as we'll set result_show_emerald_set_by_number + -- again in show_retry_screen_async to show missing emeralds + -- no need to preserve fadeout_rect either because in phase 2, we cls() on render start anyway self.result_overlay:clear_drawables() - - -- just re-add the black rectangle as background for the retry menu - -- (no need for zigzag edge itself, since the rectangle body now covers the whole screen) - self.result_overlay:add_drawable("bg_rect", bg_rect) end function stage_clear_state:show_retry_screen_async() From 22039e9713b21123a9f9eabe8718f6eb4f47b316 Mon Sep 17 00:00:00 2001 From: huulong Date: Wed, 23 Dec 2020 00:24:55 +0100 Subject: [PATCH 26/91] [STAGE CLEAR] Zigzag fade out on top of retry menu on confirm (not too visible, but avoids brutal change) --- src/stage_clear/stage_clear_state.lua | 31 +++++++++++++++------------ 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/src/stage_clear/stage_clear_state.lua b/src/stage_clear/stage_clear_state.lua index db18f95f..2c63330a 100644 --- a/src/stage_clear/stage_clear_state.lua +++ b/src/stage_clear/stage_clear_state.lua @@ -1,3 +1,4 @@ +local flow = require("engine/application/flow") local gamestate = require("engine/application/gamestate") local label = require("engine/ui/label") local overlay = require("engine/ui/overlay") @@ -34,9 +35,8 @@ stage_clear_state.retry_items = transform({ -- menu callbacks function stage_clear_state.retry_stage_async() - -- wait just a little before loading so player can hear confirm SFX - -- (1 frame is enough since sound is played on another thread) - yield_delay(5) + -- zigzag fadeout will also give time to player to hear confirm SFX + flow.curr_state:zigzag_fade_out_async() load('picosonic_ingame.p8') end @@ -50,8 +50,8 @@ function stage_clear_state.back_to_titlemenu_async() -- remember to clear picked emerald data, so if we start again from titlemenu we'll also restart from zero poke(0x4300, 0) - -- wait just a little before loading so player can hear confirm SFX - yield_delay(5) + -- zigzag fadeout will also give time to player to hear confirm SFX + flow.curr_state:zigzag_fade_out_async() load('picosonic_titlemenu.p8') end @@ -168,17 +168,20 @@ function stage_clear_state:render() if self.retry_menu then self.retry_menu:draw(29, 95) end + end - if self.global_darkness > 0 then - -- black can't get darker, just check the other 15 colors - for c = 1, 15 do - pal(c, visual.swap_palette_by_darkness[c][self.global_darkness], 1) - end - else - -- post-render palette swap seems to persist even after cls() - -- so, although render_emeralds indirectly calls pal(), it's safer to manually reset - pal() + -- draw overlay on top to hide result widgets + self:render_overlay() + + if self.global_darkness > 0 then + -- black can't get darker, just check the other 15 colors + for c = 1, 15 do + pal(c, visual.swap_palette_by_darkness[c][self.global_darkness], 1) end + else + -- post-render palette swap seems to persist even after cls() + -- so, although render_emeralds indirectly calls pal(), it's safer to manually reset + pal() end end From 534118e05f4105eee54878ab202c1c038db5d53b Mon Sep 17 00:00:00 2001 From: huulong Date: Wed, 23 Dec 2020 00:49:31 +0100 Subject: [PATCH 27/91] [STAGE CLEAR] Extracted postprocess into engine render --- pico-boots | 2 +- src/resources/visual_stage_clear_addon.lua | 24 +------------- src/stage_clear/stage_clear_state.lua | 34 ++++++-------------- src/stage_clear/stage_clear_state_utest.lua | 35 ++++++++++----------- 4 files changed, 28 insertions(+), 67 deletions(-) diff --git a/pico-boots b/pico-boots index f841b663..b56a2320 160000 --- a/pico-boots +++ b/pico-boots @@ -1 +1 @@ -Subproject commit f841b6635624c91f7c8dffd51b7e405b0e7d9f69 +Subproject commit b56a23208c1981d8e15081f95686c25f469e8c6b diff --git a/src/resources/visual_stage_clear_addon.lua b/src/resources/visual_stage_clear_addon.lua index 7d8a924e..deefbe3c 100644 --- a/src/resources/visual_stage_clear_addon.lua +++ b/src/resources/visual_stage_clear_addon.lua @@ -4,29 +4,7 @@ local sprite_data = require("engine/render/sprite_data") local stage_clear_visual = { fadeout_zigzag_width = 11, -- doesn't include pixel 0, so actually count +1 pixel in total - missed_emeralds_radius = 15, - - -- use this table with pal() to recolor things darker (level 1-2, don't use at level 0) - -- we skip black which doesn't get darker, so we can start at index 1 and we don't need - -- explicit keys at all - swap_palette_by_darkness = { - -- [colors.black] = {colors.black, colors.black}, - --[[ [colors.dark_blue] = ]] {colors.black, colors.black}, - --[[ [colors.dark_purple] = ]] {colors.black, colors.black}, - --[[ [colors.dark_green] = ]] {colors.black, colors.black}, - --[[ [colors.brown] = ]] {colors.black, colors.black}, - --[[ [colors.dark_gray] = ]] {colors.black, colors.black}, - --[[ [colors.light_gray] = ]] {colors.dark_gray, colors.black}, - --[[ [colors.white] = ]] {colors.light_gray, colors.dark_gray}, - --[[ [colors.red] = ]] {colors.dark_purple, colors.black}, - --[[ [colors.orange] = ]] {colors.brown, colors.black}, - --[[ [colors.yellow] = ]] {colors.orange, colors.brown}, - --[[ [colors.green] = ]] {colors.dark_green, colors.black}, - --[[ [colors.blue] = ]] {colors.dark_blue, colors.black}, - --[[ [colors.indigo] = ]] {colors.dark_gray, colors.black}, - --[[ [colors.pink] = ]] {colors.dark_purple, colors.black}, - --[[ [colors.peach] = ]] {colors.orange, colors.brown} - } + missed_emeralds_radius = 15 } -- visuals for stage_clear only diff --git a/src/stage_clear/stage_clear_state.lua b/src/stage_clear/stage_clear_state.lua index 2c63330a..868887ec 100644 --- a/src/stage_clear/stage_clear_state.lua +++ b/src/stage_clear/stage_clear_state.lua @@ -1,5 +1,6 @@ local flow = require("engine/application/flow") local gamestate = require("engine/application/gamestate") +local postprocess = require("engine/render/postprocess") local label = require("engine/ui/label") local overlay = require("engine/ui/overlay") local rectangle = require("engine/ui/rectangle") @@ -62,8 +63,8 @@ function stage_clear_state:init() -- phase 1: retry menu self.phase = 0 - -- global darkness affect post-draw screen palette - self.global_darkness = 0 + -- postprocessing for fade out effect + self.postproc = postprocess() -- result (stage clear) overlay self.result_overlay = overlay() @@ -151,38 +152,23 @@ function stage_clear_state:render() -- see set_camera_with_origin for value explanation (we must pass camera position) visual_stage.render_background(vector(3392, 328)) self:render_stage_elements() - - -- draw picked emeralds - self:render_emeralds() - - -- draw overlay on top to hide result widgets - self:render_overlay() else -- phase 1: retry menu cls() - -- draw missed emeralds - self:render_emeralds() - -- for retry menu if self.retry_menu then self.retry_menu:draw(29, 95) end end + -- draw picked/missed emeralds + self:render_emeralds() + -- draw overlay on top to hide result widgets self:render_overlay() - if self.global_darkness > 0 then - -- black can't get darker, just check the other 15 colors - for c = 1, 15 do - pal(c, visual.swap_palette_by_darkness[c][self.global_darkness], 1) - end - else - -- post-render palette swap seems to persist even after cls() - -- so, although render_emeralds indirectly calls pal(), it's safer to manually reset - pal() - end + self.postproc:apply() end @@ -432,11 +418,11 @@ function stage_clear_state:show_retry_screen_async() self.retry_menu:show_items(stage_clear_state.retry_items) -- color fade in - self.global_darkness = 2 + self.postproc.darkness = 2 yield_delay(8) - self.global_darkness = 1 + self.postproc.darkness = 1 yield_delay(8) - self.global_darkness = 0 + self.postproc.darkness = 0 end diff --git a/src/stage_clear/stage_clear_state_utest.lua b/src/stage_clear/stage_clear_state_utest.lua index e8a3b478..557e2f3a 100644 --- a/src/stage_clear/stage_clear_state_utest.lua +++ b/src/stage_clear/stage_clear_state_utest.lua @@ -313,16 +313,31 @@ describe('stage_clear_state', function () stage_clear_state.render_emeralds:clear() end) - it('should call render_background, render_stage_elements, render_overlay, render_emeralds', function () + it('(phase 0) should call render_background, render_stage_elements, render_overlay, render_emeralds', function () + state.phase = 0 + state:render() + assert.spy(visual_stage.render_background).was_called(1) assert.spy(visual_stage.render_background).was_called_with(vector(3392, 328)) assert.spy(stage_clear_state.render_stage_elements).was_called(1) assert.spy(stage_clear_state.render_stage_elements).was_called_with(match.ref(state)) + assert.spy(stage_clear_state.render_emeralds).was_called(1) + assert.spy(stage_clear_state.render_emeralds).was_called_with(match.ref(state)) assert.spy(stage_clear_state.render_overlay).was_called(1) assert.spy(stage_clear_state.render_overlay).was_called_with(match.ref(state)) + end) + + + it('should call render_background, render_stage_elements, render_overlay, render_emeralds', function () + state.phase = 1 + + state:render() + assert.spy(stage_clear_state.render_emeralds).was_called(1) assert.spy(stage_clear_state.render_emeralds).was_called_with(match.ref(state)) + assert.spy(stage_clear_state.render_overlay).was_called(1) + assert.spy(stage_clear_state.render_overlay).was_called_with(match.ref(state)) end) end) -- state.render @@ -366,24 +381,6 @@ describe('stage_clear_state', function () end) - describe('back_to_titlemenu', function () - - setup(function () - stub(_G, "load") - end) - - teardown(function () - load:revert() - end) - - it('should load cartridge: picosonic_titlemenu.p8', function () - state:back_to_titlemenu() - assert.spy(load).was_called(1) - assert.spy(load).was_called_with('picosonic_titlemenu.p8') - end) - - end) - describe('set_camera_with_origin', function () it('should set the pico8 camera to hardcoded position around goal', function () From 36fcc9be53f41d4485a1cb117ddc17f7e66b21fc Mon Sep 17 00:00:00 2001 From: huulong Date: Wed, 23 Dec 2020 02:05:54 +0100 Subject: [PATCH 28/91] [RENDER] Use 5-6 steps of improved postprocess darkness --- pico-boots | 2 +- src/ingame/stage_state.lua | 9 +++++++++ src/stage_clear/stage_clear_state.lua | 11 +++++------ 3 files changed, 15 insertions(+), 7 deletions(-) diff --git a/pico-boots b/pico-boots index b56a2320..ecb6c765 160000 --- a/pico-boots +++ b/pico-boots @@ -1 +1 @@ -Subproject commit b56a23208c1981d8e15081f95686c25f469e8c6b +Subproject commit ecb6c7652978be3159739313ddd0f08afcb98607 diff --git a/src/ingame/stage_state.lua b/src/ingame/stage_state.lua index ec4760d2..cec857a0 100644 --- a/src/ingame/stage_state.lua +++ b/src/ingame/stage_state.lua @@ -1,5 +1,6 @@ local gamestate = require("engine/application/gamestate") local volume = require("engine/audio/volume") +local postprocess = require("engine/render/postprocess") local label = require("engine/ui/label") local overlay = require("engine/ui/overlay") local rectangle = require("engine/ui/rectangle") @@ -49,6 +50,7 @@ function stage_state:init() -- create camera, but wait for player character to spawn before assigning it a target -- see on_enter for how we warp it to a good place first self.camera = camera_class() + self.postproc = postprocess() -- title overlay self.title_overlay = overlay() @@ -240,6 +242,7 @@ function stage_state:render() self:render_fx() self:render_hud() self:render_overlay() + self.postproc:apply() end @@ -945,6 +948,12 @@ end -- ui function stage_state:show_stage_splash_async() + -- fade in + for i = 5, 0, -1 do + self.postproc.darkness = i + yield_delay(7) + end + self.app:yield_delay_s(stage_data.show_stage_splash_delay) -- FIXME: draw iteration order not guaranteed, pico-sonic may be hidden "below" banner diff --git a/src/stage_clear/stage_clear_state.lua b/src/stage_clear/stage_clear_state.lua index 868887ec..8a2e07db 100644 --- a/src/stage_clear/stage_clear_state.lua +++ b/src/stage_clear/stage_clear_state.lua @@ -417,12 +417,11 @@ function stage_clear_state:show_retry_screen_async() self.retry_menu = menu(self.app, alignments.left, 1, colors.white, visual.sprite_data_t.menu_cursor, 7) self.retry_menu:show_items(stage_clear_state.retry_items) - -- color fade in - self.postproc.darkness = 2 - yield_delay(8) - self.postproc.darkness = 1 - yield_delay(8) - self.postproc.darkness = 0 + -- fade in (we start from everything black so skip max darkness 5) + for i = 4, 0, -1 do + self.postproc.darkness = i + yield_delay(5) + end end From 201804c7dca2e185a07a8f4ca3ffeb14add07c3d Mon Sep 17 00:00:00 2001 From: huulong Date: Thu, 24 Dec 2020 15:27:23 +0100 Subject: [PATCH 29/91] [POSTPROCESS] Updated postprocess from engine with more darkness steps --- pico-boots | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pico-boots b/pico-boots index ecb6c765..57ffb3a5 160000 --- a/pico-boots +++ b/pico-boots @@ -1 +1 @@ -Subproject commit ecb6c7652978be3159739313ddd0f08afcb98607 +Subproject commit 57ffb3a5d120d3c9183de7e864d2142fcdde1719 From c825e4175e70fabe58ee69db78c14cecca84725f Mon Sep 17 00:00:00 2001 From: huulong Date: Tue, 29 Dec 2020 13:55:31 +0100 Subject: [PATCH 30/91] [UNIFY] Build with new --unify option and adapted code to work with it Compressed chars reduced from 17322 (110%) to 16017 (102%) - make all non-global modules define local table and return it (mandatory) - minimize outer scope definitions and move most of it to constructors (e.g. menu items). It is actually optional since the new ordered_require file will guarantee dependencies will be defined above Misc changes: - goal_plate _tostring if #tostring instead of #log - added _tostring method to stage_state --- build_single_cartridge.sh | 3 +- pico-boots | 2 +- sonic-2d-tech-demo.sublime-project | 44 +++++++++++++++++- src/data/collision_data.lua | 13 +++--- src/data/stage_clear_data.lua | 4 +- src/data/stage_data.lua | 4 +- src/ingame/goal_plate.lua | 2 +- src/ingame/stage_state.lua | 6 +++ src/ingame/stage_state_utest.lua | 8 ++++ src/menu/credits.lua | 21 ++++++--- src/menu/credits_utest.lua | 2 +- src/menu/titlemenu.lua | 26 +++++++---- src/menu/titlemenu_utest.lua | 2 +- src/stage_clear/stage_clear_state.lua | 50 ++++++++++----------- src/stage_clear/stage_clear_state_utest.lua | 25 +++++------ 15 files changed, 139 insertions(+), 73 deletions(-) diff --git a/build_single_cartridge.sh b/build_single_cartridge.sh index 6a619945..37c5d376 100755 --- a/build_single_cartridge.sh +++ b/build_single_cartridge.sh @@ -120,7 +120,8 @@ fi -c "$config" \ --no-append-config \ -s "$symbols" \ - --minify-level 3 + --minify-level 3 \ + --unify if [[ $? -ne 0 ]]; then echo "" diff --git a/pico-boots b/pico-boots index 57ffb3a5..9626598e 160000 --- a/pico-boots +++ b/pico-boots @@ -1 +1 @@ -Subproject commit 57ffb3a5d120d3c9183de7e864d2142fcdde1719 +Subproject commit 9626598ee5f2d854606791908697627d43dad460 diff --git a/sonic-2d-tech-demo.sublime-project b/sonic-2d-tech-demo.sublime-project index cf56ac6c..8921e741 100644 --- a/sonic-2d-tech-demo.sublime-project +++ b/sonic-2d-tech-demo.sublime-project @@ -17,6 +17,22 @@ "file_regex": "\"(.*\\.py)\"(?:, line )([\\d]+)(?:\\:)?([\\d]+)?(.*)", "syntax": "Packages/Python/Python.sublime-syntax" }, + { + "name": "Dev: generate dependency graph", + "shell": true, + "working_dir": "${project_path}", + "cmd": ["python3 -m pico-boots.scripts.generate_dependency_graph log/dependency_graph.dot src pico-boots/src"], + "file_regex": "\"(.*\\.py)\"(?:, line )([\\d]+)(?:\\:)?([\\d]+)?(.*)", + "syntax": "Packages/Python/Python.sublime-syntax" + }, + { + "name": "Dev: generate ordered require file for unity build (for testing)", + "shell": true, + "working_dir": "${project_path}", + "cmd": ["python3 -m pico-boots.scripts.generate_ordered_require_file log/ordered_require.lua main_ingame src pico-boots/src"], + "file_regex": "\"(.*\\.py)\"(?:, line )([\\d]+)(?:\\:)?([\\d]+)?(.*)", + "syntax": "Packages/Python/Python.sublime-syntax" + }, { "name": "Engine: setup", "working_dir": "${project_path}/pico-boots", @@ -926,6 +942,14 @@ "file_regex": "\"(.*\\.py)\"(?:, line )([\\d]+)(?:\\:)?([\\d]+)?(.*)", "syntax": "Packages/Python/Python.sublime-syntax" }, + { + "name": "Engine: Postbuild: test preprocess", + "working_dir": "${project_path}/pico-boots", + "shell": true, + "cmd": ["python3 -m scripts.test_preprocess"], + "file_regex": "\"(.*\\.py)\"(?:, line )([\\d]+)(?:\\:)?([\\d]+)?(.*)", + "syntax": "Packages/Python/Python.sublime-syntax" + }, { "name": "Engine: Postbuild: test minify", "working_dir": "${project_path}/pico-boots", @@ -935,10 +959,26 @@ "syntax": "Packages/Python/Python.sublime-syntax" }, { - "name": "Engine: Postbuild: test preprocess", + "name": "Engine: Postbuild: test unify", "working_dir": "${project_path}/pico-boots", "shell": true, - "cmd": ["python3 -m scripts.test_preprocess"], + "cmd": ["python3 -m scripts.test_unify"], + "file_regex": "\"(.*\\.py)\"(?:, line )([\\d]+)(?:\\:)?([\\d]+)?(.*)", + "syntax": "Packages/Python/Python.sublime-syntax" + }, + { + "name": "Engine: Postbuild: test generate_dependency_graph", + "working_dir": "${project_path}/pico-boots", + "shell": true, + "cmd": ["python3 -m scripts.test_generate_dependency_graph"], + "file_regex": "\"(.*\\.py)\"(?:, line )([\\d]+)(?:\\:)?([\\d]+)?(.*)", + "syntax": "Packages/Python/Python.sublime-syntax" + }, + { + "name": "Engine: Postbuild: test generate_ordered_require_file", + "working_dir": "${project_path}/pico-boots", + "shell": true, + "cmd": ["python3 -m scripts.test_generate_ordered_require_file"], "file_regex": "\"(.*\\.py)\"(?:, line )([\\d]+)(?:\\:)?([\\d]+)?(.*)", "syntax": "Packages/Python/Python.sublime-syntax" }, diff --git a/src/data/collision_data.lua b/src/data/collision_data.lua index b57e2845..6c9e766f 100644 --- a/src/data/collision_data.lua +++ b/src/data/collision_data.lua @@ -1,5 +1,6 @@ local tile_collision_data = require("data/tile_collision_data") +local collision_data = {} -- below, we are not using serialization.parse_expression anymore -- because we want to insert inline comments and have unlimited tokens anyway @@ -329,9 +330,9 @@ for sprite_id, mask_tile_id in pairs(mask_tile_ids) do tiles_collision_data[sprite_id] = tile_collision_data.from_raw_tile_collision_data(mask_tile_id, mask_tile_angles[mask_tile_id]) end -return { - -- proxy getter is only here to make stubbing possible in tile_test_data - get_tile_collision_data = function (sprite_id) - return tiles_collision_data[sprite_id] - end -} +-- proxy getter is only here to make stubbing possible in tile_test_data +collision_data.get_tile_collision_data = function (sprite_id) + return tiles_collision_data[sprite_id] +end + +return collision_data diff --git a/src/data/stage_clear_data.lua b/src/data/stage_clear_data.lua index 3724f48a..a1e7a1ca 100644 --- a/src/data/stage_clear_data.lua +++ b/src/data/stage_clear_data.lua @@ -1,4 +1,4 @@ -return { +local stage_clear_data = { -- stage clear sequence timing @@ -15,3 +15,5 @@ return { -- delay after zigzag fade out, before showing retry screen content (s) delay_after_zigzag_fadeout = 1.0, } + +return stage_clear_data diff --git a/src/data/stage_data.lua b/src/data/stage_data.lua index 3a3465af..eea76e92 100644 --- a/src/data/stage_data.lua +++ b/src/data/stage_data.lua @@ -1,6 +1,6 @@ local location_rect = require("engine/core/location_rect") -return { +local stage_data = { -- common data @@ -111,3 +111,5 @@ return { } } + +return stage_data diff --git a/src/ingame/goal_plate.lua b/src/ingame/goal_plate.lua index 630935d2..062c6aba 100644 --- a/src/ingame/goal_plate.lua +++ b/src/ingame/goal_plate.lua @@ -15,7 +15,7 @@ function goal_plate:init(global_loc) self.anim_spr:play("goal") end ---#if log +--#if tostring function goal_plate:_tostring() return "goal_plate("..self.global_loc..")" end diff --git a/src/ingame/stage_state.lua b/src/ingame/stage_state.lua index cec857a0..b62508db 100644 --- a/src/ingame/stage_state.lua +++ b/src/ingame/stage_state.lua @@ -61,6 +61,12 @@ function stage_state:init() --#endif end +--#if tostring +function stage_state:_tostring() + return "stage_state("..self.curr_stage_id..")" +end +--#endif + function stage_state:on_enter() -- don't initialize loaded region coords to force first -- (we don't know in which region player character will spawn) diff --git a/src/ingame/stage_state_utest.lua b/src/ingame/stage_state_utest.lua index 899f62b7..aee3f69a 100644 --- a/src/ingame/stage_state_utest.lua +++ b/src/ingame/stage_state_utest.lua @@ -87,6 +87,14 @@ describe('stage_state', function () }) end) + describe('_tostring', function () + + it('should return "stage_state(1)"', function () + assert.are_equal("stage_state(1)", state:_tostring()) + end) + + end) + describe('on_enter', function () setup(function () diff --git a/src/menu/credits.lua b/src/menu/credits.lua index b26099dd..1fc35e67 100644 --- a/src/menu/credits.lua +++ b/src/menu/credits.lua @@ -14,20 +14,27 @@ credits.type = ':credits' -- parameters data +local menu_item_params = { + {"back", function(app) + flow:query_gamestate_type(':titlemenu') + end} +} + local copyright_text = wwrap("this is a fan game distributed for free and is not endorsed by sega games co. ltd, which owns the sonic the hedgehog trademark and copyrights.", 31) --- sequence of menu items to display, with their target states -credits.items = transform({ - {"back", function(app) - flow:query_gamestate_type(':titlemenu') - end}, - }, unpacking(menu_item)) +function credits:init() + -- sequence of menu items to display, with their target states + -- this could be static, but defining in init allows us to avoid + -- outer scope definition, so we don't need to declare local menu_item + -- at source top for unity build + self.items = transform(menu_item_params, unpacking(menu_item)) +end function credits:on_enter() music(-1) self.menu = menu(self.app--[[, 2]], alignments.left, 3, colors.white--[[skip prev_page_arrow_offset]], visual.sprite_data_t.menu_cursor, 7) - self.menu:show_items(credits.items) + self.menu:show_items(self.items) end function credits:on_exit() diff --git a/src/menu/credits_utest.lua b/src/menu/credits_utest.lua index c09274c2..1cdd8c9e 100644 --- a/src/menu/credits_utest.lua +++ b/src/menu/credits_utest.lua @@ -55,7 +55,7 @@ describe('credits', function () c:on_enter() assert.spy(menu.show_items).was_called(1) - assert.spy(menu.show_items).was_called_with(match.ref(c.menu), match.ref(credits.items)) + assert.spy(menu.show_items).was_called_with(match.ref(c.menu), match.ref(c.items)) end) end) diff --git a/src/menu/titlemenu.lua b/src/menu/titlemenu.lua index 96a9c9b4..41a79471 100644 --- a/src/menu/titlemenu.lua +++ b/src/menu/titlemenu.lua @@ -16,18 +16,26 @@ titlemenu.type = ':titlemenu' -- parameters data -- sequence of menu items to display, with their target states -titlemenu.items = transform({ - {"start", function(app) - load('picosonic_ingame.p8') - end}, - {"credits", function(app) - flow:query_gamestate_type(':credits') - end}, - }, unpacking(menu_item)) +local menu_item_params = { + {"start", function(app) + load('picosonic_ingame.p8') + end}, + {"credits", function(app) + flow:query_gamestate_type(':credits') + end}, +} -- attributes: -- menu menu title menu showing items (only created when it must be shown) +function titlemenu:init() + -- sequence of menu items to display, with their target states + -- this could be static, but defining in init allows us to avoid + -- outer scope definition, so we don't need to declare local menu_item + -- at source top for unity build + self.items = transform(menu_item_params, unpacking(menu_item)) +end + function titlemenu:on_enter() self.app:start_coroutine(self.opening_sequence_async, self) end @@ -58,7 +66,7 @@ end function titlemenu:show_menu() self.menu = menu(self.app--[[, 2]], alignments.left, 3, colors.white--[[skip prev_page_arrow_offset]], visual.sprite_data_t.menu_cursor_shoe, 7) - self.menu:show_items(titlemenu.items) + self.menu:show_items(self.items) end function titlemenu:on_exit() diff --git a/src/menu/titlemenu_utest.lua b/src/menu/titlemenu_utest.lua index 55103b50..54f2781d 100644 --- a/src/menu/titlemenu_utest.lua +++ b/src/menu/titlemenu_utest.lua @@ -73,7 +73,7 @@ describe('titlemenu', function () tm:show_menu() assert.spy(menu.show_items).was_called(1) - assert.spy(menu.show_items).was_called_with(match.ref(tm.menu), match.ref(titlemenu.items)) + assert.spy(menu.show_items).was_called_with(match.ref(tm.menu), match.ref(tm.items)) end) end) diff --git a/src/stage_clear/stage_clear_state.lua b/src/stage_clear/stage_clear_state.lua index 8a2e07db..4fb07a15 100644 --- a/src/stage_clear/stage_clear_state.lua +++ b/src/stage_clear/stage_clear_state.lua @@ -20,18 +20,18 @@ local stage_clear_state = derived_class(gamestate) stage_clear_state.type = ':stage_clear' -- sequence of menu items to display, with their target states -stage_clear_state.retry_items = transform({ - {"retry (keep emeralds)", function(app) - -- load stage cartridge without clearing picked emerald data in general memory - app:start_coroutine(stage_clear_state.retry_stage_async) - end}, - {"retry from zero", function(app) - app:start_coroutine(stage_clear_state.retry_from_zero_async) - end}, - {"back to title", function(app) - app:start_coroutine(stage_clear_state.back_to_titlemenu_async) - end}, - }, unpacking(menu_item)) +local retry_menu_item_params = { + {"retry (keep emeralds)", function(app) + -- load stage cartridge without clearing picked emerald data in general memory + app:start_coroutine(stage_clear_state.retry_stage_async) + end}, + {"retry from zero", function(app) + app:start_coroutine(stage_clear_state.retry_from_zero_async) + end}, + {"back to title", function(app) + app:start_coroutine(stage_clear_state.back_to_titlemenu_async) + end}, +} -- menu callbacks @@ -59,6 +59,12 @@ end function stage_clear_state:init() -- gamestate.init(self) -- kept for expliciteness, but does nothing + -- sequence of menu items to display, with their target states + -- this could be static, but defining in init allows us to avoid + -- outer scope definition, so we don't need to declare local menu_item + -- at source top for unity build + self.retry_menu_items = transform(retry_menu_item_params, unpacking(menu_item)) + -- phase 0: stage result -- phase 1: retry menu self.phase = 0 @@ -184,16 +190,6 @@ function stage_clear_state:spawn_goal_plate_at(global_loc) self.goal_plate.anim_spr:play("sonic") end --- register spawn object callbacks by tile id to find them easily in scan_current_region_to_spawn_objects -stage_clear_state.spawn_object_callbacks_by_tile_id = { - [visual.goal_plate_base_id] = stage_clear_state.spawn_goal_plate_at, -} - --- proxy for table above, mostly to ease testing -function stage_clear_state:get_spawn_object_callback(tile_id) - return stage_clear_state.spawn_object_callbacks_by_tile_id[tile_id] -end - -- iterate over each tile of the current region -- and apply method callback for each of them (to spawn objects, etc.) -- the method callback but take self, a global tile location and the sprite id at this location @@ -203,9 +199,9 @@ function stage_clear_state:scan_current_region_to_spawn_objects() -- here we already have region (i, j), so no need to convert for mget local tile_sprite_id = mget(i, j) - local spawn_object_callback = self:get_spawn_object_callback(tile_sprite_id) - - if spawn_object_callback then + -- there is only one object type we are interested in, the goal plate, + -- so check it manually instead of using a table of spawn callbacks as in stage_state + if tile_sprite_id == visual.goal_plate_base_id then -- tile has been recognized as a representative tile for object spawning -- apply callback now @@ -213,7 +209,7 @@ function stage_clear_state:scan_current_region_to_spawn_objects() local region_loc = location(i, j) -- hardcoded region 31 local global_loc = region_loc + location(map_region_tile_width * 3, map_region_tile_height * 1) - spawn_object_callback(self, global_loc, tile_sprite_id) + self:spawn_goal_plate_at(global_loc) end end end @@ -415,7 +411,7 @@ function stage_clear_state:show_retry_screen_async() end self.retry_menu = menu(self.app, alignments.left, 1, colors.white, visual.sprite_data_t.menu_cursor, 7) - self.retry_menu:show_items(stage_clear_state.retry_items) + self.retry_menu:show_items(self.retry_menu_items) -- fade in (we start from everything black so skip max darkness 5) for i = 4, 0, -1 do diff --git a/src/stage_clear/stage_clear_state_utest.lua b/src/stage_clear/stage_clear_state_utest.lua index 557e2f3a..57f813b9 100644 --- a/src/stage_clear/stage_clear_state_utest.lua +++ b/src/stage_clear/stage_clear_state_utest.lua @@ -18,6 +18,7 @@ local overlay = require("engine/ui/overlay") local picosonic_app = require("application/picosonic_app_stage_clear") local goal_plate = require("ingame/goal_plate") local titlemenu = require("menu/titlemenu") +local visual = require("resources/visual_common") local visual_stage = require("resources/visual_stage") local tile_repr = require("test_data/tile_representation") local tile_test_data = require("test_data/tile_test_data") @@ -149,18 +150,12 @@ describe('stage_clear_state', function () describe('scan_current_region_to_spawn_objects', function () - local dummy_callback = spy.new(function (self, global_loc) end) - setup(function () - stub(stage_clear_state, "get_spawn_object_callback", function (self, tile_id) - if tile_id == 21 then - return dummy_callback - end - end) + stub(stage_clear_state, "spawn_goal_plate_at") end) teardown(function () - stage_clear_state.get_spawn_object_callback:revert() + stage_clear_state.spawn_goal_plate_at:revert() end) -- setup is too early, stage state will start afterward in before_each, @@ -170,9 +165,9 @@ describe('stage_clear_state', function () -- we're not using tile_test_data.setup here -- (since objects are checked directly by id, not using collision data) -- so don't use mock_mset - mset(1, 1, 21) - mset(2, 2, 21) - mset(3, 3, 21) + mset(1, 1, visual.goal_plate_base_id) + mset(2, 2, visual.goal_plate_base_id) + mset(3, 3, visual.goal_plate_base_id) -- mock stage dimensions, not too big to avoid test too long -- (just 2 regions so we can check that location conversion works) @@ -185,16 +180,16 @@ describe('stage_clear_state', function () end) after_each(function () - dummy_callback:clear() + stage_clear_state.spawn_goal_plate_at:clear() pico8:clear_map() end) - it('should call spawn object callbacks for recognized representative tiles', function () + it('should call spawn_goal_plate_at global location', function () state:scan_current_region_to_spawn_objects() - assert.spy(dummy_callback).was_called(3) - assert.spy(dummy_callback).was_called_with(match.ref(state), location(1 + map_region_tile_width * 3, 1 + map_region_tile_height * 1), 21) + assert.spy(stage_clear_state.spawn_goal_plate_at).was_called(3) + assert.spy(stage_clear_state.spawn_goal_plate_at).was_called_with(match.ref(state), location(1 + map_region_tile_width * 3, 1 + map_region_tile_height * 1)) end) end) From 27d8fb40c1804f40e885fca3fe34c8ecc553d104 Mon Sep 17 00:00:00 2001 From: huulong Date: Tue, 29 Dec 2020 17:17:58 +0100 Subject: [PATCH 31/91] [UNIFY] To match new build API, pass _cartridge_suffix with --unify and require it yourself in each common file for each cartridge, with correct suffix --- pico-boots | 2 +- src/common_ingame.lua | 22 ++++++++++++++++++++++ src/common_stage_clear.lua | 9 +++++++++ src/common_titlemenu.lua | 9 +++++++++ 4 files changed, 41 insertions(+), 1 deletion(-) diff --git a/pico-boots b/pico-boots index 9626598e..e574b0a3 160000 --- a/pico-boots +++ b/pico-boots @@ -1 +1 @@ -Subproject commit 9626598ee5f2d854606791908697627d43dad460 +Subproject commit e574b0a3e6aa2509e0b8fa4de34c7e24c4ae8730 diff --git a/src/common_ingame.lua b/src/common_ingame.lua index 205f6167..643ee9b6 100644 --- a/src/common_ingame.lua +++ b/src/common_ingame.lua @@ -9,3 +9,25 @@ require("engine/core/table_helper") require("data/sprite_flags") require("ingame/playercharacter_enums") + +--[[#pico8 +--#if unity +-- When doing a unity build, all modules must be concatenated in dependency, with modules relied upon +-- above modules relying on them. +-- This matters for two reasons: +-- 1. Some statements are done in outer scope and rely on other modules (derived_class(), data tables defining +-- sprite_data(), table merge(), etc.) so the struct/class/function used must be defined at evaluation time, +-- and there is no picotool package definition callback wrapper to delay package evaluation to main evaluation +-- time (which is done at the end). +-- 2. Even in inner scope (method calls), statements refer to named modules normally stored in local vars via +-- require. In theory, *declaring* the local module at the top of whole file and defining it at runtime +-- at any point before main is evaluation would be enough, but it's cumbersome to remove "local" in front +-- of the local my_module = ... inside each package definition, so we prefer reordering the packages +-- so that the declaration-definition is always above all usages. +-- Interestingly, ordered_require will contain the global requires in this very file (keeping same order) +-- for minification lvl3, but it redundancy doesn't matter as all require calls will be stripped. + +require("ordered_require_ingame") + +--#endif +--#pico8]] diff --git a/src/common_stage_clear.lua b/src/common_stage_clear.lua index 23c19a3a..a103cebe 100644 --- a/src/common_stage_clear.lua +++ b/src/common_stage_clear.lua @@ -9,3 +9,12 @@ require("engine/core/table_helper") -- merge (to add the visual_ingame_addon an -- we need sprite flags to draw grass on top of the rest require("data/sprite_flags") + +--[[#pico8 +--#if unity + +-- see explanations in common_ingame.lua +require("ordered_require_stage_clear") + +--#endif +--#pico8]] diff --git a/src/common_titlemenu.lua b/src/common_titlemenu.lua index 1c72916a..41b3e708 100644 --- a/src/common_titlemenu.lua +++ b/src/common_titlemenu.lua @@ -20,3 +20,12 @@ require("engine/core/table_helper") -- since it is not part of engine/common) strspl = 0 --#endif + +--[[#pico8 +--#if unity + +-- see explanations in common_ingame.lua +require("ordered_require_titlemenu") + +--#endif +--#pico8]] From 311f6b783ac6c2394c7eda35e2e9fae4be580c60 Mon Sep 17 00:00:00 2001 From: huulong Date: Tue, 29 Dec 2020 17:18:48 +0100 Subject: [PATCH 32/91] [STAGE INTRO] Added new cartridge stage_intro, currently a stub that just loads ingame Titlemenu now loads stage_intro instead of ingame --- .travis.yml | 2 + build_all_cartridges.sh | 2 +- ..._and_install_single_cartridge_with_data.sh | 2 +- build_itest.sh | 2 +- build_single_cartridge.sh | 6 +- data/builtin_data_stage_intro.p8 | 280 ++++++++++++++++++ export_cartridge_release.p8 | 3 +- install_all_cartridges.sh | 2 +- install_single_cartridge.sh | 4 +- install_single_cartridge_with_data.sh | 4 +- run_cartridge.sh | 2 +- run_itest.sh | 2 +- sonic-2d-tech-demo.sublime-project | 169 ++++++++++- src/application/picosonic_app_stage_intro.lua | 17 ++ src/application/picosonic_app_titlemenu.lua | 6 +- src/common_stage_intro.lua | 23 ++ src/itest_main_stage_intro.lua | 69 +++++ src/itests/stage_clear/iteststage_clear.lua | 2 +- src/itests/stage_intro/iteststage_intro.lua | 27 ++ src/main_stage_intro.lua | 90 ++++++ src/menu/titlemenu.lua | 2 +- src/resources/visual_ingame_addon.lua | 2 +- src/stage_intro/stage_intro_state.lua | 12 + src/stage_intro/stage_intro_state_utest.lua | 0 src/test/bustedhelper_stage_intro.lua | 14 + src/tests/headless_itests_utest.lua | 2 + 26 files changed, 725 insertions(+), 21 deletions(-) create mode 100644 data/builtin_data_stage_intro.p8 create mode 100644 src/application/picosonic_app_stage_intro.lua create mode 100644 src/common_stage_intro.lua create mode 100644 src/itest_main_stage_intro.lua create mode 100644 src/itests/stage_intro/iteststage_intro.lua create mode 100644 src/main_stage_intro.lua create mode 100644 src/stage_intro/stage_intro_state.lua create mode 100644 src/stage_intro/stage_intro_state_utest.lua create mode 100644 src/test/bustedhelper_stage_intro.lua diff --git a/.travis.yml b/.travis.yml index 41ef6542..be4ddcd8 100644 --- a/.travis.yml +++ b/.travis.yml @@ -71,6 +71,8 @@ script: - ITEST_CARTRIDGE_SUFFIX=ignore ./test.sh # test: all headless itests for titlemenu (ENABLE_RENDER set in env) - ITEST_CARTRIDGE_SUFFIX=titlemenu ./test.sh -f headless_itests + # test: all headless itests for stage_intro (ENABLE_RENDER set in env) + - ITEST_CARTRIDGE_SUFFIX=stage_intro ./test.sh -f headless_itests # test: all headless itests for stage (ENABLE_RENDER set in env) - ITEST_CARTRIDGE_SUFFIX=ingame ./test.sh -f headless_itests # test: all headless itests for stage_clear (ENABLE_RENDER set in env) diff --git a/build_all_cartridges.sh b/build_all_cartridges.sh index 30fe31de..a9d0fe2a 100755 --- a/build_all_cartridges.sh +++ b/build_all_cartridges.sh @@ -57,7 +57,7 @@ if [[ ${#positional_args[@]} -ge 1 ]]; then config="${positional_args[0]}" fi -cartridge_list="titlemenu ingame stage_clear" +cartridge_list="titlemenu stage_intro ingame stage_clear" for cartridge in $cartridge_list; do "$game_scripts_path/build_single_cartridge.sh" "$cartridge" "$config" diff --git a/build_and_install_single_cartridge_with_data.sh b/build_and_install_single_cartridge_with_data.sh index acd64970..9a886706 100755 --- a/build_and_install_single_cartridge_with_data.sh +++ b/build_and_install_single_cartridge_with_data.sh @@ -18,7 +18,7 @@ usage() { ARGUMENTS CARTRIDGE_SUFFIX Cartridge to build for the multi-cartridge game - 'titlemenu', 'ingame' or 'stage_clear' + 'titlemenu', 'stage_intro', ingame' or 'stage_clear' CONFIG Build config. Determines defined preprocess symbols. (default: 'debug') diff --git a/build_itest.sh b/build_itest.sh index 248f316e..7b0bd8e4 100755 --- a/build_itest.sh +++ b/build_itest.sh @@ -4,7 +4,7 @@ # This is essentially a proxy script for pico-boots/scripts/build_cartridge.sh with the right parameters. # Usage: build_itest.sh cartridge_suffix -# cartridge_suffix 'titlemenu', 'ingame' or 'stage_clear' +# cartridge_suffix 'titlemenu', 'stage_intro', 'ingame' or 'stage_clear' # Configuration: paths picoboots_scripts_path="$(dirname "$0")/pico-boots/scripts" diff --git a/build_single_cartridge.sh b/build_single_cartridge.sh index 37c5d376..7df20920 100755 --- a/build_single_cartridge.sh +++ b/build_single_cartridge.sh @@ -26,7 +26,7 @@ usage() { ARGUMENTS CARTRIDGE_SUFFIX Cartridge to build for the multi-cartridge game - 'titlemenu', 'ingame' or 'stage_clear' + 'titlemenu', 'stage_intro', 'ingame' or 'stage_clear' CONFIG Build config. Determines defined preprocess symbols. (default: 'debug') @@ -108,7 +108,7 @@ fi # so we can use PICO-8 load() with a cartridge file name # independent from the version and config -# Build cartridge (titlemenu, ingame or stage_clear) +# Build cartridge (titlemenu, 'stage_intro', ingame or stage_clear) # metadata really counts for the entry cartridge (titlemenu) "$picoboots_scripts_path/build_cartridge.sh" \ "$game_src_path" main_${cartridge_suffix}.lua \ @@ -121,7 +121,7 @@ fi --no-append-config \ -s "$symbols" \ --minify-level 3 \ - --unify + --unify "_${cartridge_suffix}" if [[ $? -ne 0 ]]; then echo "" diff --git a/data/builtin_data_stage_intro.p8 b/data/builtin_data_stage_intro.p8 new file mode 100644 index 00000000..7eee5835 --- /dev/null +++ b/data/builtin_data_stage_intro.p8 @@ -0,0 +1,280 @@ +pico-8 cartridge // http://www.pico-8.com +version 29 +__lua__ +-- this is the built-in data cartridge +-- for the ingame cartridge + +-- the collision masks at the top will be overwritten by runtime sprites +-- via reload +__gfx__ +00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +00000000777700000000000000000000000000000000000000000000000000000000000000000000000000000000777777000000000000000000000000000077 +00700700777777777777777700000000000000000000000000000000000000000000000000000000000000007777777777770000000000000000000000007777 +00077000777777777777777777770000000000000000000000000000000000000000000000000000000077777777777777777700000000000000000000777777 +00077000777777777777777777777777777777770000000000000000000000000000000000000000777777777777777777777777000000000000000077777777 +00700700777777777777777777777777777777777777000000000000000000000000000000007777777777777777777777777777770000000000007777777777 +00000000777777777777777777777777777777777777777777777777000000000000000077777777777777777777777777777777777700000000777777777777 +00000000777777777777777777777777777777777777777777777777777700000000777777777777777777777777777777777777777777000077777777777777 +70000000700000000000000000000000000000070000000700000007777777777777777770000000000000000000000000007777777777770000000000000000 +77000000770000000000000000000000000000770000007700000007077777777777777070000000000000000000000000007777777777770000000000000000 +77700000777700000000000000000000000077770000077700000007077777777777777070000000000077777777777700007777777777770000000000000000 +77770000777770000000000000000000000777770000777700000077007777777777770077000000000077777777777700007777777777770000000000000000 +77777000777777700000000000000000077777770007777700000077000777777777700077000000000077777777777700007777777777770000000000000000 +77777700777777777000000000000007777777770077777700000077000777777777700077000000000077777777777700007777777777770000000000000000 +77777770777777777770000000000777777777770777777700000777000077777777000077700000000077777777777700007777777777770000000000000000 +77777777777777777777700000077777777777777777777700000777000077777777000077700000000077777777777700007777777777770000000000000000 +77777777777777777777700000077777777777777777777700007777000007777770000077770000000000000000007700000000000000000000000000000000 +77777770777777777770000000000777777777770777777700007777000007777770000077770000000000000000777700000000000000000000000000000000 +77777700777777777000000000000007777777770077777700077777000000777700000077777000000000000077777700000000000000000000000000000000 +77777000777777700000000000000000077777770007777700077777000000777700000077777000000000777777777700000000000000000000000000000000 +77770000777770000000000000000000000777770000777700777777000000777700000077777700000077777777777700007777000000000000000000000000 +77700000777700000000000000000000000077770000077707777777000000077000000077777770007777777777777700007777000000000000000000000000 +77000000770000000000000000000000000000770000007707777777000000077000000077777770777777777777777700007777000000000000000000000000 +70000000700000000000000000000000000000070000000777777777000000077000000077777777777777777777777700007777000000000000000000000000 +44444444eaeaeeeeeeeeaeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeaebeeeaeeeeeeeeeeeeeeeeeeeeeeeeeeeee +44449444babaeabeeaeebeebeeaeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeaeeebaeabbbabbeeeeeeeeeeeeeeeeeeeeeeeeeaeab +44444494bbbababbbababbbbeebeeaeeeeeeeebeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeaeeaeebbabab3babbbeaeeeeeeeeeeeeeeeeeeaeeebbab +44949444bbbbbbbbabbbbbbababbeaaeebeaeebeeaeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeaeeaaebbabbbbbbb3bbbbbbaeeeeeeeeeeeeeeeeeeaeabbbbb +44499444b3babbb3bbbbabbababbb3ababbababaeaeeeeaeeeeeaeeeeeeeeeeeeeeeeeeeeaeeeeaeba3bbbabbb3bab3bb3bababbeaeeeeeeeeeeeeaeabbab3bb +44949944b3bb3bb3bb3ba3bbb3bbb3b3b3bbbbbababaebaeeeaebeeaeeeeeeeeeeeeeeeeeabeababbb3bbbbbbb3bbbbbb3babbbbbaeaeeeeeeeeaeabbbbab3bb +44949944bbbb3bbbbb3bb3b3bbb3b3bbb3bb3bb3bbbabbabababbabbbaeeaeeeeeeaeeabbabbabbbbb3bbbbbbbbbbb3bb3bbb3bbbbbaeeaeeaeeabbbb3bbbb3b +44449444bb3bbbbb3bbbb3bbbbb3bbbbbbbb3bb3bb3abbbbbb3bbbbbbabaaeaeeaeaababbbbbabbbbbbb3b3bbbbbbbbbbbbbbbbb3ababbaeeabbaba3b3b3bb3b +44949444bbbbbbbbbbbbbbbbbbbb3bbbb3bbbbbbbbbbbb3bbbbbbb3bbababbabbabbababb3bbbbbbeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee +44999444bb0bb3b3bb3bb0bbbbb303bbb3b3bb3bb3bb3b3bb3b3bbbbb3bbbbabbabbbbbbbbbb3b3beeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee +449944443003b030b30b30033b300033bbbbbb3bb3bbbbbbbbb3bb3bb3bbbbbbbbbb3b3bb3bb3bbbeeee999aaa7777a9eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee +49494944004030003003040003004000bbbb0bbbbbb0bbbb3bbbb33bbbbb3b3bbbbbbb3bb33bbbb3eeee444999aa7aa9eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee +44499444044400440000044440044440b03b03bbbb30b30b033b303bbbbbbb3bbb3bbbbbb303b330eeee44999aa777a9eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee +44999444444444444400449444444444000b00b03b00b00003033003bbbbbbbbbb3bbbbb30033030eeeeeeddddddddeeeeeeeeeeeeeaeeeeeeeeeaeeeeeeeeee +44994944444944444404449444494444040300300300304040003040b3b3bbbbbbbb3b3b30400000eeeeed66777766deeaeeeeaeeaeaeeaeeeaeaaeeeeeeeeee +444949444444444444444444444944444440040000440444404004403bb3bb3bb3bb3bb304400404eeeeeeddddddddeeebeebeaebaebeebeeabeabebeeeeeeee +49499444eeeeeeebbeeeeeee4444444494444444bb3bbbb33bbbbbbb0b30b30bb3bb03b03bbbb3bbeeeeeeeeeeeeeeeed7dd11ddd71eeeee4444449999494444 +44949444eeeeeeabbaeeeeee4449444494944444bbbb3b300bb3b3b30030b00bb03b030003b3bbbbeeeeeeeeeeeeeeee777dd1ddd711eeee4444949999444444 +49949944eeeeeeabbaeeeeee4444494444444444b3bb3b0403b333b040003003b003000440b3bb3beeeeeeeeeeeeeeee7777dd1d7d111eee4344999994494444 +44949494eeeeeeb33beeeeee4444494994444444b33bbb0440303030440400403000404440bbb33beeeeeeeeeeeeeeeedd777dd0dd1111ee4b34394999494444 +44999944eeeeeebeebeeeeee44444449949444443003b304400000004444444004044444403b3003eeee999aaa7777a9ddd7dd101111111e3bb4b94999994444 +49994934eeeeeebeebeeeeee44449499994444440403300444040404444494440444444440033040eeee444999aa7aa9dd1dd1101ddd111ebbbb3999999944b3 +b39933b3eeeeeeeeeeeeeeee44449499494944444440304444444444444494444449444444030444eeee44999aa777a97ddd1111dd77ddd1b33bb939993b43bb +bb93bbbbeeeeeeeeeeeeeeee44449449494944444444004444444444444444444444444444004444eeeeeeedd766deee77ddd0ddd7777dd1bb33b3b3393b3bbb +0eeeeeee0eeeeeeeeeeeeeeeeeeeeeeeeeeeeee0eeeeeee0eeeeeee404444444444444444eeeeeeeeeeeeed67dddeeee77ddd0ddddd7ddd1bb3b3bbbbb3bbb3b +40eeeeee40eeeeeeeeeeeeeeeeeeeeeeeeeeee04eeeeee04eeeeeee4e04944444444944e4eeeeeeeeeeeed6ddeeeeeee777dd1ddddddd111b3bb33bbb333bb3b +444eeeee4440eeeeeeeeeeeeeeeeeeeeeeee0044eeeee444eeeeeee4e04944444944440e4eeeeeeeeeeeee55dddeeeee77dd11ddddd1111103b3bbbbb3b3b3b0 +4940eeee44440eeeeeeeeeeeeeeeeeeeeee04444eeee0494eeeeee04ee044944449440ee40eeeeeeeeeeeee55ddddeeedd11111ddd011111b03bbb333bbbbbb0 +44440eee4944400eeeeeeeeeeeeeeeaee0444944eee04494eeeeee04eee0444444940eee40eeeeeeeeeeeeeeeedd6dee1111111ddd011111b333bbbbb33b30bb +494944ee449444400eeeeeeeeeeeaea004444994ee449444eeeeee44eee0449444440eee40eeeeeeeeeeeeeeed77deee1111111dd1011111b3bbb3bb30033bbb +4449440e44944944440eeeeeeeee303b44944494e0444494eeeee044eeee44944944eeee440eeeeeeeeeeeed77ddeeee11111011d10111103bb330bbbb3b33bb +444444404444444444440eeeebebbb3b4444444404444444eeeee044eeee04444440eeee444eeeeeeeeeeed6ddeeeeee111000011000000ebbbbb0bbb33bb3bb +444444404444444444440eeeeee044444444444404444444eeee0444eeeee494440eeeee4440eeee777ddd11111111114444444444444444e3bbbbb3bbbb3bbe +4494440e44444444440eeeeeeeeee00494494444e0449494eeee4444eeeee044440eeeee4944eeeedddddd11111111110449444449444444eeba3b3e3bb3e3b3 +449940ee499449400eeeeeeeeeeeeee044444944ee444494eee04494eeeeee4444eeeeee49440eeeddddd111110111110449494449444940e3b3e3ee3bb3eebb +44444eee4494440eeeeeeeeeeeeeeeeee0044994eee04444eee04494eeeeee0444eeeeee44440eeedddd1111101111114449494444444940ebb3eeeeeabbeea3 +4940eeee44440eeeeeeeeeeeeeeeeeeeeee04494eeee0494ee044444eeeeee0440eeeeee494440eedddd1111001111110444494444440944ebbeeeeee3b3ee3e +440eeeee4400eeeeeeeeeeeeeeeeeeeeeeee0444eeeee444e0444494eeeeeee44eeeeeee4449440e1dd11110011111100044444400000440eabbeeeeee3eeeee +44eeeeee40eeeeeeeeeeeeeeeeeeeeeeeeeeee04eeeeee04e4449444eeeeeee44eeeeeee4949444e11111100111111000000040000e00000ee3beeeeeeeeeeee +0eeeeeee0eeeeeeeeeeeeeeeeeeeeeeeeeeeeee0eeeeeee004444444eeeeeee44eeeeeee44444440000000000000000ee00000000ee00000eeebbeeeeeeeeeee +eeeeeeccceeeeeeeeeeeeccceeeeeeeeeeeeeccceeeeeeeeeeeeeeccceeeeeeeeeeecccceeeeeeeeeeeecccceeeeeeeeeeeecccceeeeeeeeeeeeeeecccceeeee +eeeeeccccceeceeeeeeeccccceeceeeeeeeeccccceeceeeeeeeeeccccceeceeeeeecccccceceeeeeeeecccccceceeeeeeeecccccceceeeeeeeeeeeeeccccceee +eeeeeecffcccceeeeeeeecffcccceeeeeeeeecffcccceeeeeeeeeecffcccceeeeeeeeccfccceeeeeeeeeeccfccceeeeeeeeeeccfccceeeeeeeeeeeeeccccccee +eeeeeccfcc7ccceeeeeeccfcc7ccceeeeeeeccfcc7ccceeeeeeeeccfcc7ccceeeeeccccfc7cceeeeeeeccccfc7cceeeeeeeccccfc7cceeeeee77eeccffccccce +eeeeccccc770cceeeeeccccc770cceeeeeeccccc770cceeeeeeeccccc770cceeeeccccccc71ceeeeeeccccccc71ceeeeeeccccccc71ceeeee7777cccfc7ccc77 +eeecccccc770ceeeeecccccc770ceeeeeecccccc770ceeeeeeecccccc770ceeeecccccccc70ceeeeecccccccc70ceeeeecccccccc70ceeeeeee7ffccc707ccee +eeeeeeccff77f0eeeeeeeccff77f0eeeeeeeeccff77f0eeeeeeeeeccff77f0eeeeeecccff77f0eeeeeeecccff77f0eeeeeeecccff77f0eeeee7eecfccf07c67e +eeeeeccccfffeeeeeeeeccccfffeeeeeeeeeccccfffeeeeeeeeeeccccfffeeeeeeecccc7fffeeeeeeeecccccff7eeeeeeeecccccfffeeeeeeeeecccccffff0ee +eeeeccc99ccfeeeeeeecffcff6eeeeeeeeecffccfee56eeeeee777fccfeeeeeeeeccc9777eeeeeeeeeccccffe777eeeeeeccccffee7eeeeeeeeeeecf9effeeee +eeeeeeef77ff66eeeeefeecffeeeeeeee77feccff9966ee8ee7770ccff66eeeeeeee9f07eeeeeeeee6ceccfff770eeeeeeceecffe777eeeeeeeeeccffeeeeeee +eeeeeecc777f56eeee770ccc7feeeeee707ecccffeeeee88eee7ececff56eeeeeeeecccf111eeeeee66ccccf1eeee20eee07ccccf770eeeeeeeececcceeeeeee +eeeeeece0711eeeeee777ec887eeeeeee7eececcccc78780e002661cccee80eeeeeececce61eeeeeeeececeee172700eee087eee1eeeeeeeeeeeeeecceeeeeee +eeeeeeeeece1eeeeeee7e260887eeeeeee02611eeec7870e0262611ec78780eeee87ccce0262eeeee86cceeee77200eeee088eeee16eeeeeeeeeeee6ceeeeeee +eeeeeeeee7e7eeeeeeee022000788eeee02261eeeee8880e022eeeee78780eeee088eeeee0222eee6886eeeeee200eeeeee078eee626220eeeeeeee2ceeeeeee +eeeeeeee0878022eeeee0022eee00eee062eeeeeeeee00eeeeeeeeee8870eeeee0877eeeeee00eee860eeeeeee00eeeeeeee00eee02600eeeeeeee06c7eeeeee +eeeeeeee07888022eeeee00600eeeeee22eeeeeeeeeeeeeeeeeeeeee000eeeeeee08888eeeeeeeee00eeeeeeeeeeeeeeeeeeeeeeee00eeeeeeeeee0277eeeeee +eeeeecccceeeeeeeeeeeeecccceeeeeeeeeeecccceeeeeeeeeeeeecccceeeeeeeeeeeeedeee11111eeeeeeee99000000eeeeeeeeeeeeeeeeeeeeee0288eeeeee +eeeeeecffcceceeeeeeeecccccceceeeeeeeeecffcceceeeeeeeecccccceceeeeeeeedddeee11dddeeeeee99aa9000499eeeeeeeeeeeeeeeeeeeeee0870eeeee +eeeeeeecffccceeeeeeeeecffcccceeeeeeeeeecffccceeeeeeeeecffcccceeeeeeeedddeedddd77eeeee9aa9a94049aa9eeeeeeeeeeeeeeeeeeeeee780eeeee +eeeeccccccc7cceeeeeeeccfccc7cceeeeeeccccccc7cceeeeeeeccfccc7cceeeeeee1ddedddddd7eeee9aa9aa94049aaa9eeeeeeeeeeeeeeeeeeeee080eeeee +eeeccccccc770ceeeeeecccccc770ceeeeeccccccc770ceeeeeecccccc770ceeeeeee111edddddddeeee9a9aaa94049aaa9eeeeeeeeeeeeeeeeeeeeee0eeeeee +eeeeeccccc770ceeeeeccccccc770ceeeeeeeccccc770ceeeeeccccccc770ceeeeeee11dedddddd1eeee9a9aa994004999eeeeeeeeeeeeeeeeeeeeeeeeeeeeee +eeeeeecccff77f0eeeeeeecccff77f0eeeeeeecccff77f0eeeeeeecccff77f0eeeeee1ddeddd1d11eeeee99999403344eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee +eeec9cc70cfffeeeeeee9c888cfffeeeeeec9cc702f2feeeeeee9cc70cfffeeeeeeeddd7e1d11111eeeeeeee449aa934eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee +eee9ff77756e2eeeeee9f888856eeeeeeee9ff77752e22eeeee9ff777e6eeeeeeeeedd77ee1111111ddddddd49aaaa94eeeeeeeeeeeeeeee1110110111dd1111 +ee7ecc77766220eeee228887766eeeeeeeee11777662222eee221177766eeeeeeeedddd7ee111117e1dddddd3bb99bb3eeeeeeeeeeeeeeeedd1111011ddddd11 +e777cccff1e260eee22888cffeeeeee2ee87e11ffeee280ee228e11ffeeeeeeeeeddddddeee11d77e011dddd4b9aa9b4eeeeeeeeeeeeeeee7dd11101dddd77d1 +087cccece16220ee222ccee11eeee2e2088111ecccee780e22211eecceeeeee2edddddddeeeed777ee1111dd4b9aa9b4eeeeeeeeeeeeeeeedd1110011ddddd11 +0888eeeee6820eee22eeeeee1eeee2220887eeeeeec7870e22eeeeeceeeee2e2edddddddeeedd777eee1111d49aaaa94eeeeeeeeeeeeebbb1111101111dd1110 +08878eeee8880eee2eeee871eeee222e08822eeeee7880ee2eeeee77eeee2222edddddddeedd7777eeee11113bb99bb3eeeeeeeeeeeebabb1111101101111000 +e07782eeee80eeeee2ee8877222222eee02222eeee8880eee2eee0887822222eeddddd11eddd7777eeeee0114b9aa9b4eeeeeeeeeebbbbbe111100110110000e +ee0888222eeeeeeeeeee82227222eeeeee0222222ee80eeeeeeee08788822eeeed111111eddddd77eeeeee004b9aa9b4eeeeeeeeebbabbbe11100010000000ee +eeeeccccceeeeeeeeeeecccceeeeeeeeeeeeccecceeeeeeeeeee2222eeeeeeeeeeeeeee8eeeeeeeeeeeeeeee49aaaa94beeeeeeebaabbeee177011111ddd11ee +eecccccccceeeeeeeeccccccceeeeeeeeeceeccecceeeeeeee278227cceeeeeeeeeeee0e82eeeeeeeeeeeeee3bb99bb3bbeeeeebbbbbbbee7777d11dd777ddde +eccccccc7cceeeeeeccccfccceeeeeeeececccccccceeeeee28777ccceceeeeeeeecccfee82eeeeeeeeeeeee4b9449b4bbbeeebbabbbbbee77777ddd77777ddd +eccccc777cceeeeecccccfc7cceeeeeeecccccccccceeeeee8ee777fcceeeeeeeccc007fe77eeeeeeeeeeeee49b44b94bbbbebbbbbbbbeee777777ddd77777dd +ccccccc777cceeeececcccc70ceeeeee27cfcccccccceeee8eefffcccceceeeeccc7777f7782eeeeeeeeeeee04bbbb40babbbbbbbb3eeeee7777777dddd7ddd1 +ccccccc7cccceeeeecccccc70ceeeeee22c7cfccffcceeeee0f77ffccccceeeeccccccff7722eeeeeeeeeeee049bb940bbabb3bbbbbbeeee777777771dddd111 +cccccccccccceeeecccccff77f0eeeee2277ffcccccceeeeeec07cccccceeeeeccffccfc7c22eeeeeeeeeeee00433400bbbbb33bebbeeeee7777777d11111101 +cccccccccccceeeececcccfffee8eeee2877f7777ccceeeeeec07ccccceceeeeccccccccfc72eeeeeeeeeeee0a4334a0bbbabbbbbee33e3377777dd111110011 +ccccccccccceeeeeeeccf777ee8eeeeee77ef700ccceeeeeeecc7cfccccceeeeecccccccccceeeee4444444999494444bbbbbbbbe333333333eebbbbbbeeeeee +ecccccccccceeeeeececcc77782eeeeee28eefccceeeeeeeeeecccfcccceeeeeecccccccceceeeee4444494994444444bbbbabbb33333bbbbbbbbbbaabbbeeee +eecccccccceeeeeeeecc722872eeeeeeee28e0eeeeeeeeeeeeeccccccceeeeeeeeccecceeceeeeee444449499444444433bbabbbbb3bbabbabbbbebbbbbbbeee +eeeecccceeeeeeeeeeee2222eeeeeeeeeeee8eeeeeeeeeeeeeeecccceeeeeeeeeeeccecceeeeeeee44444449994944443bbbb3bbbb3bbabbbbbbbbebbbbbbbee +eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee4444444999444444bb3bbbbbbabbbbbbbbebbbbbbbebbbbe +eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee4444949499944444bb3bbb3bbbbbbbebbbbebbeeeeeeeeee +eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee4444949499494444abbbbb33bbbbbbbebbbbeeeeeeeeeeee +eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee4444949999494444bbbbbbbbbbbbb33eeeeeeeeeeeeeeeee +eeeeeeeeeeeeeeeeeee5d5eeeeeeeeeeaaaaaaaaaaaaaaa9eeeeee56deeeeeeeeeeeeeed65eeeeeee30bbebe44444444ababb3bbbbb33bbbbeeeeeeeeeeeeeee +eeeeeeeeeeeeeeeeeeed65eeeeeeeeeaaaaaaaaaaaaaaa94ed6566666666666ee6666666666656de3bebeeb349444444bba33bbbbabbb3333bbeeeeeeeeeeeee +eeeeeeeeeeeeeeeeeeed65eeeeeeee9a9a9a9a9a9aaa9940d656000000000005500000000000656dbeb33b3e44444494bbbbbbbbbbbabb33333bbeeeeeeeeeee +eeeeeeeeeeeeeeeeeeed65eeeeeee9a9a9a9a9a9aaa94400d656d944dc49d91551d944dc49d9656deb3e33eb44494444b3abba3bbbbbabb333333beeeeeeeeee +eeeea994eeeeeeeeeeed65eeeeee999999999999aa944400d65694cdc0ca9a155194cdc0ca9a656de03bb03b44494444b00bb00bbbbbbbbbe33333beeeeeeeee +eeeaaaa94eeeeeeeeeed65eeeee99999999999aa99444000d656dcacac75471551dcacac7547656d3beb3ebe44444494b00bb00bbbbbbbab3333b33beeeeeeee +eeaaaaaaa999eeeeeeed65eeee9999999999aa9449444400d6569ac7ca7a7a15519ac7ca7a7a656db3b33eb3449444443b0330b33b3bbbbbb33bee3beeeeeeee +eaaaaaaaaaaa999eeeed65eee4949494999a944444444000d6564c000c094715514c000c0947656deebeebee444444440b0330b0eeebbebbbbeeeeeeeeeeeeee +eeeeeeeeeeeeeeeeeeeeeee949494449a99494444444440ed656c0ca7a77701551c0ca7a7770656de77778ee4444444444444444eeeeebbbbbeeeeeeeeeeeeee +eeeeeeeeeeeeeeeeeeeee99444449a9944444444444440eed65649a04f4977155149a04f4977656d7777788e0449449949944444eeeeeebbbbaeeeeeeeeeeeee +eeeeeeeeeeeeeeeeeeee44449a994444444444994440eeeed6569afaf00a7a15519afaf00a7a656d2288822e0449444994444000eeeeeeeebbbbeeeeeeeeeeee +eeeeeeeeeeeeeeeeee4499994444494444944494444eeeeed656dcafaf05071551dcafaf0507656de28882ee4444444999444044eeeeeeeeebbbeeeeeeeeeeee +eeeeeeeeeeeeeeeea9994494499449444494444440eeeeeed65690cdf09a5d155190cdf09a5d656dee282eee0444499444444444eeeeeeeeebbbbeeeeeeeeeee +eeeeeeeeeeee99994494444444944444449444444eeeeeeed656dc09d9d9d61551dc09d9d9d6656deee2eeee0444444444444440eeeeeeeeeebbaeeeeeeeeeee +eeeeeeee44994494449444444494444444444400eeeeeeeed60d777777777775577777777777d06deeeeeeee004499440e004440eeeeeeeeeeebaeeeeeeeeeee +eeee44994444444444444444444444444440eeeeeeeeeeeeed6000000000000ee0000000000006deeeeeeeeee0049eeeeeee000eeeeeeeeeeeeeeeeeeeeeeeee +__gff__ +0041414141414141414141414141414141414141414141414141414141410000414141434341414141414141410000004141414141414141414141414141414141414141414141414141616180808000414040414141414141414040414141414545454343434343454541414141414145455149434343434545414141418080 +0000000000000000000000000000000000000000000000000000000000000000000000000000000041418080800000000000000000000000414141800080414100000000000000000000008080804141000000000000000000004141808080804040404043430000000080418080800041414141434300000000004040008000 +__map__ +000000000000000000006e6f6e6f6e6f6e6f6f6e6f6e6f6e6f6e6f6e6f6e49466e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f497e7f7e7f7e7f7e7f7e7f7e7f7e7f7e7f7e7f7e7f000000000000000000007c7d7c7d7c7d7c7d1e1f1e1f1e1f1e1f5e5f5e5f5f +000000000000000000006e6f6e6f6e6f6e6f6f585945434443454244455554307e7f7e7f7e7f7e7f7e7f7e7f7e2e6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6f6f6e6f6e6f6e6f6e6f6e6f44417000000000000000000000000000000000000000000000000000000000000000000000000000007c7d5e5f5e5f5e5f6e6f6e6f6f +000000000000000000006e6f6e6f6e6f6e6f6f7c7d7c7d535453545354531f40000000000000000000000000002e6e6f6e6f6e6f6e6f6f584345436e6f6e6f6e6f6e6f6e6e6f6e6f6e6f520000000000000000000000000000000000000000000000000000000000000000000000000000000000516e6f6e6f6e6f7e7f7e7f7f +000000000000000000006e6f6e6f6e6f6e6f6f2e0000001e1f1e1f1e1f1e1f504d4e7a4c4e00000000000000000057464344464344425853547c7d002e2e2e6e6f6e6f6e6f2e2e2e2e2e000000000000000000000000000000000000000000000000000000000000000000000000000000000000007e7f7e7f7e7f0000000000 +000000000000000000006e6f6e6f6e6f6e6f6e2e0000005e5f5e5f5e5f5e5f6e48454645415200000000000000002f2f2f2f2f2f2f2f307d70000000002e2e2e2e2e2e2e2e2e000000000000000000000000000000007a0000000000000000000000000000000000000000000000000000000000000000000000000000000000 +000000000000000000006e6f6e6f6e6f6e6f6e2e0000516e6f6e6f6e6f6e6f49582f2f2f7d0000004d4c000000002f2f2f2f2f2f2f2f70000000000000002e2e2e2e2e2e2e000000000000000000000000000000005c5d0000000000000000000000000000000000000000000000000000000000000000000000000000000000 +000000000000000000006e6f6e6f6e6f6e6f6f2e0000007e7f7f7e7e7f6f582f2f2f2f7d000000514342520000007c7d2f2f7c7d7c7d000000000000000000000000000000000000000000004e4d4d4c4d4e5c5d4e6c6d4d00000000000000000000000000004d4e7a4c4d00000000000000004e4e4c4d4e4c4d4d4c00000000 +000000000000000000006e6f6e6f6e6f6e6f6f2e0000000000000000007f7c7c7d7c7d00000000007c7d0000000000007c7d00000000000000000000000000000000000000000000000000514241424441564146424445483c3d0000000000000000000000514344454943520000000000005142414643424246464352000000 +000000000000000000006e6f6e6f6e6f6e6f6f2e0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000535453545354535453542f5759425200000000000000000000005354305354000000000000000040407172007374404000000000 +000000000000000000006e6f6e6f6e4543444352000000000000000000000000000000000000000000000000000000000000000000000000004d4e000000004d4e4d38393434343d000000001e1f1e1f1e1f1e1f1e1f5e5f7c7d000000000000000000000000757c7d7c70000000000000000040680000000000674000000000 +000000000000000000004542444558535453544c4e7a4d4c4d000000000000000000000000000000000000000000000000000000004e4d4c5147493132323b4745444644454342463c3d4d4c1e1f1e1f1e1f1e1f5e5f6e6f00000000000000000000000000000000000000000000000000000040780000000000774000000000 +0000000000000000000030535453541e1f1e1f5642454344555200000000000000000000000000000000004d4d000000004c393a3b49444644555357454442585354535453545354594542561e1f1e1f5e5f5e5f6e6f7f7e00000000000000000000000000000000000000000000000000000040000000000000004000000000 +00000000000000000000401e1f1e1f1e1f1e1f535453545354004d4e00000000000000000000000000005143463132323b4749455554535453545354535453541e1f1e1f1e1f1e1f1e1f1e1f5e5f5e5f6e6f6e6f7e7f000000000000000000000000000000000000000000000000000000000067690000000000664000000000 +00000000000000000000401e1f1e1f1e1f1e1f1e1f1e1f7c7d514142520000000000000000000000000000535459454542555354531f1e1f1e1f1e1f1e1f1e1f1e1f1e1f1e1f1e1f1e1f5e5f6e6f6e6f7f7e7f7e0000000000000000000000000000000000000000000000000000000000000077790000000000766800000000 +000000000000000000007c7d7c7d7c7d7c7d1f1e1f1e7d0000007c7d0000000000000000000000000000007c2f53545354301e1f1e1f1e1f1e1f1e1f1e1f1e1f1e1f1e1f1e1f1e1f5e5f6e6f7f7e7e7f00000000000000000000005c5d00004d4c000000000000000000000000000051313335377561624c6364407800000000 +0000000000000000000000000000000000007c1e1f7d000000000000000000000000000000004e4d000000007c5e5f5e5f505e5f5e5f5e5f5e5f5e5f5e5f5e5f5e5f5e5f5e5f5e5f6e6f7f7e0000000000000000000000005c5d006c6d005143463c3d000000005c5d0000000000000057444447474955595854533c3d000000 +000000000000000000000000000000000000007c7d0000000000000000000000000000000051424152000000006e6f6e6f6e6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f7e7f000000000000000000004c5c5d4e6c6d4c6c6d4c4e1e1f59483c3d00006c6d00000000000000535453595854535453544059483c3d4c +00000000000000000000000000000000000000000000000000000000000000000000000000007c7d00000000007e7e7f7e7f7e7f7e7f7f7e7e7e7f7e7e7f7f7e7f7e7e7f7e7f7e7f00000000000000000000005142434649434646474848481e1f1e57594546434549434631330000001e1f1e1f1e1f1e1f1e1f404057444243 +00000000000000000000000000000000000000000000000000000000000000004a4b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004d4c00007c7d7c402f2f2f576e6f6e5e5f5e5f5e5f5e5f5e5f5e5f57555200005e5f5e5f5e5f5e5f5e5f50505e5f5e5f +0000000000000000000000000000000000000000000000000000000000000051434252000000000000000000000000000000000000000000000000000000000000000000000000000000000000000051434152000000004071727374596e6f6e6f6e6f6e6f6e6f6e6f6e6f7c7d0000516e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f +00000000000000000000000000000000000000000000000000000000000000007c7d000000000000000000000000004c4e5c5d4e4c5c5d4e4c4c000000000000000000000000000000000000000000007c7d00000000006800000000677e7f7f7e7f7f7e7e7e7f7e7f7e7f00000000002e2e2e2e2e2e2e2e2e2e2e2e6e6f6e6f +00000000000000000000000000000000000000000000000000000000000000000000000000000000004e4c38393a3b47484847484847484748473133000000000000000000000000000000000000000000000000000000780000000077000000000000000000000000000000000000000000000000000000000000002e2e2e2e +00000000000000000000000000000000000000000000000000000000000000000000000000000000514346474849414343445556434649424143414252000000000000000000000000000000000000000000000000000069000000006600000000000000000000000000000000000000000000000000000000000000002e2e2e +00000000000000000000000000004c4e0000000000000000000000000000004c4c4e4d4a4b4e4d4c4d535457585354535453545354535453545e5f7d000000000000000000000000000000004d4c5c5d4e4d4e000000007900000000760000000000000000000000000000000000004e4c000000000000000000000000002e2e +4d000000004c0000000038393a3b45463c3d00000000000000000000000066564142434142414346555e5f5e5f5e5f5e5f5e5f5e5f5e5f5e5f6e6f0000000000000000000000000000000051434455434441463133353740616263644000000000000000000000000000000000005144463c3d0000000000000000000000002e +483132323b473132323b45424155535457463c3d000000000000000000007653545354535453545e5f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f7f7e00000000000000000000000000004e4e4d53545354535430574447474748495853543c3d00000000006570000000000000000000535457463c3d4d4e5c5d00004d4e4c4e4c +41434649414243444558535453541e1f535457443c3d0000000000000065401e1f1e1f1e1f5e5f6e6f6e6f7e7f7e7f7e7f2e2e2e2e2e2e2e2e000000000000000000000000000000514149561e1f1e1f1e1f40301e5945445854401e1f42463c3d4d636470000000000000000000001e1f535459434245424152514441454645 +535453545354535453541e1f1e1f1e1f1e1f535459463c3d000000636440401e1f1e1f5e5f6e6f6e6f6e6f0000000000002e2e2e2e2e2e2e2e00000000000000000000000000004e4e301e1f1e1f1e1f1e1f40401e545354531f505e5f5e5f59454455700000004c4e4d4c4d4e4a4b1e1f1e1f535430307c7d00003053545354 +5e5f5e5f5e5f5e5f5e5f5e5f5e5f1e1f1e1f5e5f53545741424341584050505e5f5e5f6e6f6e6f6e6f6e6f00000000004d2e2e2e2e2e2e2e2e4d00000000000000000000003e3f4749401e1f1e1f1e1f1e1f50501e1f1e1f1e1f7e7f7e7f7e000000000000006556444246555644451e1f1e1f1e1f407d00000000401e1f1e1f +6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f5e5f535453545354506e6f6e6f6e6f6e6f6e6f6e6f6e6f00007a005148474748484748474748313335374e4d4c4d4c3e3f47495830505e5f5e5f5e5f5e5f6e6f5e5f1e1f1e1f000000000000004d4c4d63643053545354535453541e1f1e1f1e1f400000000000401e1f1e1f +6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f5e5f5e5f5e5f6e6f6e6f6e6f6e6f6e6f6e6e6f6e6f3536373839495857555859434143444946444748474848474845555354506f6e6f6e6f6e6f6e6f6e6f6e6f5e5f1e1f4d4c7a4e4d3e3f4749445530405e5f5e5f5e5f5e5f5e5f5e5f5e5f504e7a4d3e3f505e5f5e5f +6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6e6f6e6e6e48474558535453545354535453545354305745555657445853541e1f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f5e5f56464748474845555f5e5f50506e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f47484849466e6f6e6f +__sfx__ +011000003005032050300503205030050320503005032050300503205030050320503005032050300503205030050320503005032050300503205030050320503005032050300503205000000000000000000000 +010c00001835018350183501835018350183501835018350183501835018350183501835018350183501835018350183501835018350183501835018350183501835018350183501835018350183501835018350 +001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0110000024755287552b75530755307002d7550000032755327000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +010900003f5733f5633f5433f5233f5133f5233f5433f5633f5733f5633f5433f5233f5133f503025030250302503025030250301503025030250302503025030050300503005030050300503005030050300503 +010300001354013540145401555016550185501b5601e560235712a56100501005010050100501005010050100501005010050100501005010050100501005010050100501005010050100000000000000000000 +01030000144721447215462164621a4621d4522144222432234222342224422244222442225412254122541225412254121640216402164021640216402164021640216402164021640216402164021640216402 +01070000285512c5512d5512d5512d5512d5412d5412d5312d5212d5212d5112d5112d5112d5112d5112d5002a5012a5012a5012a5012a5012c5012c5012c5012c5012d5012d5012d5011e5011e5011e5011e501 +010400002433024330243002433024330243302433024330243302433024330243302433022300223000830008300083000830008300083000830008300083000830008300083000830008300083000830008300 +011000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +__music__ +00 41424344 +00 41424344 +00 41424344 +00 41424344 +00 41424344 +00 41424344 +00 41424344 +00 41424344 +00 41424344 +00 41424344 +00 41424344 +00 41424344 +00 41424344 +00 41424344 +00 41424344 +00 41424344 +00 41424344 +00 41424344 +00 41424344 +00 41424344 +00 41424344 +00 41424344 +00 41424344 +00 41424344 +00 41424344 +00 41424344 +00 41424344 +00 41424344 +00 41424344 +00 41424344 +00 41424344 +00 41424344 +00 41424344 +00 41424344 +00 41424344 +00 41424344 +00 41424344 +00 41424344 +00 41424344 +00 41424344 +04 39777879 diff --git a/export_cartridge_release.p8 b/export_cartridge_release.p8 index 7f2cfe95..cd17431e 100644 --- a/export_cartridge_release.p8 +++ b/export_cartridge_release.p8 @@ -14,6 +14,7 @@ cd("picosonic/v5.1_release") local entry_cartridge = "picosonic_titlemenu.p8" local additional_cartridges_list = { + "picosonic_stage_intro.p8", "picosonic_ingame.p8", "picosonic_stage_clear.p8", "data_bgm1.p8", @@ -57,7 +58,7 @@ end -- icon is a 16x16 square => -s 2 tiles wide -- with top-left at sprite 160 (run1) => -i 160 -- on pink (color 14) background => -c 14 --- and most importantly we pass ingame, stage_clear and data files as additional cartridges +-- and most importantly we pass additional logic and data files as additional cartridges export("picosonic_v5.1_release.bin "..additional_cartridges_string.." -i 160 -s 2 -c 14") mkdir("picosonic_v5.1_release.web") diff --git a/install_all_cartridges.sh b/install_all_cartridges.sh index c46109b6..de757520 100755 --- a/install_all_cartridges.sh +++ b/install_all_cartridges.sh @@ -36,7 +36,7 @@ else suffix="" fi -cartridge_list="titlemenu ingame stage_clear" +cartridge_list="titlemenu stage_intro ingame stage_clear" for cartridge in $cartridge_list; do "$game_scripts_path/install_single_cartridge.sh" "$cartridge" "$config" "$suffix" diff --git a/install_single_cartridge.sh b/install_single_cartridge.sh index a8d82dd1..db723191 100755 --- a/install_single_cartridge.sh +++ b/install_single_cartridge.sh @@ -8,7 +8,7 @@ # to manually copy data cartridges after this. # Usage: install_single_cartridge.sh cartridge_suffix config [png] -# cartridge_suffix 'titlemenu', 'ingame' or 'stage_clear' +# cartridge_suffix 'titlemenu', 'stage_intro', 'ingame' or 'stage_clear' # config build config (e.g. 'debug' or 'release'. Default: 'debug') # png if passed, the .png cartridge is installed @@ -21,7 +21,7 @@ # check that source and output paths have been provided if ! [[ $# -ge 1 && $# -le 3 ]] ; then echo "build.sh takes 1 to 2 params, provided $#: - \$1: cartridge_suffix ('titlemenu', 'ingame' or 'stage_clear') + \$1: cartridge_suffix ('titlemenu', 'stage_intro', 'ingame' or 'stage_clear') \$2: config ('debug', 'release', etc. Default: 'debug') \$3: optional suffix ('png' for .png cartridge install)" exit 1 diff --git a/install_single_cartridge_with_data.sh b/install_single_cartridge_with_data.sh index 4944a446..dfaa5a96 100755 --- a/install_single_cartridge_with_data.sh +++ b/install_single_cartridge_with_data.sh @@ -5,7 +5,7 @@ # as install_single_cartridge.sh will not copy data along and is not reliable alone. # Usage: install_single_cartridge_with_data.sh config [png] -# cartridge_suffix 'titlemenu', 'ingame' or 'stage_clear' +# cartridge_suffix 'titlemenu', 'stage_intro', 'ingame' or 'stage_clear' # config build config (e.g. 'debug' or 'release'. Default: 'debug') # png if passed, the .png cartridge is installed @@ -21,7 +21,7 @@ game_scripts_path="$(dirname "$0")" # check that source and output paths have been provided if ! [[ $# -ge 1 && $# -le 3 ]] ; then echo "build.sh takes 1 or 2 params, provided $#: - \$1: cartridge_suffix ('titlemenu', 'ingame' or 'stage_clear') + \$1: cartridge_suffix ('titlemenu', 'stage_intro', 'ingame' or 'stage_clear') \$2: config ('debug', 'release', etc. Default: 'debug') \$3: optional suffix ('png' for .png cartridge install)" exit 1 diff --git a/run_cartridge.sh b/run_cartridge.sh index 764449f0..327ff085 100755 --- a/run_cartridge.sh +++ b/run_cartridge.sh @@ -3,7 +3,7 @@ # Run game cartridge located in PICO-8 carts install folder with PICO-8 executable # Must be called after build and install script for that cartridge suffix. # Usage: run_game.sh cartridge_suffix config [extra] -# cartridge_suffix 'titlemenu', 'ingame' or 'stage_clear' +# cartridge_suffix 'titlemenu', 'stage_intro', 'ingame' or 'stage_clear' # config build config (e.g. 'debug' or 'release') # Any extra arguments are passed to pico8 diff --git a/run_itest.sh b/run_itest.sh index c0d7f0e6..ad45f918 100755 --- a/run_itest.sh +++ b/run_itest.sh @@ -3,7 +3,7 @@ # Run itest with PICO-8 executable # Usage: build_itest.sh cartridge_suffix -# cartridge_suffix 'titlemenu', 'ingame' or 'stage_clear' +# cartridge_suffix 'titlemenu', 'stage_intro', 'ingame' or 'stage_clear' # Any extra arguments are passed to pico8 diff --git a/sonic-2d-tech-demo.sublime-project b/sonic-2d-tech-demo.sublime-project index 8921e741..b11ae66a 100644 --- a/sonic-2d-tech-demo.sublime-project +++ b/sonic-2d-tech-demo.sublime-project @@ -52,6 +52,12 @@ "cmd": ["pico8 data/builtin_data_ingame.p8"], "working_dir": "${project_path}" }, + { + "name": "Game: edit built-in data: stage_intro", + "shell": true, + "cmd": ["pico8 data/builtin_data_stage_intro.p8"], + "working_dir": "${project_path}" + }, { "name": "Game: edit built-in data: stage_clear", "shell": true, @@ -190,6 +196,29 @@ } ] }, + { + "name": "Game: build cartridge stage_intro", + "working_dir": "${project_path}", + "shell": true, + "cmd": ["./build_and_install_single_cartridge_with_data.sh stage_intro debug"], + "file_regex": "(.*\\.sh)(?:\\: line )([\\d]+)(?:\\:)([\\d]+)? (.*)", + "syntax": "Packages/Python/Python.sublime-syntax", + "variants": + [ + { + "name": "debug", + "cmd": ["./build_and_install_single_cartridge_with_data.sh stage_intro debug"] + }, + { + "name": "cheat", + "cmd": ["./build_and_install_single_cartridge_with_data.sh stage_intro cheat"] + }, + { + "name": "release", + "cmd": ["./build_and_install_single_cartridge_with_data.sh stage_intro release"] + } + ] + }, { "name": "Game: build cartridge stage_clear", "working_dir": "${project_path}", @@ -252,6 +281,14 @@ "file_regex": "(.*\\.sh)(?:\\: line )([\\d]+)(?:\\:)([\\d]+)? (.*)", "syntax": "Packages/Python/Python.sublime-syntax", }, + { + "name": "Game: build itests stage_intro", + "working_dir": "${project_path}", + "shell": true, + "cmd": ["./build_itest.sh stage_intro"], + "file_regex": "(.*\\.sh)(?:\\: line )([\\d]+)(?:\\:)([\\d]+)? (.*)", + "syntax": "Packages/Python/Python.sublime-syntax", + }, { "name": "Game: build itests stage_clear", "working_dir": "${project_path}", @@ -346,6 +383,33 @@ }, ] }, + { + "name": "Game: build and run stage_intro", + "working_dir": "${project_path}", + "shell": true, + "cmd": ["./build_and_install_single_cartridge_with_data.sh stage_intro debug && (./pico-boots/scripts/reload.sh || ./run_cartridge.sh stage_intro debug)"], + "file_regex": "(.*\\.sh)(?:\\: line )([\\d]+)(?:\\:)([\\d]+)? (.*)", + "syntax": "Packages/Python/Python.sublime-syntax", + "variants": + [ + { + "name": "debug", + "cmd": ["./build_and_install_single_cartridge_with_data.sh stage_intro debug && (./pico-boots/scripts/reload.sh || ./run_cartridge.sh stage_intro debug)"] + }, + { + "name": "cheat", + "cmd": ["./build_and_install_single_cartridge_with_data.sh stage_intro cheat && (./pico-boots/scripts/reload.sh || ./run_cartridge.sh stage_intro cheat)"] + }, + { + "name": "tuner", + "cmd": ["./build_and_install_single_cartridge_with_data.sh stage_intro tuner && (./pico-boots/scripts/reload.sh || ./run_cartridge.sh stage_intro tuner)"] + }, + { + "name": "release", + "cmd": ["./build_and_install_single_cartridge_with_data.sh stage_intro release && (./pico-boots/scripts/reload.sh || ./run_cartridge.sh stage_intro release)"] + }, + ] + }, { "name": "Game: build and run stage_clear", "working_dir": "${project_path}", @@ -416,6 +480,14 @@ "file_regex": "(.*\\.sh)(?:\\: line )([\\d]+)(?:\\:)([\\d]+)? (.*)", "syntax": "Packages/Python/Python.sublime-syntax", }, + { + "name": "Game: build and run itests stage_intro", + "working_dir": "${project_path}", + "shell": true, + "cmd": ["./build_itest.sh stage_intro && (./pico-boots/scripts/reload.sh || ./run_itest.sh stage_intro)"], + "file_regex": "(.*\\.sh)(?:\\: line )([\\d]+)(?:\\:)([\\d]+)? (.*)", + "syntax": "Packages/Python/Python.sublime-syntax", + }, { "name": "Game: build and run itests stage_clear", "working_dir": "${project_path}", @@ -492,6 +564,22 @@ "name": "titlemenu release", "cmd": ["./run_cartridge.sh titlemenu release"] }, + { + "name": "stage_intro debug", + "cmd": ["./run_cartridge.sh stage_intro debug"] + }, + { + "name": "stage_intro cheat", + "cmd": ["./run_cartridge.sh stage_intro cheat"] + }, + { + "name": "stage_intro tuner", + "cmd": ["./run_cartridge.sh stage_intro tuner"] + }, + { + "name": "stage_intro release", + "cmd": ["./run_cartridge.sh stage_intro release"] + }, { "name": "stage_clear debug", "cmd": ["./run_cartridge.sh stage_clear debug"] @@ -610,6 +698,36 @@ } ] }, + { + "name": "Game: install cartridge stage_intro (with data)", + "working_dir": "${project_path}", + "shell": true, + "cmd": ["./install_single_cartridge_with_data.sh stage_intro debug"], + "syntax": "Packages/Python/Python.sublime-syntax", + "variants": + [ + { + "name": "debug", + "cmd": ["./install_single_cartridge_with_data.sh stage_intro debug"] + }, + { + "name": "cheat", + "cmd": ["./install_single_cartridge_with_data.sh stage_intro cheat"] + }, + { + "name": "release", + "cmd": ["./install_single_cartridge_with_data.sh stage_intro release"] + }, + { + "name": "cheat (png)", + "cmd": ["./install_single_cartridge_with_data.sh stage_intro cheat png"] + }, + { + "name": "release (png)", + "cmd": ["./install_single_cartridge_with_data.sh stage_intro release png"] + } + ] + }, { "name": "Game: install cartridge stage_clear (with data)", "working_dir": "${project_path}", @@ -709,6 +827,10 @@ "name": "ingame", "cmd": ["./test.sh ingame"], }, + { + "name": "stage_intro", + "cmd": ["./test.sh stage_intro"], + }, { "name": "stage_clear", "cmd": ["./test.sh stage_clear"], @@ -819,6 +941,51 @@ "ENABLE_RENDER": "1" } }, + { + "name": "headless_itests stage_intro", + "cmd": ["./test.sh -f headless_itests"], + "env": { + "ITEST_CARTRIDGE_SUFFIX": "stage_intro" + } + }, + { + "name": "headless_itests stage_intro (coverage)", + "cmd": ["./test.sh -C -f headless_itests"], + "env": { + "ITEST_CARTRIDGE_SUFFIX": "stage_intro" + } + }, + { + "name": "headless_itests stage_intro (render)", + "cmd": ["./test.sh -f headless_itests"], + "env": { + "ITEST_CARTRIDGE_SUFFIX": "stage_intro", + "ENABLE_RENDER": "1" + } + }, + { + "name": "headless_itests stage_intro (render + coverage)", + "cmd": ["./test.sh -C -f headless_itests"], + "env": { + "ITEST_CARTRIDGE_SUFFIX": "stage_intro", + "ENABLE_RENDER": "1" + } + }, + { + "name": "headless_itests stage_intro - solo", + "cmd": ["./test.sh -f headless_itests -m solo"], + "env": { + "ITEST_CARTRIDGE_SUFFIX": "stage_intro" + } + }, + { + "name": "headless_itests stage_intro - solo (render)", + "cmd": ["./test.sh -f headless_itests -m solo"], + "env": { + "ITEST_CARTRIDGE_SUFFIX": "stage_intro", + "ENABLE_RENDER": "1" + } + }, { "name": "headless_itests stage_clear", "cmd": ["./test.sh -f headless_itests"], @@ -903,7 +1070,7 @@ "working_dir": "${project_path}", "shell": true, // note that luacov report is generated in the working directory, so the second test report will overwrite the previous one - "cmd": ["ITEST_CARTRIDGE_SUFFIX=ignore ./test.sh && echo '' && ITEST_CARTRIDGE_SUFFIX=titlemenu ./test.sh -f headless_itests && echo '' && ITEST_CARTRIDGE_SUFFIX=ingame ./test.sh -f headless_itests && echo '' && ITEST_CARTRIDGE_SUFFIX=stage_clear ./test.sh -f headless_itests && echo '' && pico-boots/test.sh"], + "cmd": ["ITEST_CARTRIDGE_SUFFIX=ignore ./test.sh && echo '' && ITEST_CARTRIDGE_SUFFIX=titlemenu ./test.sh -f headless_itests && echo '' && ITEST_CARTRIDGE_SUFFIX=ingame ./test.sh -f headless_itests && echo '' && ITEST_CARTRIDGE_SUFFIX=stage_clear ./test.sh -f headless_itests && echo '' && ITEST_CARTRIDGE_SUFFIX=stage_intro ./test.sh -f headless_itests && echo '' && pico-boots/test.sh"], // known limitation: last \n won't be detected in build output, so cannot display multi-line errors "file_regex": "((?:pico-boots/src|src)\\/[-\\w\\/]+\\.lua)(?:\\:| @ )([\\d]+)(?:\\:)?([\\d]+)?\\n?(.*)", "syntax": "Packages/Python/Python.sublime-syntax", diff --git a/src/application/picosonic_app_stage_intro.lua b/src/application/picosonic_app_stage_intro.lua new file mode 100644 index 00000000..0cef875e --- /dev/null +++ b/src/application/picosonic_app_stage_intro.lua @@ -0,0 +1,17 @@ +-- game application for state: stage_intro +-- used by main and itest_main + +-- this really only defines used gamestates +-- and wouldn't be necessary if we injected gamestates from main scripts + +local picosonic_app_base = require("application/picosonic_app_base") + +local stage_intro_state = require("stage_intro/stage_intro_state") + +local picosonic_app_stage_intro = derived_class(picosonic_app_base) + +function picosonic_app_stage_intro:instantiate_gamestates() -- override + return {stage_intro_state()} +end + +return picosonic_app_stage_intro diff --git a/src/application/picosonic_app_titlemenu.lua b/src/application/picosonic_app_titlemenu.lua index 4f934f74..e05ea4df 100644 --- a/src/application/picosonic_app_titlemenu.lua +++ b/src/application/picosonic_app_titlemenu.lua @@ -9,10 +9,10 @@ local picosonic_app_base = require("application/picosonic_app_base") local titlemenu = require("menu/titlemenu") local credits = require("menu/credits") -local picosonic_app_ingame = derived_class(picosonic_app_base) +local picosonic_app_titlemenu = derived_class(picosonic_app_base) -function picosonic_app_ingame:instantiate_gamestates() -- override +function picosonic_app_titlemenu:instantiate_gamestates() -- override return {titlemenu(), credits()} end -return picosonic_app_ingame +return picosonic_app_titlemenu diff --git a/src/common_stage_intro.lua b/src/common_stage_intro.lua new file mode 100644 index 00000000..b05392a3 --- /dev/null +++ b/src/common_stage_intro.lua @@ -0,0 +1,23 @@ +-- Require all common stage_intro modules (used across various scripts in game project) +-- that define globals and don't return a module table +-- Equivalent to engine/common.lua but for stage_intro cartridge. +-- Usage: add require("common_stage_intro") at the top of each of your stage_intro main scripts +-- (along with "engine/common") and in bustedhelper_stage_intro + +require("engine/core/table_helper") -- merge (to add the visual_stage_intro_addon and visual_menu_addon) + +-- we need sprite flags to draw grass on top of the rest +require("data/sprite_flags") + +-- just kept for the scripted player character fall (there is no real physics so we could also get +-- around using actual motion states) +require("ingame/playercharacter_enums") + +--[[#pico8 +--#if unity + +-- see explanations in common_ingame.lua +require("ordered_require_stage_intro") + +--#endif +--#pico8]] diff --git a/src/itest_main_stage_intro.lua b/src/itest_main_stage_intro.lua new file mode 100644 index 00000000..207b13e1 --- /dev/null +++ b/src/itest_main_stage_intro.lua @@ -0,0 +1,69 @@ +-- main source file for all itests, used to run itests in pico8 + +-- must require at main top, to be used in any required modules from here +require("engine/pico8/api") +require("engine/common") +require("common_stage_intro") + +-- require visual add-on for ingame (also used for stage_intro), so any require visual_common +-- in this cartridge will get both common data and ingame data +require("resources/visual_ingame_addon") + +local itest_manager = require("engine/test/itest_manager") + +--#if log +local logging = require("engine/debug/logging") +--#endif + +local picosonic_app_stage_intro = require("application/picosonic_app_stage_intro") + +-- set app immediately so during itest registration by require, +-- time_trigger can access app fps +local app = picosonic_app_stage_intro() +itest_manager.itest_run.app = app + +-- tag to add require for itest files here +--[[add_require]] + +function _init() +--#if log + -- register log streams to output logs to both the console and the file log + logging.logger:register_stream(logging.console_log_stream) + logging.logger:register_stream(logging.file_log_stream) + logging.file_log_stream.file_prefix = "picosonic_itest_stage_intro" + + -- clear log file on new itest session + logging.file_log_stream:clear() + + logging.logger.active_categories = { + -- engine + ['default'] = true, + -- ['codetuner'] = nil, + -- ['flow'] = nil, + ['itest'] = true, + -- ['log'] = nil, + -- ['ui'] = nil, + ['frame'] = true, + ['frame2'] = true, + ['trace'] = true, + + -- game + -- ['spring'] = true, + -- ['...'] = true, + } +--#endif + + picosonic_app_stage_intro.initial_gamestate = ':stage_intro' + + -- start first itest + itest_manager:init_game_and_start_next_itest() +end + +function _update60() + itest_manager:handle_input() + itest_manager:update() +end + +function _draw() + itest_manager:draw() +end diff --git a/src/itests/stage_clear/iteststage_clear.lua b/src/itests/stage_clear/iteststage_clear.lua index 6ee5abce..52d5d39d 100644 --- a/src/itests/stage_clear/iteststage_clear.lua +++ b/src/itests/stage_clear/iteststage_clear.lua @@ -10,7 +10,7 @@ local visual = require("resources/visual_common") -- we should require ingamead itest_manager:register_itest('player waits', {':stage_clear'}, function () - -- enter title menu + -- enter stage clear state, simulate data from ingame cartridge and goal plate in tilemap setup_callback(function (app) -- simulate having stored picked emeralds bitset from ingame cartridge -- 0b01001001 -> 73 (low-endian, so lowest bit is for emerald 1) diff --git a/src/itests/stage_intro/iteststage_intro.lua b/src/itests/stage_intro/iteststage_intro.lua new file mode 100644 index 00000000..733d7743 --- /dev/null +++ b/src/itests/stage_intro/iteststage_intro.lua @@ -0,0 +1,27 @@ +-- gamestates: stage_intro +local itest_manager = require("engine/test/itest_manager") +local input = require("engine/input/input") +local flow = require("engine/application/flow") +local visual = require("resources/visual_common") -- we should require ingameadd-on in main + +-- testing credits is easier than entering stage +-- because stage in on another cartridge (ingame), +-- and itest builds are done separately (so we'd need to stub load) +itest_manager:register_itest('player waits', + {':stage_intro'}, function () + + -- enter stage intro state + setup_callback(function (app) + flow:change_gamestate_by_type(':stage_intro') + end) + + -- let stage intro sequence play and see if nothing crashes + wait(750, true) + + -- we should still be in stage intro (because even if we load() titlemenu cartridge in headless, + -- it won't do anything) + final_assert(function () + return flow.curr_state.type == ':stage_intro', "current game state is not ':stage_intro', has instead type: "..flow.curr_state.type + end) + +end) diff --git a/src/main_stage_intro.lua b/src/main_stage_intro.lua new file mode 100644 index 00000000..19ab4547 --- /dev/null +++ b/src/main_stage_intro.lua @@ -0,0 +1,90 @@ +-- main entry file for the stage_intro cartridge +-- game states: stage_intro + +-- must require at main top, to be used in any required modules from here +require("engine/pico8/api") +require("engine/common") +require("common_stage_intro") + +-- require ingame visual add-on for stage clear since we already show the stage +-- any require visual_common in this cartridge will get both common data and ingame data +require("resources/visual_ingame_addon") + +-- we also require codetuner so any file can used tuned() +-- if tuner symbol is defined, then we also initialize it in init +local codetuner = require("engine/debug/codetuner") + +--#if log +local logging = require("engine/debug/logging") +--#endif + +--#if visual_logger +local vlogger = require("engine/debug/visual_logger") +--#endif + +--#if profiler +local profiler = require("engine/debug/profiler") +--#endif + +local picosonic_app_stage_intro = require("application/picosonic_app_stage_intro") + +local app = picosonic_app_stage_intro() + +function _init() +--#if log + -- start logging before app in case we need to read logs about app start itself + logging.logger:register_stream(logging.console_log_stream) + logging.logger:register_stream(logging.file_log_stream) +--#if visual_logger + logging.logger:register_stream(vlogger.vlog_stream) +--#endif + + logging.file_log_stream.file_prefix = "picosonic_stage_intro" + + -- clear log file on new game session (or to preserve the previous log, + -- you could add a newline and some "[SESSION START]" tag instead) + logging.file_log_stream:clear() + + logging.logger.active_categories = { + -- engine + ['default'] = true, + ['codetuner'] = true, + ['flow'] = true, + ['itest'] = true, + ['log'] = true, + -- ['ui'] = true, + -- ['trace'] = true, + -- ['trace2'] = true, + -- ['frame'] = true, + + -- game + -- ['...'] = true, + } +--#endif + +--#if visual_logger + -- uncomment to enable visual logger + -- vlogger.window:show() +--#endif + +--#if profiler + -- uncomment to enable profiler + profiler.window:show(colors.orange) +--#endif + +--#if tuner + codetuner:show() + codetuner.active = true +--#endif + + app.initial_gamestate = ':stage_intro' + app:start() +end + +function _update60() + app:update() +end + +function _draw() + app:draw() +end diff --git a/src/menu/titlemenu.lua b/src/menu/titlemenu.lua index 41a79471..dd8a09fd 100644 --- a/src/menu/titlemenu.lua +++ b/src/menu/titlemenu.lua @@ -18,7 +18,7 @@ titlemenu.type = ':titlemenu' -- sequence of menu items to display, with their target states local menu_item_params = { {"start", function(app) - load('picosonic_ingame.p8') + load('picosonic_stage_intro.p8') end}, {"credits", function(app) flow:query_gamestate_type(':credits') diff --git a/src/resources/visual_ingame_addon.lua b/src/resources/visual_ingame_addon.lua index ca3bee57..b55b4b10 100644 --- a/src/resources/visual_ingame_addon.lua +++ b/src/resources/visual_ingame_addon.lua @@ -70,7 +70,7 @@ local ingame_animated_sprite_data_t = { ingame_sprite_data_t.goal_plate_sonic, ingame_sprite_data_t.goal_plate_rotating_45_ccw, ingame_sprite_data_t.goal_plate_rotating_90, - ingame_sprite_data_t.goal_plate_rotating_45_cw, + ingame_sprite_data_t.goal_plate_rotating_45_cw }, 3, 4 -- anim_loop_modes.loop (will be stopped from code) diff --git a/src/stage_intro/stage_intro_state.lua b/src/stage_intro/stage_intro_state.lua new file mode 100644 index 00000000..33eccec7 --- /dev/null +++ b/src/stage_intro/stage_intro_state.lua @@ -0,0 +1,12 @@ +local gamestate = require("engine/application/gamestate") + +local stage_intro_state = derived_class(gamestate) + +stage_intro_state.type = ':stage_intro' + +function stage_intro_state:on_enter() + -- immediately load stage for now, as this is just a stub for the future stage intro + load('picosonic_ingame.p8') +end + +return stage_intro_state diff --git a/src/stage_intro/stage_intro_state_utest.lua b/src/stage_intro/stage_intro_state_utest.lua new file mode 100644 index 00000000..e69de29b diff --git a/src/test/bustedhelper_stage_intro.lua b/src/test/bustedhelper_stage_intro.lua new file mode 100644 index 00000000..d3e57497 --- /dev/null +++ b/src/test/bustedhelper_stage_intro.lua @@ -0,0 +1,14 @@ +-- engine bustedhelper equivalent for game project +-- it adds stage_intro common module, since the original bustedhelper.lua +-- is part of engine and therefore cannot reference game modules +-- it also adds visual stage_intro add-on to simulate main providing it to any stage_intro scripts +-- this is useful even when the utest doesn't test visual data usage directly, +-- as some modules like stage_state and tile_test_data define outer scope vars +-- relying on stage_intro visual data +-- Usage: +-- in your game utests, always require("test/bustedhelper_stage_intro") at the top +-- instead of "engine/test/bustedhelper" +require("engine/test/bustedhelper") +require("common_stage_intro") +-- we already need to draw the environment and sonic, so also use ingame visuals during stage_intro +require("resources/visual_ingame_addon") diff --git a/src/tests/headless_itests_utest.lua b/src/tests/headless_itests_utest.lua index 32331f95..1f5838d3 100644 --- a/src/tests/headless_itests_utest.lua +++ b/src/tests/headless_itests_utest.lua @@ -35,6 +35,8 @@ elseif cartridge_suffix == 'ingame' then initial_gamestate = ':stage' elseif cartridge_suffix == 'stage_clear' then initial_gamestate = ':stage_clear' +elseif cartridge_suffix == 'stage_intro' then + initial_gamestate = ':stage_intro' else assert(false, "unknown cartridge_suffix "..cartridge_suffix) end From e5864c21b2ee80a2fc511379ddf5b692589196f2 Mon Sep 17 00:00:00 2001 From: huulong Date: Tue, 29 Dec 2020 17:35:03 +0100 Subject: [PATCH 33/91] [VERSION] Bumped to 5.1+ and now also write version in cartridge metadata (first two commented lines) --- build_itest.sh | 2 +- build_pico8_utests.sh | 2 +- build_single_cartridge.sh | 4 ++-- data/metadata.p8 | 1 + install_all_cartridges.sh | 2 +- install_single_cartridge.sh | 2 +- install_single_cartridge_with_data.sh | 2 +- run_cartridge.sh | 2 +- run_itest.sh | 2 +- run_pico8_utests.sh | 2 +- 10 files changed, 11 insertions(+), 10 deletions(-) diff --git a/build_itest.sh b/build_itest.sh index 7b0bd8e4..ff0419ce 100755 --- a/build_itest.sh +++ b/build_itest.sh @@ -16,7 +16,7 @@ build_output_path="$(dirname "$0")/build" author="leyn" title="pico sonic itests (all)" cartridge_stem="picosonic_itest_all" -version="5.1" +version="5.1+" config='itest' # symbols='assert,log,visual_logger,tuner,profiler,mouse,itest' # cheat needed to set debug motion mode diff --git a/build_pico8_utests.sh b/build_pico8_utests.sh index aad24187..cbda59ab 100755 --- a/build_pico8_utests.sh +++ b/build_pico8_utests.sh @@ -16,7 +16,7 @@ build_output_path="$(dirname "$0")/build" author="leyn" title="pico sonic - pico8 utests (all)" cartridge_stem="picosonic_pico8_utests_all" -version="5.1" +version="5.1+" config='debug' symbols='assert,tostring,dump,log,p8utest' diff --git a/build_single_cartridge.sh b/build_single_cartridge.sh index 7df20920..aa7eb544 100755 --- a/build_single_cartridge.sh +++ b/build_single_cartridge.sh @@ -12,9 +12,9 @@ data_path="$(dirname "$0")/data" # Configuration: cartridge author="leyn" -title="pico sonic" cartridge_stem="picosonic" -version="5.1" +version="5.1+" +title="pico sonic v$version" help() { echo "Build a PICO-8 cartridge with the passed config." diff --git a/data/metadata.p8 b/data/metadata.p8 index fc7c01f6..1430d8e8 100644 --- a/data/metadata.p8 +++ b/data/metadata.p8 @@ -3,6 +3,7 @@ version 16 __lua__ -- pico sonic -- by leyn +-- (title and author above will be overwritten by add_metadata.py, so don't mind them too much) __label__ 1c1111111111111111111111111111111111c11111111111111111111111111111111111c11111111111111111111111111111111111c1111111111111111111 1111111111111111111111111111111c1111111111111111111111111111111111c11111111111111111111111111111111111c1111111111111111111111111 diff --git a/install_all_cartridges.sh b/install_all_cartridges.sh index de757520..cac23662 100755 --- a/install_all_cartridges.sh +++ b/install_all_cartridges.sh @@ -26,7 +26,7 @@ if ! [[ $# -ge 1 && $# -le 2 ]] ; then fi # Configuration: cartridge -version="5.1" +version="5.1+" config="$1"; shift # option "png" will export the png cartridge diff --git a/install_single_cartridge.sh b/install_single_cartridge.sh index db723191..9f36d56e 100755 --- a/install_single_cartridge.sh +++ b/install_single_cartridge.sh @@ -29,7 +29,7 @@ fi # Configuration: cartridge cartridge_stem="picosonic" -version="5.1" +version="5.1+" cartridge_suffix="$1"; shift config="$1"; shift diff --git a/install_single_cartridge_with_data.sh b/install_single_cartridge_with_data.sh index dfaa5a96..27a5aafd 100755 --- a/install_single_cartridge_with_data.sh +++ b/install_single_cartridge_with_data.sh @@ -28,7 +28,7 @@ if ! [[ $# -ge 1 && $# -le 3 ]] ; then fi # Configuration: cartridge -version="5.1" +version="5.1+" cartridge_suffix="$1"; shift config="$1"; shift diff --git a/run_cartridge.sh b/run_cartridge.sh index 327ff085..158bb6cb 100755 --- a/run_cartridge.sh +++ b/run_cartridge.sh @@ -11,7 +11,7 @@ # Configuration: cartridge cartridge_stem="picosonic" -version="5.1" +version="5.1+" # shift allows to pass extra arguments as $@ cartridge_suffix="$1"; shift diff --git a/run_itest.sh b/run_itest.sh index ad45f918..c52bd4b7 100755 --- a/run_itest.sh +++ b/run_itest.sh @@ -9,7 +9,7 @@ # Configuration: cartridge cartridge_stem="picosonic_itest_all" -version="5.1" +version="5.1+" cartridge_suffix="$1"; shift diff --git a/run_pico8_utests.sh b/run_pico8_utests.sh index 5f5c2c62..e9695d42 100755 --- a/run_pico8_utests.sh +++ b/run_pico8_utests.sh @@ -5,7 +5,7 @@ # Configuration: cartridge cartridge_stem="picosonic_pico8_utests_all" -version="5.1" +version="5.1+" run_cmd="pico8 -run build/${cartridge_stem}_v${version}_debug.p8 -screenshot_scale 4 -gif_scale 4 $@" From 8e0e44b43c69a4e904d196dd6d4c2cbf923cc412 Mon Sep 17 00:00:00 2001 From: huulong Date: Tue, 29 Dec 2020 17:35:11 +0100 Subject: [PATCH 34/91] [CHANGELOG] Added unreleased changelog --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2283ba53..624a7630 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,10 +8,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] ### Added - Physics: added one-way platform system. Added one-way platform tiles and integrated them in stage +- Stage clear: added retry screen with missed emeralds and options to retry with or without emerald, or go back to title menu +- Stage clear: retry screen fades in and out with a zigzag swipe animation and/or gradual color darkness (palette swap) +- Stage intro: stage fades in with gradual color darkness (palette swap) ### Changed - Sprite: fixed top row of some Sonic rotated run sprites missing - Sprite: reduced emerald size by 2px in both directions, adjusted HUD +- Stage clear: removed emerald cross, arranged emerald position to match retry screen ## [5.1] - 2020-12-17 ### Changed From 4b1d12f073e71685a114af90d174d280d5ba19fd Mon Sep 17 00:00:00 2001 From: huulong Date: Tue, 29 Dec 2020 17:36:15 +0100 Subject: [PATCH 35/91] [VERSION] Also updated version in export .p8 --- export_cartridge_release.p8 | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/export_cartridge_release.p8 b/export_cartridge_release.p8 index cd17431e..04f4b6df 100644 --- a/export_cartridge_release.p8 +++ b/export_cartridge_release.p8 @@ -9,7 +9,7 @@ __lua__ -- Note that it will not warn if cartridge is not found. -- Paths are relative to PICO-8 carts directory. -cd("picosonic/v5.1_release") +cd("picosonic/v5.1+_release") local entry_cartridge = "picosonic_titlemenu.p8" @@ -24,7 +24,7 @@ local additional_cartridges_list = { } -- prepare folder for png cartridges -mkdir("picosonic_v5.1_release.png") +mkdir("picosonic_v5.1+_release.png") -- load each additional cartridge to save it as png cartridge -- in folder created above @@ -32,7 +32,7 @@ mkdir("picosonic_v5.1_release.png") for cartridge_name in all(additional_cartridges_list) do load(cartridge_name) - cd("picosonic_v5.1_release.png") + cd("picosonic_v5.1+_release.png") save(cartridge_name..".png") cd("..") end @@ -42,7 +42,7 @@ end load(entry_cartridge) -- save as png cartridge -cd("picosonic_v5.1_release.png") +cd("picosonic_v5.1+_release.png") save(entry_cartridge..".png") cd("..") @@ -59,9 +59,9 @@ end -- with top-left at sprite 160 (run1) => -i 160 -- on pink (color 14) background => -c 14 -- and most importantly we pass additional logic and data files as additional cartridges -export("picosonic_v5.1_release.bin "..additional_cartridges_string.." -i 160 -s 2 -c 14") +export("picosonic_v5.1+_release.bin "..additional_cartridges_string.." -i 160 -s 2 -c 14") -mkdir("picosonic_v5.1_release.web") -cd("picosonic_v5.1_release.web") -export("picosonic_v5.1_release.html "..additional_cartridges_string.." -i 160 -s 2 -c 14") +mkdir("picosonic_v5.1+_release.web") +cd("picosonic_v5.1+_release.web") +export("picosonic_v5.1+_release.html "..additional_cartridges_string.." -i 160 -s 2 -c 14") cd("..") From b289d8068d0305f7552024df48236c8b8f03aaed Mon Sep 17 00:00:00 2001 From: huulong Date: Tue, 29 Dec 2020 17:47:20 +0100 Subject: [PATCH 36/91] [VERSION] Moved version to data/version.txt, except for export .p8 script which cannot read external files --- build_itest.sh | 4 ++-- build_pico8_utests.sh | 4 ++-- build_single_cartridge.sh | 2 +- data/version.txt | 1 + export_cartridge_release.p8 | 20 ++++++++++++-------- install_all_cartridges.sh | 3 ++- install_single_cartridge.sh | 5 ++++- install_single_cartridge_with_data.sh | 3 ++- run_cartridge.sh | 5 ++++- run_itest.sh | 5 ++++- run_pico8_utests.sh | 5 ++++- 11 files changed, 38 insertions(+), 19 deletions(-) create mode 100644 data/version.txt diff --git a/build_itest.sh b/build_itest.sh index ff0419ce..08510451 100755 --- a/build_itest.sh +++ b/build_itest.sh @@ -13,10 +13,10 @@ data_path="$(dirname "$0")/data" build_output_path="$(dirname "$0")/build" # Configuration: cartridge +version=`cat "$data_path/version.txt"` author="leyn" -title="pico sonic itests (all)" +title="pico sonic itests (all) v$version" cartridge_stem="picosonic_itest_all" -version="5.1+" config='itest' # symbols='assert,log,visual_logger,tuner,profiler,mouse,itest' # cheat needed to set debug motion mode diff --git a/build_pico8_utests.sh b/build_pico8_utests.sh index cbda59ab..ba46309e 100755 --- a/build_pico8_utests.sh +++ b/build_pico8_utests.sh @@ -13,10 +13,10 @@ data_path="$(dirname "$0")/data" build_output_path="$(dirname "$0")/build" # Configuration: cartridge +version=`cat "$data_path/version.txt"` author="leyn" -title="pico sonic - pico8 utests (all)" +title="pico sonic - pico8 utests (all) v$version" cartridge_stem="picosonic_pico8_utests_all" -version="5.1+" config='debug' symbols='assert,tostring,dump,log,p8utest' diff --git a/build_single_cartridge.sh b/build_single_cartridge.sh index aa7eb544..ba7382d9 100755 --- a/build_single_cartridge.sh +++ b/build_single_cartridge.sh @@ -11,9 +11,9 @@ game_src_path="$(dirname "$0")/src" data_path="$(dirname "$0")/data" # Configuration: cartridge +version=`cat "$data_path/version.txt"` author="leyn" cartridge_stem="picosonic" -version="5.1+" title="pico sonic v$version" help() { diff --git a/data/version.txt b/data/version.txt new file mode 100644 index 00000000..34dd4fc6 --- /dev/null +++ b/data/version.txt @@ -0,0 +1 @@ +5.1+ diff --git a/export_cartridge_release.p8 b/export_cartridge_release.p8 index 04f4b6df..df957853 100644 --- a/export_cartridge_release.p8 +++ b/export_cartridge_release.p8 @@ -9,7 +9,11 @@ __lua__ -- Note that it will not warn if cartridge is not found. -- Paths are relative to PICO-8 carts directory. -cd("picosonic/v5.1+_release") +-- PICO-8 cannot read data/version.txt, so exceptionally set the version manually here +local version = "5.1+" +local cartridge_basename = "picosonic_v"..version.."_release" + +cd("picosonic/v"..version.."_release") local entry_cartridge = "picosonic_titlemenu.p8" @@ -24,7 +28,7 @@ local additional_cartridges_list = { } -- prepare folder for png cartridges -mkdir("picosonic_v5.1+_release.png") +mkdir(cartridge_basename..".png") -- load each additional cartridge to save it as png cartridge -- in folder created above @@ -32,7 +36,7 @@ mkdir("picosonic_v5.1+_release.png") for cartridge_name in all(additional_cartridges_list) do load(cartridge_name) - cd("picosonic_v5.1+_release.png") + cd(cartridge_basename..".png") save(cartridge_name..".png") cd("..") end @@ -42,7 +46,7 @@ end load(entry_cartridge) -- save as png cartridge -cd("picosonic_v5.1+_release.png") +cd(cartridge_basename..".png") save(entry_cartridge..".png") cd("..") @@ -59,9 +63,9 @@ end -- with top-left at sprite 160 (run1) => -i 160 -- on pink (color 14) background => -c 14 -- and most importantly we pass additional logic and data files as additional cartridges -export("picosonic_v5.1+_release.bin "..additional_cartridges_string.." -i 160 -s 2 -c 14") +export(cartridge_basename..".bin "..additional_cartridges_string.." -i 160 -s 2 -c 14") -mkdir("picosonic_v5.1+_release.web") -cd("picosonic_v5.1+_release.web") -export("picosonic_v5.1+_release.html "..additional_cartridges_string.." -i 160 -s 2 -c 14") +mkdir(cartridge_basename..".web") +cd(cartridge_basename..".web") +export(cartridge_basename..".html "..additional_cartridges_string.." -i 160 -s 2 -c 14") cd("..") diff --git a/install_all_cartridges.sh b/install_all_cartridges.sh index cac23662..f51a9bcd 100755 --- a/install_all_cartridges.sh +++ b/install_all_cartridges.sh @@ -16,6 +16,7 @@ # Configuration: paths game_scripts_path="$(dirname "$0")" +data_path="$(dirname "$0")/data" # check that source and output paths have been provided if ! [[ $# -ge 1 && $# -le 2 ]] ; then @@ -26,7 +27,7 @@ if ! [[ $# -ge 1 && $# -le 2 ]] ; then fi # Configuration: cartridge -version="5.1+" +version=`cat "$data_path/version.txt"` config="$1"; shift # option "png" will export the png cartridge diff --git a/install_single_cartridge.sh b/install_single_cartridge.sh index 9f36d56e..3f7cbd5d 100755 --- a/install_single_cartridge.sh +++ b/install_single_cartridge.sh @@ -18,6 +18,9 @@ # since p8tool fails to build .p8.png properly, png will be directly # saved from PICO-8 with export_cartridge_release.p8 into PICO-8 carts folder +# Configuration: paths +data_path="$(dirname "$0")/data" + # check that source and output paths have been provided if ! [[ $# -ge 1 && $# -le 3 ]] ; then echo "build.sh takes 1 to 2 params, provided $#: @@ -29,7 +32,7 @@ fi # Configuration: cartridge cartridge_stem="picosonic" -version="5.1+" +version=`cat "$data_path/version.txt"` cartridge_suffix="$1"; shift config="$1"; shift diff --git a/install_single_cartridge_with_data.sh b/install_single_cartridge_with_data.sh index 27a5aafd..454415d6 100755 --- a/install_single_cartridge_with_data.sh +++ b/install_single_cartridge_with_data.sh @@ -17,6 +17,7 @@ # Configuration: paths game_scripts_path="$(dirname "$0")" +data_path="$(dirname "$0")/data" # check that source and output paths have been provided if ! [[ $# -ge 1 && $# -le 3 ]] ; then @@ -28,7 +29,7 @@ if ! [[ $# -ge 1 && $# -le 3 ]] ; then fi # Configuration: cartridge -version="5.1+" +version=`cat "$data_path/version.txt"` cartridge_suffix="$1"; shift config="$1"; shift diff --git a/run_cartridge.sh b/run_cartridge.sh index 158bb6cb..93880b84 100755 --- a/run_cartridge.sh +++ b/run_cartridge.sh @@ -9,9 +9,12 @@ # Any extra arguments are passed to pico8 # Currently only supported on Linux +# Configuration: paths +data_path="$(dirname "$0")/data" + # Configuration: cartridge cartridge_stem="picosonic" -version="5.1+" +version=`cat "$data_path/version.txt"` # shift allows to pass extra arguments as $@ cartridge_suffix="$1"; shift diff --git a/run_itest.sh b/run_itest.sh index c52bd4b7..53519fc2 100755 --- a/run_itest.sh +++ b/run_itest.sh @@ -7,9 +7,12 @@ # Any extra arguments are passed to pico8 +# Configuration: paths +data_path="$(dirname "$0")/data" + # Configuration: cartridge cartridge_stem="picosonic_itest_all" -version="5.1+" +version=`cat "$data_path/version.txt"` cartridge_suffix="$1"; shift diff --git a/run_pico8_utests.sh b/run_pico8_utests.sh index e9695d42..a71ff68d 100755 --- a/run_pico8_utests.sh +++ b/run_pico8_utests.sh @@ -3,9 +3,12 @@ # Run itest with PICO-8 executable (itests only work in debug config) # Pass any extra arguments to pico8 +# Configuration: paths +data_path="$(dirname "$0")/data" + # Configuration: cartridge cartridge_stem="picosonic_pico8_utests_all" -version="5.1+" +version=`cat "$data_path/version.txt"` run_cmd="pico8 -run build/${cartridge_stem}_v${version}_debug.p8 -screenshot_scale 4 -gif_scale 4 $@" From 7599c04dbd0f104e97224fccc1ae315fe3d93a23 Mon Sep 17 00:00:00 2001 From: huulong Date: Tue, 29 Dec 2020 18:07:35 +0100 Subject: [PATCH 37/91] [TITLE] Added version (now located in 3 places: data/version.txt, titlemenu.lua, export_cartridge_release.p8) --- src/menu/titlemenu.lua | 9 +++++++++ src/menu/titlemenu_utest.lua | 10 ++++++++++ 2 files changed, 19 insertions(+) diff --git a/src/menu/titlemenu.lua b/src/menu/titlemenu.lua index dd8a09fd..e5914359 100644 --- a/src/menu/titlemenu.lua +++ b/src/menu/titlemenu.lua @@ -87,6 +87,8 @@ end function titlemenu:render() self:draw_background() self:draw_title() + self:draw_version() + if self.menu then self.menu:draw(55, 101) end @@ -114,4 +116,11 @@ function titlemenu:draw_title() visual.sprite_data_t.title_logo:render(vector(8, 16)) end +function titlemenu:draw_version() + -- PICO-8 cannot access data/version.txt and we don't want to preprocess substitute some $version + -- tag in build script just for this, so we exceptionally hardcode version number + -- coords correspond to top-right corner with a small margin + text_helper.print_aligned("V5.1+", 126, 2, alignments.right, colors.white, colors.black) +end + return titlemenu diff --git a/src/menu/titlemenu_utest.lua b/src/menu/titlemenu_utest.lua index 54f2781d..b9766384 100644 --- a/src/menu/titlemenu_utest.lua +++ b/src/menu/titlemenu_utest.lua @@ -147,6 +147,7 @@ describe('titlemenu', function () setup(function () stub(titlemenu, "draw_background") stub(titlemenu, "draw_title") + stub(titlemenu, "draw_version") -- stub menu.draw completely to avoid altering the count of text_helper.print_centered calls stub(menu, "draw") end) @@ -154,12 +155,14 @@ describe('titlemenu', function () teardown(function () titlemenu.draw_background:revert() titlemenu.draw_title:revert() + titlemenu.draw_version:revert() menu.draw:revert() end) after_each(function () titlemenu.draw_background:clear() titlemenu.draw_title:clear() + titlemenu.draw_version:clear() menu.draw:clear() end) @@ -177,6 +180,13 @@ describe('titlemenu', function () assert.spy(titlemenu.draw_title).was_called_with(match.ref(tm)) end) + it('should draw version', function () + tm:render() + + assert.spy(titlemenu.draw_version).was_called(1) + assert.spy(titlemenu.draw_version).was_called_with(match.ref(tm)) + end) + it('should not try to render menu if nil', function () tm:render() From 1976c94575507a90e5dffce5eb4782f86d2e1d01 Mon Sep 17 00:00:00 2001 From: huulong Date: Tue, 29 Dec 2020 18:40:29 +0100 Subject: [PATCH 38/91] [INTRO] Extracted splash screen into stage_intro cartridge --- src/data/stage_data.lua | 22 +- src/data/stage_intro_data.lua | 6 + src/ingame/stage_state.lua | 70 +- src/ingame/stage_state_utest.lua | 3044 +++++++++---------- src/stage_intro/stage_intro_state.lua | 91 +- src/stage_intro/stage_intro_state_utest.lua | 149 + 6 files changed, 1745 insertions(+), 1637 deletions(-) create mode 100644 src/data/stage_intro_data.lua diff --git a/src/data/stage_data.lua b/src/data/stage_data.lua index eea76e92..0e162892 100644 --- a/src/data/stage_data.lua +++ b/src/data/stage_data.lua @@ -2,27 +2,29 @@ local location_rect = require("engine/core/location_rect") local stage_data = { - -- common data + -- Common data - -- gameplay + -- Gameplay emerald_pick_radius = 8, - -- UI - -- delay between stage enter and showing stage title (s) - show_stage_splash_delay = 0.2, + -- Visual + -- duration of goal plate rotating before stage clear (results sub-state) starts (frames) goal_rotating_anim_duration = 120, - -- duration of bgm fade out after reaching goal (s) - bgm_fade_out_duration = 1.0, - - -- other visuals -- spring extension duration (tiles use custom animation via async instead of animated_sprite) spring_extend_duration = 0.15, - -- stage-specific data, per id + + -- Audio + + -- duration of bgm fade out after reaching goal (s) + bgm_fade_out_duration = 1.0, + + + -- Stage-specific data, per id for_stage = { diff --git a/src/data/stage_intro_data.lua b/src/data/stage_intro_data.lua new file mode 100644 index 00000000..86194979 --- /dev/null +++ b/src/data/stage_intro_data.lua @@ -0,0 +1,6 @@ +local stage_intro_data = { + -- delay between stage intro state enter and showing stage title (s) + show_stage_splash_delay = 0.2, +} + +return stage_intro_data diff --git a/src/ingame/stage_state.lua b/src/ingame/stage_state.lua index b62508db..13ed2de1 100644 --- a/src/ingame/stage_state.lua +++ b/src/ingame/stage_state.lua @@ -1,9 +1,5 @@ local gamestate = require("engine/application/gamestate") local volume = require("engine/audio/volume") -local postprocess = require("engine/render/postprocess") -local label = require("engine/ui/label") -local overlay = require("engine/ui/overlay") -local rectangle = require("engine/ui/rectangle") local camera_class = require("ingame/camera") local emerald = require("ingame/emerald") @@ -14,7 +10,6 @@ local stage_data = require("data/stage_data") local audio = require("resources/audio") local visual = require("resources/visual_common") -- we should require ingameadd-on in main local visual_stage = require("resources/visual_stage") -local ui_animation = require("ui/ui_animation") local stage_state = derived_class(gamestate) @@ -50,10 +45,6 @@ function stage_state:init() -- create camera, but wait for player character to spawn before assigning it a target -- see on_enter for how we warp it to a good place first self.camera = camera_class() - self.postproc = postprocess() - - -- title overlay - self.title_overlay = overlay() --#if itest -- set to false in itest setup to disable object spawning, which relies on very slow map scan @@ -107,8 +98,6 @@ function stage_state:on_enter() self.has_player_char_reached_goal = false - self.app:start_coroutine(self.show_stage_splash_async, self) - -- reload bgm only once, then we can play bgm whenever we want for this stage self:reload_bgm() -- initial play bgm @@ -212,6 +201,8 @@ function stage_state:reload_runtime_data() -- so we just have a little margin! end +-- never called, we directly load stage_clear cartridge +--[[ function stage_state:on_exit() -- clear all coroutines (we normally let app handle them, but in this context -- we know that all coroutines belong to the stage state, so no risk clearing them from here) @@ -219,7 +210,6 @@ function stage_state:on_exit() -- clear object state vars self.player_char = nil - self.title_overlay:clear_drawables() -- reinit camera offset for other states camera() @@ -227,6 +217,7 @@ function stage_state:on_exit() -- stop audio self:stop_bgm() end +--]] function stage_state:update() self:update_fx() @@ -247,8 +238,6 @@ function stage_state:render() self:render_stage_elements() self:render_fx() self:render_hud() - self:render_overlay() - self.postproc:apply() end @@ -951,54 +940,6 @@ function stage_state:set_camera_with_region_origin() end --- ui - -function stage_state:show_stage_splash_async() - -- fade in - for i = 5, 0, -1 do - self.postproc.darkness = i - yield_delay(7) - end - - self.app:yield_delay_s(stage_data.show_stage_splash_delay) - - -- FIXME: draw iteration order not guaranteed, pico-sonic may be hidden "below" banner - - -- init position y is -height so it starts just at the screen top edge - local banner = rectangle(vector(9, -106), 32, 106, colors.red) - self.title_overlay:add_drawable("banner", banner) - - -- banner text accompanies text, and ends at y = 89, so starts at y = 89 - 106 = -17 - local banner_text = label("pico\nsonic", vector(16, -17), colors.white) - self.title_overlay:add_drawable("banner_text", banner_text) - - -- make banner enter from the top - ui_animation.move_drawables_on_coord_async("y", {banner, banner_text}, {0, 89}, -106, 0, 9) - - local zone_rectangle = rectangle(vector(128, 45), 47, 3, colors.black) - self.title_overlay:add_drawable("zone_rect", zone_rectangle) - - local zone_label = label(self.curr_stage_data.title, vector(129, 43), colors.white) - self.title_overlay:add_drawable("zone", zone_label) - - -- make text enter from the right - ui_animation.move_drawables_on_coord_async("x", {zone_rectangle, zone_label}, {0, 1}, 128, 41, 14) - - -- keep zone displayed for a moment - yield_delay(102) - - -- make banner exit to the top - ui_animation.move_drawables_on_coord_async("y", {banner, banner_text}, {0, 89}, 0, -106, 8) - - -- make text exit to the right - ui_animation.move_drawables_on_coord_async("x", {zone_rectangle, zone_label}, {0, 1}, 41, 128, 14) - - self.title_overlay:remove_drawable("banner") - self.title_overlay:remove_drawable("banner_text") - self.title_overlay:remove_drawable("zone") -end - - -- render -- render the stage elements with the main camera: @@ -1155,11 +1096,6 @@ function stage_state:render_hud() --#endif end --- render the title overlay with a fixed ui camera -function stage_state:render_overlay() - camera() - self.title_overlay:draw() -end -- audio diff --git a/src/ingame/stage_state_utest.lua b/src/ingame/stage_state_utest.lua index aee3f69a..209901dd 100644 --- a/src/ingame/stage_state_utest.lua +++ b/src/ingame/stage_state_utest.lua @@ -13,7 +13,6 @@ local flow = require("engine/application/flow") local location_rect = require("engine/core/location_rect") local animated_sprite = require("engine/render/animated_sprite") local sprite_data = require("engine/render/sprite_data") -local overlay = require("engine/ui/overlay") local picosonic_app = require("application/picosonic_app_ingame") local camera_data = require("data/camera_data") @@ -50,12 +49,13 @@ describe('stage_state', function () state.app = app end) - describe('state', function () + describe('init', function () - it('init', function () + it('should initialize members', function () assert.are_same({ ':stage', 1, + stage_data.for_stage[1], nil, false, {}, @@ -64,7 +64,6 @@ describe('stage_state', function () {}, {}, camera_class(), - overlay(), nil, -- itest only true, @@ -72,6 +71,7 @@ describe('stage_state', function () { state.type, state.curr_stage_id, + state.curr_stage_data, state.player_char, state.has_player_char_reached_goal, state.spawned_emerald_locations, @@ -80,2021 +80,1947 @@ describe('stage_state', function () state.emerald_pick_fxs, state.palm_tree_leaves_core_global_locations, state.camera, - state.title_overlay, state.loaded_map_region_coords, -- itest only state.enable_spawn_objects, }) end) - describe('_tostring', function () + end) - it('should return "stage_state(1)"', function () - assert.are_equal("stage_state(1)", state:_tostring()) - end) + describe('_tostring', function () + it('should return "stage_state(1)"', function () + assert.are_equal("stage_state(1)", state:_tostring()) end) - describe('on_enter', function () - - setup(function () - stub(stage_state, "spawn_player_char") - stub(picosonic_app, "start_coroutine") - stub(stage_state, "play_bgm") - stub(stage_state, "reload_bgm") - stub(stage_state, "spawn_objects_in_all_map_regions") - stub(stage_state, "restore_picked_emerald_data") - stub(camera_class, "setup_for_stage") - stub(stage_state, "check_reload_map_region") - stub(stage_state, "reload_runtime_data") - end) - - teardown(function () - stage_state.spawn_player_char:revert() - picosonic_app.start_coroutine:revert() - stage_state.play_bgm:revert() - stage_state.reload_bgm:revert() - stage_state.spawn_objects_in_all_map_regions:revert() - stage_state.restore_picked_emerald_data:revert() - camera_class.setup_for_stage:revert() - stage_state.check_reload_map_region:revert() - stage_state.reload_runtime_data:revert() - end) + end) - after_each(function () - stage_state.spawn_player_char:clear() - picosonic_app.start_coroutine:clear() - stage_state.play_bgm:clear() - stage_state.reload_bgm:clear() - stage_state.spawn_objects_in_all_map_regions:clear() - stage_state.restore_picked_emerald_data:clear() - camera_class.setup_for_stage:clear() - stage_state.check_reload_map_region:clear() - stage_state.reload_runtime_data:clear() - end) + describe('on_enter', function () + + setup(function () + stub(stage_state, "spawn_player_char") + stub(stage_state, "play_bgm") + stub(stage_state, "reload_bgm") + stub(stage_state, "spawn_objects_in_all_map_regions") + stub(stage_state, "restore_picked_emerald_data") + stub(camera_class, "setup_for_stage") + stub(stage_state, "check_reload_map_region") + stub(stage_state, "reload_runtime_data") + end) - before_each(function () - state:on_enter() - end) + teardown(function () + stage_state.spawn_player_char:revert() + stage_state.play_bgm:revert() + stage_state.reload_bgm:revert() + stage_state.spawn_objects_in_all_map_regions:revert() + stage_state.restore_picked_emerald_data:revert() + camera_class.setup_for_stage:revert() + stage_state.check_reload_map_region:revert() + stage_state.reload_runtime_data:revert() + end) - it('should call spawn_objects_in_all_map_regions', function () - assert.spy(state.spawn_objects_in_all_map_regions).was_called(1) - assert.spy(state.spawn_objects_in_all_map_regions).was_called_with(match.ref(state)) - end) + after_each(function () + stage_state.spawn_player_char:clear() + stage_state.play_bgm:clear() + stage_state.reload_bgm:clear() + stage_state.spawn_objects_in_all_map_regions:clear() + stage_state.restore_picked_emerald_data:clear() + camera_class.setup_for_stage:clear() + stage_state.check_reload_map_region:clear() + stage_state.reload_runtime_data:clear() + end) - it('should call restore_picked_emerald_data', function () - assert.spy(state.restore_picked_emerald_data).was_called(1) - assert.spy(state.restore_picked_emerald_data).was_called_with(match.ref(state)) - end) + before_each(function () + state:on_enter() + end) - it('should call setup_for_stage on camera with current stage data', function () - assert.spy(camera_class.setup_for_stage).was_called(1) - assert.spy(camera_class.setup_for_stage).was_called_with(match.ref(state.camera), state.curr_stage_data) - end) + it('should call spawn_objects_in_all_map_regions', function () + assert.spy(state.spawn_objects_in_all_map_regions).was_called(1) + assert.spy(state.spawn_objects_in_all_map_regions).was_called_with(match.ref(state)) + end) - it('should call check_reload_map_region', function () - assert.spy(state.check_reload_map_region).was_called(1) - assert.spy(state.check_reload_map_region).was_called_with(match.ref(state)) - end) + it('should call restore_picked_emerald_data', function () + assert.spy(state.restore_picked_emerald_data).was_called(1) + assert.spy(state.restore_picked_emerald_data).was_called_with(match.ref(state)) + end) - it('should call spawn_player_char', function () - assert.spy(stage_state.spawn_player_char).was_called(1) - assert.spy(stage_state.spawn_player_char).was_called_with(match.ref(state)) - end) + it('should call setup_for_stage on camera with current stage data', function () + assert.spy(camera_class.setup_for_stage).was_called(1) + assert.spy(camera_class.setup_for_stage).was_called_with(match.ref(state.camera), state.curr_stage_data) + end) - it('should assign spawned player char to camera target', function () - assert.are_equal(state.player_char, state.camera.target_pc) - end) + it('should call check_reload_map_region', function () + assert.spy(state.check_reload_map_region).was_called(1) + assert.spy(state.check_reload_map_region).was_called_with(match.ref(state)) + end) - it('should set has_player_char_reached_goal to false', function () - assert.is_false(state.has_player_char_reached_goal) - end) + it('should call spawn_player_char', function () + assert.spy(stage_state.spawn_player_char).was_called(1) + assert.spy(stage_state.spawn_player_char).was_called_with(match.ref(state)) + end) - it('should call start_coroutine_method on show_stage_splash_async', function () - local s = assert.spy(picosonic_app.start_coroutine) - s.was_called(1) - s.was_called_with(match.ref(state.app), stage_state.show_stage_splash_async, match.ref(state)) - end) + it('should assign spawned player char to camera target', function () + assert.are_equal(state.player_char, state.camera.target_pc) + end) - it('should call reload_bgm', function () - assert.spy(state.reload_bgm).was_called(1) - assert.spy(state.reload_bgm).was_called_with(match.ref(state)) - end) + it('should set has_player_char_reached_goal to false', function () + assert.is_false(state.has_player_char_reached_goal) + end) - it('should call play_bgm', function () - assert.spy(state.play_bgm).was_called(1) - assert.spy(state.play_bgm).was_called_with(match.ref(state)) - end) + it('should call reload_bgm', function () + assert.spy(state.reload_bgm).was_called(1) + assert.spy(state.reload_bgm).was_called_with(match.ref(state)) + end) - it('should call reload_runtime_data', function () - assert.spy(state.reload_runtime_data).was_called(1) - assert.spy(state.reload_runtime_data).was_called_with(match.ref(state)) - end) + it('should call play_bgm', function () + assert.spy(state.play_bgm).was_called(1) + assert.spy(state.play_bgm).was_called_with(match.ref(state)) + end) + it('should call reload_runtime_data', function () + assert.spy(state.reload_runtime_data).was_called(1) + assert.spy(state.reload_runtime_data).was_called_with(match.ref(state)) end) - describe('reload_runtime_data', function () + end) - setup(function () - stub(_G, "reload") - stub(_G, "memcpy") - end) + describe('reload_runtime_data', function () - teardown(function () - reload:revert() - memcpy:revert() - end) + setup(function () + stub(_G, "reload") + stub(_G, "memcpy") + end) - after_each(function () - reload:clear() - memcpy:clear() - end) + teardown(function () + reload:revert() + memcpy:revert() + end) - it('should reload stage runtime data into spritesheet top, and rotated sprite variants into general memory', function () - state:reload_runtime_data() - - assert.spy(reload).was_called(33) - assert.spy(reload).was_called_with(0x0, 0x0, 0x600, "data_stage1_runtime.p8") - assert.spy(reload).was_called_with(0x5800, 0x1008, 0x30, "data_stage1_runtime.p8") - assert.spy(reload).was_called_with(0x5830, 0x1048, 0x30, "data_stage1_runtime.p8") - assert.spy(reload).was_called_with(0x5b00, 0x1400, 0x20, "data_stage1_runtime.p8") - assert.spy(reload).was_called_with(0x5b20, 0x1440, 0x20, "data_stage1_runtime.p8") - -- this has become too long since we copy line by line, so we stopped checking - -- individual calls, except the first ones - end) + after_each(function () + reload:clear() + memcpy:clear() + end) - it('should copy non-rotated sprite variants into general memory', function () - state:reload_runtime_data() + it('should reload stage runtime data into spritesheet top, and rotated sprite variants into general memory', function () + state:reload_runtime_data() + + assert.spy(reload).was_called(33) + assert.spy(reload).was_called_with(0x0, 0x0, 0x600, "data_stage1_runtime.p8") + assert.spy(reload).was_called_with(0x5800, 0x1008, 0x30, "data_stage1_runtime.p8") + assert.spy(reload).was_called_with(0x5830, 0x1048, 0x30, "data_stage1_runtime.p8") + assert.spy(reload).was_called_with(0x5b00, 0x1400, 0x20, "data_stage1_runtime.p8") + assert.spy(reload).was_called_with(0x5b20, 0x1440, 0x20, "data_stage1_runtime.p8") + -- this has become too long since we copy line by line, so we stopped checking + -- individual calls, except the first ones + end) - assert.spy(memcpy).was_called(32) - -- this has become too long since we copy line by line, so we stopped checking - -- individual calls, except the first ones - assert.spy(memcpy).was_called_with(0x5300, 0x1008, 0x30) - assert.spy(memcpy).was_called_with(0x5330, 0x1048, 0x30) - assert.spy(memcpy).was_called_with(0x5600, 0x1400, 0x20) - assert.spy(memcpy).was_called_with(0x5620, 0x1440, 0x20) - end) + it('should copy non-rotated sprite variants into general memory', function () + state:reload_runtime_data() + assert.spy(memcpy).was_called(32) + -- this has become too long since we copy line by line, so we stopped checking + -- individual calls, except the first ones + assert.spy(memcpy).was_called_with(0x5300, 0x1008, 0x30) + assert.spy(memcpy).was_called_with(0x5330, 0x1048, 0x30) + assert.spy(memcpy).was_called_with(0x5600, 0x1400, 0x20) + assert.spy(memcpy).was_called_with(0x5620, 0x1440, 0x20) end) - describe('is_tile_in_area', function () + end) - it('should return true for tile in one of the entrance areas', function () - -- this depends on stage_data.for_stage[1].loop_entrance_areas content and - -- location_rect:contains correctness - assert.is_true(state:is_tile_in_area(location(4, 4), - {location_rect(0, 0, 2, 2), location_rect(4, 4, 6, 6)})) - end) + describe('is_tile_in_area', function () - it('should return false for tile not in any of the entrance areas', function () - -- this depends on stage_data.for_stage[1].loop_entrance_areas content and - -- location_rect:contains correctness - assert.is_true(state:is_tile_in_area(location(5, 5), - {location_rect(0, 0, 2, 2), location_rect(4, 4, 6, 6)})) - end) + it('should return true for tile in one of the entrance areas', function () + -- this depends on stage_data.for_stage[1].loop_entrance_areas content and + -- location_rect:contains correctness + assert.is_true(state:is_tile_in_area(location(4, 4), + {location_rect(0, 0, 2, 2), location_rect(4, 4, 6, 6)})) + end) + it('should return false for tile not in any of the entrance areas', function () + -- this depends on stage_data.for_stage[1].loop_entrance_areas content and + -- location_rect:contains correctness + assert.is_true(state:is_tile_in_area(location(5, 5), + {location_rect(0, 0, 2, 2), location_rect(4, 4, 6, 6)})) end) - describe('is_tile_in_loop_entrance', function () + end) - before_each(function () - -- customize loop areas locally. We are redefining a table so that won't affect - -- the original data table in stage_data.lua. To simplify we don't redefine everything, - -- but if we need to for the tests we'll just add the missing members - state.curr_stage_data = { - loop_entrance_areas = {location_rect(1, 0, 3, 4)} - } - end) + describe('is_tile_in_loop_entrance', function () - -- we wrote those tests before extracting is_tile_in_area and it's simpler - -- to test result than stubbing is_tile_in_area with a dummy function anyway, - -- so we keep direct testing despite overlapping is_tile_in_area utests above + before_each(function () + -- customize loop areas locally. We are redefining a table so that won't affect + -- the original data table in stage_data.lua. To simplify we don't redefine everything, + -- but if we need to for the tests we'll just add the missing members + state.curr_stage_data = { + loop_entrance_areas = {location_rect(1, 0, 3, 4)} + } + end) - it('should return true for tile in one of the entrance areas, but not the top-left corner reserved to trigger', function () - assert.is_true(state:is_tile_in_loop_entrance(location(2, 0))) - end) + -- we wrote those tests before extracting is_tile_in_area and it's simpler + -- to test result than stubbing is_tile_in_area with a dummy function anyway, + -- so we keep direct testing despite overlapping is_tile_in_area utests above - it('should return false for tile just on the top-left corner entrance trigger (and not inside another area excluding trigger)', function () - assert.is_false(state:is_tile_in_loop_entrance(location(1, 0))) - end) + it('should return true for tile in one of the entrance areas, but not the top-left corner reserved to trigger', function () + assert.is_true(state:is_tile_in_loop_entrance(location(2, 0))) + end) - it('should return false for tile not in any of the entrance areas', function () - assert.is_false(state:is_tile_in_loop_entrance(location(0, 0))) - end) + it('should return false for tile just on the top-left corner entrance trigger (and not inside another area excluding trigger)', function () + assert.is_false(state:is_tile_in_loop_entrance(location(1, 0))) + end) + it('should return false for tile not in any of the entrance areas', function () + assert.is_false(state:is_tile_in_loop_entrance(location(0, 0))) end) - describe('is_tile_in_loop_exit', function () + end) - before_each(function () - -- customize loop areas locally. We are redefining a table so that won't affect - -- the original data table in stage_data.lua. To simplify we don't redefine everything, - -- but if we need to for the tests we'll just add the missing members - state.curr_stage_data = { - loop_exit_areas = {location_rect(-1, 0, 0, 2)} - } - end) + describe('is_tile_in_loop_exit', function () - it('should return true for tile in one of the exit areas, but not the top-right corner reserved to trigger', function () - assert.is_true(state:is_tile_in_loop_exit(location(0, 1))) - end) + before_each(function () + -- customize loop areas locally. We are redefining a table so that won't affect + -- the original data table in stage_data.lua. To simplify we don't redefine everything, + -- but if we need to for the tests we'll just add the missing members + state.curr_stage_data = { + loop_exit_areas = {location_rect(-1, 0, 0, 2)} + } + end) - it('should return false for tile just on the top-right corner exit trigger (and not inside another area excluding trigger)', function () - assert.is_false(state:is_tile_in_loop_exit(location(0, 0))) - end) + it('should return true for tile in one of the exit areas, but not the top-right corner reserved to trigger', function () + assert.is_true(state:is_tile_in_loop_exit(location(0, 1))) + end) - it('should return false for tile not in any of the exit areas', function () - assert.is_false(state:is_tile_in_loop_exit(location(0, -1))) - end) + it('should return false for tile just on the top-right corner exit trigger (and not inside another area excluding trigger)', function () + assert.is_false(state:is_tile_in_loop_exit(location(0, 0))) + end) + it('should return false for tile not in any of the exit areas', function () + assert.is_false(state:is_tile_in_loop_exit(location(0, -1))) end) - describe('spawn_emerald_at', function () + end) - it('should store emerald global location', function () - state:spawn_emerald_at(location(1, 33)) + describe('spawn_emerald_at', function () - assert.are_same({ - location(1, 33), - }, state.spawned_emerald_locations) - end) + it('should store emerald global location', function () + state:spawn_emerald_at(location(1, 33)) - it('should spawn and store emerald objects for each emerald tile', function () - state:spawn_emerald_at(location(1, 33)) + assert.are_same({ + location(1, 33), + }, state.spawned_emerald_locations) + end) - assert.are_same({ - emerald(1, location(1, 33)), - }, state.emeralds) - end) + it('should spawn and store emerald objects for each emerald tile', function () + state:spawn_emerald_at(location(1, 33)) + assert.are_same({ + emerald(1, location(1, 33)), + }, state.emeralds) end) - describe('spawn_palm_tree_leaves', function () + end) - it('should spawn and store palm tree leaves core at global location', function () - state:spawn_palm_tree_leaves_at(location(1, 33)) + describe('spawn_palm_tree_leaves', function () - assert.are_same({ - location(1, 33), - }, state.palm_tree_leaves_core_global_locations) - end) + it('should spawn and store palm tree leaves core at global location', function () + state:spawn_palm_tree_leaves_at(location(1, 33)) + assert.are_same({ + location(1, 33), + }, state.palm_tree_leaves_core_global_locations) end) - describe('spawn_goal_plate_at', function () + end) - it('should spawn and store goal plate core at global location', function () - state:spawn_goal_plate_at(location(1, 33)) + describe('spawn_goal_plate_at', function () - assert.are_same(goal_plate(location(1, 33)), state.goal_plate) - end) + it('should spawn and store goal plate core at global location', function () + state:spawn_goal_plate_at(location(1, 33)) + assert.are_same(goal_plate(location(1, 33)), state.goal_plate) end) - describe('scan_current_region_to_spawn_objects', function () + end) - local dummy_callback = spy.new(function (self, global_loc) end) + describe('scan_current_region_to_spawn_objects', function () - setup(function () - stub(stage_state, "get_spawn_object_callback", function (self, tile_id) - if tile_id == 21 then - return dummy_callback - end - end) - end) + local dummy_callback = spy.new(function (self, global_loc) end) - teardown(function () - stage_state.get_spawn_object_callback:revert() + setup(function () + stub(stage_state, "get_spawn_object_callback", function (self, tile_id) + if tile_id == 21 then + return dummy_callback + end end) + end) - -- setup is too early, stage state will start afterward in before_each, - -- and its on_enter will call scan_current_region_to_spawn_objects, making it hard - -- to test in isolation. Hence before_each. - before_each(function () - -- we're not using tile_test_data.setup here - -- (since objects are checked directly by id, not using collision data) - -- so don't use mock_mset - mset(1, 1, 21) - mset(2, 2, 21) - mset(3, 3, 21) - - -- mock stage dimensions, not too big to avoid test too long - -- (just 2 regions so we can check that location conversion works) - state.curr_stage_data = { - tile_width = 128, -- 1 region per row - tile_height = 32 * 2 -- 2 regions per column - } - - state.loaded_map_region_coords = vector(0, 1) -- will add 32 to each j - end) + teardown(function () + stage_state.get_spawn_object_callback:revert() + end) - after_each(function () - dummy_callback:clear() + -- setup is too early, stage state will start afterward in before_each, + -- and its on_enter will call scan_current_region_to_spawn_objects, making it hard + -- to test in isolation. Hence before_each. + before_each(function () + -- we're not using tile_test_data.setup here + -- (since objects are checked directly by id, not using collision data) + -- so don't use mock_mset + mset(1, 1, 21) + mset(2, 2, 21) + mset(3, 3, 21) + + -- mock stage dimensions, not too big to avoid test too long + -- (just 2 regions so we can check that location conversion works) + state.curr_stage_data = { + tile_width = 128, -- 1 region per row + tile_height = 32 * 2 -- 2 regions per column + } + + state.loaded_map_region_coords = vector(0, 1) -- will add 32 to each j + end) - pico8:clear_map() - end) + after_each(function () + dummy_callback:clear() - it('should call spawn object callbacks for recognized representative tiles', function () - state:scan_current_region_to_spawn_objects() + pico8:clear_map() + end) - assert.spy(dummy_callback).was_called(3) - assert.spy(dummy_callback).was_called_with(match.ref(state), location(1, 1 + 32), 21) - end) + it('should call spawn object callbacks for recognized representative tiles', function () + state:scan_current_region_to_spawn_objects() + assert.spy(dummy_callback).was_called(3) + assert.spy(dummy_callback).was_called_with(match.ref(state), location(1, 1 + 32), 21) end) - describe('get_map_region_filename', function () + end) - it('stage 2, (1, 0) => "data_stage2_10.p8"', function () - state.curr_stage_id = 2 - assert.are_equal("data_stage2_10.p8", state:get_map_region_filename(1, 0)) - end) + describe('get_map_region_filename', function () + it('stage 2, (1, 0) => "data_stage2_10.p8"', function () + state.curr_stage_id = 2 + assert.are_equal("data_stage2_10.p8", state:get_map_region_filename(1, 0)) end) - describe('get_region_grid_dimensions', function () + end) - it('should return the number of regions per row, per column"', function () - state.curr_stage_data = { - tile_width = 250, -- not exactly 256 to test ceiling to 2 regions per row - tile_height = 32 * 3 -- 3 regions per column - } + describe('get_region_grid_dimensions', function () - assert.are_same({2, 3}, {state:get_region_grid_dimensions()}) - end) + it('should return the number of regions per row, per column"', function () + state.curr_stage_data = { + tile_width = 250, -- not exactly 256 to test ceiling to 2 regions per row + tile_height = 32 * 3 -- 3 regions per column + } + assert.are_same({2, 3}, {state:get_region_grid_dimensions()}) end) - describe('get_map_region_coords', function () + end) - before_each(function () - -- required for stage edge clamping - -- we only need to mock width and height, - -- normally we'd get full stage data as in stage_data.lua - state.curr_stage_data = { - tile_width = 250, -- not exactly 256 to test ceiling to 2 regions per row - tile_height = 32 * 3 -- 3 regions per column - } - end) + describe('get_map_region_coords', function () - it('should return (0, 0) in region (0, 0), even when close to top and left edges (limit)', function () - -- X | - -- | - -- | - -- ---+--- - -- | - -- | - -- | - -- ---+--- - -- | - -- | - -- | - assert.are_equal(vector(0, 0), state:get_map_region_coords(vector(0, 0))) - end) + before_each(function () + -- required for stage edge clamping + -- we only need to mock width and height, + -- normally we'd get full stage data as in stage_data.lua + state.curr_stage_data = { + tile_width = 250, -- not exactly 256 to test ceiling to 2 regions per row + tile_height = 32 * 3 -- 3 regions per column + } + end) - it('should return (0, 0) in region (0, 0) right in the middle', function () - -- | - -- X | - -- | - -- ---+--- - -- | - -- | - -- | - -- ---+--- - -- | - -- | - -- | - assert.are_equal(vector(0, 0), state:get_map_region_coords(vector(512, 128))) - end) + it('should return (0, 0) in region (0, 0), even when close to top and left edges (limit)', function () + -- X | + -- | + -- | + -- ---+--- + -- | + -- | + -- | + -- ---+--- + -- | + -- | + -- | + assert.are_equal(vector(0, 0), state:get_map_region_coords(vector(0, 0))) + end) - it('should return (0.5, 0) in region (0, 0) near right edge', function () - -- | - -- X| - -- | - -- ---+--- - -- | - -- | - -- | - -- ---+--- - -- | - -- | - -- | - assert.are_equal(vector(0.5, 0), state:get_map_region_coords(vector(1020, 128))) - end) + it('should return (0, 0) in region (0, 0) right in the middle', function () + -- | + -- X | + -- | + -- ---+--- + -- | + -- | + -- | + -- ---+--- + -- | + -- | + -- | + assert.are_equal(vector(0, 0), state:get_map_region_coords(vector(512, 128))) + end) - it('should return (0.5, 0) in region (1, 0) near left edge', function () - -- | - -- |X - -- | - -- ---+--- - -- | - -- | - -- | - -- ---+--- - -- | - -- | - -- | - assert.are_equal(vector(0.5, 0), state:get_map_region_coords(vector(1030, 128))) - end) + it('should return (0.5, 0) in region (0, 0) near right edge', function () + -- | + -- X| + -- | + -- ---+--- + -- | + -- | + -- | + -- ---+--- + -- | + -- | + -- | + assert.are_equal(vector(0.5, 0), state:get_map_region_coords(vector(1020, 128))) + end) - it('should return (1, 0) in region (1, 0) right in the middle', function () - -- | - -- | X - -- | - -- ---+--- - -- | - -- | - -- | - -- ---+--- - -- | - -- | - -- | - assert.are_equal(vector(1, 0), state:get_map_region_coords(vector(1536, 128))) - end) + it('should return (0.5, 0) in region (1, 0) near left edge', function () + -- | + -- |X + -- | + -- ---+--- + -- | + -- | + -- | + -- ---+--- + -- | + -- | + -- | + assert.are_equal(vector(0.5, 0), state:get_map_region_coords(vector(1030, 128))) + end) - it('should return (1, 0) in region (1, 0) even when close to top and right edges (limit)', function () - assert.are_equal(vector(1, 0), state:get_map_region_coords(vector(2047, 0))) - end) + it('should return (1, 0) in region (1, 0) right in the middle', function () + -- | + -- | X + -- | + -- ---+--- + -- | + -- | + -- | + -- ---+--- + -- | + -- | + -- | + assert.are_equal(vector(1, 0), state:get_map_region_coords(vector(1536, 128))) + end) - it('should return (0, 0.5) in region (0, 0), near bottom edge', function () - -- | - -- | - -- X | - -- ---+--- - -- | - -- | - -- | - -- ---+--- - -- | - -- | - -- | - assert.are_equal(vector(0, 0.5), state:get_map_region_coords(vector(0, 250))) - end) + it('should return (1, 0) in region (1, 0) even when close to top and right edges (limit)', function () + assert.are_equal(vector(1, 0), state:get_map_region_coords(vector(2047, 0))) + end) - it('should return (0.5, 0.5) in region (0, 0), near bottom and right edges (cross)', function () - -- | - -- | - -- X| - -- ---+--- - -- | - -- | - -- | - -- ---+--- - -- | - -- | - -- | - assert.are_equal(vector(0.5, 0.5), state:get_map_region_coords(vector(1020, 250))) - end) + it('should return (0, 0.5) in region (0, 0), near bottom edge', function () + -- | + -- | + -- X | + -- ---+--- + -- | + -- | + -- | + -- ---+--- + -- | + -- | + -- | + assert.are_equal(vector(0, 0.5), state:get_map_region_coords(vector(0, 250))) + end) - it('should return (0.5, 0.5) in region (1, 0), near bottom and left edges (cross)', function () - -- | - -- | - -- |X - -- ---+--- - -- | - -- | - -- | - -- ---+--- - -- | - -- | - -- | - assert.are_equal(vector(0.5, 0.5), state:get_map_region_coords(vector(1030, 250))) - end) + it('should return (0.5, 0.5) in region (0, 0), near bottom and right edges (cross)', function () + -- | + -- | + -- X| + -- ---+--- + -- | + -- | + -- | + -- ---+--- + -- | + -- | + -- | + assert.are_equal(vector(0.5, 0.5), state:get_map_region_coords(vector(1020, 250))) + end) - it('should return (1, 0.5) in region (1, 0), near bottom edge', function () - -- | - -- | - -- | X - -- ---+--- - -- | - -- | - -- | - -- ---+--- - -- | - -- | - -- | - assert.are_equal(vector(1, 0.5), state:get_map_region_coords(vector(1536, 250))) - end) + it('should return (0.5, 0.5) in region (1, 0), near bottom and left edges (cross)', function () + -- | + -- | + -- |X + -- ---+--- + -- | + -- | + -- | + -- ---+--- + -- | + -- | + -- | + assert.are_equal(vector(0.5, 0.5), state:get_map_region_coords(vector(1030, 250))) + end) - it('should return (1, 0.5) in region (1, 0), near bottom edge, even when close to right edge (limit)', function () - -- | - -- | - -- | X - -- ---+--- - -- | - -- | - -- | - -- ---+--- - -- | - -- | - -- | - assert.are_equal(vector(1, 0.5), state:get_map_region_coords(vector(2047, 250))) - end) + it('should return (1, 0.5) in region (1, 0), near bottom edge', function () + -- | + -- | + -- | X + -- ---+--- + -- | + -- | + -- | + -- ---+--- + -- | + -- | + -- | + assert.are_equal(vector(1, 0.5), state:get_map_region_coords(vector(1536, 250))) + end) - it('should return (0, 0.5) in region (0, 1), near top edge', function () - -- | - -- | - -- | - -- ---+--- - -- X | - -- | - -- | - -- ---+--- - -- | - -- | - -- | - assert.are_equal(vector(0, 0.5), state:get_map_region_coords(vector(0, 260))) - end) + it('should return (1, 0.5) in region (1, 0), near bottom edge, even when close to right edge (limit)', function () + -- | + -- | + -- | X + -- ---+--- + -- | + -- | + -- | + -- ---+--- + -- | + -- | + -- | + assert.are_equal(vector(1, 0.5), state:get_map_region_coords(vector(2047, 250))) + end) - it('should return (0.5, 0.5) in region (0, 1), near top and right edges (cross)', function () - -- | - -- | - -- | - -- ---+--- - -- X| - -- | - -- | - -- ---+--- - -- | - -- | - -- | - assert.are_equal(vector(0.5, 0.5), state:get_map_region_coords(vector(1020, 260))) - end) + it('should return (0, 0.5) in region (0, 1), near top edge', function () + -- | + -- | + -- | + -- ---+--- + -- X | + -- | + -- | + -- ---+--- + -- | + -- | + -- | + assert.are_equal(vector(0, 0.5), state:get_map_region_coords(vector(0, 260))) + end) - it('should return (0.5, 0.5) in region (1, 1), near top and left edges (cross)', function () - -- | - -- | - -- | - -- ---+--- - -- |X - -- | - -- | - -- ---+--- - -- | - -- | - -- | - assert.are_equal(vector(0.5, 0.5), state:get_map_region_coords(vector(1030, 260))) - end) + it('should return (0.5, 0.5) in region (0, 1), near top and right edges (cross)', function () + -- | + -- | + -- | + -- ---+--- + -- X| + -- | + -- | + -- ---+--- + -- | + -- | + -- | + assert.are_equal(vector(0.5, 0.5), state:get_map_region_coords(vector(1020, 260))) + end) - it('should return (1, 0.5) in region (1, 1), near top edge', function () - -- | - -- | - -- | - -- ---+--- - -- | X - -- | - -- | - -- ---+--- - -- | - -- | - -- | - assert.are_equal(vector(1, 0.5), state:get_map_region_coords(vector(1536, 260))) - end) + it('should return (0.5, 0.5) in region (1, 1), near top and left edges (cross)', function () + -- | + -- | + -- | + -- ---+--- + -- |X + -- | + -- | + -- ---+--- + -- | + -- | + -- | + assert.are_equal(vector(0.5, 0.5), state:get_map_region_coords(vector(1030, 260))) + end) - it('should return (0, 1) in region (0, 1) even when close to left edge (limit)', function () - -- | - -- | - -- | - -- ---+--- - -- | - -- X | - -- | - -- ---+--- - -- | - -- | - -- | - assert.are_equal(vector(0, 1), state:get_map_region_coords(vector(0, 384))) - end) + it('should return (1, 0.5) in region (1, 1), near top edge', function () + -- | + -- | + -- | + -- ---+--- + -- | X + -- | + -- | + -- ---+--- + -- | + -- | + -- | + assert.are_equal(vector(1, 0.5), state:get_map_region_coords(vector(1536, 260))) + end) - it('should return (0, 1) in region (0, 1) right in the middle', function () - -- | - -- | - -- | - -- ---+--- - -- | - -- X | - -- | - -- ---+--- - -- | - -- | - -- | - assert.are_equal(vector(0, 1), state:get_map_region_coords(vector(512, 384))) - end) + it('should return (0, 1) in region (0, 1) even when close to left edge (limit)', function () + -- | + -- | + -- | + -- ---+--- + -- | + -- X | + -- | + -- ---+--- + -- | + -- | + -- | + assert.are_equal(vector(0, 1), state:get_map_region_coords(vector(0, 384))) + end) - it('should return (0, 2) in region (0, 2) even when close to bottom and left edges (limit)', function () - -- | - -- | - -- | - -- ---+--- - -- | - -- | - -- | - -- ---+--- - -- | - -- | - -- X | - assert.are_equal(vector(0, 2), state:get_map_region_coords(vector(0, 767))) - end) + it('should return (0, 1) in region (0, 1) right in the middle', function () + -- | + -- | + -- | + -- ---+--- + -- | + -- X | + -- | + -- ---+--- + -- | + -- | + -- | + assert.are_equal(vector(0, 1), state:get_map_region_coords(vector(512, 384))) + end) - it('should return (1, 2) in region (1, 2) even when close to bottom and right edges (limit)', function () - -- | - -- | - -- | - -- ---+--- - -- | - -- | - -- | - -- ---+--- - -- | - -- | - -- | X - assert.are_equal(vector(1, 2), state:get_map_region_coords(vector(2047, 767))) - end) + it('should return (0, 2) in region (0, 2) even when close to bottom and left edges (limit)', function () + -- | + -- | + -- | + -- ---+--- + -- | + -- | + -- | + -- ---+--- + -- | + -- | + -- X | + assert.are_equal(vector(0, 2), state:get_map_region_coords(vector(0, 767))) + end) + it('should return (1, 2) in region (1, 2) even when close to bottom and right edges (limit)', function () + -- | + -- | + -- | + -- ---+--- + -- | + -- | + -- | + -- ---+--- + -- | + -- | + -- | X + assert.are_equal(vector(1, 2), state:get_map_region_coords(vector(2047, 767))) end) - describe('reload_map_region', function () + end) - setup(function () - stub(_G, "reload") - stub(stage_state, "reload_vertical_half_of_map_region") - stub(stage_state, "reload_horizontal_half_of_map_region") - stub(stage_state, "reload_quarter_of_map_region") - end) + describe('reload_map_region', function () - teardown(function () - _G.reload:revert() - stage_state.reload_vertical_half_of_map_region:revert() - stage_state.reload_horizontal_half_of_map_region:revert() - stage_state.reload_quarter_of_map_region:revert() - end) + setup(function () + stub(_G, "reload") + stub(stage_state, "reload_vertical_half_of_map_region") + stub(stage_state, "reload_horizontal_half_of_map_region") + stub(stage_state, "reload_quarter_of_map_region") + end) - -- on_enter calls check_reload_map_region, so reset count for all reload utility methods - before_each(function () - _G.reload:clear() - stage_state.reload_vertical_half_of_map_region:clear() - stage_state.reload_horizontal_half_of_map_region:clear() - stage_state.reload_quarter_of_map_region:clear() + teardown(function () + _G.reload:revert() + stage_state.reload_vertical_half_of_map_region:revert() + stage_state.reload_horizontal_half_of_map_region:revert() + stage_state.reload_quarter_of_map_region:revert() + end) - state.curr_stage_id = 2 - end) + -- on_enter calls check_reload_map_region, so reset count for all reload utility methods + before_each(function () + _G.reload:clear() + stage_state.reload_vertical_half_of_map_region:clear() + stage_state.reload_horizontal_half_of_map_region:clear() + stage_state.reload_quarter_of_map_region:clear() - it('should call reload for map 01 for region coords (0, 1)', function () - state:reload_map_region(vector(0, 1)) + state.curr_stage_id = 2 + end) - assert.spy(reload).was_called(1) - assert.spy(reload).was_called_with(0x2000, 0x2000, 0x1000, "data_stage2_01.p8") - end) + it('should call reload for map 01 for region coords (0, 1)', function () + state:reload_map_region(vector(0, 1)) - it('should call reload_vertical_half_of_map_region for map 10 and 11 for region coords (1, 0.5)', function () - state:reload_map_region(vector(1, 0.5)) + assert.spy(reload).was_called(1) + assert.spy(reload).was_called_with(0x2000, 0x2000, 0x1000, "data_stage2_01.p8") + end) - assert.spy(stage_state.reload_vertical_half_of_map_region).was_called(2) - assert.spy(stage_state.reload_vertical_half_of_map_region).was_called_with(match.ref(state), vertical_dirs.up, "data_stage2_10.p8") - assert.spy(stage_state.reload_vertical_half_of_map_region).was_called_with(match.ref(state), vertical_dirs.down, "data_stage2_11.p8") - end) + it('should call reload_vertical_half_of_map_region for map 10 and 11 for region coords (1, 0.5)', function () + state:reload_map_region(vector(1, 0.5)) - it('should call reload_horizontal_half_of_map_region for map 00 and 10 for region coords (0.5, 0)', function () - state:reload_map_region(vector(0.5, 0)) + assert.spy(stage_state.reload_vertical_half_of_map_region).was_called(2) + assert.spy(stage_state.reload_vertical_half_of_map_region).was_called_with(match.ref(state), vertical_dirs.up, "data_stage2_10.p8") + assert.spy(stage_state.reload_vertical_half_of_map_region).was_called_with(match.ref(state), vertical_dirs.down, "data_stage2_11.p8") + end) - assert.spy(stage_state.reload_horizontal_half_of_map_region).was_called(2) - assert.spy(stage_state.reload_horizontal_half_of_map_region).was_called_with(match.ref(state), horizontal_dirs.left, "data_stage2_00.p8") - assert.spy(stage_state.reload_horizontal_half_of_map_region).was_called_with(match.ref(state), horizontal_dirs.right, "data_stage2_10.p8") - end) + it('should call reload_horizontal_half_of_map_region for map 00 and 10 for region coords (0.5, 0)', function () + state:reload_map_region(vector(0.5, 0)) - it('should call reload_horizontal_half_of_map_region for map 00 and 10 for region coords (0.5, 0)', function () - state:reload_map_region(vector(0.5, 0.5)) + assert.spy(stage_state.reload_horizontal_half_of_map_region).was_called(2) + assert.spy(stage_state.reload_horizontal_half_of_map_region).was_called_with(match.ref(state), horizontal_dirs.left, "data_stage2_00.p8") + assert.spy(stage_state.reload_horizontal_half_of_map_region).was_called_with(match.ref(state), horizontal_dirs.right, "data_stage2_10.p8") + end) - assert.spy(stage_state.reload_quarter_of_map_region).was_called(4) - assert.spy(stage_state.reload_quarter_of_map_region).was_called_with(match.ref(state), horizontal_dirs.left, vertical_dirs.up, "data_stage2_00.p8") - assert.spy(stage_state.reload_quarter_of_map_region).was_called_with(match.ref(state), horizontal_dirs.right, vertical_dirs.up, "data_stage2_10.p8") - assert.spy(stage_state.reload_quarter_of_map_region).was_called_with(match.ref(state), horizontal_dirs.left, vertical_dirs.down, "data_stage2_01.p8") - assert.spy(stage_state.reload_quarter_of_map_region).was_called_with(match.ref(state), horizontal_dirs.right, vertical_dirs.down, "data_stage2_11.p8") - end) + it('should call reload_horizontal_half_of_map_region for map 00 and 10 for region coords (0.5, 0)', function () + state:reload_map_region(vector(0.5, 0.5)) - it('should set loaded_map_region_coords to the passed region', function () - state.loaded_map_region_coords = vector(0, 0) + assert.spy(stage_state.reload_quarter_of_map_region).was_called(4) + assert.spy(stage_state.reload_quarter_of_map_region).was_called_with(match.ref(state), horizontal_dirs.left, vertical_dirs.up, "data_stage2_00.p8") + assert.spy(stage_state.reload_quarter_of_map_region).was_called_with(match.ref(state), horizontal_dirs.right, vertical_dirs.up, "data_stage2_10.p8") + assert.spy(stage_state.reload_quarter_of_map_region).was_called_with(match.ref(state), horizontal_dirs.left, vertical_dirs.down, "data_stage2_01.p8") + assert.spy(stage_state.reload_quarter_of_map_region).was_called_with(match.ref(state), horizontal_dirs.right, vertical_dirs.down, "data_stage2_11.p8") + end) - state:reload_map_region(vector(1, 0.5)) + it('should set loaded_map_region_coords to the passed region', function () + state.loaded_map_region_coords = vector(0, 0) - assert.are_equal(vector(1, 0.5), state.loaded_map_region_coords) - end) + state:reload_map_region(vector(1, 0.5)) + assert.are_equal(vector(1, 0.5), state.loaded_map_region_coords) end) - describe('check_reload_map_region', function () + end) - setup(function () - stub(stage_state, "get_map_region_coords", function (self, position) - -- see before_each below - if position == vector(200, 64) then - return vector(1, 0.5) - end - return vector(0, 0) - end) - stub(stage_state, "reload_map_region") - end) + describe('check_reload_map_region', function () - teardown(function () - stage_state.get_map_region_coords:revert() - stage_state.reload_map_region:revert() + setup(function () + stub(stage_state, "get_map_region_coords", function (self, position) + -- see before_each below + if position == vector(200, 64) then + return vector(1, 0.5) + end + return vector(0, 0) end) + stub(stage_state, "reload_map_region") + end) - before_each(function () - -- dummy PC so it doesn't error, the stub above really decides of the result - state.player_char = {position = vector(0, 0)} - -- at least set some camera position used in get_map_region_coords stub - -- so we can verify we are passing it correctly - state.camera.position = vector(200, 64) - end) + teardown(function () + stage_state.get_map_region_coords:revert() + stage_state.reload_map_region:revert() + end) - after_each(function () - stage_state.get_map_region_coords:clear() - stage_state.reload_map_region:clear() - end) + before_each(function () + -- dummy PC so it doesn't error, the stub above really decides of the result + state.player_char = {position = vector(0, 0)} + -- at least set some camera position used in get_map_region_coords stub + -- so we can verify we are passing it correctly + state.camera.position = vector(200, 64) + end) - it('should call reload_map_region with (1, 0.5)', function () - state.loaded_map_region_coords = vector(0, 0) + after_each(function () + stage_state.get_map_region_coords:clear() + stage_state.reload_map_region:clear() + end) - state:check_reload_map_region() + it('should call reload_map_region with (1, 0.5)', function () + state.loaded_map_region_coords = vector(0, 0) - assert.spy(stage_state.reload_map_region).was_called(1) - assert.spy(stage_state.reload_map_region).was_called_with(match.ref(state), vector(1, 0.5)) - end) + state:check_reload_map_region() - it('should not call reload_map_region with (1, 0.5) if no change occurs', function () - state.loaded_map_region_coords = vector(1, 0.5) - state:check_reload_map_region() + assert.spy(stage_state.reload_map_region).was_called(1) + assert.spy(stage_state.reload_map_region).was_called_with(match.ref(state), vector(1, 0.5)) + end) - assert.spy(stage_state.reload_map_region).was_not_called() - end) + it('should not call reload_map_region with (1, 0.5) if no change occurs', function () + state.loaded_map_region_coords = vector(1, 0.5) + state:check_reload_map_region() + assert.spy(stage_state.reload_map_region).was_not_called() end) - describe('get_spawn_object_callback', function () + end) - it('should return stage_state.spawn_emerald_at for visual.emerald_repr_sprite_id', function () - assert.are_equal(stage_state.spawn_emerald_at, state:get_spawn_object_callback(visual.emerald_repr_sprite_id)) - end) + describe('get_spawn_object_callback', function () - it('should return stage_state.spawn_palm_tree_leaves_at for visual.palm_tree_leaves_core_id', function () - assert.are_equal(stage_state.spawn_palm_tree_leaves_at, state:get_spawn_object_callback(visual.palm_tree_leaves_core_id)) - end) + it('should return stage_state.spawn_emerald_at for visual.emerald_repr_sprite_id', function () + assert.are_equal(stage_state.spawn_emerald_at, state:get_spawn_object_callback(visual.emerald_repr_sprite_id)) + end) - it('should return stage_state.spawn_goal_plate_at for visual.goal_plate_base_id', function () - assert.are_equal(stage_state.spawn_goal_plate_at, state:get_spawn_object_callback(visual.goal_plate_base_id)) - end) + it('should return stage_state.spawn_palm_tree_leaves_at for visual.palm_tree_leaves_core_id', function () + assert.are_equal(stage_state.spawn_palm_tree_leaves_at, state:get_spawn_object_callback(visual.palm_tree_leaves_core_id)) + end) + it('should return stage_state.spawn_goal_plate_at for visual.goal_plate_base_id', function () + assert.are_equal(stage_state.spawn_goal_plate_at, state:get_spawn_object_callback(visual.goal_plate_base_id)) end) - -- we stub spawn_objects_in_all_map_regions in (stage state entered) region, so test it outside - describe('spawn_objects_in_all_map_regions', function () + end) - setup(function () - stub(stage_state, "reload_map_region") - stub(stage_state, "scan_current_region_to_spawn_objects") - end) + -- we stub spawn_objects_in_all_map_regions in (stage state entered) region, so test it outside + describe('spawn_objects_in_all_map_regions', function () - teardown(function () - stage_state.reload_map_region:revert() - stage_state.scan_current_region_to_spawn_objects:revert() - end) + setup(function () + stub(stage_state, "reload_map_region") + stub(stage_state, "scan_current_region_to_spawn_objects") + end) - after_each(function () - stage_state.reload_map_region:clear() - stage_state.scan_current_region_to_spawn_objects:clear() - end) + teardown(function () + stage_state.reload_map_region:revert() + stage_state.scan_current_region_to_spawn_objects:revert() + end) - it('should call reload every map on the 2x3 grid = 6 calls, calling scan_current_region_to_spawn_objects as many times', function () - state.curr_stage_data = { - tile_width = 250, -- not exactly 256 to test ceiling to 2 regions per row - tile_height = 32 * 3 -- 3 regions per column - } - state.loaded_map_region_coords = vector(1, 0.5) + after_each(function () + stage_state.reload_map_region:clear() + stage_state.scan_current_region_to_spawn_objects:clear() + end) - state:spawn_objects_in_all_map_regions() + it('should call reload every map on the 2x3 grid = 6 calls, calling scan_current_region_to_spawn_objects as many times', function () + state.curr_stage_data = { + tile_width = 250, -- not exactly 256 to test ceiling to 2 regions per row + tile_height = 32 * 3 -- 3 regions per column + } + state.loaded_map_region_coords = vector(1, 0.5) - assert.spy(stage_state.reload_map_region).was_called(6) - assert.spy(stage_state.reload_map_region).was_called_with(match.ref(state), vector(0, 0)) - assert.spy(stage_state.reload_map_region).was_called_with(match.ref(state), vector(1, 0)) - assert.spy(stage_state.reload_map_region).was_called_with(match.ref(state), vector(0, 1)) - assert.spy(stage_state.reload_map_region).was_called_with(match.ref(state), vector(1, 1)) - assert.spy(stage_state.reload_map_region).was_called_with(match.ref(state), vector(0, 2)) - assert.spy(stage_state.reload_map_region).was_called_with(match.ref(state), vector(1, 2)) + state:spawn_objects_in_all_map_regions() - assert.spy(stage_state.scan_current_region_to_spawn_objects).was_called(6) - end) + assert.spy(stage_state.reload_map_region).was_called(6) + assert.spy(stage_state.reload_map_region).was_called_with(match.ref(state), vector(0, 0)) + assert.spy(stage_state.reload_map_region).was_called_with(match.ref(state), vector(1, 0)) + assert.spy(stage_state.reload_map_region).was_called_with(match.ref(state), vector(0, 1)) + assert.spy(stage_state.reload_map_region).was_called_with(match.ref(state), vector(1, 1)) + assert.spy(stage_state.reload_map_region).was_called_with(match.ref(state), vector(0, 2)) + assert.spy(stage_state.reload_map_region).was_called_with(match.ref(state), vector(1, 2)) + assert.spy(stage_state.scan_current_region_to_spawn_objects).was_called(6) end) - -- we stub restore_picked_emerald_data in (stage state entered) region, so test it outside - describe('restore_picked_emerald_data', function () + end) - before_each(function () - -- 0b01001001 -> 73 (low-endian, so lowest bit is for emerald 1) - poke(0x4300, 73) - end) + -- we stub restore_picked_emerald_data in (stage state entered) region, so test it outside + describe('restore_picked_emerald_data', function () - after_each(function () - poke(0x4300, 0) - end) + before_each(function () + -- 0b01001001 -> 73 (low-endian, so lowest bit is for emerald 1) + poke(0x4300, 73) + end) - it('should read 1 byte in general memory representing picked emeralds bitset', function () - state:restore_picked_emerald_data() + after_each(function () + poke(0x4300, 0) + end) - assert.are_same({ - [1] = true, - [4] = true, - [7] = true, - }, state.picked_emerald_numbers_set) - end) + it('should read 1 byte in general memory representing picked emeralds bitset', function () + state:restore_picked_emerald_data() - it('should delete emerald object for every picked emerald', function () - state.emeralds = {"dummy1", "dummy2", "dummy3", "dummy4", "dummy5", "dummy6", "dummy7", "dummy8"} + assert.are_same({ + [1] = true, + [4] = true, + [7] = true, + }, state.picked_emerald_numbers_set) + end) - state:restore_picked_emerald_data() + it('should delete emerald object for every picked emerald', function () + state.emeralds = {"dummy1", "dummy2", "dummy3", "dummy4", "dummy5", "dummy6", "dummy7", "dummy8"} - assert.are_same({"dummy2", "dummy3", "dummy5", "dummy6", "dummy8"}, state.emeralds) - end) + state:restore_picked_emerald_data() + assert.are_same({"dummy2", "dummy3", "dummy5", "dummy6", "dummy8"}, state.emeralds) end) - describe('(stage states added)', function () + end) - before_each(function () - flow:add_gamestate(state) + describe('(stage states added)', function () + + before_each(function () + flow:add_gamestate(state) + end) + + after_each(function () + flow:init() + end) + + describe('(stage state entered)', function () + + setup(function () + -- we don't really mind spying on spawn_objects_in_all_map_regions + -- but we do not want to spend several seconds finding all of them + -- in before_each every time due to on_enter just for tests, + -- so we stub this + stub(stage_state, "spawn_objects_in_all_map_regions") + + -- restore_picked_emerald_data relies on peek which will find nil memory if not set + -- so stub it + stub(stage_state, "restore_picked_emerald_data") + end) + + teardown(function () + stage_state.spawn_objects_in_all_map_regions:revert() + stage_state.restore_picked_emerald_data:revert() end) after_each(function () - flow:init() + stage_state.spawn_objects_in_all_map_regions:clear() + stage_state.restore_picked_emerald_data:clear() + end) + + before_each(function () + flow:change_state(state) end) - describe('(stage state entered)', function () + describe('spawn_player_char', function () setup(function () - -- we don't really mind spying on spawn_objects_in_all_map_regions - -- but we do not want to spend several seconds finding all of them - -- in before_each every time due to on_enter just for tests, - -- so we stub this - stub(stage_state, "spawn_objects_in_all_map_regions") - - -- restore_picked_emerald_data relies on peek which will find nil memory if not set - -- so stub it - stub(stage_state, "restore_picked_emerald_data") + spy.on(player_char, "spawn_at") end) teardown(function () - stage_state.spawn_objects_in_all_map_regions:revert() - stage_state.restore_picked_emerald_data:revert() - end) - - after_each(function () - stage_state.spawn_objects_in_all_map_regions:clear() - stage_state.restore_picked_emerald_data:clear() + player_char.spawn_at:revert() end) before_each(function () - flow:change_state(state) - -- entering stage currently starts coroutine show_stage_splash_async - -- which will cause side effects when updating coroutines to test other - -- async functions, so clear that now - state.app:stop_all_coroutines() + -- clear count before test as entering stage will auto-spawn character once + player_char.spawn_at:clear() end) - describe('spawn_player_char', function () - - setup(function () - spy.on(player_char, "spawn_at") - end) + it('should spawn the player character at the stage spawn location', function () + state:spawn_player_char() + local player_char = state.player_char + assert.is_not_nil(player_char) + local spawn_position = state.curr_stage_data.spawn_location:to_center_position() - teardown(function () - player_char.spawn_at:revert() - end) + -- interface + assert.are_equal(spawn_position, player_char.position) + -- we haven't initialized any map in busted, so the character is falling in the air and spawn_at detected this + assert.are_equal(motion_states.falling, player_char.motion_state) - before_each(function () - -- clear count before test as entering stage will auto-spawn character once - player_char.spawn_at:clear() - end) + -- implementation + assert.spy(player_char.spawn_at).was_called(1) + assert.spy(player_char.spawn_at).was_called_with(match.ref(state.player_char), spawn_position) + end) - it('should spawn the player character at the stage spawn location', function () - state:spawn_player_char() - local player_char = state.player_char - assert.is_not_nil(player_char) - local spawn_position = state.curr_stage_data.spawn_location:to_center_position() + end) - -- interface - assert.are_equal(spawn_position, player_char.position) - -- we haven't initialized any map in busted, so the character is falling in the air and spawn_at detected this - assert.are_equal(motion_states.falling, player_char.motion_state) + describe('update_fx', function () - -- implementation - assert.spy(player_char.spawn_at).was_called(1) - assert.spy(player_char.spawn_at).was_called_with(match.ref(state.player_char), spawn_position) + setup(function () + stub(emerald_fx, "update", function (self) + -- just a trick to force fx deactivation without going through + -- the full animated sprite logic (nor stubbing is_active itself, + -- as we really want to deactivate on update only to make sure it was called) + if self.position.x == 999 then + self.anim_spr.playing = false + end end) - end) - describe('update_fx', function () + teardown(function () + emerald_fx.update:revert() + end) - setup(function () - stub(emerald_fx, "update", function (self) - -- just a trick to force fx deactivation without going through - -- the full animated sprite logic (nor stubbing is_active itself, - -- as we really want to deactivate on update only to make sure it was called) - if self.position.x == 999 then - self.anim_spr.playing = false - end - end) - end) + after_each(function () + emerald_fx.update:clear() + end) - teardown(function () - emerald_fx.update:revert() - end) + it('should call update on each emerald fx', function () + state.emerald_pick_fxs = { + emerald_fx(1, vector(0, 0)), + emerald_fx(2, vector(12, 4)) + } - after_each(function () - emerald_fx.update:clear() - end) + state:update_fx() - it('should call update on each emerald fx', function () - state.emerald_pick_fxs = { - emerald_fx(1, vector(0, 0)), - emerald_fx(2, vector(12, 4)) - } + assert.spy(emerald_fx.update).was_called(2) + assert.spy(emerald_fx.update).was_called_with(match.ref(state.emerald_pick_fxs[1])) + assert.spy(emerald_fx.update).was_called_with(match.ref(state.emerald_pick_fxs[2])) + end) - state:update_fx() + it('should call delete on each emerald fx inactive *after* update', function () + -- add fx to delete on first and last position, to make sure + -- we don't make the mistake or deleting fx during iteration, which tends + -- to make us miss the last elements + state.emerald_pick_fxs = { + emerald_fx(1, vector(999, 1)), + emerald_fx(2, vector(2, 2)), + emerald_fx(3, vector(999, 3)) + } + + state:update_fx() + + assert.are_same({ + emerald_fx(2, vector(2, 2)) + }, state.emerald_pick_fxs) + end) - assert.spy(emerald_fx.update).was_called(2) - assert.spy(emerald_fx.update).was_called_with(match.ref(state.emerald_pick_fxs[1])) - assert.spy(emerald_fx.update).was_called_with(match.ref(state.emerald_pick_fxs[2])) - end) + end) - it('should call delete on each emerald fx inactive *after* update', function () - -- add fx to delete on first and last position, to make sure - -- we don't make the mistake or deleting fx during iteration, which tends - -- to make us miss the last elements - state.emerald_pick_fxs = { - emerald_fx(1, vector(999, 1)), - emerald_fx(2, vector(2, 2)), - emerald_fx(3, vector(999, 3)) - } - - state:update_fx() - - assert.are_same({ - emerald_fx(2, vector(2, 2)) - }, state.emerald_pick_fxs) - end) + describe('render_fx', function () + setup(function () + stub(stage_state, "set_camera_with_origin") + stub(emerald_fx, "render") end) - describe('render_fx', function () - - setup(function () - stub(stage_state, "set_camera_with_origin") - stub(emerald_fx, "render") - end) - - teardown(function () - stage_state.set_camera_with_origin:revert() - emerald_fx.render:revert() - end) + teardown(function () + stage_state.set_camera_with_origin:revert() + emerald_fx.render:revert() + end) - after_each(function () - stage_state.set_camera_with_origin:clear() - emerald_fx.render:clear() - end) + after_each(function () + stage_state.set_camera_with_origin:clear() + emerald_fx.render:clear() + end) - it('render_player_char should call set_camera_with_origin', function () - state:render_fx() + it('render_player_char should call set_camera_with_origin', function () + state:render_fx() - assert.spy(stage_state.set_camera_with_origin).was_called(1) - assert.spy(stage_state.set_camera_with_origin).was_called_with(match.ref(state)) - end) + assert.spy(stage_state.set_camera_with_origin).was_called(1) + assert.spy(stage_state.set_camera_with_origin).was_called_with(match.ref(state)) + end) - it('should call render on each emerald fx', function () - state.emerald_pick_fxs = { - emerald_fx(1, vector(0, 0)), - emerald_fx(2, vector(12, 4)) - } + it('should call render on each emerald fx', function () + state.emerald_pick_fxs = { + emerald_fx(1, vector(0, 0)), + emerald_fx(2, vector(12, 4)) + } - state:render_fx() - - assert.spy(emerald_fx.render).was_called(2) - assert.spy(emerald_fx.render).was_called_with(match.ref(state.emerald_pick_fxs[1])) - assert.spy(emerald_fx.render).was_called_with(match.ref(state.emerald_pick_fxs[2])) - end) + state:render_fx() + assert.spy(emerald_fx.render).was_called(2) + assert.spy(emerald_fx.render).was_called_with(match.ref(state.emerald_pick_fxs[1])) + assert.spy(emerald_fx.render).was_called_with(match.ref(state.emerald_pick_fxs[2])) end) - describe('update', function () - - setup(function () - stub(stage_state, "update_fx") - stub(player_char, "update") - stub(stage_state, "check_reached_goal") - stub(goal_plate, "update") - stub(camera_class, "update") - end) + end) - teardown(function () - stage_state.update_fx:revert() - player_char.update:revert() - stage_state.check_reached_goal:revert() - goal_plate.update:revert() - camera_class.update:revert() - end) + describe('update', function () - before_each(function () - -- check_reload_map_region must not be stubbed in setup, which would happen - -- before the before_each -> flow:change_state(state) of (stage state entered) - -- context. Instead we stub and revert before and after each - -- (alternatively we could spy.on if we don't mind extra work during tests) - -- in general we should actually avoid relying on complex methods like change_state - -- in before_each and just manually set the properties we really need on state - stub(stage_state, "check_reload_map_region") - end) + setup(function () + stub(stage_state, "update_fx") + stub(player_char, "update") + stub(stage_state, "check_reached_goal") + stub(goal_plate, "update") + stub(camera_class, "update") + end) - after_each(function () - stage_state.update_fx:clear() - player_char.update:clear() - stage_state.check_reached_goal:clear() - goal_plate.update:clear() - camera_class.update:clear() + teardown(function () + stage_state.update_fx:revert() + player_char.update:revert() + stage_state.check_reached_goal:revert() + goal_plate.update:revert() + camera_class.update:revert() + end) - stage_state.check_reload_map_region:revert() - end) + before_each(function () + -- check_reload_map_region must not be stubbed in setup, which would happen + -- before the before_each -> flow:change_state(state) of (stage state entered) + -- context. Instead we stub and revert before and after each + -- (alternatively we could spy.on if we don't mind extra work during tests) + -- in general we should actually avoid relying on complex methods like change_state + -- in before_each and just manually set the properties we really need on state + stub(stage_state, "check_reload_map_region") + end) - it('should call fx and character update, check_reached_goal, goal update, camera update, check_reload_map_region', function () - state.goal_plate = goal_plate(location(100, 0)) + after_each(function () + stage_state.update_fx:clear() + player_char.update:clear() + stage_state.check_reached_goal:clear() + goal_plate.update:clear() + camera_class.update:clear() - state:update() - - assert.spy(stage_state.update_fx).was_called(1) - assert.spy(stage_state.update_fx).was_called_with(match.ref(state)) - assert.spy(player_char.update).was_called(1) - assert.spy(player_char.update).was_called_with(match.ref(state.player_char)) - - assert.spy(stage_state.check_reached_goal).was_called(1) - assert.spy(stage_state.check_reached_goal).was_called_with(match.ref(state)) - assert.spy(goal_plate.update).was_called(1) - assert.spy(goal_plate.update).was_called_with(match.ref(state.goal_plate)) - assert.spy(camera_class.update).was_called(1) - assert.spy(camera_class.update).was_called_with(match.ref(state.camera)) - assert.spy(stage_state.check_reload_map_region).was_called(1) - assert.spy(stage_state.check_reload_map_region).was_called_with(match.ref(state)) - end) + stage_state.check_reload_map_region:revert() + end) - it('should not try to update goal if no goal plate found (safety check for itests)', function () - state.goal_plate = nil + it('should call fx and character update, check_reached_goal, goal update, camera update, check_reload_map_region', function () + state.goal_plate = goal_plate(location(100, 0)) - state:update() + state:update() - assert.spy(goal_plate.update).was_not_called() - end) + assert.spy(stage_state.update_fx).was_called(1) + assert.spy(stage_state.update_fx).was_called_with(match.ref(state)) + assert.spy(player_char.update).was_called(1) + assert.spy(player_char.update).was_called_with(match.ref(state.player_char)) - end) -- update + assert.spy(stage_state.check_reached_goal).was_called(1) + assert.spy(stage_state.check_reached_goal).was_called_with(match.ref(state)) + assert.spy(goal_plate.update).was_called(1) + assert.spy(goal_plate.update).was_called_with(match.ref(state.goal_plate)) + assert.spy(camera_class.update).was_called(1) + assert.spy(camera_class.update).was_called_with(match.ref(state.camera)) + assert.spy(stage_state.check_reload_map_region).was_called(1) + assert.spy(stage_state.check_reload_map_region).was_called_with(match.ref(state)) + end) - describe('render', function () + it('should not try to update goal if no goal plate found (safety check for itests)', function () + state.goal_plate = nil - setup(function () - stub(visual_stage, "render_background") - stub(stage_state, "render_stage_elements") - stub(stage_state, "render_fx") - stub(stage_state, "render_hud") - stub(stage_state, "render_overlay") - stub(stage_state, "render_emerald_cross") - end) + state:update() - teardown(function () - visual_stage.render_background:revert() - stage_state.render_stage_elements:revert() - stage_state.render_fx:revert() - stage_state.render_hud:revert() - stage_state.render_overlay:revert() - stage_state.render_emerald_cross:revert() - end) + assert.spy(goal_plate.update).was_not_called() + end) - after_each(function () - visual_stage.render_background:clear() - stage_state.render_stage_elements:clear() - stage_state.render_fx:clear() - stage_state.render_hud:clear() - stage_state.render_overlay:clear() - stage_state.render_emerald_cross:clear() - end) + end) -- update - it('should call render_background, render_stage_elements, render_fx, render_hud, render_overlay', function () - state:render() - assert.spy(visual_stage.render_background).was_called(1) - assert.spy(visual_stage.render_background).was_called_with(state.camera.position) - assert.spy(stage_state.render_stage_elements).was_called(1) - assert.spy(stage_state.render_stage_elements).was_called_with(match.ref(state)) - assert.spy(stage_state.render_fx).was_called(1) - assert.spy(stage_state.render_fx).was_called_with(match.ref(state)) - assert.spy(stage_state.render_hud).was_called(1) - assert.spy(stage_state.render_hud).was_called_with(match.ref(state)) - assert.spy(stage_state.render_overlay).was_called(1) - assert.spy(stage_state.render_overlay).was_called_with(match.ref(state)) - end) + describe('render', function () - end) -- state.render + setup(function () + stub(visual_stage, "render_background") + stub(stage_state, "render_stage_elements") + stub(stage_state, "render_fx") + stub(stage_state, "render_hud") + stub(stage_state, "render_emerald_cross") + end) - describe('extend_spring', function () + teardown(function () + visual_stage.render_background:revert() + stage_state.render_stage_elements:revert() + stage_state.render_fx:revert() + stage_state.render_hud:revert() + stage_state.render_emerald_cross:revert() + end) - setup(function () - stub(picosonic_app, "start_coroutine") - end) + after_each(function () + visual_stage.render_background:clear() + stage_state.render_stage_elements:clear() + stage_state.render_fx:clear() + stage_state.render_hud:clear() + stage_state.render_emerald_cross:clear() + end) - teardown(function () - picosonic_app.start_coroutine:revert() - end) + it('should call render_background, render_stage_elements, render_fx, render_hud', function () + state:render() + assert.spy(visual_stage.render_background).was_called(1) + assert.spy(visual_stage.render_background).was_called_with(state.camera.position) + assert.spy(stage_state.render_stage_elements).was_called(1) + assert.spy(stage_state.render_stage_elements).was_called_with(match.ref(state)) + assert.spy(stage_state.render_fx).was_called(1) + assert.spy(stage_state.render_fx).was_called_with(match.ref(state)) + assert.spy(stage_state.render_hud).was_called(1) + assert.spy(stage_state.render_hud).was_called_with(match.ref(state)) + end) - -- start_coroutine is also called on stage enter (with show_stage_splash_async) - -- so we must clear call count *before* the first test - before_each(function () - picosonic_app.start_coroutine:clear() - end) + end) -- state.render - it('should play a coroutine that replaces spring tile with extended spring tile until a certain time (only check no error)', function () - state:extend_spring(location(2, 0)) - assert.spy(picosonic_app.start_coroutine).was_called(1) - assert.spy(picosonic_app.start_coroutine).was_called_with(match.ref(state.app), stage_state.extend_spring_async, match.ref(state), location(2, 0)) - end) + describe('extend_spring', function () + setup(function () + stub(picosonic_app, "start_coroutine") end) - describe('check_emerald_pick_area', function () - - before_each(function () - state.emeralds = { - emerald(1, location(0, 0)), - emerald(2, location(1, 0)), - emerald(3, location(0, 1)), - } - end) - - it('should return nil when position is too far from all the emeralds', function () - assert.is_nil(state:check_emerald_pick_area(vector(12, 12))) - end) + teardown(function () + picosonic_app.start_coroutine:revert() + end) - it('should return emerald when position is close to that emerald (giving priority to lower index)', function () - assert.are_equal(state.emeralds[1], state:check_emerald_pick_area(vector(8, 4))) - end) + before_each(function () + picosonic_app.start_coroutine:clear() + end) + it('should play a coroutine that replaces spring tile with extended spring tile until a certain time (only check no error)', function () + state:extend_spring(location(2, 0)) + assert.spy(picosonic_app.start_coroutine).was_called(1) + assert.spy(picosonic_app.start_coroutine).was_called_with(match.ref(state.app), stage_state.extend_spring_async, match.ref(state), location(2, 0)) end) - describe('character_pick_emerald', function () + end) - -- we need to stub start_coroutine on the child class, - -- not gameapp, or calls won't be monitored + describe('check_emerald_pick_area', function () - setup(function () - stub(picosonic_app, "start_coroutine") - end) + before_each(function () + state.emeralds = { + emerald(1, location(0, 0)), + emerald(2, location(1, 0)), + emerald(3, location(0, 1)), + } + end) - teardown(function () - picosonic_app.start_coroutine:revert() - end) + it('should return nil when position is too far from all the emeralds', function () + assert.is_nil(state:check_emerald_pick_area(vector(12, 12))) + end) - -- clear in before_each as stage_state on_enter - -- will start some coroutune already - before_each(function () - picosonic_app.start_coroutine:clear() - end) + it('should return emerald when position is close to that emerald (giving priority to lower index)', function () + assert.are_equal(state.emeralds[1], state:check_emerald_pick_area(vector(8, 4))) + end) - before_each(function () - state.emeralds = { - emerald(1, location(0, 0)), - emerald(2, location(1, 0)), - emerald(3, location(0, 1)), - } - end) + end) - it('should add an emerald number to the picked set', function () - state.picked_emerald_numbers_set = { - [4] = true - } - state.emeralds = { - emerald(1, location(0, 0)), - emerald(2, location(1, 0)), - emerald(3, location(0, 1)), - } - state:character_pick_emerald(state.emeralds[2]) - assert.are_same({[2] = true, [4] = true}, state.picked_emerald_numbers_set) - end) + describe('character_pick_emerald', function () - it('should create a pick FX and play it', function () - state.emerald_pick_fxs = { - emerald_fx(1, vector(0, 0)) - } + -- we need to stub start_coroutine on the child class, + -- not gameapp, or calls won't be monitored - state:character_pick_emerald(state.emeralds[2]) + setup(function () + stub(picosonic_app, "start_coroutine") + end) - -- emerald 2 was at location (1, 0), - -- so its center was at (12, 4) - assert.are_same({ - emerald_fx(1, vector(0, 0)), - emerald_fx(2, vector(12, 4)) - }, - state.emerald_pick_fxs) - end) + teardown(function () + picosonic_app.start_coroutine:revert() + end) - it('should remove an emerald from the sequence', function () - state.emeralds = { - emerald(1, location(0, 0)), - emerald(2, location(1, 0)), - emerald(3, location(0, 1)), - } - state:character_pick_emerald(state.emeralds[2]) - assert.are_same({emerald(1, location(0, 0)), emerald(3, location(0, 1))}, state.emeralds) - end) + -- clear in before_each as stage_state on_enter + -- will start some coroutune already + before_each(function () + picosonic_app.start_coroutine:clear() + end) - it('should play character_pick_emerald sfx', function () - state:character_pick_emerald(state.emeralds[2]) - assert.spy(picosonic_app.start_coroutine).was_called(1) - assert.spy(picosonic_app.start_coroutine).was_called_with(match.ref(state.app), stage_state.play_pick_emerald_jingle_async, match.ref(state)) - end) + before_each(function () + state.emeralds = { + emerald(1, location(0, 0)), + emerald(2, location(1, 0)), + emerald(3, location(0, 1)), + } + end) + it('should add an emerald number to the picked set', function () + state.picked_emerald_numbers_set = { + [4] = true + } + state.emeralds = { + emerald(1, location(0, 0)), + emerald(2, location(1, 0)), + emerald(3, location(0, 1)), + } + state:character_pick_emerald(state.emeralds[2]) + assert.are_same({[2] = true, [4] = true}, state.picked_emerald_numbers_set) end) - describe('check_loop_external_triggers', function () + it('should create a pick FX and play it', function () + state.emerald_pick_fxs = { + emerald_fx(1, vector(0, 0)) + } - before_each(function () - -- customize loop areas locally. We are redefining a table so that won't affect - -- the original data table in stage_data.lua. To simplify we don't redefine everything, - -- but if we need to for the tests we'll just add the missing members - state.curr_stage_data = { - loop_exit_areas = {location_rect(-1, 0, 0, 2)}, - loop_entrance_areas = {location_rect(1, 0, 3, 4)} - } - end) + state:character_pick_emerald(state.emeralds[2]) - it('should return nil when not entering external entrance trigger at all', function () - assert.is_nil(state:check_loop_external_triggers(vector(-20, 0), 2)) - end) + -- emerald 2 was at location (1, 0), + -- so its center was at (12, 4) + assert.are_same({ + emerald_fx(1, vector(0, 0)), + emerald_fx(2, vector(12, 4)) + }, + state.emerald_pick_fxs) + end) - it('should return 1 when entering external entrance trigger and not yet on layer 1', function () - assert.are_equal(1, state:check_loop_external_triggers(vector(-11, 0), 2)) - end) + it('should remove an emerald from the sequence', function () + state.emeralds = { + emerald(1, location(0, 0)), + emerald(2, location(1, 0)), + emerald(3, location(0, 1)), + } + state:character_pick_emerald(state.emeralds[2]) + assert.are_same({emerald(1, location(0, 0)), emerald(3, location(0, 1))}, state.emeralds) + end) - it('should return 1 when entering external entrance trigger but already on layer 1', function () - assert.is_nil(state:check_loop_external_triggers(vector(-11, 0), 1)) - end) + it('should play character_pick_emerald sfx', function () + state:character_pick_emerald(state.emeralds[2]) + assert.spy(picosonic_app.start_coroutine).was_called(1) + assert.spy(picosonic_app.start_coroutine).was_called_with(match.ref(state.app), stage_state.play_pick_emerald_jingle_async, match.ref(state)) + end) - it('should return 2 when entering external entrance trigger and not yet on layer 2', function () - -- to get bottom/left of a tile you need to add 1 to i/j - assert.are_equal(2, state:check_loop_external_triggers(vector((3+1)*8+4, (4+1)*8), 1)) - end) + end) - it('should return nil when entering external entrance trigger but already on layer 2', function () - assert.is_nil(state:check_loop_external_triggers(vector((3+1)*8+4, (4+1)*8), 2)) - end) + describe('check_loop_external_triggers', function () + before_each(function () + -- customize loop areas locally. We are redefining a table so that won't affect + -- the original data table in stage_data.lua. To simplify we don't redefine everything, + -- but if we need to for the tests we'll just add the missing members + state.curr_stage_data = { + loop_exit_areas = {location_rect(-1, 0, 0, 2)}, + loop_entrance_areas = {location_rect(1, 0, 3, 4)} + } end) - describe('check_reached_goal', function () + it('should return nil when not entering external entrance trigger at all', function () + assert.is_nil(state:check_loop_external_triggers(vector(-20, 0), 2)) + end) - setup(function () - stub(picosonic_app, "start_coroutine") - end) + it('should return 1 when entering external entrance trigger and not yet on layer 1', function () + assert.are_equal(1, state:check_loop_external_triggers(vector(-11, 0), 2)) + end) - teardown(function () - picosonic_app.start_coroutine:revert() - end) + it('should return 1 when entering external entrance trigger but already on layer 1', function () + assert.is_nil(state:check_loop_external_triggers(vector(-11, 0), 1)) + end) - -- start_coroutine is also called on stage enter (with show_stage_splash_async) - -- so we must clear call count *before* the first test - before_each(function () - picosonic_app.start_coroutine:clear() - end) + it('should return 2 when entering external entrance trigger and not yet on layer 2', function () + -- to get bottom/left of a tile you need to add 1 to i/j + assert.are_equal(2, state:check_loop_external_triggers(vector((3+1)*8+4, (4+1)*8), 1)) + end) - describe('(no goal)', function () + it('should return nil when entering external entrance trigger but already on layer 2', function () + assert.is_nil(state:check_loop_external_triggers(vector((3+1)*8+4, (4+1)*8), 2)) + end) - -- should be each - before_each(function () - state.player_char.position = vector(1000, 0) - state:check_reached_goal() - end) + end) - it('should not set has_player_char_reached_goal to true', function () - assert.is_false(state.has_player_char_reached_goal) - end) + describe('check_reached_goal', function () - it('should not start on_reached_goal_async', function () - assert.spy(picosonic_app.start_coroutine).was_not_called() - end) + setup(function () + stub(picosonic_app, "start_coroutine") + end) - end) + teardown(function () + picosonic_app.start_coroutine:revert() + end) - describe('(before the goal)', function () + before_each(function () + picosonic_app.start_coroutine:clear() + end) - -- should be each - before_each(function () - state.goal_plate = goal_plate(location(100, 0)) - state.player_char.position = vector(804 - 1, 0) - state:check_reached_goal() - end) + describe('(no goal)', function () - it('should not set has_player_char_reached_goal to true', function () - assert.is_false(state.has_player_char_reached_goal) - end) + -- should be each + before_each(function () + state.player_char.position = vector(1000, 0) + state:check_reached_goal() + end) - it('should not start on_reached_goal_async', function () - assert.spy(picosonic_app.start_coroutine).was_not_called() - end) + it('should not set has_player_char_reached_goal to true', function () + assert.is_false(state.has_player_char_reached_goal) + end) + it('should not start on_reached_goal_async', function () + assert.spy(picosonic_app.start_coroutine).was_not_called() end) - describe('(just on the goal)', function () + end) - before_each(function () - state.goal_plate = goal_plate(location(100, 0)) - state.player_char.position = vector(804, 0) - state:check_reached_goal() - end) + describe('(before the goal)', function () - it('should set has_player_char_reached_goal to true', function () - assert.is_true(state.has_player_char_reached_goal) - end) + -- should be each + before_each(function () + state.goal_plate = goal_plate(location(100, 0)) + state.player_char.position = vector(804 - 1, 0) + state:check_reached_goal() + end) - it('should start on_reached_goal_async', function () - assert.spy(picosonic_app.start_coroutine).was_called(1) - assert.spy(picosonic_app.start_coroutine).was_called_with(match.ref(state.app), stage_state.on_reached_goal_async, match.ref(state)) - end) + it('should not set has_player_char_reached_goal to true', function () + assert.is_false(state.has_player_char_reached_goal) + end) + it('should not start on_reached_goal_async', function () + assert.spy(picosonic_app.start_coroutine).was_not_called() end) - describe('(just on the goal, but already reached once)', function () + end) - before_each(function () - state.goal_plate = goal_plate(location(100, 0)) - state.player_char.position = vector(804, 0) - state.has_player_char_reached_goal = true - state:check_reached_goal() - end) + describe('(just on the goal)', function () - it('should keep has_player_char_reached_goal as true', function () - assert.is_true(state.has_player_char_reached_goal) - end) + before_each(function () + state.goal_plate = goal_plate(location(100, 0)) + state.player_char.position = vector(804, 0) + state:check_reached_goal() + end) - it('should not call on_reached_goal_async again', function () - assert.spy(picosonic_app.start_coroutine).was_not_called() - end) + it('should set has_player_char_reached_goal to true', function () + assert.is_true(state.has_player_char_reached_goal) + end) + it('should start on_reached_goal_async', function () + assert.spy(picosonic_app.start_coroutine).was_called(1) + assert.spy(picosonic_app.start_coroutine).was_called_with(match.ref(state.app), stage_state.on_reached_goal_async, match.ref(state)) end) end) - describe('on_reached_goal_async', function () - - -- removed actual tests, too hard to maintain - -- instead, just run it and see if it crashes - - local corunner + describe('(just on the goal, but already reached once)', function () before_each(function () state.goal_plate = goal_plate(location(100, 0)) - state.spawned_emerald_locations = {1, 2, 3, 4, 5, 6, 7, 8} - - corunner = coroutine_runner() - corunner:start_coroutine(stage_state.on_reached_goal_async, state) + state.player_char.position = vector(804, 0) + state.has_player_char_reached_goal = true + state:check_reached_goal() end) - it('should not crash with a few emeralds', function () - state.emeralds = {5, 6, 7, 8} - - -- a time long enough to cover everything until load() - for i = 1, 1000 do - corunner:update_coroutines() - end + it('should keep has_player_char_reached_goal as true', function () + assert.is_true(state.has_player_char_reached_goal) end) - it('should not crash with all emeralds', function () - state.emeralds = {} - - -- a time long enough to cover everything until load() - for i = 1, 1000 do - corunner:update_coroutines() - end + it('should not call on_reached_goal_async again', function () + assert.spy(picosonic_app.start_coroutine).was_not_called() end) end) - describe('store_picked_emerald_data', function () + end) - it('should store 1 byte in general memory representing picked emeralds bitset', function () - state.picked_emerald_numbers_set = { - [1] = true, - [4] = true, - [7] = true, - } - -- 0b01001001 -> 73 (low-endian, so lowest bit is for emerald 1) - state:store_picked_emerald_data() - assert.are_equal(73, peek(0x4300)) - end) + describe('on_reached_goal_async', function () - end) + -- removed actual tests, too hard to maintain + -- instead, just run it and see if it crashes - describe('feedback_reached_goal', function () + local corunner - setup(function () - stub(_G, "sfx") - stub(animated_sprite, "play") - end) + before_each(function () + state.goal_plate = goal_plate(location(100, 0)) + state.spawned_emerald_locations = {1, 2, 3, 4, 5, 6, 7, 8} - teardown(function () - sfx:revert() - animated_sprite.play:revert() - end) + corunner = coroutine_runner() + corunner:start_coroutine(stage_state.on_reached_goal_async, state) + end) - after_each(function () - sfx:clear() - end) + it('should not crash with a few emeralds', function () + state.emeralds = {5, 6, 7, 8} - before_each(function () - state.goal_plate = goal_plate(location(100, 0)) + -- a time long enough to cover everything until load() + for i = 1, 1000 do + corunner:update_coroutines() + end + end) - -- was called before, including just above as goal_plat:init - -- has a default animation, so clear at the end of before_each - animated_sprite.play:clear() - end) + it('should not crash with all emeralds', function () + state.emeralds = {} - it('should play goal_reached sfx', function () - state:feedback_reached_goal() - assert.spy(sfx).was_called(1) - assert.spy(sfx).was_called_with(audio.sfx_ids.goal_reached) - end) + -- a time long enough to cover everything until load() + for i = 1, 1000 do + corunner:update_coroutines() + end + end) - it('should play goal_plate "rotating" anim', function () - state:feedback_reached_goal() - assert.spy(animated_sprite.play).was_called(1) - assert.spy(animated_sprite.play).was_called_with(match.ref(state.goal_plate.anim_spr), "rotating") - end) + end) + describe('store_picked_emerald_data', function () + + it('should store 1 byte in general memory representing picked emeralds bitset', function () + state.picked_emerald_numbers_set = { + [1] = true, + [4] = true, + [7] = true, + } + -- 0b01001001 -> 73 (low-endian, so lowest bit is for emerald 1) + state:store_picked_emerald_data() + assert.are_equal(73, peek(0x4300)) end) - describe('show_stage_splash_async', function () + end) - local corunner + describe('feedback_reached_goal', function () - before_each(function () - corunner = coroutine_runner() - corunner:start_coroutine(stage_state.show_stage_splash_async, state) - end) + setup(function () + stub(_G, "sfx") + stub(animated_sprite, "play") + end) - -- this coroutine become more complex, so only test it doesn't crash - it('show_stage_splash_async should not crash', function () - -- a time long enough to cover initial delay then full animation - for i = 1, stage_data.show_stage_splash_delay * state.app.fps - 1 + 160 do - corunner:update_coroutines() - end - end) + teardown(function () + sfx:revert() + animated_sprite.play:revert() + end) + after_each(function () + sfx:clear() end) - describe('set_camera_with_origin', function () + before_each(function () + state.goal_plate = goal_plate(location(100, 0)) - it('should set the pico8 camera so that it is centered on the camera position, with origin (0, 0) by default', function () - state.camera.position = vector(24, 13) - state:set_camera_with_origin() - assert.are_same(vector(24 - 128 / 2, 13 - 128 / 2), vector(pico8.camera_x, pico8.camera_y)) - end) + -- was called before, including just above as goal_plat:init + -- has a default animation, so clear at the end of before_each + animated_sprite.play:clear() + end) - it('should set the pico8 camera so that it is centered on the camera position, with custom origin subtracted', function () - state.camera.position = vector(24, 13) - state:set_camera_with_origin(vector(10, 20)) - assert.are_same(vector(24 - 128 / 2 - 10, 13 - 128 / 2 - 20), vector(pico8.camera_x, pico8.camera_y)) - end) + it('should play goal_reached sfx', function () + state:feedback_reached_goal() + assert.spy(sfx).was_called(1) + assert.spy(sfx).was_called_with(audio.sfx_ids.goal_reached) + end) + it('should play goal_plate "rotating" anim', function () + state:feedback_reached_goal() + assert.spy(animated_sprite.play).was_called(1) + assert.spy(animated_sprite.play).was_called_with(match.ref(state.goal_plate.anim_spr), "rotating") end) - describe('set_camera_with_region_origin', function () + end) - setup(function () - stub(stage_state, "set_camera_with_origin") - end) + describe('set_camera_with_origin', function () - teardown(function () - stage_state.set_camera_with_origin:revert() - end) + it('should set the pico8 camera so that it is centered on the camera position, with origin (0, 0) by default', function () + state.camera.position = vector(24, 13) + state:set_camera_with_origin() + assert.are_same(vector(24 - 128 / 2, 13 - 128 / 2), vector(pico8.camera_x, pico8.camera_y)) + end) - after_each(function () - stage_state.set_camera_with_origin:clear() - end) + it('should set the pico8 camera so that it is centered on the camera position, with custom origin subtracted', function () + state.camera.position = vector(24, 13) + state:set_camera_with_origin(vector(10, 20)) + assert.are_same(vector(24 - 128 / 2 - 10, 13 - 128 / 2 - 20), vector(pico8.camera_x, pico8.camera_y)) + end) - it('should call set_camera_with_origin with current region topleft xy', function () - state.loaded_map_region_coords = vector(2, 1) + end) - state:set_camera_with_region_origin() + describe('set_camera_with_region_origin', function () - assert.spy(state.set_camera_with_origin).was_called(1) - assert.spy(state.set_camera_with_origin).was_called_with(match.ref(state), vector(tile_size * map_region_tile_width * 2, tile_size * map_region_tile_height * 1)) - end) + setup(function () + stub(stage_state, "set_camera_with_origin") + end) + teardown(function () + stage_state.set_camera_with_origin:revert() end) - describe('render_stage_elements', function () + after_each(function () + stage_state.set_camera_with_origin:clear() + end) - setup(function () - stub(stage_state, "render_environment_midground") - stub(stage_state, "render_emeralds") - stub(stage_state, "render_goal_plate") - stub(stage_state, "render_player_char") - stub(stage_state, "render_environment_foreground") - stub(stage_state, "debug_render_trigger") - stub(player_char, "debug_draw_rays") - end) + it('should call set_camera_with_origin with current region topleft xy', function () + state.loaded_map_region_coords = vector(2, 1) - teardown(function () - stage_state.render_environment_midground:revert() - stage_state.render_emeralds:revert() - stage_state.render_goal_plate:revert() - stage_state.render_player_char:revert() - stage_state.render_environment_foreground:revert() - stage_state.debug_render_trigger:revert() - player_char.debug_draw_rays:revert() - end) + state:set_camera_with_region_origin() - after_each(function () - stage_state.render_environment_midground:clear() - stage_state.render_emeralds:clear() - stage_state.render_goal_plate:clear() - stage_state.render_player_char:clear() - stage_state.render_environment_foreground:clear() - stage_state.debug_render_trigger:clear() - player_char.debug_draw_rays:clear() - end) + assert.spy(state.set_camera_with_origin).was_called(1) + assert.spy(state.set_camera_with_origin).was_called_with(match.ref(state), vector(tile_size * map_region_tile_width * 2, tile_size * map_region_tile_height * 1)) + end) - it('should call render methods on everything in the stage', function () - state:render_stage_elements() - assert.spy(state.render_environment_midground).was_called(1) - assert.spy(state.render_environment_midground).was_called_with(match.ref(state)) - assert.spy(state.render_emeralds).was_called(1) - assert.spy(state.render_emeralds).was_called_with(match.ref(state)) - assert.spy(state.render_goal_plate).was_called(1) - assert.spy(state.render_goal_plate).was_called_with(match.ref(state)) - assert.spy(state.render_player_char).was_called(1) - assert.spy(state.render_player_char).was_called_with(match.ref(state)) - assert.spy(state.render_environment_foreground).was_called(1) - assert.spy(state.render_environment_foreground).was_called_with(match.ref(state)) - -- #debug_trigger only - assert.spy(state.debug_render_trigger).was_called(1) - assert.spy(state.debug_render_trigger).was_called_with(match.ref(state)) - -- #debug_trigger only end - -- #debug_character only - assert.spy(player_char.debug_draw_rays).was_called(1) - assert.spy(player_char.debug_draw_rays).was_called_with(match.ref(state.player_char)) - -- #debug_character only end - end) + end) + + describe('render_stage_elements', function () + setup(function () + stub(stage_state, "render_environment_midground") + stub(stage_state, "render_emeralds") + stub(stage_state, "render_goal_plate") + stub(stage_state, "render_player_char") + stub(stage_state, "render_environment_foreground") + stub(stage_state, "debug_render_trigger") + stub(player_char, "debug_draw_rays") end) - describe('render_overlay', function () + teardown(function () + stage_state.render_environment_midground:revert() + stage_state.render_emeralds:revert() + stage_state.render_goal_plate:revert() + stage_state.render_player_char:revert() + stage_state.render_environment_foreground:revert() + stage_state.debug_render_trigger:revert() + player_char.debug_draw_rays:revert() + end) - setup(function () - stub(overlay, "draw") - end) + after_each(function () + stage_state.render_environment_midground:clear() + stage_state.render_emeralds:clear() + stage_state.render_goal_plate:clear() + stage_state.render_player_char:clear() + stage_state.render_environment_foreground:clear() + stage_state.debug_render_trigger:clear() + player_char.debug_draw_rays:clear() + end) - teardown(function () - overlay.draw:revert() - end) + it('should call render methods on everything in the stage', function () + state:render_stage_elements() + assert.spy(state.render_environment_midground).was_called(1) + assert.spy(state.render_environment_midground).was_called_with(match.ref(state)) + assert.spy(state.render_emeralds).was_called(1) + assert.spy(state.render_emeralds).was_called_with(match.ref(state)) + assert.spy(state.render_goal_plate).was_called(1) + assert.spy(state.render_goal_plate).was_called_with(match.ref(state)) + assert.spy(state.render_player_char).was_called(1) + assert.spy(state.render_player_char).was_called_with(match.ref(state)) + assert.spy(state.render_environment_foreground).was_called(1) + assert.spy(state.render_environment_foreground).was_called_with(match.ref(state)) + -- #debug_trigger only + assert.spy(state.debug_render_trigger).was_called(1) + assert.spy(state.debug_render_trigger).was_called_with(match.ref(state)) + -- #debug_trigger only end + -- #debug_character only + assert.spy(player_char.debug_draw_rays).was_called(1) + assert.spy(player_char.debug_draw_rays).was_called_with(match.ref(state.player_char)) + -- #debug_character only end + end) - after_each(function () - overlay.draw:clear() - end) + end) - it('should reset camera', function () - state:render_overlay() - assert.are_same(vector.zero(), vector(pico8.camera_x, pico8.camera_y)) - end) + describe('render_player_char', function () - it('should call title_overlay:draw', function () - state:render_overlay() - assert.spy(overlay.draw).was_called(1) - assert.spy(overlay.draw).was_called_with(match.ref(state.title_overlay)) - end) + setup(function () + stub(stage_state, "set_camera_with_origin") + stub(player_char, "render") + end) + teardown(function () + stage_state.set_camera_with_origin:revert() + player_char.render:revert() end) - describe('render_player_char', function () + after_each(function () + stage_state.set_camera_with_origin:clear() + player_char.render:clear() + end) - setup(function () - stub(stage_state, "set_camera_with_origin") - stub(player_char, "render") - end) + it('should call set_camera_with_origin and player_char:render', function () + state:render_player_char() - teardown(function () - stage_state.set_camera_with_origin:revert() - player_char.render:revert() - end) + assert.spy(stage_state.set_camera_with_origin).was_called(1) + assert.spy(stage_state.set_camera_with_origin).was_called_with(match.ref(state)) + assert.spy(player_char.render).was_called(1) + assert.spy(player_char.render).was_called_with(match.ref(state.player_char)) + end) - after_each(function () - stage_state.set_camera_with_origin:clear() - player_char.render:clear() - end) + end) - it('should call set_camera_with_origin and player_char:render', function () - state:render_player_char() + describe('render_emeralds', function () - assert.spy(stage_state.set_camera_with_origin).was_called(1) - assert.spy(stage_state.set_camera_with_origin).was_called_with(match.ref(state)) - assert.spy(player_char.render).was_called(1) - assert.spy(player_char.render).was_called_with(match.ref(state.player_char)) - end) + setup(function () + stub(stage_state, "set_camera_with_origin") + stub(emerald, "render") + end) + teardown(function () + stage_state.set_camera_with_origin:revert() + emerald.render:revert() end) - describe('render_emeralds', function () + after_each(function () + stage_state.set_camera_with_origin:clear() + emerald.render:clear() + end) - setup(function () - stub(stage_state, "set_camera_with_origin") - stub(emerald, "render") - end) + it('should call set_camera_with_origin and emerald:render', function () + state.emeralds = { + emerald(1, location(1, 1)), + emerald(2, location(2, 2)), + } - teardown(function () - stage_state.set_camera_with_origin:revert() - emerald.render:revert() - end) + state:render_emeralds() - after_each(function () - stage_state.set_camera_with_origin:clear() - emerald.render:clear() - end) + assert.spy(stage_state.set_camera_with_origin).was_called(1) + assert.spy(stage_state.set_camera_with_origin).was_called_with(match.ref(state)) + assert.spy(emerald.render).was_called(2) + assert.spy(emerald.render).was_called_with(match.ref(state.emeralds[1])) + assert.spy(emerald.render).was_called_with(match.ref(state.emeralds[2])) + end) - it('should call set_camera_with_origin and emerald:render', function () - state.emeralds = { - emerald(1, location(1, 1)), - emerald(2, location(2, 2)), - } + end) - state:render_emeralds() + describe('render_goal_plate', function () - assert.spy(stage_state.set_camera_with_origin).was_called(1) - assert.spy(stage_state.set_camera_with_origin).was_called_with(match.ref(state)) - assert.spy(emerald.render).was_called(2) - assert.spy(emerald.render).was_called_with(match.ref(state.emeralds[1])) - assert.spy(emerald.render).was_called_with(match.ref(state.emeralds[2])) - end) + setup(function () + stub(stage_state, "set_camera_with_origin") + stub(goal_plate, "render") + end) + teardown(function () + stage_state.set_camera_with_origin:revert() + goal_plate.render:revert() end) - describe('render_goal_plate', function () + after_each(function () + stage_state.set_camera_with_origin:clear() + goal_plate.render:clear() + end) - setup(function () - stub(stage_state, "set_camera_with_origin") - stub(goal_plate, "render") - end) + it('(no goal plate found) should do nothing', function () + state:render_goal_plate() - teardown(function () - stage_state.set_camera_with_origin:revert() - goal_plate.render:revert() - end) + assert.spy(stage_state.set_camera_with_origin).was_not_called() + assert.spy(goal_plate.render).was_not_called() + end) - after_each(function () - stage_state.set_camera_with_origin:clear() - goal_plate.render:clear() - end) + it('(goal plate found) should call set_camera_with_origin and goal_plate:render', function () + state.goal_plate = goal_plate(location(2, 33)) - it('(no goal plate found) should do nothing', function () - state:render_goal_plate() + state:render_goal_plate() - assert.spy(stage_state.set_camera_with_origin).was_not_called() - assert.spy(goal_plate.render).was_not_called() - end) + assert.spy(stage_state.set_camera_with_origin).was_called(1) + assert.spy(stage_state.set_camera_with_origin).was_called_with(match.ref(state)) + assert.spy(goal_plate.render).was_called(1) + assert.spy(goal_plate.render).was_called_with(match.ref(state.goal_plate)) + end) - it('(goal plate found) should call set_camera_with_origin and goal_plate:render', function () - state.goal_plate = goal_plate(location(2, 33)) + end) - state:render_goal_plate() + describe('render_hud', function () - assert.spy(stage_state.set_camera_with_origin).was_called(1) - assert.spy(stage_state.set_camera_with_origin).was_called_with(match.ref(state)) - assert.spy(goal_plate.render).was_called(1) - assert.spy(goal_plate.render).was_called_with(match.ref(state.goal_plate)) - end) + setup(function () + stub(emerald, "draw") + stub(player_char, "debug_print_info") + end) + teardown(function () + emerald.draw:revert() + player_char.debug_print_info:revert() end) - describe('render_hud', function () + after_each(function () + emerald.draw:clear() + player_char.debug_print_info:clear() + end) - setup(function () - stub(emerald, "draw") - stub(player_char, "debug_print_info") - end) + it('should call emerald.draw for each emerald, true color for picked ones and silhouette for unpicked ones', function () + state.spawned_emerald_locations = { + -- dummy values just to have correct count (3, counting hole on 2) + location(1, 1), location(2, 2), location(3, 3) + } + state.picked_emerald_numbers_set = { + [1] = true, + [3] = true + } + + state:render_hud() + + assert.spy(emerald.draw).was_called(3) + assert.spy(emerald.draw).was_called_with(1, vector(4, 3)) + -- silhouette only + assert.spy(emerald.draw).was_called_with(-1, vector(12, 3)) + assert.spy(emerald.draw).was_called_with(3, vector(20, 3)) + end) - teardown(function () - emerald.draw:revert() - player_char.debug_print_info:revert() - end) + it('should debug render character info (#debug_character only)', function () + state:render_hud() - after_each(function () - emerald.draw:clear() - player_char.debug_print_info:clear() - end) + assert.spy(player_char.debug_print_info).was_called(1) + assert.spy(player_char.debug_print_info).was_called_with(match.ref(state.player_char)) + end) - it('should call emerald.draw for each emerald, true color for picked ones and silhouette for unpicked ones', function () - state.spawned_emerald_locations = { - -- dummy values just to have correct count (3, counting hole on 2) - location(1, 1), location(2, 2), location(3, 3) - } - state.picked_emerald_numbers_set = { - [1] = true, - [3] = true - } - - state:render_hud() - - assert.spy(emerald.draw).was_called(3) - assert.spy(emerald.draw).was_called_with(1, vector(4, 3)) - -- silhouette only - assert.spy(emerald.draw).was_called_with(-1, vector(12, 3)) - assert.spy(emerald.draw).was_called_with(3, vector(20, 3)) - end) + end) - it('should debug render character info (#debug_character only)', function () - state:render_hud() + describe('(region at (2, 3))', function () - assert.spy(player_char.debug_print_info).was_called(1) - assert.spy(player_char.debug_print_info).was_called_with(match.ref(state.player_char)) + setup(function () + stub(stage_state, "get_region_topleft_location", function (self) + return location(2, 3) end) - end) - describe('(region at (2, 3))', function () + teardown(function () + stage_state.get_region_topleft_location:revert() + end) - setup(function () - stub(stage_state, "get_region_topleft_location", function (self) - return location(2, 3) - end) + describe('global_to_region_location', function () + it('global loc (2, 4) - (2, 3) => (0, 1)', function () + assert.are_equal(location(0, 1), state:global_to_region_location(location(2, 4))) end) + end) - teardown(function () - stage_state.get_region_topleft_location:revert() + describe('region_to_global_location', function () + it('region loc (0, 1) + (2, 3) => (2, 4)', function () + assert.are_equal(location(2, 4), state:region_to_global_location(location(0, 1))) end) + end) - describe('global_to_region_location', function () - it('global loc (2, 4) - (2, 3) => (0, 1)', function () - assert.are_equal(location(0, 1), state:global_to_region_location(location(2, 4))) - end) - end) + end) - describe('region_to_global_location', function () - it('region loc (0, 1) + (2, 3) => (2, 4)', function () - assert.are_equal(location(2, 4), state:region_to_global_location(location(0, 1))) - end) - end) + describe('get_region_topleft_location', function () + it('region (0, 0) => (0, 0)', function () + state.loaded_map_region_coords = vector(0, 0) + assert.are_same(location(0, 0), state:get_region_topleft_location()) end) - describe('get_region_topleft_location', function () + it('region (0.5, 1) => (64, 32)', function () + state.loaded_map_region_coords = vector(0.5, 1) + assert.are_same(location(64, 32), state:get_region_topleft_location()) + end) - it('region (0, 0) => (0, 0)', function () - state.loaded_map_region_coords = vector(0, 0) - assert.are_same(location(0, 0), state:get_region_topleft_location()) - end) + end) - it('region (0.5, 1) => (64, 32)', function () - state.loaded_map_region_coords = vector(0.5, 1) - assert.are_same(location(64, 32), state:get_region_topleft_location()) - end) + describe('(with tile_test_data)', function () + + setup(function () + tile_test_data.setup() + stub(stage_state, "set_camera_with_origin") + stub(stage_state, "set_camera_with_region_origin") + stub(sprite_data, "render") + stub(_G, "spr") + stub(_G, "map") end) - describe('(with tile_test_data)', function () + teardown(function () + tile_test_data.teardown() - setup(function () - tile_test_data.setup() + stage_state.set_camera_with_origin:revert() + stage_state.set_camera_with_region_origin:revert() + sprite_data.render:revert() + spr:revert() + map:revert() + end) - stub(stage_state, "set_camera_with_origin") - stub(stage_state, "set_camera_with_region_origin") - stub(sprite_data, "render") - stub(_G, "spr") - stub(_G, "map") - end) + before_each(function () + -- 2 midground tiles on screen, 1 outside when camera is at (0, 0) + mock_mset(0, 0, tile_repr.spring_left_id) + mock_mset(3, 0, tile_repr.spring_left_id) + mock_mset(9, 0, tile_repr.spring_left_id) + -- 1 undefined tile onscreen (it's foreground hiding leaf in PICO-8, + -- but what matters here is that midground flag is not set) + mock_mset(5, 0, 46) + -- foreground tile to test foreground layer + mock_mset(0, 1, tile_repr.grass_top_decoration1) + + state.curr_stage_data = { + loop_exit_areas = {location_rect(-1, 0, 0, 2)}, + loop_entrance_areas = {location_rect(1, 0, 3, 4)}, + goal_x = 3000 + } + + -- palm tree example to demonstrate extra foreground + state.palm_tree_leaves_core_global_locations = { + location(10, 2) + } + end) - teardown(function () - tile_test_data.teardown() + after_each(function () + pico8:clear_map() - stage_state.set_camera_with_origin:revert() - stage_state.set_camera_with_region_origin:revert() - sprite_data.render:revert() - spr:revert() - map:revert() - end) + stage_state.set_camera_with_origin:clear() + stage_state.set_camera_with_region_origin:clear() + sprite_data.render:clear() + spr:clear() + map:clear() + end) - before_each(function () - -- 2 midground tiles on screen, 1 outside when camera is at (0, 0) - mock_mset(0, 0, tile_repr.spring_left_id) - mock_mset(3, 0, tile_repr.spring_left_id) - mock_mset(9, 0, tile_repr.spring_left_id) - -- 1 undefined tile onscreen (it's foreground hiding leaf in PICO-8, - -- but what matters here is that midground flag is not set) - mock_mset(5, 0, 46) - -- foreground tile to test foreground layer - mock_mset(0, 1, tile_repr.grass_top_decoration1) - - state.curr_stage_data = { - loop_exit_areas = {location_rect(-1, 0, 0, 2)}, - loop_entrance_areas = {location_rect(1, 0, 3, 4)}, - goal_x = 3000 - } - - -- palm tree example to demonstrate extra foreground - state.palm_tree_leaves_core_global_locations = { - location(10, 2) - } - end) + it('render_environment_midground should call map for all midground sprites', function () + -- note that we reverted to using map for performance, so this test doesn't need to be + -- in the tile test data setup context anymore + state.camera.position = vector(0, 0) + state.loaded_map_region_coords = vector(0, 0) - after_each(function () - pico8:clear_map() + state:render_environment_midground() - stage_state.set_camera_with_origin:clear() - stage_state.set_camera_with_region_origin:clear() - sprite_data.render:clear() - spr:clear() - map:clear() - end) + assert.spy(stage_state.set_camera_with_region_origin).was_called(1) + assert.spy(stage_state.set_camera_with_region_origin).was_called_with(match.ref(state)) + + assert.spy(map).was_called(1) + assert.spy(map).was_called_with(0, 0, 0, 0, map_region_tile_width, map_region_tile_height, sprite_masks.midground) + end) - it('render_environment_midground should call map for all midground sprites', function () - -- note that we reverted to using map for performance, so this test doesn't need to be - -- in the tile test data setup context anymore - state.camera.position = vector(0, 0) - state.loaded_map_region_coords = vector(0, 0) + it('render_environment_foreground should call spr on tiles present on screen', function () + -- this test was copy-pasted from render_environment_midground + state.camera.position = vector(0, 0) + state.loaded_map_region_coords = vector(2, 1) - state:render_environment_midground() + state:render_environment_foreground() - assert.spy(stage_state.set_camera_with_region_origin).was_called(1) - assert.spy(stage_state.set_camera_with_region_origin).was_called_with(match.ref(state)) + -- we can't check call order, but set camera methods should be called consistently with map! + assert.spy(stage_state.set_camera_with_region_origin).was_called(1) + assert.spy(stage_state.set_camera_with_region_origin).was_called_with(match.ref(state)) - assert.spy(map).was_called(1) - assert.spy(map).was_called_with(0, 0, 0, 0, map_region_tile_width, map_region_tile_height, sprite_masks.midground) - end) + assert.spy(map).was_called(2) - it('render_environment_foreground should call spr on tiles present on screen', function () - -- this test was copy-pasted from render_environment_midground - state.camera.position = vector(0, 0) - state.loaded_map_region_coords = vector(2, 1) + assert.spy(map).was_called_with(0, 0, 0, 0, map_region_tile_width, map_region_tile_height, sprite_masks.foreground) - state:render_environment_foreground() + assert.spy(stage_state.set_camera_with_origin).was_called(1) + assert.spy(stage_state.set_camera_with_origin).was_called_with(match.ref(state)) - -- we can't check call order, but set camera methods should be called consistently with map! - assert.spy(stage_state.set_camera_with_region_origin).was_called(1) - assert.spy(stage_state.set_camera_with_region_origin).was_called_with(match.ref(state)) + local area = state.curr_stage_data.loop_entrance_areas[1] + -- (2, 1) comes from state.loaded_map_region_coords + assert.spy(map).was_called_with(area.left - 2 * 128, area.top - 1 * 32, + tile_size * area.left, tile_size * area.top, + area.right - area.left + 1, area.bottom - area.top + 1, + sprite_masks.midground) - assert.spy(map).was_called(2) + assert.spy(sprite_data.render).was_called(3) + -- top + assert.spy(sprite_data.render).was_called_with(match.ref(visual.sprite_data_t.palm_tree_leaves_top), vector(8 * 10, 8 * 2)) + -- right + assert.spy(sprite_data.render).was_called_with(match.ref(visual.sprite_data_t.palm_tree_leaves_right), vector(8 * 11, 8 * 2)) + -- left (right flipped x) + assert.spy(sprite_data.render).was_called_with(match.ref(visual.sprite_data_t.palm_tree_leaves_right), vector(8 * 10, 8 * 2), true) + end) - assert.spy(map).was_called_with(0, 0, 0, 0, map_region_tile_width, map_region_tile_height, sprite_masks.foreground) + end) -- (with tile_test_data) - assert.spy(stage_state.set_camera_with_origin).was_called(1) - assert.spy(stage_state.set_camera_with_origin).was_called_with(match.ref(state)) + describe('state audio methods', function () - local area = state.curr_stage_data.loop_entrance_areas[1] - -- (2, 1) comes from state.loaded_map_region_coords - assert.spy(map).was_called_with(area.left - 2 * 128, area.top - 1 * 32, - tile_size * area.left, tile_size * area.top, - area.right - area.left + 1, area.bottom - area.top + 1, - sprite_masks.midground) + setup(function () + stub(_G, "reload") + end) - assert.spy(sprite_data.render).was_called(3) - -- top - assert.spy(sprite_data.render).was_called_with(match.ref(visual.sprite_data_t.palm_tree_leaves_top), vector(8 * 10, 8 * 2)) - -- right - assert.spy(sprite_data.render).was_called_with(match.ref(visual.sprite_data_t.palm_tree_leaves_right), vector(8 * 11, 8 * 2)) - -- left (right flipped x) - assert.spy(sprite_data.render).was_called_with(match.ref(visual.sprite_data_t.palm_tree_leaves_right), vector(8 * 10, 8 * 2), true) - end) + teardown(function () + reload:revert() + end) + + -- reload is called during on_enter for region loading, so clear call count now + before_each(function () + reload:clear() + end) - end) -- (with tile_test_data) + after_each(function () + pico8.current_music = nil + end) describe('state audio methods', function () setup(function () - stub(_G, "reload") + stub(stage_state, "reload_bgm_tracks") end) teardown(function () - reload:revert() + stage_state.reload_bgm_tracks:revert() end) - -- reload is called during on_enter for region loading, so clear call count now before_each(function () - reload:clear() - end) - - after_each(function () - pico8.current_music = nil - end) - - describe('state audio methods', function () - - setup(function () - stub(stage_state, "reload_bgm_tracks") - end) - - teardown(function () - stage_state.reload_bgm_tracks:revert() - end) - - before_each(function () - stage_state.reload_bgm_tracks:clear() - end) - - it('reload_bgm should reload music memory from bgm cartridge and call reload_bgm_tracks', function () - state:reload_bgm() - - assert.spy(reload).was_called(1) - assert.spy(reload).was_called_with(0x3100, 0x3100, 0xa0, "data_bgm1.p8") - assert.spy(stage_state.reload_bgm_tracks).was_called(1) - assert.spy(stage_state.reload_bgm_tracks).was_called_with(match.ref(state)) - end) - + stage_state.reload_bgm_tracks:clear() end) - it('reload_bgm_tracks should reload sfx from bgm cartridge', function () - state:reload_bgm_tracks() + it('reload_bgm should reload music memory from bgm cartridge and call reload_bgm_tracks', function () + state:reload_bgm() assert.spy(reload).was_called(1) - assert.spy(reload).was_called_with(0x3200, 0x3200, 0xd48, "data_bgm1.p8") + assert.spy(reload).was_called_with(0x3100, 0x3100, 0xa0, "data_bgm1.p8") + assert.spy(stage_state.reload_bgm_tracks).was_called(1) + assert.spy(stage_state.reload_bgm_tracks).was_called_with(match.ref(state)) end) - it('play_bgm should start level bgm', function () - state:play_bgm() + end) - assert.are_same({music=state.curr_stage_data.bgm_id, fadems=0, channel_mask=(1 << 0) + (1 << 1) + (1 << 2)}, pico8.current_music) - end) + it('reload_bgm_tracks should reload sfx from bgm cartridge', function () + state:reload_bgm_tracks() - it('stop_bgm should stop level bgm if started, else do nothing', function () - state:stop_bgm() - assert.is_nil(pico8.current_music) - state:play_bgm() - state:stop_bgm() - assert.is_nil(pico8.current_music) - state:play_bgm() - state:stop_bgm(2.0) - assert.is_nil(pico8.current_music) - end) + assert.spy(reload).was_called(1) + assert.spy(reload).was_called_with(0x3200, 0x3200, 0xd48, "data_bgm1.p8") + end) - end) -- state audio methods + it('play_bgm should start level bgm', function () + state:play_bgm() - -- unlike above, we test on_exit method itself here - describe('on_exit', function () + assert.are_same({music=state.curr_stage_data.bgm_id, fadems=0, channel_mask=(1 << 0) + (1 << 1) + (1 << 2)}, pico8.current_music) + end) - setup(function () - stub(overlay, "clear_drawables") - stub(picosonic_app, "stop_all_coroutines") - stub(stage_state, "stop_bgm") - end) + it('stop_bgm should stop level bgm if started, else do nothing', function () + state:stop_bgm() + assert.is_nil(pico8.current_music) + state:play_bgm() + state:stop_bgm() + assert.is_nil(pico8.current_music) + state:play_bgm() + state:stop_bgm(2.0) + assert.is_nil(pico8.current_music) + end) - teardown(function () - overlay.clear_drawables:revert() - picosonic_app.stop_all_coroutines:revert() - stage_state.stop_bgm:revert() - end) + end) -- state audio methods - after_each(function () - overlay.clear_drawables:clear() - stage_state.stop_bgm:clear() - end) + -- unlike above, we test on_exit method itself here + -- now commented out to spare tokens, as we never use it + --[[ + describe('on_exit', function () - before_each(function () - -- another before_each called stop_all_coroutines, - -- so we must clear the count - picosonic_app.stop_all_coroutines:clear() + setup(function () + stub(picosonic_app, "stop_all_coroutines") + stub(stage_state, "stop_bgm") + end) - state:on_exit() - end) + teardown(function () + picosonic_app.stop_all_coroutines:revert() + stage_state.stop_bgm:revert() + end) - it('should stop all the coroutines', function () - assert.spy(picosonic_app.stop_all_coroutines).was_called(1) - assert.spy(picosonic_app.stop_all_coroutines).was_called_with(match.ref(state.app)) - end) + after_each(function () + stage_state.stop_bgm:clear() + end) - it('should clear the player character', function () - assert.is_nil(state.player_char) - end) + before_each(function () + -- another before_each called stop_all_coroutines, + -- so we must clear the count + picosonic_app.stop_all_coroutines:clear() - it('should call clear all drawables', function () - assert.spy(overlay.clear_drawables).was_called(1) - assert.spy(overlay.clear_drawables).was_called_with(match.ref(state.title_overlay)) - end) + state:on_exit() + end) - it('should reset pico8 camera', function () - assert.are_same({0, 0}, {pico8.camera_x, pico8.camera_y}) - end) + it('should stop all the coroutines', function () + assert.spy(picosonic_app.stop_all_coroutines).was_called(1) + assert.spy(picosonic_app.stop_all_coroutines).was_called_with(match.ref(state.app)) + end) - it('should call stop_bgm', function () - assert.spy(stage_state.stop_bgm).was_called(1) - assert.spy(stage_state.stop_bgm).was_called_with(match.ref(state)) - end) + it('should clear the player character', function () + assert.is_nil(state.player_char) + end) + + it('should reset pico8 camera', function () + assert.are_same({0, 0}, {pico8.camera_x, pico8.camera_y}) + end) + it('should call stop_bgm', function () + assert.spy(stage_state.stop_bgm).was_called(1) + assert.spy(stage_state.stop_bgm).was_called_with(match.ref(state)) end) - end) -- (stage state entered) + end) + --]] - end) -- (stage states added) + end) -- (stage state entered) - end) -- (with instance) + end) -- (stage states added) - end) -- (stage state) + end) -- (with instance) end) diff --git a/src/stage_intro/stage_intro_state.lua b/src/stage_intro/stage_intro_state.lua index 33eccec7..8101ab55 100644 --- a/src/stage_intro/stage_intro_state.lua +++ b/src/stage_intro/stage_intro_state.lua @@ -1,11 +1,100 @@ local gamestate = require("engine/application/gamestate") +local postprocess = require("engine/render/postprocess") +local label = require("engine/ui/label") +local overlay = require("engine/ui/overlay") +local rectangle = require("engine/ui/rectangle") + +local stage_data = require("data/stage_data") +local stage_intro_data = require("data/stage_intro_data") +local ui_animation = require("ui/ui_animation") local stage_intro_state = derived_class(gamestate) stage_intro_state.type = ':stage_intro' +function stage_intro_state:init() + -- data + self.curr_stage_data = stage_data.for_stage[1] + + -- render + self.overlay = overlay() + self.postproc = postprocess() +end + function stage_intro_state:on_enter() - -- immediately load stage for now, as this is just a stub for the future stage intro + self.app:start_coroutine(self.show_stage_splash_async, self) +end + +-- never called, we directly load ingame cartridge +--[[ +function stage_intro_state:on_exit() + -- clear all coroutines + self.app:stop_all_coroutines() + + -- clear object state vars + self.overlay:clear_drawables() + + -- reinit camera offset for other states + camera() +end +--]] + +function stage_intro_state:render() + self:render_overlay() + self.postproc:apply() +end + +-- render the title overlay with a fixed ui camera +function stage_intro_state:render_overlay() + camera() + self.overlay:draw() +end + +function stage_intro_state:show_stage_splash_async() + -- fade in + for i = 5, 0, -1 do + self.postproc.darkness = i + yield_delay(7) + end + + self.app:yield_delay_s(stage_intro_data.show_stage_splash_delay) + + -- FIXME: draw iteration order not guaranteed, pico-sonic may be hidden "below" banner + + -- init position y is -height so it starts just at the screen top edge + local banner = rectangle(vector(9, -106), 32, 106, colors.red) + self.overlay:add_drawable("banner", banner) + + -- banner text accompanies text, and ends at y = 89, so starts at y = 89 - 106 = -17 + local banner_text = label("pico\nsonic", vector(16, -17), colors.white) + self.overlay:add_drawable("banner_text", banner_text) + + -- make banner enter from the top + ui_animation.move_drawables_on_coord_async("y", {banner, banner_text}, {0, 89}, -106, 0, 9) + + local zone_rectangle = rectangle(vector(128, 45), 47, 3, colors.black) + self.overlay:add_drawable("zone_rect", zone_rectangle) + + local zone_label = label(self.curr_stage_data.title, vector(129, 43), colors.white) + self.overlay:add_drawable("zone", zone_label) + + -- make text enter from the right + ui_animation.move_drawables_on_coord_async("x", {zone_rectangle, zone_label}, {0, 1}, 128, 41, 14) + + -- keep zone displayed for a moment + yield_delay(102) + + -- make banner exit to the top + ui_animation.move_drawables_on_coord_async("y", {banner, banner_text}, {0, 89}, 0, -106, 8) + + -- make text exit to the right + ui_animation.move_drawables_on_coord_async("x", {zone_rectangle, zone_label}, {0, 1}, 41, 128, 14) + + self.overlay:remove_drawable("banner") + self.overlay:remove_drawable("banner_text") + self.overlay:remove_drawable("zone") + + -- splash is over, load ingame cartridge and give control to player load('picosonic_ingame.p8') end diff --git a/src/stage_intro/stage_intro_state_utest.lua b/src/stage_intro/stage_intro_state_utest.lua index e69de29b..9de16e63 100644 --- a/src/stage_intro/stage_intro_state_utest.lua +++ b/src/stage_intro/stage_intro_state_utest.lua @@ -0,0 +1,149 @@ +require("test/bustedhelper_stage_intro") +require("common_stage_intro") +require("resources/visual_ingame_addon") -- stage_intro mostly uses ingame visuals + +local stage_intro_state = require("stage_intro/stage_intro_state") + +local coroutine_runner = require("engine/application/coroutine_runner") +local postprocess = require("engine/render/postprocess") +local overlay = require("engine/ui/overlay") + +local picosonic_app = require("application/picosonic_app_stage_intro") +local stage_data = require("data/stage_data") +local stage_intro_data = require("data/stage_intro_data") + +describe('stage_intro_state', function () + + describe('static members', function () + + it('type is ":stage_intro"', function () + assert.are_equal(':stage_intro', stage_intro_state.type) + end) + + end) + + describe('(with instance)', function () + + local state + + before_each(function () + local app = picosonic_app() + state = stage_intro_state() + -- no need to register gamestate properly, just add app member to pass tests + state.app = app + end) + + describe('init', function () + + it('should initialize members', function () + assert.are_same({ + ':stage_intro', + stage_data.for_stage[1], + overlay(), + postprocess(), + }, + { + state.type, + state.curr_stage_data, + state.overlay, + state.postproc, + }) + end) + + end) + + describe('on_enter', function () + + setup(function () + stub(picosonic_app, "start_coroutine") + end) + + teardown(function () + picosonic_app.start_coroutine:revert() + end) + + after_each(function () + picosonic_app.start_coroutine:clear() + end) + + it('should call start_coroutine_method on show_stage_splash_async', function () + state:on_enter() + + assert.spy(picosonic_app.start_coroutine).was_called(1) + assert.spy(picosonic_app.start_coroutine).was_called_with(match.ref(state.app), stage_intro_state.show_stage_splash_async, match.ref(state)) + end) + + end) + + describe('render', function () + + setup(function () + stub(stage_intro_state, "render_overlay") + end) + + teardown(function () + stage_intro_state.render_overlay:revert() + end) + + after_each(function () + stage_intro_state.render_overlay:clear() + end) + + it('should call render_overlay', function () + state:render() + assert.spy(stage_intro_state.render_overlay).was_called(1) + assert.spy(stage_intro_state.render_overlay).was_called_with(match.ref(state)) + end) + + end) + + describe('render_overlay', function () + + setup(function () + stub(overlay, "draw") + end) + + teardown(function () + overlay.draw:revert() + end) + + after_each(function () + overlay.draw:clear() + end) + + it('should reset camera', function () + state:render_overlay() + assert.are_same(vector.zero(), vector(pico8.camera_x, pico8.camera_y)) + end) + + it('should call overlay:draw', function () + state:render_overlay() + assert.spy(overlay.draw).was_called(1) + assert.spy(overlay.draw).was_called_with(match.ref(state.overlay)) + end) + + end) + + + describe('show_stage_splash_async', function () + + local corunner + + before_each(function () + corunner = coroutine_runner() + corunner:start_coroutine(stage_intro_state.show_stage_splash_async, state) + end) + + -- this coroutine become more complex, so only test it doesn't crash + it('show_stage_splash_async should not crash', function () + -- a time long enough to cover initial delay then full animation + for i = 1, stage_intro_data.show_stage_splash_delay * state.app.fps - 1 + 160 do + corunner:update_coroutines() + end + end) + + end) + + end) -- (with instance) + +end) From 0b0c2f3e9cdf87950208d1c85286228c7100d54f Mon Sep 17 00:00:00 2001 From: huulong Date: Tue, 29 Dec 2020 18:40:40 +0100 Subject: [PATCH 39/91] [CHANGELOG] Added entry for version number on title menu --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 624a7630..714d075d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Stage clear: added retry screen with missed emeralds and options to retry with or without emerald, or go back to title menu - Stage clear: retry screen fades in and out with a zigzag swipe animation and/or gradual color darkness (palette swap) - Stage intro: stage fades in with gradual color darkness (palette swap) +- Titlemenu: added version number in top-right corner ### Changed - Sprite: fixed top row of some Sonic rotated run sprites missing From bba3afced657357a858450367542174af0d85bbc Mon Sep 17 00:00:00 2001 From: huulong Date: Wed, 30 Dec 2020 17:48:15 +0100 Subject: [PATCH 40/91] [INTRO] Render environment and player character during stage intro to transition perfectly with ingame - define new symbol #cartridge_suffix for each cartridge build to allow customization of how to use a script put in common for multiple cartridges - extracted base class base_stage_state from stage_intro_state, stage_state and stage_clear_state: it contains functionality common to at least two of them, and strips functionality not required in all cartridges with new symbol #cartridge_suffix - stage_intro now properly spawns player character and reloads map data + render them - player character is also stripped from handling input and item-related features in #stage_intro - modified assert in player character to allow stage_intro state - added vector_ext_angle to common_stage_intro because player character motion needs it - stage_clear_state cheats a little less and uses methods in base_stage_state, which are common with other stage states. It must now set camera position, loaded_map_region_coords, etc. properly so the common methods can work. --- build_single_cartridge.sh | 7 + data/builtin_data_stage_intro.p8 | 1 + src/common_stage_intro.lua | 6 + src/ingame/base_stage_state.lua | 171 ++++ src/ingame/base_stage_state_utest.lua | 314 +++++++ src/ingame/playercharacter.lua | 30 +- src/ingame/stage_state.lua | 158 +--- src/ingame/stage_state_utest.lua | 287 +------ src/stage_clear/stage_clear_state.lua | 59 +- src/stage_clear/stage_clear_state_utest.lua | 894 ++++++++++---------- src/stage_intro/stage_intro_state.lua | 76 +- src/stage_intro/stage_intro_state_utest.lua | 107 ++- 12 files changed, 1207 insertions(+), 903 deletions(-) create mode 100644 src/ingame/base_stage_state.lua create mode 100644 src/ingame/base_stage_state_utest.lua diff --git a/build_single_cartridge.sh b/build_single_cartridge.sh index ba7382d9..7ed09cc2 100755 --- a/build_single_cartridge.sh +++ b/build_single_cartridge.sh @@ -27,6 +27,8 @@ usage() { ARGUMENTS CARTRIDGE_SUFFIX Cartridge to build for the multi-cartridge game 'titlemenu', 'stage_intro', 'ingame' or 'stage_clear' + A symbol equal to the cartridge suffix is always added + to the config symbols. CONFIG Build config. Determines defined preprocess symbols. (default: 'debug') @@ -104,6 +106,11 @@ elif [[ $config == 'profiler' ]]; then symbols='profiler,cheat' fi +# we always add a symbol for the cartridge suffix in case +# we want to customize the build of the same script +# depending on the cartridge it is built into +symbols+="$cartridge_suffix" + # Build cartridges without version nor config appended to name # so we can use PICO-8 load() with a cartridge file name # independent from the version and config diff --git a/data/builtin_data_stage_intro.p8 b/data/builtin_data_stage_intro.p8 index 7eee5835..d945d3a9 100644 --- a/data/builtin_data_stage_intro.p8 +++ b/data/builtin_data_stage_intro.p8 @@ -278,3 +278,4 @@ __music__ 00 41424344 00 41424344 04 39777879 + diff --git a/src/common_stage_intro.lua b/src/common_stage_intro.lua index b05392a3..338d9fd7 100644 --- a/src/common_stage_intro.lua +++ b/src/common_stage_intro.lua @@ -4,6 +4,12 @@ -- Usage: add require("common_stage_intro") at the top of each of your stage_intro main scripts -- (along with "engine/common") and in bustedhelper_stage_intro +-- currently, content is same as common_ingame as we need to move character a little to place it +-- properly for camera +-- we may replace character with a proxy character with full custom physics for the "falling on ground" +-- intro later + +require("engine/core/vector_ext_angle") -- character motion require("engine/core/table_helper") -- merge (to add the visual_stage_intro_addon and visual_menu_addon) -- we need sprite flags to draw grass on top of the rest diff --git a/src/ingame/base_stage_state.lua b/src/ingame/base_stage_state.lua new file mode 100644 index 00000000..e3de5621 --- /dev/null +++ b/src/ingame/base_stage_state.lua @@ -0,0 +1,171 @@ +local gamestate = require("engine/application/gamestate") + +local camera_class = require("ingame/camera") +local visual = require("resources/visual_common") + +-- abstract base class for stage_state, stage_intro_state and stage_clear_state +-- it contains functionality common to all three cartridges showing stage content, +-- such as rendering the environment +local base_stage_state = derived_class(gamestate) + +function base_stage_state:init() + -- create camera, but wait for player character to spawn before assigning it a target + -- see on_enter for how we warp it to a good place first + self.camera = camera_class() +end + + +-- extended map system + +-- return map filename for current stage and given region coordinates (u: int, v: int) +-- do not try this with transitional regions, instead we'll patch them from individual regions +function base_stage_state:get_map_region_filename(u, v) + return "data_stage"..self.curr_stage_id.."_"..u..v..".p8" +end + + +--#ifn stage_clear + +-- global <-> region location converters + +function base_stage_state:global_to_region_location(global_loc) + return global_loc - self:get_region_topleft_location() +end + +function base_stage_state:region_to_global_location(region_loc) + return region_loc + self:get_region_topleft_location() +end + +-- queries + +-- return true iff global_tile_loc: location is in any of the areas: {location_rect} +function base_stage_state:is_tile_in_area(global_tile_loc, areas, extra_condition_callback) + for area in all(areas) do + if (extra_condition_callback == nil or extra_condition_callback(global_tile_loc, area)) and + area:contains(global_tile_loc) then + return true + end + end + return false +end + +-- return true iff tile is located in loop entrance area +-- *except at its top-left which is reversed to non-layered entrance trigger* +function base_stage_state:is_tile_in_loop_entrance(global_tile_loc) + return self:is_tile_in_area(global_tile_loc, self.curr_stage_data.loop_entrance_areas, function (global_tile_loc, area) + return global_tile_loc ~= location(area.left, area.top) + end) +end + +-- return true iff tile is located in loop entrance area +-- *except at its top-right which is reversed to non-layered entrance trigger* +function base_stage_state:is_tile_in_loop_exit(global_tile_loc) + return self:is_tile_in_area(global_tile_loc, self.curr_stage_data.loop_exit_areas, function (global_tile_loc, area) + return global_tile_loc ~= location(area.right, area.top) + end) +end + +-- return true iff tile is located at the top-left (trigger location) of any entrance loop +function base_stage_state:is_tile_loop_entrance_trigger(global_tile_loc) + for area in all(self.curr_stage_data.loop_entrance_areas) do + if global_tile_loc == location(area.left, area.top) then + return true + end + end +end + +-- return true iff tile is located at the top-right (trigger location) of any exit loop +function base_stage_state:is_tile_loop_exit_trigger(global_tile_loc) + for area in all(self.curr_stage_data.loop_exit_areas) do + if global_tile_loc == location(area.right, area.top) then + return true + end + end +end + +--#endif + + +-- camera + +-- set the camera offset to draw stage elements with optional origin (default (0, 0)) +-- tilemap should be drawn with region map topleft (in px) as origin +-- characters and items should be drawn with extended map topleft (0, 0) as origin +function base_stage_state:set_camera_with_origin(origin) + origin = origin or vector.zero() + -- the camera position is used to render the stage. it represents the screen center + -- whereas pico-8 defines a top-left camera position, so we subtract a half screen to center the view + -- finally subtract the origin to place tiles correctly + camera(self.camera.position.x - screen_width / 2 - origin.x, self.camera.position.y - screen_height / 2 - origin.y) +end + +-- set the camera offset to draw stage elements with region origin +-- use this to draw tiles with relative location +function base_stage_state:set_camera_with_region_origin() + local region_topleft_loc = self:get_region_topleft_location() + self:set_camera_with_origin(vector(tile_size * region_topleft_loc.i, tile_size * region_topleft_loc.j)) +end + + +-- region helpers + +-- return current region topleft as location (convert uv to ij) +function base_stage_state:get_region_topleft_location() + -- note that result should be integer, although due to region coords being sometimes in .5 for transitional areas + -- they will be considered as fractional numbers by Lua (displayed with '.0' in native Lua) + return location(map_region_tile_width * self.loaded_map_region_coords.x, map_region_tile_height * self.loaded_map_region_coords.y) +end + + +-- render + +-- render the stage environment (tiles) +function base_stage_state:render_environment_midground() + -- possible optimize: don't draw the whole stage offset by camera, + -- instead just draw the portion of the level of interest + -- (and either keep camera offset or offset manually and subtract from camera offset) + -- that said, I didn't notice a performance drop by drawing the full tilemap + -- so I guess map is already optimized to only draw what's on camera + set_unique_transparency(colors.pink) + + -- only draw midground tiles + -- note that we are drawing loop entrance tiles even though they will be (they'll be drawn on foreground later) + self:set_camera_with_region_origin() + map(0, 0, 0, 0, map_region_tile_width, map_region_tile_height, sprite_masks.midground) +end + +function base_stage_state:render_environment_foreground() + set_unique_transparency(colors.pink) + + -- draw tiles always on foreground first + self:set_camera_with_region_origin() + map(0, 0, 0, 0, map_region_tile_width, map_region_tile_height, sprite_masks.foreground) + + local region_topleft_loc = self:get_region_topleft_location() + + -- draw loop entrances on the foreground (it was already drawn on the midground, so we redraw on top of it; + -- it's ultimately more performant to draw twice than to cherry-pick, in case loop entrance tiles + -- are reused in loop exit or other possibly disabled layers so we cannot just tag them all foreground) + self:set_camera_with_origin() + for area in all(self.curr_stage_data.loop_entrance_areas) do + -- draw map subset just for the loop entrance + -- if this is out-of-screen, map will know it should draw nothing so this is very performant already + map(area.left - region_topleft_loc.i, area.top - region_topleft_loc.j, + tile_size * area.left, tile_size * area.top, + area.right - area.left + 1, area.bottom - area.top + 1, + sprite_masks.midground) + end + + -- draw palm tree extension sprites on the foreground, so they can hide the character and items at the top + for global_loc in all(self.palm_tree_leaves_core_global_locations) do + -- top has pivot at its bottom-left = the top-left of the core + visual.sprite_data_t.palm_tree_leaves_top:render(global_loc:to_topleft_position()) + -- right has pivot at is bottom-left = the top-right of the core + local right_global_loc = global_loc + location(1, 0) + visual.sprite_data_t.palm_tree_leaves_right:render(right_global_loc:to_topleft_position()) + -- left is mirrored from right, so its pivot is at its bottom-right = the top-left of the core + visual.sprite_data_t.palm_tree_leaves_right:render(global_loc:to_topleft_position(), --[[flip_x:]] true) + end +end + +return base_stage_state diff --git a/src/ingame/base_stage_state_utest.lua b/src/ingame/base_stage_state_utest.lua new file mode 100644 index 00000000..3fb7e044 --- /dev/null +++ b/src/ingame/base_stage_state_utest.lua @@ -0,0 +1,314 @@ +-- base class is used across three cartridges, but ingame has the most info, +-- bustedhelper_ingame in particular requires resources/visual_ingame_addon +-- so it's more convenient to just require that for the tests +require("test/bustedhelper_ingame") + +local base_stage_state = require("ingame/base_stage_state") + +local location_rect = require("engine/core/location_rect") +local sprite_data = require("engine/render/sprite_data") + +-- same remark as for bustedhelper, we just pick picosonic_app_ingame for convenience +local picosonic_app = require("application/picosonic_app_ingame") +local camera_class = require("ingame/camera") +local visual = require("resources/visual_common") +local tile_repr = require("test_data/tile_representation") +local tile_test_data = require("test_data/tile_test_data") + +describe('base_base_stage_state', function () + + describe('init', function () + + it('should initialize members', function () + local state = base_stage_state() + assert.are_same({ + camera_class(), + }, + { + state.camera, + }) + end) + + end) + + describe('(with instance)', function () + + local state + + before_each(function () + local app = picosonic_app() + state = base_stage_state() + -- no need to register gamestate properly, just add app member to pass tests + state.app = app + end) + + -- camera + + describe('set_camera_with_origin', function () + + it('should set the pico8 camera so that it is centered on the camera position, with origin (0, 0) by default', function () + state.camera.position = vector(24, 13) + state:set_camera_with_origin() + assert.are_same(vector(24 - 128 / 2, 13 - 128 / 2), vector(pico8.camera_x, pico8.camera_y)) + end) + + it('should set the pico8 camera so that it is centered on the camera position, with custom origin subtracted', function () + state.camera.position = vector(24, 13) + state:set_camera_with_origin(vector(10, 20)) + assert.are_same(vector(24 - 128 / 2 - 10, 13 - 128 / 2 - 20), vector(pico8.camera_x, pico8.camera_y)) + end) + + end) + + describe('(region at (2, 3))', function () + + setup(function () + stub(base_stage_state, "get_region_topleft_location", function (self) + return location(2, 3) + end) + end) + + teardown(function () + base_stage_state.get_region_topleft_location:revert() + end) + + describe('global_to_region_location', function () + it('global loc (2, 4) - (2, 3) => (0, 1)', function () + assert.are_equal(location(0, 1), state:global_to_region_location(location(2, 4))) + end) + end) + + describe('region_to_global_location', function () + it('region loc (0, 1) + (2, 3) => (2, 4)', function () + assert.are_equal(location(2, 4), state:region_to_global_location(location(0, 1))) + end) + end) + + end) + + describe('is_tile_in_area', function () + + it('should return true for tile in one of the entrance areas', function () + -- this depends on stage_data.for_stage[1].loop_entrance_areas content and + -- location_rect:contains correctness + assert.is_true(state:is_tile_in_area(location(4, 4), + {location_rect(0, 0, 2, 2), location_rect(4, 4, 6, 6)})) + end) + + it('should return false for tile not in any of the entrance areas', function () + -- this depends on stage_data.for_stage[1].loop_entrance_areas content and + -- location_rect:contains correctness + assert.is_true(state:is_tile_in_area(location(5, 5), + {location_rect(0, 0, 2, 2), location_rect(4, 4, 6, 6)})) + end) + + end) + + describe('is_tile_in_loop_entrance', function () + + before_each(function () + -- customize loop areas locally. We are redefining a table so that won't affect + -- the original data table in stage_data.lua. To simplify we don't redefine everything, + -- but if we need to for the tests we'll just add the missing members + state.curr_stage_data = { + loop_entrance_areas = {location_rect(1, 0, 3, 4)} + } + end) + + -- we wrote those tests before extracting is_tile_in_area and it's simpler + -- to test result than stubbing is_tile_in_area with a dummy function anyway, + -- so we keep direct testing despite overlapping is_tile_in_area utests above + + it('should return true for tile in one of the entrance areas, but not the top-left corner reserved to trigger', function () + assert.is_true(state:is_tile_in_loop_entrance(location(2, 0))) + end) + + it('should return false for tile just on the top-left corner entrance trigger (and not inside another area excluding trigger)', function () + assert.is_false(state:is_tile_in_loop_entrance(location(1, 0))) + end) + + it('should return false for tile not in any of the entrance areas', function () + assert.is_false(state:is_tile_in_loop_entrance(location(0, 0))) + end) + + end) + + describe('is_tile_in_loop_exit', function () + + before_each(function () + -- customize loop areas locally. We are redefining a table so that won't affect + -- the original data table in stage_data.lua. To simplify we don't redefine everything, + -- but if we need to for the tests we'll just add the missing members + state.curr_stage_data = { + loop_exit_areas = {location_rect(-1, 0, 0, 2)} + } + end) + + it('should return true for tile in one of the exit areas, but not the top-right corner reserved to trigger', function () + assert.is_true(state:is_tile_in_loop_exit(location(0, 1))) + end) + + it('should return false for tile just on the top-right corner exit trigger (and not inside another area excluding trigger)', function () + assert.is_false(state:is_tile_in_loop_exit(location(0, 0))) + end) + + it('should return false for tile not in any of the exit areas', function () + assert.is_false(state:is_tile_in_loop_exit(location(0, -1))) + end) + + end) + + describe('set_camera_with_region_origin', function () + + setup(function () + stub(base_stage_state, "set_camera_with_origin") + end) + + teardown(function () + base_stage_state.set_camera_with_origin:revert() + end) + + after_each(function () + base_stage_state.set_camera_with_origin:clear() + end) + + it('should call set_camera_with_origin with current region topleft xy', function () + state.loaded_map_region_coords = vector(2, 1) + + state:set_camera_with_region_origin() + + assert.spy(state.set_camera_with_origin).was_called(1) + assert.spy(state.set_camera_with_origin).was_called_with(match.ref(state), vector(tile_size * map_region_tile_width * 2, tile_size * map_region_tile_height * 1)) + end) + + end) + + + -- region helpers + + describe('get_region_topleft_location', function () + + it('region (0, 0) => (0, 0)', function () + state.loaded_map_region_coords = vector(0, 0) + assert.are_same(location(0, 0), state:get_region_topleft_location()) + end) + + it('region (0.5, 1) => (64, 32)', function () + state.loaded_map_region_coords = vector(0.5, 1) + assert.are_same(location(64, 32), state:get_region_topleft_location()) + end) + + end) + + + -- render + + describe('(with tile_test_data)', function () + + setup(function () + tile_test_data.setup() + + stub(base_stage_state, "set_camera_with_origin") + stub(base_stage_state, "set_camera_with_region_origin") + stub(sprite_data, "render") + stub(_G, "spr") + stub(_G, "map") + end) + + teardown(function () + tile_test_data.teardown() + + base_stage_state.set_camera_with_origin:revert() + base_stage_state.set_camera_with_region_origin:revert() + sprite_data.render:revert() + spr:revert() + map:revert() + end) + + before_each(function () + -- 2 midground tiles on screen, 1 outside when camera is at (0, 0) + mock_mset(0, 0, tile_repr.spring_left_id) + mock_mset(3, 0, tile_repr.spring_left_id) + mock_mset(9, 0, tile_repr.spring_left_id) + -- 1 undefined tile onscreen (it's foreground hiding leaf in PICO-8, + -- but what matters here is that midground flag is not set) + mock_mset(5, 0, 46) + -- foreground tile to test foreground layer + mock_mset(0, 1, tile_repr.grass_top_decoration1) + + state.curr_stage_data = { + loop_exit_areas = {location_rect(-1, 0, 0, 2)}, + loop_entrance_areas = {location_rect(1, 0, 3, 4)}, + goal_x = 3000 + } + + -- palm tree example to demonstrate extra foreground + state.palm_tree_leaves_core_global_locations = { + location(10, 2) + } + end) + + after_each(function () + pico8:clear_map() + + base_stage_state.set_camera_with_origin:clear() + base_stage_state.set_camera_with_region_origin:clear() + sprite_data.render:clear() + spr:clear() + map:clear() + end) + + it('render_environment_midground should call map for all midground sprites', function () + -- note that we reverted to using map for performance, so this test doesn't need to be + -- in the tile test data setup context anymore + state.camera.position = vector(0, 0) + state.loaded_map_region_coords = vector(0, 0) + + state:render_environment_midground() + + assert.spy(base_stage_state.set_camera_with_region_origin).was_called(1) + assert.spy(base_stage_state.set_camera_with_region_origin).was_called_with(match.ref(state)) + + assert.spy(map).was_called(1) + assert.spy(map).was_called_with(0, 0, 0, 0, map_region_tile_width, map_region_tile_height, sprite_masks.midground) + end) + + it('render_environment_foreground should call spr on tiles present on screen', function () + -- this test was copy-pasted from render_environment_midground + state.camera.position = vector(0, 0) + state.loaded_map_region_coords = vector(2, 1) + + state:render_environment_foreground() + + -- we can't check call order, but set camera methods should be called consistently with map! + assert.spy(base_stage_state.set_camera_with_region_origin).was_called(1) + assert.spy(base_stage_state.set_camera_with_region_origin).was_called_with(match.ref(state)) + + assert.spy(map).was_called(2) + + assert.spy(map).was_called_with(0, 0, 0, 0, map_region_tile_width, map_region_tile_height, sprite_masks.foreground) + + assert.spy(base_stage_state.set_camera_with_origin).was_called(1) + assert.spy(base_stage_state.set_camera_with_origin).was_called_with(match.ref(state)) + + local area = state.curr_stage_data.loop_entrance_areas[1] + -- (2, 1) comes from state.loaded_map_region_coords + assert.spy(map).was_called_with(area.left - 2 * 128, area.top - 1 * 32, + tile_size * area.left, tile_size * area.top, + area.right - area.left + 1, area.bottom - area.top + 1, + sprite_masks.midground) + + assert.spy(sprite_data.render).was_called(3) + -- top + assert.spy(sprite_data.render).was_called_with(match.ref(visual.sprite_data_t.palm_tree_leaves_top), vector(8 * 10, 8 * 2)) + -- right + assert.spy(sprite_data.render).was_called_with(match.ref(visual.sprite_data_t.palm_tree_leaves_right), vector(8 * 11, 8 * 2)) + -- left (right flipped x) + assert.spy(sprite_data.render).was_called_with(match.ref(visual.sprite_data_t.palm_tree_leaves_right), vector(8 * 10, 8 * 2), true) + end) + + end) -- (with tile_test_data) + + end) -- (with instance) + +end) diff --git a/src/ingame/playercharacter.lua b/src/ingame/playercharacter.lua index 81c49d54..be2cd43e 100644 --- a/src/ingame/playercharacter.lua +++ b/src/ingame/playercharacter.lua @@ -195,6 +195,8 @@ function player_char:warp_bottom_to(bottom_position) end --#endif +--#if ingame + --#if cheat -- same as warp_to, but with bottom position function player_char:warp_to_emerald_by(delta) @@ -222,6 +224,10 @@ function player_char:warp_to_emerald_by(delta) local target_pos = curr_stage_state.spawned_emerald_locations[self.last_emerald_warp_nb]:to_center_position() self:warp_to(target_pos) end +--(cheat) +--#endif + +--(ingame) --#endif -- move the player character so that the bottom center is at the given position @@ -243,6 +249,8 @@ function player_char:set_ground_tile_location(global_tile_loc) if self.ground_tile_location ~= global_tile_loc then self.ground_tile_location = global_tile_loc +--#if ingame + -- when touching (internal) loop entrance trigger, enable entrance (and disable exit) layer -- and reversely -- we are now checking loop triggers directly from stage data @@ -264,6 +272,8 @@ function player_char:set_ground_tile_location(global_tile_loc) log("internal trigger detected, set active loop layer: 2", 'loop') self.active_loop_layer = 2 end + +--#endif end end @@ -288,7 +298,11 @@ function player_char:set_slope_angle_with_quadrant(angle, force_upward_sprite) end function player_char:update() +-- in stage_intro cartridge, we want Sonic to stay idle, so no input +-- but update physics and render as usual +--#if ingame self:handle_input() +--#endif self:update_motion() self:update_anim() self.anim_spr:update() @@ -395,12 +409,14 @@ end function player_char:update_motion() self:update_collision_timer() +--#if ingame --#if cheat if self.motion_mode == motion_modes.debug then self:update_debug() return end -- else: self.motion_mode == motion_modes.platformer +--#endif --#endif self:update_platformer_motion() @@ -502,7 +518,7 @@ local function iterate_over_collision_tiles(pc, collision_check_quadrant, start_ -- note that we never change region during a collision check, but the 8 tiles margin -- should be enough compared to the short distance along which we check for ground, wall and ceiling local curr_stage_state = flow.curr_state - assert(curr_stage_state.type == ':stage') + assert(curr_stage_state.type == ':stage' or curr_stage_state.type == ':stage_intro') local region_topleft_loc = curr_stage_state:get_region_topleft_location() -- get check quadrant down vector (for ceiling check, it's actually up relative to character quadrant) @@ -561,6 +577,8 @@ local function iterate_over_collision_tiles(pc, collision_check_quadrant, start_ local visual_tile_id = mget(tile_region_loc.i, tile_region_loc.j) local is_oneway = fget(visual_tile_id, sprite_flags.oneway) +--#if ingame + -- we now check for ignored tiles: -- a. ramps just after launching -- b. loops on inactive layer from PC's point-of-view @@ -572,6 +590,8 @@ local function iterate_over_collision_tiles(pc, collision_check_quadrant, start_ ignore_tile = true end +--#endif + if ignore_tile then -- tile is on layer with disabled collision, return emptiness qcolumn_height, slope_angle = 0--, nil @@ -886,10 +906,12 @@ function player_char:update_platformer_motion() self:update_platformer_motion_airborne() end +--#if ingame self:check_spring() self:check_launch_ramp() self:check_emerald() self:check_loop_external_triggers() +--#endif end -- check if character is fast enough to roll and wants to roll @@ -2020,6 +2042,8 @@ end -- item and trigger checks +--#if ingame + function player_char:check_spring() if self.ground_tile_location then -- get stage state for global to region location conversion @@ -2196,6 +2220,10 @@ function player_char:update_velocity_component_debug(coord) self.debug_velocity:set(coord, new_debug_velocity_comp) end +--(cheat) +--#endif + +--(ingame) --#endif -- update sprite animation state diff --git a/src/ingame/stage_state.lua b/src/ingame/stage_state.lua index 13ed2de1..90dc0a71 100644 --- a/src/ingame/stage_state.lua +++ b/src/ingame/stage_state.lua @@ -1,7 +1,6 @@ -local gamestate = require("engine/application/gamestate") local volume = require("engine/audio/volume") -local camera_class = require("ingame/camera") +local base_stage_state = require("ingame/base_stage_state") local emerald = require("ingame/emerald") local emerald_fx = require("ingame/emerald_fx") local goal_plate = require("ingame/goal_plate") @@ -11,12 +10,12 @@ local audio = require("resources/audio") local visual = require("resources/visual_common") -- we should require ingameadd-on in main local visual_stage = require("resources/visual_stage") -local stage_state = derived_class(gamestate) +local stage_state = derived_class(base_stage_state) stage_state.type = ':stage' function stage_state:init() - -- gamestate.init(self) -- kept for expliciteness, but does nothing + base_stage_state.init(self) -- stage id self.curr_stage_id = 1 @@ -25,7 +24,8 @@ function stage_state:init() self.curr_stage_data = stage_data.for_stage[self.curr_stage_id] -- player character - self.player_char = nil + -- self.player_char = nil -- commented out to spare characters + -- has the player character already reached the goal once? self.has_player_char_reached_goal = false @@ -42,10 +42,6 @@ function stage_state:init() -- used to draw the palm tree extension sprites on foreground self.palm_tree_leaves_core_global_locations = {} - -- create camera, but wait for player character to spawn before assigning it a target - -- see on_enter for how we warp it to a good place first - self.camera = camera_class() - --#if itest -- set to false in itest setup to disable object spawning, which relies on very slow map scan self.enable_spawn_objects = true @@ -241,54 +237,6 @@ function stage_state:render() end --- queries - --- return true iff global_tile_loc: location is in any of the areas: {location_rect} -function stage_state:is_tile_in_area(global_tile_loc, areas, extra_condition_callback) - for area in all(areas) do - if (extra_condition_callback == nil or extra_condition_callback(global_tile_loc, area)) and - area:contains(global_tile_loc) then - return true - end - end - return false -end - --- return true iff tile is located in loop entrance area --- *except at its top-left which is reversed to non-layered entrance trigger* -function stage_state:is_tile_in_loop_entrance(global_tile_loc) - return self:is_tile_in_area(global_tile_loc, self.curr_stage_data.loop_entrance_areas, function (global_tile_loc, area) - return global_tile_loc ~= location(area.left, area.top) - end) -end - --- return true iff tile is located in loop entrance area --- *except at its top-right which is reversed to non-layered entrance trigger* -function stage_state:is_tile_in_loop_exit(global_tile_loc) - return self:is_tile_in_area(global_tile_loc, self.curr_stage_data.loop_exit_areas, function (global_tile_loc, area) - return global_tile_loc ~= location(area.right, area.top) - end) -end - --- return true iff tile is located at the top-left (trigger location) of any entrance loop -function stage_state:is_tile_loop_entrance_trigger(global_tile_loc) - for area in all(self.curr_stage_data.loop_entrance_areas) do - if global_tile_loc == location(area.left, area.top) then - return true - end - end -end - --- return true iff tile is located at the top-right (trigger location) of any exit loop -function stage_state:is_tile_loop_exit_trigger(global_tile_loc) - for area in all(self.curr_stage_data.loop_exit_areas) do - if global_tile_loc == location(area.right, area.top) then - return true - end - end -end - - -- setup -- spawn the player character at the stage spawn location @@ -388,12 +336,6 @@ end -- for instance, with an extended map compounded of a grid of 2x2 = 4 maps, we'd have -- 4 full reloads, 4 2-half reloads, 1 4-quarter reloads for a total of 9 possible reloads --- return map filename for current stage and given region coordinates (u: int, v: int) --- do not try this with transitional regions, instead we'll patch them from individual regions -function stage_state:get_map_region_filename(u, v) - return "data_stage"..self.curr_stage_id.."_"..u..v..".p8" -end - function stage_state:get_region_grid_dimensions() local region_count_per_row = ceil(self.curr_stage_data.tile_width / map_region_tile_width) local region_count_per_column = ceil(self.curr_stage_data.tile_height / map_region_tile_height) @@ -919,27 +861,6 @@ function stage_state:render_fx() end --- camera - --- set the camera offset to draw stage elements with optional origin (default (0, 0)) --- tilemap should be drawn with region map topleft (in px) as origin --- characters and items should be drawn with extended map topleft (0, 0) as origin -function stage_state:set_camera_with_origin(origin) - origin = origin or vector.zero() - -- the camera position is used to render the stage. it represents the screen center - -- whereas pico-8 defines a top-left camera position, so we subtract a half screen to center the view - -- finally subtract the origin to place tiles correctly - camera(self.camera.position.x - screen_width / 2 - origin.x, self.camera.position.y - screen_height / 2 - origin.y) -end - --- set the camera offset to draw stage elements with region origin --- use this to draw tiles with relative location -function stage_state:set_camera_with_region_origin() - local region_topleft_loc = self:get_region_topleft_location() - self:set_camera_with_origin(vector(tile_size * region_topleft_loc.i, tile_size * region_topleft_loc.j)) -end - - -- render -- render the stage elements with the main camera: @@ -959,78 +880,13 @@ function stage_state:render_stage_elements() --#endif end --- global <-> region location converters - -function stage_state:global_to_region_location(global_loc) - return global_loc - self:get_region_topleft_location() -end - -function stage_state:region_to_global_location(region_loc) - return region_loc + self:get_region_topleft_location() -end - --- same kind of helper, but for mset +-- same kind of helper as base_stage_state:global_to_region_location and region_to_global_location, +-- but for mset function stage_state:mset_global_to_region(global_loc_i, global_loc_j, sprite_id) local region_loc = location(global_loc_i, global_loc_j) - self:get_region_topleft_location() mset(region_loc.i, region_loc.j, sprite_id) end --- return current region topleft as location (convert uv to ij) -function stage_state:get_region_topleft_location() - -- note that result should be integer, although due to region coords being sometimes in .5 for transitional areas - -- they will be considered as fractional numbers by Lua (displayed with '.0' in native Lua) - return location(map_region_tile_width * self.loaded_map_region_coords.x, map_region_tile_height * self.loaded_map_region_coords.y) -end - --- render the stage environment (tiles) -function stage_state:render_environment_midground() - -- possible optimize: don't draw the whole stage offset by camera, - -- instead just draw the portion of the level of interest - -- (and either keep camera offset or offset manually and subtract from camera offset) - -- that said, I didn't notice a performance drop by drawing the full tilemap - -- so I guess map is already optimized to only draw what's on camera - set_unique_transparency(colors.pink) - - -- only draw midground tiles - -- note that we are drawing loop entrance tiles even though they will be (they'll be drawn on foreground later) - self:set_camera_with_region_origin() - map(0, 0, 0, 0, map_region_tile_width, map_region_tile_height, sprite_masks.midground) -end - -function stage_state:render_environment_foreground() - set_unique_transparency(colors.pink) - - -- draw tiles always on foreground first - self:set_camera_with_region_origin() - map(0, 0, 0, 0, map_region_tile_width, map_region_tile_height, sprite_masks.foreground) - - local region_topleft_loc = self:get_region_topleft_location() - - -- draw loop entrances on the foreground (it was already drawn on the midground, so we redraw on top of it; - -- it's ultimately more performant to draw twice than to cherry-pick, in case loop entrance tiles - -- are reused in loop exit or other possibly disabled layers so we cannot just tag them all foreground) - self:set_camera_with_origin() - for area in all(self.curr_stage_data.loop_entrance_areas) do - -- draw map subset just for the loop entrance - -- if this is out-of-screen, map will know it should draw nothing so this is very performant already - map(area.left - region_topleft_loc.i, area.top - region_topleft_loc.j, - tile_size * area.left, tile_size * area.top, - area.right - area.left + 1, area.bottom - area.top + 1, - sprite_masks.midground) - end - - -- draw palm tree extension sprites on the foreground, so they can hide the character and items at the top - for global_loc in all(self.palm_tree_leaves_core_global_locations) do - -- top has pivot at its bottom-left = the top-left of the core - visual.sprite_data_t.palm_tree_leaves_top:render(global_loc:to_topleft_position()) - -- right has pivot at is bottom-left = the top-right of the core - local right_global_loc = global_loc + location(1, 0) - visual.sprite_data_t.palm_tree_leaves_right:render(right_global_loc:to_topleft_position()) - -- left is mirrored from right, so its pivot is at its bottom-right = the top-left of the core - visual.sprite_data_t.palm_tree_leaves_right:render(global_loc:to_topleft_position(), --[[flip_x:]] true) - end -end - -- render the player character at its current position function stage_state:render_player_char() self:set_camera_with_origin() diff --git a/src/ingame/stage_state_utest.lua b/src/ingame/stage_state_utest.lua index 209901dd..046aff45 100644 --- a/src/ingame/stage_state_utest.lua +++ b/src/ingame/stage_state_utest.lua @@ -4,7 +4,6 @@ require("test/bustedhelper_ingame") -- like fun_helper (we should actually isolate tests and reverse cross-testing to itests, -- whether complex tests done via busted but done in dedicated files, or simulation tests) require("common_titlemenu") -require("resources/visual_ingame_addon") local stage_state = require("ingame/stage_state") @@ -12,11 +11,11 @@ local coroutine_runner = require("engine/application/coroutine_runner") local flow = require("engine/application/flow") local location_rect = require("engine/core/location_rect") local animated_sprite = require("engine/render/animated_sprite") -local sprite_data = require("engine/render/sprite_data") local picosonic_app = require("application/picosonic_app_ingame") local camera_data = require("data/camera_data") local stage_data = require("data/stage_data") +local base_stage_state = require("ingame/base_stage_state") local camera_class = require("ingame/camera") local emerald = require("ingame/emerald") local emerald_fx = require("ingame/emerald_fx") @@ -25,8 +24,6 @@ local player_char = require("ingame/playercharacter") local audio = require("resources/audio") local visual = require("resources/visual_common") local visual_stage = require("resources/visual_stage") -local tile_repr = require("test_data/tile_representation") -local tile_test_data = require("test_data/tile_test_data") describe('stage_state', function () @@ -51,7 +48,26 @@ describe('stage_state', function () describe('init', function () - it('should initialize members', function () + setup(function () + -- base constructor is important, do not stub it (although we are not checking + -- base members below so it could work with stub too) + spy.on(base_stage_state, "init") + end) + + teardown(function () + base_stage_state.init:revert() + end) + + after_each(function () + base_stage_state.init:clear() + end) + + it('should call base constructor', function () + assert.spy(base_stage_state.init).was_called(1) + assert.spy(base_stage_state.init).was_called_with(match.ref(state)) + end) + + it('#solo should initialize members', function () assert.are_same({ ':stage', 1, @@ -63,7 +79,6 @@ describe('stage_state', function () {}, {}, {}, - camera_class(), nil, -- itest only true, @@ -79,7 +94,6 @@ describe('stage_state', function () state.picked_emerald_numbers_set, state.emerald_pick_fxs, state.palm_tree_leaves_core_global_locations, - state.camera, state.loaded_map_region_coords, -- itest only state.enable_spawn_objects, @@ -229,78 +243,6 @@ describe('stage_state', function () end) - describe('is_tile_in_area', function () - - it('should return true for tile in one of the entrance areas', function () - -- this depends on stage_data.for_stage[1].loop_entrance_areas content and - -- location_rect:contains correctness - assert.is_true(state:is_tile_in_area(location(4, 4), - {location_rect(0, 0, 2, 2), location_rect(4, 4, 6, 6)})) - end) - - it('should return false for tile not in any of the entrance areas', function () - -- this depends on stage_data.for_stage[1].loop_entrance_areas content and - -- location_rect:contains correctness - assert.is_true(state:is_tile_in_area(location(5, 5), - {location_rect(0, 0, 2, 2), location_rect(4, 4, 6, 6)})) - end) - - end) - - describe('is_tile_in_loop_entrance', function () - - before_each(function () - -- customize loop areas locally. We are redefining a table so that won't affect - -- the original data table in stage_data.lua. To simplify we don't redefine everything, - -- but if we need to for the tests we'll just add the missing members - state.curr_stage_data = { - loop_entrance_areas = {location_rect(1, 0, 3, 4)} - } - end) - - -- we wrote those tests before extracting is_tile_in_area and it's simpler - -- to test result than stubbing is_tile_in_area with a dummy function anyway, - -- so we keep direct testing despite overlapping is_tile_in_area utests above - - it('should return true for tile in one of the entrance areas, but not the top-left corner reserved to trigger', function () - assert.is_true(state:is_tile_in_loop_entrance(location(2, 0))) - end) - - it('should return false for tile just on the top-left corner entrance trigger (and not inside another area excluding trigger)', function () - assert.is_false(state:is_tile_in_loop_entrance(location(1, 0))) - end) - - it('should return false for tile not in any of the entrance areas', function () - assert.is_false(state:is_tile_in_loop_entrance(location(0, 0))) - end) - - end) - - describe('is_tile_in_loop_exit', function () - - before_each(function () - -- customize loop areas locally. We are redefining a table so that won't affect - -- the original data table in stage_data.lua. To simplify we don't redefine everything, - -- but if we need to for the tests we'll just add the missing members - state.curr_stage_data = { - loop_exit_areas = {location_rect(-1, 0, 0, 2)} - } - end) - - it('should return true for tile in one of the exit areas, but not the top-right corner reserved to trigger', function () - assert.is_true(state:is_tile_in_loop_exit(location(0, 1))) - end) - - it('should return false for tile just on the top-right corner exit trigger (and not inside another area excluding trigger)', function () - assert.is_false(state:is_tile_in_loop_exit(location(0, 0))) - end) - - it('should return false for tile not in any of the exit areas', function () - assert.is_false(state:is_tile_in_loop_exit(location(0, -1))) - end) - - end) - describe('spawn_emerald_at', function () it('should store emerald global location', function () @@ -1512,47 +1454,6 @@ describe('stage_state', function () end) - describe('set_camera_with_origin', function () - - it('should set the pico8 camera so that it is centered on the camera position, with origin (0, 0) by default', function () - state.camera.position = vector(24, 13) - state:set_camera_with_origin() - assert.are_same(vector(24 - 128 / 2, 13 - 128 / 2), vector(pico8.camera_x, pico8.camera_y)) - end) - - it('should set the pico8 camera so that it is centered on the camera position, with custom origin subtracted', function () - state.camera.position = vector(24, 13) - state:set_camera_with_origin(vector(10, 20)) - assert.are_same(vector(24 - 128 / 2 - 10, 13 - 128 / 2 - 20), vector(pico8.camera_x, pico8.camera_y)) - end) - - end) - - describe('set_camera_with_region_origin', function () - - setup(function () - stub(stage_state, "set_camera_with_origin") - end) - - teardown(function () - stage_state.set_camera_with_origin:revert() - end) - - after_each(function () - stage_state.set_camera_with_origin:clear() - end) - - it('should call set_camera_with_origin with current region topleft xy', function () - state.loaded_map_region_coords = vector(2, 1) - - state:set_camera_with_region_origin() - - assert.spy(state.set_camera_with_origin).was_called(1) - assert.spy(state.set_camera_with_origin).was_called_with(match.ref(state), vector(tile_size * map_region_tile_width * 2, tile_size * map_region_tile_height * 1)) - end) - - end) - describe('render_stage_elements', function () setup(function () @@ -1753,152 +1654,6 @@ describe('stage_state', function () end) - describe('(region at (2, 3))', function () - - setup(function () - stub(stage_state, "get_region_topleft_location", function (self) - return location(2, 3) - end) - end) - - teardown(function () - stage_state.get_region_topleft_location:revert() - end) - - describe('global_to_region_location', function () - it('global loc (2, 4) - (2, 3) => (0, 1)', function () - assert.are_equal(location(0, 1), state:global_to_region_location(location(2, 4))) - end) - end) - - describe('region_to_global_location', function () - it('region loc (0, 1) + (2, 3) => (2, 4)', function () - assert.are_equal(location(2, 4), state:region_to_global_location(location(0, 1))) - end) - end) - - end) - - describe('get_region_topleft_location', function () - - it('region (0, 0) => (0, 0)', function () - state.loaded_map_region_coords = vector(0, 0) - assert.are_same(location(0, 0), state:get_region_topleft_location()) - end) - - it('region (0.5, 1) => (64, 32)', function () - state.loaded_map_region_coords = vector(0.5, 1) - assert.are_same(location(64, 32), state:get_region_topleft_location()) - end) - - end) - - describe('(with tile_test_data)', function () - - setup(function () - tile_test_data.setup() - - stub(stage_state, "set_camera_with_origin") - stub(stage_state, "set_camera_with_region_origin") - stub(sprite_data, "render") - stub(_G, "spr") - stub(_G, "map") - end) - - teardown(function () - tile_test_data.teardown() - - stage_state.set_camera_with_origin:revert() - stage_state.set_camera_with_region_origin:revert() - sprite_data.render:revert() - spr:revert() - map:revert() - end) - - before_each(function () - -- 2 midground tiles on screen, 1 outside when camera is at (0, 0) - mock_mset(0, 0, tile_repr.spring_left_id) - mock_mset(3, 0, tile_repr.spring_left_id) - mock_mset(9, 0, tile_repr.spring_left_id) - -- 1 undefined tile onscreen (it's foreground hiding leaf in PICO-8, - -- but what matters here is that midground flag is not set) - mock_mset(5, 0, 46) - -- foreground tile to test foreground layer - mock_mset(0, 1, tile_repr.grass_top_decoration1) - - state.curr_stage_data = { - loop_exit_areas = {location_rect(-1, 0, 0, 2)}, - loop_entrance_areas = {location_rect(1, 0, 3, 4)}, - goal_x = 3000 - } - - -- palm tree example to demonstrate extra foreground - state.palm_tree_leaves_core_global_locations = { - location(10, 2) - } - end) - - after_each(function () - pico8:clear_map() - - stage_state.set_camera_with_origin:clear() - stage_state.set_camera_with_region_origin:clear() - sprite_data.render:clear() - spr:clear() - map:clear() - end) - - it('render_environment_midground should call map for all midground sprites', function () - -- note that we reverted to using map for performance, so this test doesn't need to be - -- in the tile test data setup context anymore - state.camera.position = vector(0, 0) - state.loaded_map_region_coords = vector(0, 0) - - state:render_environment_midground() - - assert.spy(stage_state.set_camera_with_region_origin).was_called(1) - assert.spy(stage_state.set_camera_with_region_origin).was_called_with(match.ref(state)) - - assert.spy(map).was_called(1) - assert.spy(map).was_called_with(0, 0, 0, 0, map_region_tile_width, map_region_tile_height, sprite_masks.midground) - end) - - it('render_environment_foreground should call spr on tiles present on screen', function () - -- this test was copy-pasted from render_environment_midground - state.camera.position = vector(0, 0) - state.loaded_map_region_coords = vector(2, 1) - - state:render_environment_foreground() - - -- we can't check call order, but set camera methods should be called consistently with map! - assert.spy(stage_state.set_camera_with_region_origin).was_called(1) - assert.spy(stage_state.set_camera_with_region_origin).was_called_with(match.ref(state)) - - assert.spy(map).was_called(2) - - assert.spy(map).was_called_with(0, 0, 0, 0, map_region_tile_width, map_region_tile_height, sprite_masks.foreground) - - assert.spy(stage_state.set_camera_with_origin).was_called(1) - assert.spy(stage_state.set_camera_with_origin).was_called_with(match.ref(state)) - - local area = state.curr_stage_data.loop_entrance_areas[1] - -- (2, 1) comes from state.loaded_map_region_coords - assert.spy(map).was_called_with(area.left - 2 * 128, area.top - 1 * 32, - tile_size * area.left, tile_size * area.top, - area.right - area.left + 1, area.bottom - area.top + 1, - sprite_masks.midground) - - assert.spy(sprite_data.render).was_called(3) - -- top - assert.spy(sprite_data.render).was_called_with(match.ref(visual.sprite_data_t.palm_tree_leaves_top), vector(8 * 10, 8 * 2)) - -- right - assert.spy(sprite_data.render).was_called_with(match.ref(visual.sprite_data_t.palm_tree_leaves_right), vector(8 * 11, 8 * 2)) - -- left (right flipped x) - assert.spy(sprite_data.render).was_called_with(match.ref(visual.sprite_data_t.palm_tree_leaves_right), vector(8 * 10, 8 * 2), true) - end) - - end) -- (with tile_test_data) - describe('state audio methods', function () setup(function () diff --git a/src/stage_clear/stage_clear_state.lua b/src/stage_clear/stage_clear_state.lua index 4fb07a15..988ce89d 100644 --- a/src/stage_clear/stage_clear_state.lua +++ b/src/stage_clear/stage_clear_state.lua @@ -1,11 +1,11 @@ local flow = require("engine/application/flow") -local gamestate = require("engine/application/gamestate") local postprocess = require("engine/render/postprocess") local label = require("engine/ui/label") local overlay = require("engine/ui/overlay") local rectangle = require("engine/ui/rectangle") local stage_clear_data = require("data/stage_clear_data") +local base_stage_state = require("ingame/base_stage_state") local emerald = require("ingame/emerald") local goal_plate = require("ingame/goal_plate") local menu_item = require("menu/menu_item") @@ -15,7 +15,7 @@ local ui_animation = require("ui/ui_animation") local visual = require("resources/visual_common") -- we should require ingameadd-on in main local visual_stage = require("resources/visual_stage") -local stage_clear_state = derived_class(gamestate) +local stage_clear_state = derived_class(base_stage_state) stage_clear_state.type = ':stage_clear' @@ -57,7 +57,10 @@ function stage_clear_state.back_to_titlemenu_async() end function stage_clear_state:init() - -- gamestate.init(self) -- kept for expliciteness, but does nothing + base_stage_state.init(self) + + -- stage id + self.curr_stage_id = 1 -- sequence of menu items to display, with their target states -- this could be static, but defining in init allows us to avoid @@ -95,9 +98,20 @@ function stage_clear_state:on_enter() -- first, restore picked emerald data set in ingame, just before loading this cartridge self:restore_picked_emerald_data() + -- Hardcoded: in stage_clear the camera doesn't move, so we don't need to call self.camera:setup_for_stage, + -- pass a player character, etc. (and player char is absent anyway). + -- Instead we just set the position directly to values observed at the end + -- of ingame cartridge, just before loading stage_clear. This allows us to use base_stage_state methods + -- relying on the camera position. + self.camera.position.x = 3392 + self.camera.position.y = 328 + + -- Hardcoded: we know that goal is in region (3, 1) + reload(0x2000, 0x2000, 0x1000, self:get_map_region_filename(3, 1)) + self.loaded_map_region_coords = vector(3, 1) + -- we still need to reload map region hardcoded to where goal is, -- and spawn objects just there (basically just spawn the goal plate) - self:reload_map_region() self:scan_current_region_to_spawn_objects() self.app:start_coroutine(self.play_stage_clear_sequence_async, self) @@ -215,51 +229,14 @@ function stage_clear_state:scan_current_region_to_spawn_objects() end end --- return map filename for current stage and given region coordinates (u: int, v: int) --- do not try this with transitional regions, instead we'll patch them from individual regions -function stage_clear_state:get_map_region_filename(u, v) - -- hardcoded for stage 1 - return "data_stage1_"..u..v..".p8" -end - -function stage_clear_state:reload_map_region() - -- hardcoded for goal region - reload(0x2000, 0x2000, 0x1000, self:get_map_region_filename(3, 1)) -end - -- camera methods, also simplified versions of stage_stage equivalent --- hardcoded version of stage_state:set_camera_with_origin -function stage_clear_state:set_camera_with_origin(origin) - origin = origin or vector.zero() - -- hardcoded version: we printed the following value during ingame just before loading stage_clear: - -- self.camera.position.x = 3392 - -- self.camera.position.y = 328 - -- self.camera.position.x - screen_width / 2 = 3328 - -- self.camera.position.y - screen_height / 2 = 264 - -- and reinjected the values below (correspond to camera approx. centered on goal plate) - camera(3328 - origin.x, 264 - origin.y) -end - --- same as stage_state:set_camera_with_region_origin but short enough to copy -function stage_clear_state:set_camera_with_region_origin() - local region_topleft_loc = self:get_region_topleft_location() - self:set_camera_with_origin(vector(tile_size * region_topleft_loc.i, tile_size * region_topleft_loc.j)) -end - -- same as stage_state:region_to_global_location but short enough to copy function stage_clear_state:region_to_global_location(region_loc) return region_loc + self:get_region_topleft_location() end --- return current region topleft as location (convert uv to ij) --- hardcoded version of stage_state:get_region_topleft_location --- for stage_clear: goal is in region (3, 1) for pico island -function stage_clear_state:get_region_topleft_location() - return location(map_region_tile_width * 3, map_region_tile_height * 1) -end - -- actual stage clear sequence functions diff --git a/src/stage_clear/stage_clear_state_utest.lua b/src/stage_clear/stage_clear_state_utest.lua index 57f813b9..820ee408 100644 --- a/src/stage_clear/stage_clear_state_utest.lua +++ b/src/stage_clear/stage_clear_state_utest.lua @@ -12,10 +12,12 @@ local stage_clear_state = require("stage_clear/stage_clear_state") local coroutine_runner = require("engine/application/coroutine_runner") local flow = require("engine/application/flow") local animated_sprite = require("engine/render/animated_sprite") +local postprocess = require("engine/render/postprocess") local sprite_data = require("engine/render/sprite_data") local overlay = require("engine/ui/overlay") local picosonic_app = require("application/picosonic_app_stage_clear") +local base_stage_state = require("ingame/base_stage_state") local goal_plate = require("ingame/goal_plate") local titlemenu = require("menu/titlemenu") local visual = require("resources/visual_common") @@ -49,623 +51,639 @@ describe('stage_clear_state', function () titlemenu_state.app = app end) - describe('state', function () + describe('init', function () - it('init', function () + setup(function () + spy.on(base_stage_state, "init") + end) + + teardown(function () + base_stage_state.init:revert() + end) + + after_each(function () + base_stage_state.init:clear() + end) + + it('#solo should call base constructor', function () + assert.spy(base_stage_state.init).was_called(1) + assert.spy(base_stage_state.init).was_called_with(match.ref(state)) + end) + + it('should initialize members', function () assert.are_same({ ':stage_clear', + 1, + 3, + 0, + postprocess(), overlay(), {}, + 0, + {}, {}, }, { state.type, - -- result UI animation only (async methods changing this won't be fully tested) + state.curr_stage_id, + #state.retry_menu_items, -- a bit complex to check transform worked, so just check count + state.phase, + state.postproc, state.result_overlay, + state.picked_emerald_numbers_set, + state.picked_emerald_count, state.result_show_emerald_set_by_number, state.result_emerald_brightness_levels }) end) - describe('on_enter', function () + end) - setup(function () - stub(stage_clear_state, "restore_picked_emerald_data") - stub(stage_clear_state, "reload_map_region") - stub(stage_clear_state, "scan_current_region_to_spawn_objects") - stub(picosonic_app, "start_coroutine") - end) + describe('on_enter', function () - teardown(function () - stage_clear_state.restore_picked_emerald_data:revert() - stage_clear_state.reload_map_region:revert() - stage_clear_state.scan_current_region_to_spawn_objects:revert() - picosonic_app.start_coroutine:revert() - end) + setup(function () + stub(stage_clear_state, "restore_picked_emerald_data") + stub(_G, "reload") + stub(stage_clear_state, "scan_current_region_to_spawn_objects") + stub(picosonic_app, "start_coroutine") + end) - after_each(function () - stage_clear_state.restore_picked_emerald_data:clear() - stage_clear_state.reload_map_region:clear() - stage_clear_state.scan_current_region_to_spawn_objects:clear() - picosonic_app.start_coroutine:clear() - end) + teardown(function () + stage_clear_state.restore_picked_emerald_data:revert() + reload:revert() + stage_clear_state.scan_current_region_to_spawn_objects:revert() + picosonic_app.start_coroutine:revert() + end) - before_each(function () - state:on_enter() - end) + after_each(function () + stage_clear_state.restore_picked_emerald_data:clear() + reload:clear() + stage_clear_state.scan_current_region_to_spawn_objects:clear() + picosonic_app.start_coroutine:clear() + end) - it('should call restore_picked_emerald_data', function () - assert.spy(state.restore_picked_emerald_data).was_called(1) - assert.spy(state.restore_picked_emerald_data).was_called_with(match.ref(state)) - end) + before_each(function () + state:on_enter() + end) - it('should call reload_map_region, scan_current_region_to_spawn_objects', function () - assert.spy(state.reload_map_region).was_called(1) - assert.spy(state.reload_map_region).was_called_with(match.ref(state)) - assert.spy(state.scan_current_region_to_spawn_objects).was_called(1) - assert.spy(state.scan_current_region_to_spawn_objects).was_called_with(match.ref(state)) - end) + it('should call restore_picked_emerald_data', function () + assert.spy(state.restore_picked_emerald_data).was_called(1) + assert.spy(state.restore_picked_emerald_data).was_called_with(match.ref(state)) + end) - it('should call start_coroutine_method on play_stage_clear_sequence_async', function () - local s = assert.spy(picosonic_app.start_coroutine) - s.was_called(1) - s.was_called_with(match.ref(state.app), stage_clear_state.play_stage_clear_sequence_async, match.ref(state)) - end) + it('should hardcode set camera position', function () + assert.are_equal(vector(3392, 328), state.camera.position) + end) + it('should hardcode set loaded_map_region_coords', function () + assert.are_equal(vector(3, 1), state.loaded_map_region_coords) end) - describe('spawn_goal_plate_at', function () + it('should call reload for stage1, map 31 (hardcoded)', function () + assert.spy(reload).was_called(1) + assert.spy(reload).was_called_with(0x2000, 0x2000, 0x1000, "data_stage1_31.p8") + end) - setup(function () - spy.on(animated_sprite, "play") - end) + it('should call scan_current_region_to_spawn_objects', function () + assert.spy(state.scan_current_region_to_spawn_objects).was_called(1) + assert.spy(state.scan_current_region_to_spawn_objects).was_called_with(match.ref(state)) + end) - teardown(function () - animated_sprite.play:revert() - end) + it('should call start_coroutine_method on play_stage_clear_sequence_async', function () + local s = assert.spy(picosonic_app.start_coroutine) + s.was_called(1) + s.was_called_with(match.ref(state.app), stage_clear_state.play_stage_clear_sequence_async, match.ref(state)) + end) - after_each(function () - animated_sprite.play:clear() - end) + end) + + describe('spawn_goal_plate_at', function () - it('should spawn and store goal plate core at global location', function () - state:spawn_goal_plate_at(location(1, 33)) + setup(function () + spy.on(animated_sprite, "play") + end) - local gp = goal_plate(location(1, 33)) - gp.anim_spr:play("sonic") + teardown(function () + animated_sprite.play:revert() + end) - assert.are_same(gp, state.goal_plate) - end) + after_each(function () + animated_sprite.play:clear() + end) - it('should show sonic face of goal plate by playing corresponding animation', function () - state:spawn_goal_plate_at(location(1, 33)) + it('should spawn and store goal plate core at global location', function () + state:spawn_goal_plate_at(location(1, 33)) - -- goal plate creation defaults to "goal" animation so play is actually called twice, - -- but we only care about last call - assert.spy(animated_sprite.play).was_called(2) - assert.spy(animated_sprite.play).was_called_with(match.ref(state.goal_plate.anim_spr), "sonic") - end) + local gp = goal_plate(location(1, 33)) + gp.anim_spr:play("sonic") + assert.are_same(gp, state.goal_plate) end) - describe('scan_current_region_to_spawn_objects', function () + it('should show sonic face of goal plate by playing corresponding animation', function () + state:spawn_goal_plate_at(location(1, 33)) - setup(function () - stub(stage_clear_state, "spawn_goal_plate_at") - end) + -- goal plate creation defaults to "goal" animation so play is actually called twice, + -- but we only care about last call + assert.spy(animated_sprite.play).was_called(2) + assert.spy(animated_sprite.play).was_called_with(match.ref(state.goal_plate.anim_spr), "sonic") + end) - teardown(function () - stage_clear_state.spawn_goal_plate_at:revert() - end) + end) - -- setup is too early, stage state will start afterward in before_each, - -- and its on_enter will call scan_current_region_to_spawn_objects, making it hard - -- to test in isolation. Hence before_each. - before_each(function () - -- we're not using tile_test_data.setup here - -- (since objects are checked directly by id, not using collision data) - -- so don't use mock_mset - mset(1, 1, visual.goal_plate_base_id) - mset(2, 2, visual.goal_plate_base_id) - mset(3, 3, visual.goal_plate_base_id) - - -- mock stage dimensions, not too big to avoid test too long - -- (just 2 regions so we can check that location conversion works) - state.curr_stage_data = { - tile_width = 128, -- 1 region per row - tile_height = 32 * 2 -- 2 regions per column - } - - state.loaded_map_region_coords = vector(0, 1) -- will add 32 to each j - end) + describe('scan_current_region_to_spawn_objects', function () - after_each(function () - stage_clear_state.spawn_goal_plate_at:clear() + setup(function () + stub(stage_clear_state, "spawn_goal_plate_at") + end) - pico8:clear_map() - end) + teardown(function () + stage_clear_state.spawn_goal_plate_at:revert() + end) - it('should call spawn_goal_plate_at global location', function () - state:scan_current_region_to_spawn_objects() + -- setup is too early, stage state will start afterward in before_each, + -- and its on_enter will call scan_current_region_to_spawn_objects, making it hard + -- to test in isolation. Hence before_each. + before_each(function () + -- we're not using tile_test_data.setup here + -- (since objects are checked directly by id, not using collision data) + -- so don't use mock_mset + mset(1, 1, visual.goal_plate_base_id) + mset(2, 2, visual.goal_plate_base_id) + mset(3, 3, visual.goal_plate_base_id) + + -- mock stage dimensions, not too big to avoid test too long + -- (just 2 regions so we can check that location conversion works) + state.curr_stage_data = { + tile_width = 128, -- 1 region per row + tile_height = 32 * 2 -- 2 regions per column + } + + state.loaded_map_region_coords = vector(0, 1) -- will add 32 to each j + end) - assert.spy(stage_clear_state.spawn_goal_plate_at).was_called(3) - assert.spy(stage_clear_state.spawn_goal_plate_at).was_called_with(match.ref(state), location(1 + map_region_tile_width * 3, 1 + map_region_tile_height * 1)) - end) + after_each(function () + stage_clear_state.spawn_goal_plate_at:clear() + pico8:clear_map() end) - describe('get_map_region_filename', function () - - it('(1, 0) => "data_stage1_10.p8"', function () - -- hardcoded to stage 1 - assert.are_equal("data_stage1_10.p8", state:get_map_region_filename(1, 0)) - end) + it('should call spawn_goal_plate_at global location', function () + state:scan_current_region_to_spawn_objects() + assert.spy(stage_clear_state.spawn_goal_plate_at).was_called(3) + assert.spy(stage_clear_state.spawn_goal_plate_at).was_called_with(match.ref(state), location(1 + map_region_tile_width * 3, 1 + map_region_tile_height * 1)) end) - describe('reload_map_region', function () + end) - setup(function () - stub(_G, "reload") - end) + describe('get_map_region_filename', function () - teardown(function () - _G.reload:revert() - end) + it('(1, 0) => "data_stage1_10.p8"', function () + -- hardcoded to stage 1 + assert.are_equal("data_stage1_10.p8", state:get_map_region_filename(1, 0)) + end) - before_each(function () - _G.reload:clear() - end) + end) - it('should call reload for stage1, map 31 (hardcoded)', function () - state:reload_map_region() + describe('(stage states added)', function () - assert.spy(reload).was_called(1) - assert.spy(reload).was_called_with(0x2000, 0x2000, 0x1000, "data_stage1_31.p8") - end) + before_each(function () + flow:add_gamestate(state) + flow:add_gamestate(titlemenu_state) -- for transition on reached goal + end) + after_each(function () + flow:init() end) - describe('(stage states added)', function () + describe('(stage state entered)', function () - before_each(function () - flow:add_gamestate(state) - flow:add_gamestate(titlemenu_state) -- for transition on reached goal + setup(function () + -- restore_picked_emerald_data relies on peek which will find nil memory if not set + -- so stub it + stub(stage_clear_state, "restore_picked_emerald_data") + + -- we don't really mind spying on scan_current_region_to_spawn_objects + -- but we do not want to spend several seconds finding all of them + -- in before_each every time due to on_enter just for tests, + -- so we stub this + stub(stage_clear_state, "scan_current_region_to_spawn_objects") + end) + + teardown(function () + stage_clear_state.scan_current_region_to_spawn_objects:revert() + stage_clear_state.restore_picked_emerald_data:revert() end) after_each(function () - flow:init() + stage_clear_state.scan_current_region_to_spawn_objects:clear() + stage_clear_state.restore_picked_emerald_data:clear() end) - describe('(stage state entered)', function () + before_each(function () + flow:change_state(state) + -- entering stage currently starts coroutine play_stage_clear_sequence_async + -- which will cause side effects when updating coroutines to test other + -- async functions, so clear that now + state.app:stop_all_coroutines() + end) - setup(function () - -- restore_picked_emerald_data relies on peek which will find nil memory if not set - -- so stub it - stub(stage_clear_state, "restore_picked_emerald_data") + describe('update', function () - -- we don't really mind spying on scan_current_region_to_spawn_objects - -- but we do not want to spend several seconds finding all of them - -- in before_each every time due to on_enter just for tests, - -- so we stub this - stub(stage_clear_state, "scan_current_region_to_spawn_objects") + setup(function () end) teardown(function () - stage_clear_state.scan_current_region_to_spawn_objects:revert() - stage_clear_state.restore_picked_emerald_data:revert() - end) - - after_each(function () - stage_clear_state.scan_current_region_to_spawn_objects:clear() - stage_clear_state.restore_picked_emerald_data:clear() end) before_each(function () - flow:change_state(state) - -- entering stage currently starts coroutine play_stage_clear_sequence_async - -- which will cause side effects when updating coroutines to test other - -- async functions, so clear that now - state.app:stop_all_coroutines() end) - describe('update', function () - - setup(function () - end) - - teardown(function () - end) - - before_each(function () - end) - - after_each(function () - end) - - end) -- update + after_each(function () + end) - describe('render', function () + end) -- update - setup(function () - stub(visual_stage, "render_background") - stub(stage_clear_state, "render_stage_elements") - stub(stage_clear_state, "render_overlay") - stub(stage_clear_state, "render_emeralds") - end) + describe('render', function () - teardown(function () - visual_stage.render_background:revert() - stage_clear_state.render_stage_elements:revert() - stage_clear_state.render_overlay:revert() - stage_clear_state.render_emeralds:revert() - end) + setup(function () + stub(visual_stage, "render_background") + stub(stage_clear_state, "render_stage_elements") + stub(stage_clear_state, "render_overlay") + stub(stage_clear_state, "render_emeralds") + end) - after_each(function () - visual_stage.render_background:clear() - stage_clear_state.render_stage_elements:clear() - stage_clear_state.render_overlay:clear() - stage_clear_state.render_emeralds:clear() - end) + teardown(function () + visual_stage.render_background:revert() + stage_clear_state.render_stage_elements:revert() + stage_clear_state.render_overlay:revert() + stage_clear_state.render_emeralds:revert() + end) - it('(phase 0) should call render_background, render_stage_elements, render_overlay, render_emeralds', function () - state.phase = 0 + after_each(function () + visual_stage.render_background:clear() + stage_clear_state.render_stage_elements:clear() + stage_clear_state.render_overlay:clear() + stage_clear_state.render_emeralds:clear() + end) - state:render() + it('#solo (phase 0) should call render_background, render_stage_elements, render_overlay, render_emeralds', function () + state.phase = 0 - assert.spy(visual_stage.render_background).was_called(1) - assert.spy(visual_stage.render_background).was_called_with(vector(3392, 328)) - assert.spy(stage_clear_state.render_stage_elements).was_called(1) - assert.spy(stage_clear_state.render_stage_elements).was_called_with(match.ref(state)) - assert.spy(stage_clear_state.render_emeralds).was_called(1) - assert.spy(stage_clear_state.render_emeralds).was_called_with(match.ref(state)) - assert.spy(stage_clear_state.render_overlay).was_called(1) - assert.spy(stage_clear_state.render_overlay).was_called_with(match.ref(state)) - end) + state:render() + assert.spy(visual_stage.render_background).was_called(1) + assert.spy(visual_stage.render_background).was_called_with(vector(3392, 328)) + assert.spy(stage_clear_state.render_stage_elements).was_called(1) + assert.spy(stage_clear_state.render_stage_elements).was_called_with(match.ref(state)) + assert.spy(stage_clear_state.render_emeralds).was_called(1) + assert.spy(stage_clear_state.render_emeralds).was_called_with(match.ref(state)) + assert.spy(stage_clear_state.render_overlay).was_called(1) + assert.spy(stage_clear_state.render_overlay).was_called_with(match.ref(state)) + end) - it('should call render_background, render_stage_elements, render_overlay, render_emeralds', function () - state.phase = 1 - state:render() + it('should call render_background, render_stage_elements, render_overlay, render_emeralds', function () + state.phase = 1 - assert.spy(stage_clear_state.render_emeralds).was_called(1) - assert.spy(stage_clear_state.render_emeralds).was_called_with(match.ref(state)) - assert.spy(stage_clear_state.render_overlay).was_called(1) - assert.spy(stage_clear_state.render_overlay).was_called_with(match.ref(state)) - end) + state:render() - end) -- state.render + assert.spy(stage_clear_state.render_emeralds).was_called(1) + assert.spy(stage_clear_state.render_emeralds).was_called_with(match.ref(state)) + assert.spy(stage_clear_state.render_overlay).was_called(1) + assert.spy(stage_clear_state.render_overlay).was_called_with(match.ref(state)) + end) - describe('play_stage_clear_sequence_async', function () + end) -- state.render - -- removed actual tests, too hard to maintain - -- instead, just run it and see if it crashes + describe('play_stage_clear_sequence_async', function () - local corunner + -- removed actual tests, too hard to maintain + -- instead, just run it and see if it crashes - before_each(function () - state.goal_plate = goal_plate(location(100, 0)) - state.spawned_emerald_locations = {1, 2, 3, 4, 5, 6, 7, 8} + local corunner - corunner = coroutine_runner() - corunner:start_coroutine(stage_clear_state.play_stage_clear_sequence_async, state) - end) + before_each(function () + state.goal_plate = goal_plate(location(100, 0)) + state.spawned_emerald_locations = {1, 2, 3, 4, 5, 6, 7, 8} - it('should not crash with a few emeralds', function () - -- emerald bitset: 0b1010 0b0110 - pico8.poked_addresses[0x4300] = 0xa - pico8.poked_addresses[0x4301] = 0x6 + corunner = coroutine_runner() + corunner:start_coroutine(stage_clear_state.play_stage_clear_sequence_async, state) + end) - -- a time long enough to cover other async methods like assess_result_async - for i = 1, 1000 do - corunner:update_coroutines() - end - end) + it('should not crash with a few emeralds', function () + -- emerald bitset: 0b1010 0b0110 + pico8.poked_addresses[0x4300] = 0xa + pico8.poked_addresses[0x4301] = 0x6 - it('should not crash with all emeralds', function () - -- emerald bitset: 0b1111 0b1111 - pico8.poked_addresses[0x4300] = 0xf - pico8.poked_addresses[0x4301] = 0xf + -- a time long enough to cover other async methods like assess_result_async + for i = 1, 1000 do + corunner:update_coroutines() + end + end) - -- a time long enough to cover other async methods like assess_result_async - for i = 1, 1000 do - corunner:update_coroutines() - end - end) + it('should not crash with all emeralds', function () + -- emerald bitset: 0b1111 0b1111 + pico8.poked_addresses[0x4300] = 0xf + pico8.poked_addresses[0x4301] = 0xf + -- a time long enough to cover other async methods like assess_result_async + for i = 1, 1000 do + corunner:update_coroutines() + end end) - describe('set_camera_with_origin', function () + end) - it('should set the pico8 camera to hardcoded position around goal', function () - state:set_camera_with_origin() - assert.are_same(vector(3328, 264), vector(pico8.camera_x, pico8.camera_y)) - end) + describe('set_camera_with_origin', function () - it('should set the pico8 camera to hardcoded position around goal, with custom origin subtracted', function () - state:set_camera_with_origin(vector(10, 20)) - assert.are_same(vector(3328 - 10, 264 - 20), vector(pico8.camera_x, pico8.camera_y)) - end) + it('should set the pico8 camera to hardcoded position around goal', function () + state:set_camera_with_origin() + assert.are_same(vector(3328, 264), vector(pico8.camera_x, pico8.camera_y)) + end) + it('should set the pico8 camera to hardcoded position around goal, with custom origin subtracted', function () + state:set_camera_with_origin(vector(10, 20)) + assert.are_same(vector(3328 - 10, 264 - 20), vector(pico8.camera_x, pico8.camera_y)) end) - describe('set_camera_with_region_origin', function () + end) - setup(function () - stub(stage_clear_state, "set_camera_with_origin") - end) + describe('set_camera_with_region_origin', function () - teardown(function () - stage_clear_state.set_camera_with_origin:revert() - end) + setup(function () + stub(stage_clear_state, "set_camera_with_origin") + end) - after_each(function () - stage_clear_state.set_camera_with_origin:clear() - end) + teardown(function () + stage_clear_state.set_camera_with_origin:revert() + end) - it('should call set_camera_with_origin with current region topleft xy', function () - state:set_camera_with_region_origin() + after_each(function () + stage_clear_state.set_camera_with_origin:clear() + end) - assert.spy(state.set_camera_with_origin).was_called(1) - assert.spy(state.set_camera_with_origin).was_called_with(match.ref(state), vector(tile_size * map_region_tile_width * 3, tile_size * map_region_tile_height * 1)) - end) + it('should call set_camera_with_origin with current region topleft xy', function () + state:set_camera_with_region_origin() + assert.spy(state.set_camera_with_origin).was_called(1) + assert.spy(state.set_camera_with_origin).was_called_with(match.ref(state), vector(tile_size * map_region_tile_width * 3, tile_size * map_region_tile_height * 1)) end) - describe('region_to_global_location', function () - it('region loc (0, 1) => (0 + map_region_tile_width * 3, 1 + map_region_tile_height * 1)', function () - assert.are_equal(location(0 + map_region_tile_width * 3, 1 + map_region_tile_height * 1), state:region_to_global_location(location(0, 1))) - end) - end) + end) - describe('get_region_topleft_location', function () + describe('region_to_global_location', function () + it('region loc (0, 1) => (0 + map_region_tile_width * 3, 1 + map_region_tile_height * 1)', function () + assert.are_equal(location(0 + map_region_tile_width * 3, 1 + map_region_tile_height * 1), state:region_to_global_location(location(0, 1))) + end) + end) - it('should return hardcoded location(map_region_tile_width * 3, map_region_tile_height * 1)', function () - assert.are_same(location(map_region_tile_width * 3, map_region_tile_height * 1), state:get_region_topleft_location()) - end) + describe('get_region_topleft_location', function () + it('should return hardcoded location(map_region_tile_width * 3, map_region_tile_height * 1)', function () + assert.are_same(location(map_region_tile_width * 3, map_region_tile_height * 1), state:get_region_topleft_location()) end) - describe('render_stage_elements', function () + end) - setup(function () - stub(stage_clear_state, "render_environment_midground") - stub(stage_clear_state, "render_goal_plate") - stub(stage_clear_state, "render_environment_foreground") - end) + describe('render_stage_elements', function () - teardown(function () - stage_clear_state.render_environment_midground:revert() - stage_clear_state.render_goal_plate:revert() - stage_clear_state.render_environment_foreground:revert() - end) + setup(function () + stub(stage_clear_state, "render_environment_midground") + stub(stage_clear_state, "render_goal_plate") + stub(stage_clear_state, "render_environment_foreground") + end) - after_each(function () - stage_clear_state.render_environment_midground:clear() - stage_clear_state.render_goal_plate:clear() - stage_clear_state.render_environment_foreground:clear() - end) + teardown(function () + stage_clear_state.render_environment_midground:revert() + stage_clear_state.render_goal_plate:revert() + stage_clear_state.render_environment_foreground:revert() + end) - it('should call render methods on everything in the stage', function () - state:render_stage_elements() - assert.spy(state.render_environment_midground).was_called(1) - assert.spy(state.render_environment_midground).was_called_with(match.ref(state)) - assert.spy(state.render_goal_plate).was_called(1) - assert.spy(state.render_goal_plate).was_called_with(match.ref(state)) - assert.spy(state.render_environment_foreground).was_called(1) - assert.spy(state.render_environment_foreground).was_called_with(match.ref(state)) - end) + after_each(function () + stage_clear_state.render_environment_midground:clear() + stage_clear_state.render_goal_plate:clear() + stage_clear_state.render_environment_foreground:clear() + end) + it('should call render methods on everything in the stage', function () + state:render_stage_elements() + assert.spy(state.render_environment_midground).was_called(1) + assert.spy(state.render_environment_midground).was_called_with(match.ref(state)) + assert.spy(state.render_goal_plate).was_called(1) + assert.spy(state.render_goal_plate).was_called_with(match.ref(state)) + assert.spy(state.render_environment_foreground).was_called(1) + assert.spy(state.render_environment_foreground).was_called_with(match.ref(state)) end) - describe('render_overlay', function () + end) - setup(function () - stub(overlay, "draw") - end) + describe('render_overlay', function () - teardown(function () - overlay.draw:revert() - end) + setup(function () + stub(overlay, "draw") + end) - after_each(function () - overlay.draw:clear() - end) + teardown(function () + overlay.draw:revert() + end) - it('should reset camera', function () - state:render_overlay() - assert.are_same(vector.zero(), vector(pico8.camera_x, pico8.camera_y)) - end) + after_each(function () + overlay.draw:clear() + end) - it('should call result_overlay:draw', function () - state:render_overlay() - assert.spy(overlay.draw).was_called(1) - assert.spy(overlay.draw).was_called_with(match.ref(state.result_overlay)) - end) + it('should reset camera', function () + state:render_overlay() + assert.are_same(vector.zero(), vector(pico8.camera_x, pico8.camera_y)) + end) + it('should call result_overlay:draw', function () + state:render_overlay() + assert.spy(overlay.draw).was_called(1) + assert.spy(overlay.draw).was_called_with(match.ref(state.result_overlay)) end) - describe('render_goal_plate', function () + end) - setup(function () - stub(stage_clear_state, "set_camera_with_origin") - stub(goal_plate, "render") - end) + describe('render_goal_plate', function () - teardown(function () - stage_clear_state.set_camera_with_origin:revert() - goal_plate.render:revert() - end) + setup(function () + stub(stage_clear_state, "set_camera_with_origin") + stub(goal_plate, "render") + end) - after_each(function () - stage_clear_state.set_camera_with_origin:clear() - goal_plate.render:clear() - end) + teardown(function () + stage_clear_state.set_camera_with_origin:revert() + goal_plate.render:revert() + end) - it('(goal plate must be found) should call set_camera_with_origin and goal_plate:render', function () - state.goal_plate = goal_plate(location(2, 33)) + after_each(function () + stage_clear_state.set_camera_with_origin:clear() + goal_plate.render:clear() + end) - state:render_goal_plate() + it('(goal plate must be found) should call set_camera_with_origin and goal_plate:render', function () + state.goal_plate = goal_plate(location(2, 33)) - assert.spy(stage_clear_state.set_camera_with_origin).was_called(1) - assert.spy(stage_clear_state.set_camera_with_origin).was_called_with(match.ref(state)) - assert.spy(goal_plate.render).was_called(1) - assert.spy(goal_plate.render).was_called_with(match.ref(state.goal_plate)) - end) + state:render_goal_plate() + assert.spy(stage_clear_state.set_camera_with_origin).was_called(1) + assert.spy(stage_clear_state.set_camera_with_origin).was_called_with(match.ref(state)) + assert.spy(goal_plate.render).was_called(1) + assert.spy(goal_plate.render).was_called_with(match.ref(state.goal_plate)) end) - describe('(with tile_test_data)', function () + end) - setup(function () - tile_test_data.setup() + describe('(with tile_test_data)', function () - stub(stage_clear_state, "set_camera_with_origin") - stub(stage_clear_state, "set_camera_with_region_origin") - stub(sprite_data, "render") - stub(_G, "spr") - stub(_G, "map") - end) + setup(function () + tile_test_data.setup() - teardown(function () - tile_test_data.teardown() + stub(stage_clear_state, "set_camera_with_origin") + stub(stage_clear_state, "set_camera_with_region_origin") + stub(sprite_data, "render") + stub(_G, "spr") + stub(_G, "map") + end) - stage_clear_state.set_camera_with_origin:revert() - stage_clear_state.set_camera_with_region_origin:revert() - sprite_data.render:revert() - spr:revert() - map:revert() - end) + teardown(function () + tile_test_data.teardown() - before_each(function () - -- 2 midground tiles on screen, 1 outside when camera is at (0, 0) - mock_mset(0, 0, tile_repr.spring_left_id) - mock_mset(3, 0, tile_repr.spring_left_id) - mock_mset(9, 0, tile_repr.spring_left_id) - -- 1 undefined tile onscreen (it's foreground hiding leaf in PICO-8, - -- but what matters here is that midground flag is not set) - mock_mset(5, 0, 46) - -- foreground tile to test foreground layer - mock_mset(0, 1, tile_repr.grass_top_decoration1) - end) + stage_clear_state.set_camera_with_origin:revert() + stage_clear_state.set_camera_with_region_origin:revert() + sprite_data.render:revert() + spr:revert() + map:revert() + end) - after_each(function () - pico8:clear_map() + before_each(function () + -- 2 midground tiles on screen, 1 outside when camera is at (0, 0) + mock_mset(0, 0, tile_repr.spring_left_id) + mock_mset(3, 0, tile_repr.spring_left_id) + mock_mset(9, 0, tile_repr.spring_left_id) + -- 1 undefined tile onscreen (it's foreground hiding leaf in PICO-8, + -- but what matters here is that midground flag is not set) + mock_mset(5, 0, 46) + -- foreground tile to test foreground layer + mock_mset(0, 1, tile_repr.grass_top_decoration1) + end) - stage_clear_state.set_camera_with_origin:clear() - stage_clear_state.set_camera_with_region_origin:clear() - sprite_data.render:clear() - spr:clear() - map:clear() - end) + after_each(function () + pico8:clear_map() - it('render_environment_midground should call map for all midground sprites', function () - state:render_environment_midground() + stage_clear_state.set_camera_with_origin:clear() + stage_clear_state.set_camera_with_region_origin:clear() + sprite_data.render:clear() + spr:clear() + map:clear() + end) - assert.spy(stage_clear_state.set_camera_with_region_origin).was_called(1) - assert.spy(stage_clear_state.set_camera_with_region_origin).was_called_with(match.ref(state)) + it('render_environment_midground should call map for all midground sprites', function () + state:render_environment_midground() - assert.spy(map).was_called(1) - assert.spy(map).was_called_with(0, 0, 0, 0, map_region_tile_width, map_region_tile_height, sprite_masks.midground) - end) + assert.spy(stage_clear_state.set_camera_with_region_origin).was_called(1) + assert.spy(stage_clear_state.set_camera_with_region_origin).was_called_with(match.ref(state)) - it('render_environment_foreground should call spr on tiles present on screen', function () - state:render_environment_foreground() + assert.spy(map).was_called(1) + assert.spy(map).was_called_with(0, 0, 0, 0, map_region_tile_width, map_region_tile_height, sprite_masks.midground) + end) - -- we can't check call order, but set camera methods should be called consistently with map! - assert.spy(stage_clear_state.set_camera_with_region_origin).was_called(1) - assert.spy(stage_clear_state.set_camera_with_region_origin).was_called_with(match.ref(state)) + it('render_environment_foreground should call spr on tiles present on screen', function () + state:render_environment_foreground() - assert.spy(map).was_called(1) + -- we can't check call order, but set camera methods should be called consistently with map! + assert.spy(stage_clear_state.set_camera_with_region_origin).was_called(1) + assert.spy(stage_clear_state.set_camera_with_region_origin).was_called_with(match.ref(state)) - assert.spy(map).was_called_with(0, 0, 0, 0, map_region_tile_width, map_region_tile_height, sprite_masks.foreground) - end) + assert.spy(map).was_called(1) - end) -- (with tile_test_data) + assert.spy(map).was_called_with(0, 0, 0, 0, map_region_tile_width, map_region_tile_height, sprite_masks.foreground) + end) - describe('extra render methods (no-crash only)', function () + end) -- (with tile_test_data) - it('render_emeralds should not crash', function () - state:render_emeralds() - end) + describe('extra render methods (no-crash only)', function () + it('render_emeralds should not crash', function () + state:render_emeralds() end) - describe('on exit stage state to enter titlemenu state (we actually change cartridge)', function () + end) - before_each(function () - flow:change_state(titlemenu_state) - end) + describe('on exit stage state to enter titlemenu state (we actually change cartridge)', function () - it('player character should be nil', function () - assert.is_nil(state.player_char) - end) + before_each(function () + flow:change_state(titlemenu_state) + end) - it('result overlay should be empty', function () - assert.is_not_nil(state.result_overlay) - assert.is_not_nil(state.result_overlay.drawables_seq) - assert.is_true(is_empty(state.result_overlay.drawables_seq)) - end) + it('player character should be nil', function () + assert.is_nil(state.player_char) + end) - end) -- on exit stage state to enter titlemenu state + it('result overlay should be empty', function () + assert.is_not_nil(state.result_overlay) + assert.is_not_nil(state.result_overlay.drawables_seq) + assert.is_true(is_empty(state.result_overlay.drawables_seq)) + end) - -- unlike above, we test on_exit method itself here + end) -- on exit stage state to enter titlemenu state - -- COMMENTED OUT to strip characters since we just load new cartridges and never exit - -- the stage_clear state - --[[ - describe('on_exit', function () + -- unlike above, we test on_exit method itself here - setup(function () - stub(overlay, "clear_drawables") - stub(picosonic_app, "stop_all_coroutines") - end) + -- COMMENTED OUT to strip characters since we just load new cartridges and never exit + -- the stage_clear state + --[[ + describe('on_exit', function () - teardown(function () - overlay.clear_drawables:revert() - picosonic_app.stop_all_coroutines:revert() - end) + setup(function () + stub(overlay, "clear_drawables") + stub(picosonic_app, "stop_all_coroutines") + end) - after_each(function () - overlay.clear_drawables:clear() - end) + teardown(function () + overlay.clear_drawables:revert() + picosonic_app.stop_all_coroutines:revert() + end) - before_each(function () - -- another before_each called stop_all_coroutines, - -- so we must clear the count - picosonic_app.stop_all_coroutines:clear() + after_each(function () + overlay.clear_drawables:clear() + end) - state:on_exit() - end) + before_each(function () + -- another before_each called stop_all_coroutines, + -- so we must clear the count + picosonic_app.stop_all_coroutines:clear() - it('should stop all the coroutines', function () - assert.spy(picosonic_app.stop_all_coroutines).was_called(1) - assert.spy(picosonic_app.stop_all_coroutines).was_called_with(match.ref(state.app)) - end) + state:on_exit() + end) - it('should clear the player character', function () - assert.is_nil(state.player_char) - end) + it('should stop all the coroutines', function () + assert.spy(picosonic_app.stop_all_coroutines).was_called(1) + assert.spy(picosonic_app.stop_all_coroutines).was_called_with(match.ref(state.app)) + end) - it('should call clear all drawables', function () - assert.spy(overlay.clear_drawables).was_called(1) - assert.spy(overlay.clear_drawables).was_called_with(match.ref(state.result_overlay)) - end) + it('should clear the player character', function () + assert.is_nil(state.player_char) + end) - it('should reset pico8 camera', function () - assert.are_same({0, 0}, {pico8.camera_x, pico8.camera_y}) - end) + it('should call clear all drawables', function () + assert.spy(overlay.clear_drawables).was_called(1) + assert.spy(overlay.clear_drawables).was_called_with(match.ref(state.result_overlay)) + end) + it('should reset pico8 camera', function () + assert.are_same({0, 0}, {pico8.camera_x, pico8.camera_y}) end) - --]] - end) -- (stage state entered) + end) + --]] - end) -- (stage states added) + end) -- (stage state entered) - end) -- (with instance) + end) -- (stage states added) - end) -- (stage state) + end) -- (with instance) end) diff --git a/src/stage_intro/stage_intro_state.lua b/src/stage_intro/stage_intro_state.lua index 8101ab55..2d1650f4 100644 --- a/src/stage_intro/stage_intro_state.lua +++ b/src/stage_intro/stage_intro_state.lua @@ -1,4 +1,3 @@ -local gamestate = require("engine/application/gamestate") local postprocess = require("engine/render/postprocess") local label = require("engine/ui/label") local overlay = require("engine/ui/overlay") @@ -6,22 +5,57 @@ local rectangle = require("engine/ui/rectangle") local stage_data = require("data/stage_data") local stage_intro_data = require("data/stage_intro_data") +local base_stage_state = require("ingame/base_stage_state") +local camera_class = require("ingame/camera") +local player_char = require("ingame/playercharacter") +local visual_stage = require("resources/visual_stage") local ui_animation = require("ui/ui_animation") -local stage_intro_state = derived_class(gamestate) +local stage_intro_state = derived_class(base_stage_state) stage_intro_state.type = ':stage_intro' function stage_intro_state:init() + base_stage_state.init(self) + + -- stage id + self.curr_stage_id = 1 + -- data self.curr_stage_data = stage_data.for_stage[1] -- render + + -- create camera, but wait for player character to spawn before assigning it a target + -- see on_enter for how we warp it to a good place first + self.camera = camera_class() + self.overlay = overlay() self.postproc = postprocess() end function stage_intro_state:on_enter() + -- like the original stage_state, we need to have collision masks in builtin spritesheet, + -- then load runtime spritesheet top portion + -- of course, if we manage to isolate collision masks in their own spritesheet we could reload + -- them (and reload the original builtin back) in some collision data method dedicated to this + -- whole process (instead of relying on collision_data calling + -- tile_collision_data.from_raw_tile_collision_data in outer scope, so early enough to be before + -- loading runtime spritesheet...). Or we could have a custom intro cinematics that doesn't use physics + -- at all and so no tile collision data is needed. + local runtime_data_path = "data_stage"..self.curr_stage_id.."_runtime.p8" + reload(0x0, 0x0, 0x600, runtime_data_path) + + self.camera:setup_for_stage(self.curr_stage_data) + + -- for now, just hardcode region loading/coords to simplify, as we know the intro + -- only takes place in the region (0, 1) + reload(0x2000, 0x2000, 0x1000, self:get_map_region_filename(0, 1)) + self.loaded_map_region_coords = vector(0, 1) + + self:spawn_player_char() + self.camera.target_pc = self.player_char + self.app:start_coroutine(self.show_stage_splash_async, self) end @@ -39,11 +73,46 @@ function stage_intro_state:on_exit() end --]] + +-- setup + +-- spawn the player character at the stage spawn location +-- (no unit test yet, will probably change for custom intro scene later) +function stage_intro_state:spawn_player_char() + local spawn_position = self.curr_stage_data.spawn_location:to_center_position() + self.player_char = player_char() + self.player_char:spawn_at(spawn_position) +end + +function stage_intro_state:update() + self.player_char:update() + self.camera:update() + -- self:check_reload_map_region() +end + function stage_intro_state:render() + visual_stage.render_background(self.camera.position) + self:render_stage_elements() self:render_overlay() self.postproc:apply() end +-- render the stage elements with the main camera: +-- - environment +-- - player character +function stage_intro_state:render_stage_elements() + self:render_environment_midground() + self:render_player_char() + self:render_environment_foreground() +end + +-- render the player character at its current position +function stage_intro_state:render_player_char() + self:set_camera_with_origin() + + self.player_char:render() +end + -- render the title overlay with a fixed ui camera function stage_intro_state:render_overlay() camera() @@ -59,8 +128,6 @@ function stage_intro_state:show_stage_splash_async() self.app:yield_delay_s(stage_intro_data.show_stage_splash_delay) - -- FIXME: draw iteration order not guaranteed, pico-sonic may be hidden "below" banner - -- init position y is -height so it starts just at the screen top edge local banner = rectangle(vector(9, -106), 32, 106, colors.red) self.overlay:add_drawable("banner", banner) @@ -96,6 +163,7 @@ function stage_intro_state:show_stage_splash_async() -- splash is over, load ingame cartridge and give control to player load('picosonic_ingame.p8') + assert(false, "could not load picosonic_ingame.p8") end return stage_intro_state diff --git a/src/stage_intro/stage_intro_state_utest.lua b/src/stage_intro/stage_intro_state_utest.lua index 9de16e63..541659f7 100644 --- a/src/stage_intro/stage_intro_state_utest.lua +++ b/src/stage_intro/stage_intro_state_utest.lua @@ -11,6 +11,10 @@ local overlay = require("engine/ui/overlay") local picosonic_app = require("application/picosonic_app_stage_intro") local stage_data = require("data/stage_data") local stage_intro_data = require("data/stage_intro_data") +local base_stage_state = require("ingame/base_stage_state") +local camera_class = require("ingame/camera") +local player_char = require("ingame/playercharacter") +local visual_stage = require("resources/visual_stage") describe('stage_intro_state', function () @@ -35,16 +39,35 @@ describe('stage_intro_state', function () describe('init', function () + setup(function () + spy.on(base_stage_state, "init") + end) + + teardown(function () + base_stage_state.init:revert() + end) + + after_each(function () + base_stage_state.init:clear() + end) + + it('should call base constructor', function () + assert.spy(base_stage_state.init).was_called(1) + assert.spy(base_stage_state.init).was_called_with(match.ref(state)) + end) + it('should initialize members', function () assert.are_same({ ':stage_intro', stage_data.for_stage[1], + camera_class(), overlay(), postprocess(), }, { state.type, state.curr_stage_data, + state.camera, state.overlay, state.postproc, }) @@ -55,17 +78,57 @@ describe('stage_intro_state', function () describe('on_enter', function () setup(function () + stub(camera_class, "setup_for_stage") + stub(_G, "reload") + stub(stage_intro_state, "spawn_player_char") stub(picosonic_app, "start_coroutine") end) teardown(function () + camera_class.setup_for_stage:revert() + reload:revert() + stage_intro_state.spawn_player_char:revert() picosonic_app.start_coroutine:revert() end) after_each(function () + camera_class.setup_for_stage:clear() + reload:clear() + stage_intro_state.spawn_player_char:clear() picosonic_app.start_coroutine:clear() end) + it('should call setup_for_stage on camera with current stage data', function () + state:on_enter() + + assert.spy(camera_class.setup_for_stage).was_called(1) + assert.spy(camera_class.setup_for_stage).was_called_with(match.ref(state.camera), state.curr_stage_data) + end) + + it('should hardcode set loaded_map_region_coords', function () + state:on_enter() + + assert.are_equal(vector(0, 1), state.loaded_map_region_coords) + end) + + it('#solo should call reload for stage1, map 01 (hardcoded)', function () + state:on_enter() + + assert.spy(reload).was_called(1) + assert.spy(reload).was_called_with(0x2000, 0x2000, 0x1000, "data_stage1_01.p8") + end) + + it('should call spawn_player_char', function () + state:on_enter() + + assert.spy(stage_intro_state.spawn_player_char).was_called(1) + assert.spy(stage_intro_state.spawn_player_char).was_called_with(match.ref(state)) + end) + + it('should assign spawned player char to camera target', function () + assert.are_equal(state.player_char, state.camera.target_pc) + end) + it('should call start_coroutine_method on show_stage_splash_async', function () state:on_enter() @@ -75,22 +138,63 @@ describe('stage_intro_state', function () end) + describe('update', function () + + setup(function () + stub(player_char, "update") + stub(camera_class, "update") + end) + + teardown(function () + player_char.update:revert() + camera_class.update:revert() + end) + + after_each(function () + player_char.update:clear() + camera_class.update:clear() + end) + + it('should update player character, camera', function () + state.player_char = player_char() + + state:update() + + assert.spy(player_char.update).was_called(1) + assert.spy(player_char.update).was_called_with(match.ref(state.player_char)) + + assert.spy(camera_class.update).was_called(1) + assert.spy(camera_class.update).was_called_with(match.ref(state.camera)) + end) + + end) + describe('render', function () setup(function () + stub(visual_stage, "render_background") + stub(stage_intro_state, "render_stage_elements") stub(stage_intro_state, "render_overlay") end) teardown(function () + visual_stage.render_background:revert() + stage_intro_state.render_stage_elements:revert() stage_intro_state.render_overlay:revert() end) after_each(function () + visual_stage.render_background:clear() + stage_intro_state.render_stage_elements:clear() stage_intro_state.render_overlay:clear() end) - it('should call render_overlay', function () + it('should call render_background, render_stage_elements, render_overlay', function () state:render() + assert.spy(visual_stage.render_background).was_called(1) + assert.spy(visual_stage.render_background).was_called_with(state.camera.position) + assert.spy(stage_intro_state.render_stage_elements).was_called(1) + assert.spy(stage_intro_state.render_stage_elements).was_called_with(match.ref(state)) assert.spy(stage_intro_state.render_overlay).was_called(1) assert.spy(stage_intro_state.render_overlay).was_called_with(match.ref(state)) end) @@ -124,7 +228,6 @@ describe('stage_intro_state', function () end) - describe('show_stage_splash_async', function () local corunner From 2eb431945f253b7e220139b79f7c7386be44a586 Mon Sep 17 00:00:00 2001 From: huulong Date: Wed, 30 Dec 2020 17:51:40 +0100 Subject: [PATCH 41/91] [TEST] Fixed stage intro test since adding reload runtime data --- src/stage_intro/stage_intro_state_utest.lua | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/stage_intro/stage_intro_state_utest.lua b/src/stage_intro/stage_intro_state_utest.lua index 541659f7..d29b1c05 100644 --- a/src/stage_intro/stage_intro_state_utest.lua +++ b/src/stage_intro/stage_intro_state_utest.lua @@ -111,10 +111,11 @@ describe('stage_intro_state', function () assert.are_equal(vector(0, 1), state.loaded_map_region_coords) end) - it('#solo should call reload for stage1, map 01 (hardcoded)', function () + it('should call reload for runtime data and stage1, map 01 (hardcoded)', function () state:on_enter() - assert.spy(reload).was_called(1) + assert.spy(reload).was_called(2) + assert.spy(reload).was_called_with(0x0, 0x0, 0x600, "data_stage1_runtime.p8") assert.spy(reload).was_called_with(0x2000, 0x2000, 0x1000, "data_stage1_01.p8") end) From f17144ff1d200c1e07f2e82e1f1bf383b8c03241 Mon Sep 17 00:00:00 2001 From: huulong Date: Thu, 31 Dec 2020 17:35:10 +0100 Subject: [PATCH 42/91] [BUILD] Fixed cartridge suffix symbol missing separator when placed after existing symbols --- build_single_cartridge.sh | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/build_single_cartridge.sh b/build_single_cartridge.sh index 7ed09cc2..4d8a9fd6 100755 --- a/build_single_cartridge.sh +++ b/build_single_cartridge.sh @@ -109,6 +109,10 @@ fi # we always add a symbol for the cartridge suffix in case # we want to customize the build of the same script # depending on the cartridge it is built into +if [[ -n "$symbols" ]]; then + # there was at least one symbol before, so add comma separator + symbols+="," +fi symbols+="$cartridge_suffix" # Build cartridges without version nor config appended to name From b1d465437523694730c4f5c3cafddde8296698bd Mon Sep 17 00:00:00 2001 From: huulong Date: Thu, 31 Dec 2020 18:17:54 +0100 Subject: [PATCH 43/91] [PHYSICS] Fixed going through ascending slopes (when running full speed from start, and in loops) by cleaning old loop entrance/exit/trigger flags, entrance corresponding to the new one-way sprite flag Fixes #216 #217 #218 --- data/builtin_data_ingame.p8 | 2 +- data/builtin_data_stage_clear.p8 | 2 +- data/data_stage1_00.p8 | 2 +- data/data_stage1_01.p8 | 2 +- data/data_stage1_10.p8 | 2 +- data/data_stage1_11.p8 | 2 +- data/data_stage1_20.p8 | 2 +- data/data_stage1_21.p8 | 2 +- data/data_stage1_30.p8 | 2 +- data/data_stage1_31.p8 | 2 +- 10 files changed, 10 insertions(+), 10 deletions(-) diff --git a/data/builtin_data_ingame.p8 b/data/builtin_data_ingame.p8 index d945d3a9..d872d506 100644 --- a/data/builtin_data_ingame.p8 +++ b/data/builtin_data_ingame.p8 @@ -136,7 +136,7 @@ eeeeeeeeeeee99994494444444944444449444444eeeeeeed656dc09d9d9d61551dc09d9d9d6656d eeeeeeee44994494449444444494444444444400eeeeeeeed60d777777777775577777777777d06deeeeeeee004499440e004440eeeeeeeeeeebaeeeeeeeeeee eeee44994444444444444444444444444440eeeeeeeeeeeeed6000000000000ee0000000000006deeeeeeeeee0049eeeeeee000eeeeeeeeeeeeeeeeeeeeeeeee __gff__ -0041414141414141414141414141414141414141414141414141414141410000414141434341414141414141410000004141414141414141414141414141414141414141414141414141616180808000414040414141414141414040414141414545454343434343454541414141414145455149434343434545414141418080 +0041414141414141414141414141414141414141414141414141414141410000414141434341414141414141410000004141414141414141414141414141414141414141414141414141616180808000414040414141414141414040414141414141414141414141414141414141414141414141414141414141414141418080 0000000000000000000000000000000000000000000000000000000000000000000000000000000041418080800000000000000000000000414141800080414100000000000000000000008080804141000000000000000000004141808080804040404043430000000080418080800041414141434300000000004040008000 __map__ 000000000000000000006e6f6e6f6e6f6e6f6f6e6f6e6f6e6f6e6f6e6f6e49466e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f497e7f7e7f7e7f7e7f7e7f7e7f7e7f7e7f7e7f7e7f000000000000000000007c7d7c7d7c7d7c7d1e1f1e1f1e1f1e1f5e5f5e5f5f diff --git a/data/builtin_data_stage_clear.p8 b/data/builtin_data_stage_clear.p8 index cc03db75..abaa4e86 100644 --- a/data/builtin_data_stage_clear.p8 +++ b/data/builtin_data_stage_clear.p8 @@ -136,7 +136,7 @@ eeeeeeeeeeee99994494444444944444449444444eeeeeeed656dc09d9d9d61551dc09d9d9d6656d eeeeeeee44994494449444444494444444444400eeeeeeeed60d777777777775577777777777d06deeeeeeee004499440e004440eeeeeeeeeeebaeeeeeeeeeee eeee44994444444444444444444444444440eeeeeeeeeeeeed6000000000000ee0000000000006deeeeeeeeee0049eeeeeee000eeeeeeeeeeeeeeeeeeeeeeeee __gff__ -0041414141414141414141414141414141414141414141414141414141410000414141414141414141414141410000004141414141414141414141414141414141414141414141414141616180808000414040414141414141414040414141414545454343434343454541414141414145455149434343434545414141418080 +0000000000000000000000000000000040000000000000000000000000000000400000434300000000000000000000004141414141414141414141414141414141414141414141414141616180808000414040414141414141414040414141414141414141414141414141414141414141414141414141414141414141418080 0000000000000000000000000000000000000000000000000000000000000000000000000000000041418080800000000000000000000000414141800080414100000000000000000000008080804141000000000000000000004141808080804040404041410000000080418080800041414141414100000000000000008000 __map__ 000000000000000000006e6f6e6f6e6f6e6f6f6e6f6e6f6e6f6e6f6e6f6e49466e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f497e7f7e7f7e7f7e7f7e7f7e7f7e7f7e7f7e7f7e7f000000000000000000007c7d7c7d7c7d7c7d1e1f1e1f1e1f1e1f5e5f5e5f5f diff --git a/data/data_stage1_00.p8 b/data/data_stage1_00.p8 index de88d4f1..dabbfe0e 100644 --- a/data/data_stage1_00.p8 +++ b/data/data_stage1_00.p8 @@ -136,7 +136,7 @@ eeeeeeeeeeee99994494444444944444449444444eeeeeeed656dc09d9d9d61551dc09d9d9d6656d eeeeeeee44994494449444444494444444444400eeeeeeeed60d777777777775577777777777d06deeeeeeee004499440e004440eeeeeeeeeeebaeeeeeeeeeee eeee44994444444444444444444444444440eeeeeeeeeeeeed6000000000000ee0000000000006deeeeeeeeee0049eeeeeee000eeeeeeeeeeeeeeeeeeeeeeeee __gff__ -0000000000000000000000000000000000000000000000000000000000000000000000434300000000000000000000004141414141414141414141414141414141414141414141414141616180808000414040414141414141414040414141414545454343434343454541414141414145455149434343434545414141418080 +0041414141414141414141414141414141414141414141414141414141410000414141434341414141414141410000004141414141414141414141414141414141414141414141414141616180808000414040414141414141414040414141414141414141414141414141414141414141414141414141414141414141418080 0000000000000000000000000000000000000000000000000000000000000000000000000000000041418080800000000000000000000000414141800080414100000000000000000000008080804141000000000000000000004141808080804040404043430000000080418080800041414141434300000000004040008000 __map__ 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6e6f6e6e6e6f6e6f6e6f6e6f6e6f6e6f6e6e6e6e6e6e6e diff --git a/data/data_stage1_01.p8 b/data/data_stage1_01.p8 index ea8bbc8e..56aeec33 100644 --- a/data/data_stage1_01.p8 +++ b/data/data_stage1_01.p8 @@ -136,7 +136,7 @@ eeeeeeeeeeee99994494444444944444449444444eeeeeeed656dc09d9d9d61551dc09d9d9d6656d eeeeeeee44994494449444444494444444444400eeeeeeeed60d777777777775577777777777d06deeeeeeee004499440e004440eeeeeeeeeeebaeeeeeeeeeee eeee44994444444444444444444444444440eeeeeeeeeeeeed6000000000000ee0000000000006deeeeeeeeee0049eeeeeee000eeeeeeeeeeeeeeeeeeeeeeeee __gff__ -0000000000000000000000000000000000000000000000000000000000000000000000434300000000000000000000004141414141414141414141414141414141414141414141414141616180808000414040414141414141414040414141414545454343434343454541414141414145455149434343434545414141418080 +0041414141414141414141414141414141414141414141414141414141410000414141434341414141414141410000004141414141414141414141414141414141414141414141414141616180808000414040414141414141414040414141414141414141414141414141414141414141414141414141414141414141418080 0000000000000000000000000000000000000000000000000000000000000000000000000000000041418080800000000000000000000000414141800080414100000000000000000000008080804141000000000000000000004141808080804040404043430000000080418080800041414141434300000000004040008000 __map__ 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e7f0000fbfcfbfc0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007cdbdadb diff --git a/data/data_stage1_10.p8 b/data/data_stage1_10.p8 index 20e20093..049e7724 100644 --- a/data/data_stage1_10.p8 +++ b/data/data_stage1_10.p8 @@ -136,7 +136,7 @@ eeeeeeeeeeee99994494444444944444449444444eeeeeeed656dc09d9d9d61551dc09d9d9d6656d eeeeeeee44994494449444444494444444444400eeeeeeeed60d777777777775577777777777d06deeeeeeee004499440e004440eeeeeeeeeeebaeeeeeeeeeee eeee44994444444444444444444444444440eeeeeeeeeeeeed6000000000000ee0000000000006deeeeeeeeee0049eeeeeee000eeeeeeeeeeeeeeeeeeeeeeeee __gff__ -0000000000000000000000000000000000000000000000000000000000000000000000434300000000000000000000004141414141414141414141414141414141414141414141414141616180808000414040414141414141414040414141414545454343434343454541414141414145455149434343434545414141418080 +0041414141414141414141414141414141414141414141414141414141410000414141434341414141414141410000004141414141414141414141414141414141414141414141414141616180808000414040414141414141414040414141414141414141414141414141414141414141414141414141414141414141418080 0000000000000000000000000000000000000000000000000000000000000000000000000000000041418080800000000000000000000000414141800080414100000000000000000000008080804141000000000000000000004141808080804040404043430000000080418080800041414141434300000000004040008000 __map__ 6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e diff --git a/data/data_stage1_11.p8 b/data/data_stage1_11.p8 index 67ba3fdb..eb3c8f80 100644 --- a/data/data_stage1_11.p8 +++ b/data/data_stage1_11.p8 @@ -136,7 +136,7 @@ eeeeeeeeeeee99994494444444944444449444444eeeeeeed656dc09d9d9d61551dc09d9d9d6656d eeeeeeee44994494449444444494444444444400eeeeeeeed60d777777777775577777777777d06deeeeeeee004499440e004440eeeeeeeeeeebaeeeeeeeeeee eeee44994444444444444444444444444440eeeeeeeeeeeeed6000000000000ee0000000000006deeeeeeeeee0049eeeeeee000eeeeeeeeeeeeeeeeeeeeeeeee __gff__ -0000000000000000000000000000000000000000000000000000000000000000000000434300000000000000000000004141414141414141414141414141414141414141414141414141616180808000414040414141414141414040414141414545454343434343454541414141414145455149434343434545414141418080 +0041414141414141414141414141414141414141414141414141414141410000414141434341414141414141410000004141414141414141414141414141414141414141414141414141616180808000414040414141414141414040414141414141414141414141414141414141414141414141414141414141414141418080 0000000000000000000000000000000000000000000000000000000000000000000000000000000041418080800000000000000000000000414141800080414100000000000000000000008080804141000000000000000000004141808080804040404043430000000080418080800041414141434300000000004040008000 __map__ 6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6f6e6f6e6f6e6f6e6f6e6f6e6f6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f7f7e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 diff --git a/data/data_stage1_20.p8 b/data/data_stage1_20.p8 index 8e567105..dc2831b4 100644 --- a/data/data_stage1_20.p8 +++ b/data/data_stage1_20.p8 @@ -136,7 +136,7 @@ eeeeeeeeeeee99994494444444944444449444444eeeeeeed656dc09d9d9d61551dc09d9d9d6656d eeeeeeee44994494449444444494444444444400eeeeeeeed60d777777777775577777777777d06deeeeeeee004499440e004440eeeeeeeeeeebaeeeeeeeeeee eeee44994444444444444444444444444440eeeeeeeeeeeeed6000000000000ee0000000000006deeeeeeeeee0049eeeeeee000eeeeeeeeeeeeeeeeeeeeeeeee __gff__ -0000000000000000000000000000000000000000000000000000000000000000000000434300000000000000000000004141414141414141414141414141414141414141414141414141616180808000414040414141414141414040414141414545454343434343454541414141414145455149434343434545414141418080 +0041414141414141414141414141414141414141414141414141414141410000414141434341414141414141410000004141414141414141414141414141414141414141414141414141616180808000414040414141414141414040414141414141414141414141414141414141414141414141414141414141414141418080 0000000000000000000000000000000000000000000000000000000000000000000000000000000041418080800000000000000000000000414141800080414100000000000000000000008080804141000000000000000000004141808080804040404043430000000080418080800041414141434300000000004040008000 __map__ 6e6f000000000000000000000000000000000000000000006e6f6e6f6e6f6e6f6e6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6e6f6e6f diff --git a/data/data_stage1_21.p8 b/data/data_stage1_21.p8 index f2207a2d..2f4b08d6 100644 --- a/data/data_stage1_21.p8 +++ b/data/data_stage1_21.p8 @@ -136,7 +136,7 @@ eeeeeeeeeeee99994494444444944444449444444eeeeeeed656dc09d9d9d61551dc09d9d9d6656d eeeeeeee44994494449444444494444444444400eeeeeeeed60d777777777775577777777777d06deeeeeeee004499440e004440eeeeeeeeeeebaeeeeeeeeeee eeee44994444444444444444444444444440eeeeeeeeeeeeed6000000000000ee0000000000006deeeeeeeeee0049eeeeeee000eeeeeeeeeeeeeeeeeeeeeeeee __gff__ -0000000000000000000000000000000000000000000000000000000000000000000000434300000000000000000000004141414141414141414141414141414141414141414141414141616180808000414040414141414141414040414141414545454343434343454541414141414145455149434343434545414141418080 +0041414141414141414141414141414141414141414141414141414141410000414141434341414141414141410000004141414141414141414141414141414141414141414141414141616180808000414040414141414141414040414141414141414141414141414141414141414141414141414141414141414141418080 0000000000000000000000000000000000000000000000000000000000000000000000000000000041418080800000000000000000000000414141800080414100000000000000000000008080804141000000000000000000004141808080804040404043430000000080418080800041414141434300000000004040008000 __map__ 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000bb00000000000000000000004e4d393a3335374c4d4e4c4e5e5f5e5f5e5f5e5f5e5f535453545354535453545354535457463c3d0000000000000000000040780000000000000000774000000000000000000000 diff --git a/data/data_stage1_30.p8 b/data/data_stage1_30.p8 index c900d10d..4ea2bad6 100644 --- a/data/data_stage1_30.p8 +++ b/data/data_stage1_30.p8 @@ -136,7 +136,7 @@ eeeeeeeeeeee99994494444444944444449444444eeeeeeed656dc09d9d9d61551dc09d9d9d6656d eeeeeeee44994494449444444494444444444400eeeeeeeed60d777777777775577777777777d06deeeeeeee004499440e004440eeeeeeeeeeebaeeeeeeeeeee eeee44994444444444444444444444444440eeeeeeeeeeeeed6000000000000ee0000000000006deeeeeeeeee0049eeeeeee000eeeeeeeeeeeeeeeeeeeeeeeee __gff__ -0000000000000000000000000000000000000000000000000000000000000000000000434300000000000000000000004141414141414141414141414141414141414141414141414141616180808000414040414141414141414040414141414545454343434343454541414141414145455149434343434545414141418080 +0041414141414141414141414141414141414141414141414141414141410000414141434341414141414141410000004141414141414141414141414141414141414141414141414141616180808000414040414141414141414040414141414141414141414141414141414141414141414141414141414141414141418080 0000000000000000000000000000000000000000000000000000000000000000000000000000000041418080800000000000000000000000414141800080414100000000000000000000008080804141000000000000000000004141808080804040404043430000000080418080800041414141434300000000004040008000 __map__ 6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 diff --git a/data/data_stage1_31.p8 b/data/data_stage1_31.p8 index b6cd2da2..009ea611 100644 --- a/data/data_stage1_31.p8 +++ b/data/data_stage1_31.p8 @@ -136,7 +136,7 @@ eeeeeeeeeeee99994494444444944444449444444eeeeeeed656dc09d9d9d61551dc09d9d9d6656d eeeeeeee44994494449444444494444444444400eeeeeeeed60d777777777775577777777777d06deeeeeeee004499440e004440eeeeeeeeeeebaeeeeeeeeeee eeee44994444444444444444444444444440eeeeeeeeeeeeed6000000000000ee0000000000006deeeeeeeeee0049eeeeeee000eeeeeeeeeeeeeeeeeeeeeeeee __gff__ -0000000000000000000000000000000000000000000000000000000000000000000000434300000000000000000000004141414141414141414141414141414141414141414141414141616180808000414040414141414141414040414141414545454343434343454541414141414145455149434343434545414141418080 +0041414141414141414141414141414141414141414141414141414141410000414141434341414141414141410000004141414141414141414141414141414141414141414141414141616180808000414040414141414141414040414141414141414141414141414141414141414141414141414141414141414141418080 0000000000000000000000000000000000000000000000000000000000000000000000000000000041418080800000000000000000000000414141800080414100000000000000000000008080804141000000000000000000004141808080804040404043430000000080418080800041414141434300000000004040008000 __map__ 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 From 1f27664677cb64c888e950ee6cdd323e5250742e Mon Sep 17 00:00:00 2001 From: huulong Date: Thu, 31 Dec 2020 18:19:35 +0100 Subject: [PATCH 44/91] [APP] Fixed app ingame not calling base on_post_start, missing mouse initialization (could not use codetuner) --- src/application/picosonic_app_base.lua | 4 ++-- src/application/picosonic_app_ingame.lua | 3 ++- src/application/picosonic_app_ingame_utest.lua | 14 ++++++++++++++ 3 files changed, 18 insertions(+), 3 deletions(-) diff --git a/src/application/picosonic_app_base.lua b/src/application/picosonic_app_base.lua index d5ec654a..cdce7834 100644 --- a/src/application/picosonic_app_base.lua +++ b/src/application/picosonic_app_base.lua @@ -28,13 +28,13 @@ function picosonic_app_base:init() gameapp.init(self, fps60) end ---#if mouse function picosonic_app_base:on_post_start() -- override +--#if mouse -- enable mouse devkit input:toggle_mouse(true) mouse:set_cursor_sprite_data(visual.sprite_data_t.cursor) -end --#endif +end function picosonic_app_base:on_reset() -- override --#if mouse diff --git a/src/application/picosonic_app_ingame.lua b/src/application/picosonic_app_ingame.lua index 487bcb34..f00c83cc 100644 --- a/src/application/picosonic_app_ingame.lua +++ b/src/application/picosonic_app_ingame.lua @@ -15,7 +15,8 @@ function picosonic_app_ingame:instantiate_gamestates() -- override (mandatory) end function picosonic_app_ingame:on_post_start() -- override (optional) - menuitem(3, "back to title", function() load('picosonic_titlemenu.p8') end) + picosonic_app_base.on_post_start(self) + menuitem(3, "back to title", function() load('picosonic_titlemenu.p8') end) end return picosonic_app_ingame diff --git a/src/application/picosonic_app_ingame_utest.lua b/src/application/picosonic_app_ingame_utest.lua index b59b5ca4..92a07acb 100644 --- a/src/application/picosonic_app_ingame_utest.lua +++ b/src/application/picosonic_app_ingame_utest.lua @@ -1,6 +1,7 @@ require("test/bustedhelper_ingame") local picosonic_app_ingame = require("application/picosonic_app_ingame") +local picosonic_app_base = require("application/picosonic_app_base") local stage_state = require("ingame/stage_state") describe('picosonic_app_ingame', function () @@ -22,13 +23,26 @@ describe('picosonic_app_ingame', function () describe('on_post_start', function () setup(function () + stub(picosonic_app_base, "on_post_start") stub(_G, "menuitem") end) teardown(function () + picosonic_app_base.on_post_start:revert() menuitem:revert() end) + after_each(function () + picosonic_app_base.on_post_start:clear() + menuitem:clear() + end) + + it('should call base implementation', function () + app:on_post_start() + assert.spy(picosonic_app_base.on_post_start).was_called(1) + assert.spy(picosonic_app_base.on_post_start).was_called_with(match.ref(app)) + end) + it('should load cartridge: picosonic_titlemenu.p8', function () app:on_post_start() assert.spy(menuitem).was_called(1) From 387dc8fbf593f8a1350400559d0a1dc60bdfd57e Mon Sep 17 00:00:00 2001 From: huulong Date: Thu, 31 Dec 2020 18:20:08 +0100 Subject: [PATCH 45/91] [BUILD] Stripped more methods in player character unless ingame to make stage_intro cartridge lighter --- src/ingame/playercharacter.lua | 9 +++++++++ src/ingame/stage_state.lua | 6 ++++-- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/src/ingame/playercharacter.lua b/src/ingame/playercharacter.lua index be2cd43e..e3870813 100644 --- a/src/ingame/playercharacter.lua +++ b/src/ingame/playercharacter.lua @@ -308,6 +308,8 @@ function player_char:update() self.anim_spr:update() end +--#if ingame + -- update intention based on current input function player_char:handle_input() if self.control_mode == control_modes.human then @@ -376,6 +378,7 @@ function player_char:handle_input() end end + function player_char:force_move_right() -- force player to move to the right self.control_mode = control_modes.puppet @@ -385,6 +388,7 @@ function player_char:force_move_right() end --#if cheat + function player_char:toggle_debug_motion() -- 1 -> 2 (debug) -- 2 -> 1 (platformer) @@ -403,6 +407,11 @@ function player_char:set_motion_mode(val) self.debug_velocity = vector.zero() end end + +--(cheat) +--#endif + +--(ingame) --#endif -- update player position diff --git a/src/ingame/stage_state.lua b/src/ingame/stage_state.lua index 90dc0a71..0b6a35ba 100644 --- a/src/ingame/stage_state.lua +++ b/src/ingame/stage_state.lua @@ -114,8 +114,10 @@ function stage_state:reload_runtime_data() -- NOTE: we are *not* reloading sprite flags (could do by copying 0x100 bytes from 0x3000-0x30ff) -- which means our builtin spritesheet *must* contain any new flags brought by runtime extra tiles -- (located in the 3 top rows of the spritesheet). Those are rare (only one-way platform tiles) - -- but without the flags, they won't behave properly. This means you must place flags on the mask tiles - -- in the built-in spritesheet. Later, you can move all mask tiles to another spritesheet to reload + -- but without the flags, they won't behave properly. This means you must (unintuitively) place flags + -- on the mask tiles in the built-in spritesheet. To make it easier, you may edit the flags on a cartridge + -- containing all the sprites of interest, then copy-paste the __gff__ lines into the builtin .p8 cartridge + -- Later, you can move all mask tiles to another spritesheet to reload -- on start instead. local runtime_data_path = "data_stage"..self.curr_stage_id.."_runtime.p8" reload(0x0, 0x0, 0x600, runtime_data_path) From 537bbdf084fd91f5ac470b5e42e790793765bd68 Mon Sep 17 00:00:00 2001 From: huulong Date: Thu, 31 Dec 2020 19:20:15 +0100 Subject: [PATCH 46/91] [TEST] Fixed headless itest on stage_intro by adding state type check besides #if ingame, which is ignored by busted and only asserting after load if #pico8 --- src/ingame/playercharacter.lua | 118 +++++++++++++++++--------- src/stage_intro/stage_intro_state.lua | 3 + 2 files changed, 83 insertions(+), 38 deletions(-) diff --git a/src/ingame/playercharacter.lua b/src/ingame/playercharacter.lua index e3870813..a4ca642c 100644 --- a/src/ingame/playercharacter.lua +++ b/src/ingame/playercharacter.lua @@ -251,28 +251,35 @@ function player_char:set_ground_tile_location(global_tile_loc) --#if ingame - -- when touching (internal) loop entrance trigger, enable entrance (and disable exit) layer - -- and reversely - -- we are now checking loop triggers directly from stage data - -- external triggers are different and can be entered airborne, see check_loop_external_triggers - local curr_stage_state = flow.curr_state - assert(curr_stage_state.type == ':stage') - - -- new convention is to check ground location at the end of update_platformer_motion - -- like check_spring, because changing state to airborne in the middle of ground motion - -- may cause issues - -- but loops were added before springs and they keep the character grounded, so we kept - -- this behavior here - if curr_stage_state:is_tile_loop_entrance_trigger(global_tile_loc) then - -- note that active loop layer may already be 1 - log("internal trigger detected, set active loop layer: 1", 'loop') - self.active_loop_layer = 1 - elseif curr_stage_state:is_tile_loop_exit_trigger(global_tile_loc) then - -- note that active loop layer may already be 2 - log("internal trigger detected, set active loop layer: 2", 'loop') - self.active_loop_layer = 2 +--#if busted + if flow.curr_state.type == ':ingame' then +--#endif + -- when touching (internal) loop entrance trigger, enable entrance (and disable exit) layer + -- and reversely + -- we are now checking loop triggers directly from stage data + -- external triggers are different and can be entered airborne, see check_loop_external_triggers + local curr_stage_state = flow.curr_state + assert(curr_stage_state.type == ':stage') + + -- new convention is to check ground location at the end of update_platformer_motion + -- like check_spring, because changing state to airborne in the middle of ground motion + -- may cause issues + -- but loops were added before springs and they keep the character grounded, so we kept + -- this behavior here + if curr_stage_state:is_tile_loop_entrance_trigger(global_tile_loc) then + -- note that active loop layer may already be 1 + log("internal trigger detected, set active loop layer: 1", 'loop') + self.active_loop_layer = 1 + elseif curr_stage_state:is_tile_loop_exit_trigger(global_tile_loc) then + -- note that active loop layer may already be 2 + log("internal trigger detected, set active loop layer: 2", 'loop') + self.active_loop_layer = 2 + end +--#if busted end +--#endif +--(ingame) --#endif end end @@ -301,7 +308,16 @@ function player_char:update() -- in stage_intro cartridge, we want Sonic to stay idle, so no input -- but update physics and render as usual --#if ingame - self:handle_input() + +--#if busted + if flow.curr_state.type == ':ingame' then +--#endif + self:handle_input() +--#if busted + end +--#endif + +--(ingame) --#endif self:update_motion() self:update_anim() @@ -420,12 +436,21 @@ function player_char:update_motion() --#if ingame --#if cheat - if self.motion_mode == motion_modes.debug then - self:update_debug() - return + +--#if busted + if flow.curr_state.type == ':ingame' then +--#endif + if self.motion_mode == motion_modes.debug then + self:update_debug() + return + end +--#if busted end - -- else: self.motion_mode == motion_modes.platformer --#endif + +--(ingame) +--#endif +--(cheat) --#endif self:update_platformer_motion() @@ -588,17 +613,25 @@ local function iterate_over_collision_tiles(pc, collision_check_quadrant, start_ --#if ingame - -- we now check for ignored tiles: - -- a. ramps just after launching - -- b. loops on inactive layer from PC's point-of-view - -- c. one-way platforms unless we check collision downward - if pc.ignore_launch_ramp_timer > 0 and visual_tile_id == visual.launch_ramp_last_tile_id or - pc.active_loop_layer == 1 and curr_stage_state:is_tile_in_loop_exit(curr_global_tile_loc) or - pc.active_loop_layer == 2 and curr_stage_state:is_tile_in_loop_entrance(curr_global_tile_loc) or - is_oneway and collision_check_quadrant ~= directions.down then - ignore_tile = true +--#if busted + if flow.curr_state.type == ':ingame' then +--#endif + -- we now check for ignored tiles: + -- a. ramps just after launching + -- b. loops on inactive layer from PC's point-of-view + -- c. one-way platforms unless we check collision downward + if pc.ignore_launch_ramp_timer > 0 and visual_tile_id == visual.launch_ramp_last_tile_id or + pc.active_loop_layer == 1 and curr_stage_state:is_tile_in_loop_exit(curr_global_tile_loc) or + pc.active_loop_layer == 2 and curr_stage_state:is_tile_in_loop_entrance(curr_global_tile_loc) or + is_oneway and collision_check_quadrant ~= directions.down then + ignore_tile = true + end + +--#if busted end +--#endif +--(ingame) --#endif if ignore_tile then @@ -916,10 +949,19 @@ function player_char:update_platformer_motion() end --#if ingame - self:check_spring() - self:check_launch_ramp() - self:check_emerald() - self:check_loop_external_triggers() + +--#if busted + if flow.curr_state.type == ':ingame' then +--#endif + self:check_spring() + self:check_launch_ramp() + self:check_emerald() + self:check_loop_external_triggers() +--#if busted + end +--#endif + +--(ingame) --#endif end diff --git a/src/stage_intro/stage_intro_state.lua b/src/stage_intro/stage_intro_state.lua index 2d1650f4..dfa92e78 100644 --- a/src/stage_intro/stage_intro_state.lua +++ b/src/stage_intro/stage_intro_state.lua @@ -163,7 +163,10 @@ function stage_intro_state:show_stage_splash_async() -- splash is over, load ingame cartridge and give control to player load('picosonic_ingame.p8') + +--[[#pico8 assert(false, "could not load picosonic_ingame.p8") +--#pico8]] end return stage_intro_state From 0c3ef8b3730111701edbd4e9bc6381ee73fd3147 Mon Sep 17 00:00:00 2001 From: huulong Date: Thu, 31 Dec 2020 20:45:06 +0100 Subject: [PATCH 47/91] [BUSTED] Fixed state type :ingame -> :stage --- src/ingame/playercharacter.lua | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/ingame/playercharacter.lua b/src/ingame/playercharacter.lua index a4ca642c..1168c961 100644 --- a/src/ingame/playercharacter.lua +++ b/src/ingame/playercharacter.lua @@ -252,7 +252,7 @@ function player_char:set_ground_tile_location(global_tile_loc) --#if ingame --#if busted - if flow.curr_state.type == ':ingame' then + if flow.curr_state.type == ':stage' then --#endif -- when touching (internal) loop entrance trigger, enable entrance (and disable exit) layer -- and reversely @@ -310,7 +310,7 @@ function player_char:update() --#if ingame --#if busted - if flow.curr_state.type == ':ingame' then + if flow.curr_state.type == ':stage' then --#endif self:handle_input() --#if busted @@ -438,7 +438,7 @@ function player_char:update_motion() --#if cheat --#if busted - if flow.curr_state.type == ':ingame' then + if flow.curr_state.type == ':stage' then --#endif if self.motion_mode == motion_modes.debug then self:update_debug() @@ -614,7 +614,7 @@ local function iterate_over_collision_tiles(pc, collision_check_quadrant, start_ --#if ingame --#if busted - if flow.curr_state.type == ':ingame' then + if flow.curr_state.type == ':stage' then --#endif -- we now check for ignored tiles: -- a. ramps just after launching @@ -951,7 +951,7 @@ function player_char:update_platformer_motion() --#if ingame --#if busted - if flow.curr_state.type == ':ingame' then + if flow.curr_state.type == ':stage' then --#endif self:check_spring() self:check_launch_ramp() From c79bfa0eeecb7761a9abb35e17c8539d2d044df8 Mon Sep 17 00:00:00 2001 From: huulong Date: Thu, 31 Dec 2020 21:25:16 +0100 Subject: [PATCH 48/91] [SYSTEM MENU] Added menu entries to retry with and without emeralds Consume temporary picked emeralds in memory as soon as they are restored Moved picked emeralds temporary address to 0x5d00 as 0x4300 is the start of region reload buffer --- src/application/picosonic_app_ingame.lua | 15 +++++++++- .../picosonic_app_ingame_utest.lua | 2 +- src/ingame/stage_state.lua | 28 ++++++++++++------- src/ingame/stage_state_utest.lua | 14 +++++++--- src/stage_clear/stage_clear_state.lua | 6 ++-- src/stage_clear/stage_clear_state_utest.lua | 10 +++---- 6 files changed, 50 insertions(+), 25 deletions(-) diff --git a/src/application/picosonic_app_ingame.lua b/src/application/picosonic_app_ingame.lua index f00c83cc..73d72264 100644 --- a/src/application/picosonic_app_ingame.lua +++ b/src/application/picosonic_app_ingame.lua @@ -16,7 +16,20 @@ end function picosonic_app_ingame:on_post_start() -- override (optional) picosonic_app_base.on_post_start(self) - menuitem(3, "back to title", function() load('picosonic_titlemenu.p8') end) + + menuitem(3, "warp to start", function() + assert(flow.curr_state.type == ':stage') + flow.curr_state:store_picked_emerald_data() + load('picosonic_ingame.p8') + end) + + menuitem(4, "retry from zero", function() + load('picosonic_ingame.p8') + end) + + menuitem(5, "back to title", function() + load('picosonic_titlemenu.p8') + end) end return picosonic_app_ingame diff --git a/src/application/picosonic_app_ingame_utest.lua b/src/application/picosonic_app_ingame_utest.lua index 92a07acb..ed82d078 100644 --- a/src/application/picosonic_app_ingame_utest.lua +++ b/src/application/picosonic_app_ingame_utest.lua @@ -45,7 +45,7 @@ describe('picosonic_app_ingame', function () it('should load cartridge: picosonic_titlemenu.p8', function () app:on_post_start() - assert.spy(menuitem).was_called(1) + assert.spy(menuitem).was_called(3) -- no reference to lambda passed to menuitem, so don't test was_called_with end) diff --git a/src/ingame/stage_state.lua b/src/ingame/stage_state.lua index 0b6a35ba..12fdc41f 100644 --- a/src/ingame/stage_state.lua +++ b/src/ingame/stage_state.lua @@ -200,6 +200,8 @@ function stage_state:reload_runtime_data() end -- never called, we directly load stage_clear cartridge +-- if you want to optimize stage retry by just re-entering stage_state though, +-- you will need on_exit for cleanup (assuming you don't patch PICO-8 for fast load) --[[ function stage_state:on_exit() -- clear all coroutines (we normally let app handle them, but in this context @@ -789,11 +791,16 @@ end function stage_state:restore_picked_emerald_data() -- Retrieve and store picked emeralds set information from memory stored in stage_clear - -- before retry. If you come directly from the titlemenu, this should do nothing. + -- or system pause menu before warp to start / retry (keep emeralds). + -- If you come directly from the titlemenu or a retry from zero, this should do nothing. -- Similar to stage_clear_state:restore_picked_emerald_data, but we also -- remove emerald objects from the stage with a "silent pick" -- (so this method must be called after object spawning) - local picked_emerald_byte = peek(0x4300) + -- It is stored in 0x5d00, see store_picked_emerald_data below + local picked_emerald_byte = peek(0x5d00) + + -- consume emerald immediately to avoid sticky emeralds on hard ingame reload (ctrl+R) + poke(0x5d00, 0) -- read bitset low-endian, from highest bit (emerald 8) to lowest bit (emerald 1) -- the only reason we iterate from the end is because del() will remove elements @@ -811,14 +818,15 @@ function stage_state:restore_picked_emerald_data() end function stage_state:store_picked_emerald_data() - -- general memory is a good fit to store data across cartridges, - -- although this behavior is undocumented - -- we could also use persistent memory, considering we may save emeralds collected by player + -- General memory is persistent during a single session, so a good fit to store data + -- across cartridges, although this behavior is undocumented. + -- However, 0x4300-0x52ff is occupied by runtime regions, and 0x5300-0x5cff + -- is occupied non-rotated/rotated walk/run sprite variants, so store 1 byte at 0x5d00. + -- We could also use persistent memory, considering we may save emeralds collected by player -- on next run (but for now we don't, so player always starts game from zero) - -- note that Sonic is not visible so we don't mind overwriting the memory at 0x4300 - -- which during ingame contains rotated and non-rotated sprite variants - -- convert set of picked emeralds to bitset (1 if emerald was picked, low-endian) - -- there are 8 emeralds so we need 1 byte + -- + -- Convert set of picked emeralds to bitset (1 if emerald was picked, low-endian) + -- there are 8 emeralds so we need 1 byte local picked_emerald_bytes = 0 for i = 1, 8 do if self.picked_emerald_numbers_set[i] then @@ -826,7 +834,7 @@ function stage_state:store_picked_emerald_data() picked_emerald_bytes = picked_emerald_bytes + shl(1, i - 1) end end - poke(0x4300, picked_emerald_bytes) + poke(0x5d00, picked_emerald_bytes) end function stage_state:feedback_reached_goal() diff --git a/src/ingame/stage_state_utest.lua b/src/ingame/stage_state_utest.lua index 046aff45..25fd5b64 100644 --- a/src/ingame/stage_state_utest.lua +++ b/src/ingame/stage_state_utest.lua @@ -67,7 +67,7 @@ describe('stage_state', function () assert.spy(base_stage_state.init).was_called_with(match.ref(state)) end) - it('#solo should initialize members', function () + it('should initialize members', function () assert.are_same({ ':stage', 1, @@ -826,11 +826,11 @@ describe('stage_state', function () before_each(function () -- 0b01001001 -> 73 (low-endian, so lowest bit is for emerald 1) - poke(0x4300, 73) + poke(0x5d00, 73) end) after_each(function () - poke(0x4300, 0) + poke(0x5d00, 0) end) it('should read 1 byte in general memory representing picked emeralds bitset', function () @@ -851,6 +851,12 @@ describe('stage_state', function () assert.are_same({"dummy2", "dummy3", "dummy5", "dummy6", "dummy8"}, state.emeralds) end) + it('should clear picked emerald transitional memory', function () + state:restore_picked_emerald_data() + + assert.are_equal(0, peek(0x5d00)) + end) + end) describe('(stage states added)', function () @@ -1411,7 +1417,7 @@ describe('stage_state', function () } -- 0b01001001 -> 73 (low-endian, so lowest bit is for emerald 1) state:store_picked_emerald_data() - assert.are_equal(73, peek(0x4300)) + assert.are_equal(73, peek(0x5d00)) end) end) diff --git a/src/stage_clear/stage_clear_state.lua b/src/stage_clear/stage_clear_state.lua index 988ce89d..6d526cd6 100644 --- a/src/stage_clear/stage_clear_state.lua +++ b/src/stage_clear/stage_clear_state.lua @@ -43,13 +43,13 @@ end function stage_clear_state.retry_from_zero_async() -- clear picked emeralds data (see stage_state:store_picked_emerald_data) in general memory - poke(0x4300, 0) + poke(0x5d00, 0) stage_clear_state.retry_stage_async() end function stage_clear_state.back_to_titlemenu_async() -- remember to clear picked emerald data, so if we start again from titlemenu we'll also restart from zero - poke(0x4300, 0) + poke(0x5d00, 0) -- zigzag fadeout will also give time to player to hear confirm SFX flow.curr_state:zigzag_fade_out_async() @@ -245,7 +245,7 @@ function stage_clear_state:restore_picked_emerald_data() -- cartridge was loaded -- similar to stage_state:restore_picked_emerald_data, but we don't remove emerald objects -- and cache the picked count for assessment - local picked_emerald_byte = peek(0x4300) + local picked_emerald_byte = peek(0x5d00) -- read bitset low-endian, from lowest bit (emerald 1) to highest bit (emerald 8) for i = 1, 8 do diff --git a/src/stage_clear/stage_clear_state_utest.lua b/src/stage_clear/stage_clear_state_utest.lua index 820ee408..89ade0f6 100644 --- a/src/stage_clear/stage_clear_state_utest.lua +++ b/src/stage_clear/stage_clear_state_utest.lua @@ -373,9 +373,8 @@ describe('stage_clear_state', function () end) it('should not crash with a few emeralds', function () - -- emerald bitset: 0b1010 0b0110 - pico8.poked_addresses[0x4300] = 0xa - pico8.poked_addresses[0x4301] = 0x6 + -- emerald bitset: 0b10100110 + pico8.poked_addresses[0x5d00] = 0xa6 -- a time long enough to cover other async methods like assess_result_async for i = 1, 1000 do @@ -384,9 +383,8 @@ describe('stage_clear_state', function () end) it('should not crash with all emeralds', function () - -- emerald bitset: 0b1111 0b1111 - pico8.poked_addresses[0x4300] = 0xf - pico8.poked_addresses[0x4301] = 0xf + -- emerald bitset: 0b11111111 + pico8.poked_addresses[0x5d00] = 0xff -- a time long enough to cover other async methods like assess_result_async for i = 1, 1000 do From 16fb1a34da57d769f54ffea78f31bf417770a9c9 Mon Sep 17 00:00:00 2001 From: huulong Date: Thu, 31 Dec 2020 21:26:08 +0100 Subject: [PATCH 49/91] [RUN] Take screenshots at 4x again for showcase --- run_cartridge.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/run_cartridge.sh b/run_cartridge.sh index 93880b84..14d03216 100755 --- a/run_cartridge.sh +++ b/run_cartridge.sh @@ -27,7 +27,7 @@ install_dirpath="${carts_dirpath}/picosonic/v${version}_${config}" # because load() paths may be relative (in our case, inside picosonic/vX.Y) # and first cartridge path is only cd-ed into if somewhere inside carts/ # this means you must install the built cartridge before running -run_cmd="pico8 -run ${install_dirpath}/${cartridge_stem}_${cartridge_suffix}.p8 -screenshot_scale 1 -gif_scale 4 -gif_len 60 $@" +run_cmd="pico8 -run ${install_dirpath}/${cartridge_stem}_${cartridge_suffix}.p8 -screenshot_scale 4 -gif_scale 4 -gif_len 60 $@" # Support UNIX platforms without gnome-terminal by checking if the command exists # If you `reload.sh` the game, the separate terminal allows you to keep watching the program output, From 8cf848d1feeff58ddd53d81900327fd5b688dd5d Mon Sep 17 00:00:00 2001 From: huulong Date: Thu, 31 Dec 2020 21:55:29 +0100 Subject: [PATCH 50/91] [SCREENSHOT] Took screenshot of Sonic running in loop in current version for showcase --- screenshots/picosonic_showcase.png | Bin 11159 -> 9198 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/screenshots/picosonic_showcase.png b/screenshots/picosonic_showcase.png index 8521609ec2ba31539982bf4fec4342d531e6d7d1..d01a6ea0ca027ef9424979210fe25288c68c4a70 100644 GIT binary patch literal 9198 zcma)iXIRr&^LAWU*(tmIM+)+W#c@?2|n2m-mxi7yRbT%(>^Dd**Qaq|?!LKdJo$ zfk4(B``!LD1o9pD?{|>hsrNdmMx*ZLYU?ZgJRO|bzF}`-(HmUK`c#v(`W`6`iEJO*yK}pvwm2s;-U4%<6kJqmxHmOg+&4>s+R1A@Yq$fq&}PlpOcI#qqkWvPB7{gu16hRg76` zFX1H23l9s+M+hBWM=_kDU&q?3Z-(1Vg;1%i|vz;(4^ z3hZ-d=;eL8wofs;2i>+XvyjeABO;HfbwK;?(PQb}>-OAgQHDU)Kp^{9K_GhyNSoce zzZ|#k+2zsJl!Z=Ku{+;Aci;&1vQTD)4Y!X-1vN#N8*aMqU?_|>^PNg#q!g(Nd2kE0mW z?M&|BKqNT9o5^vC<6AzOSHM9aJHQ9vS{_Z+7pSCN`&zJI-*^p|;W~C%*LUXYnYJ$e z!Mtc#Kp3UVS%~PZz^y*v`)Z9O6!A$XhUg=8C4!DI(m~sl-R>s0T$GF|!j*t|a%HF$ zw+qkU!VA^-p^r>nzy0^4)FFEOY!_zhz4^{xE;3x)^^}RqRf1=j2)B;W{U97z!lPra zNwU8lB6`m|GkEL&<3hS;zrS6&7KCg>;E@InP2sI;$A^RN_6o-n55bPPB*!0m#A53ic|tM(yT*FJZpE$?9r*D@Z2*hu33!*qcnG6_=I$^(JNfd>-3TT7n2Xm8sBcDWz1%QDYJ{6n8SN#i`e$>d!YPww?IdTCOwOi}Pvv7r_auRNa}*IfGuA zbBlj0QJyKXs9(5|Rd2T5L#~|mB4n|%KKdXRv2P5r5ZsFXp8Q2o5lKf z;jQVey?=0rph6hU<#Otq%R7aqDVzL;l#%AP3eS<}31ZVb72=YPZ zk(%zN>ob`=Gtn~9yDLNiqwR&}EHX|{K04Wv=IPOv*ma!ZP0vbt*kNh8X?`Wwnt0t| ztF<1I+L4%wrV&QfxAB^wy+|7!$3Nd(u8$Q>!0e!3hp~>Rs)v&;`C9AtB+1k#sBEVG z@BaCNRfmbHCcDh?RGoE!A{lI=G^Eqm{P+St- zb1vBr%?zB;w!O#-S|spZvmy3zcBrVu9~Y|$!HYCW-~9`-7c&56<1QJmbYQ8ap`5a5 zJ|#chls)A=$I*ZrGp=ynt4as8^qmjLeuY`pxc4cGH)=^{^KN(fAr`FJLm8cT&j~6` zhIoT^K2eQkK)1`CLk-?=RyfZE8;}z1KFetMuJD>fOWYN1OshRZPs;zdxU8 z>v2LR3YTHIImp?w4WeoIKxGIQ)S4CUWexWw{V*{)@BcA(_FnWXIman3i$6Wwa=EzT zjsuU`=)J>vfNNzj=rt%DM>(Upt6%C#S62O8e{I2_VS+L0_O)3uITmCk7`>A@6rso} zeZE8D)C6_l2^Yy~;%O(un@p&D4}U8|4ZMYjpb8b^2fg;;8U;~L3Y_#%-pRlg+$2cy zIn(I{cIa18Wb;+;wsfNx7p!GV+p?eIJOA?$rwBdE6%#wJq`UByW00D?0dQ0NJGNE> zE}8W)shE-S5dTxa1X9#gTSFHcIyk#VIuc8>6m`0t7#3*T;wKhzStuMnCc3!I#wqO3 zbVBjkEP17LtL19Sc)ay0tX#Ea@o<GLCXUw)mKC zsEmp3PBY(civM|j7>h;38+q}(iu_yyMu=5RXl*4!rfVKwY~lDSCEat`@!rR@9|!vE zY*g#ZAj=0A#90laYAw#WKAz_PWos&3L}uojtyl6_m1pn_%}cBccc!U;N@tU~t({7h zjCL~nM0lpR=a7%~P*3f3glxeSD+1`RS3Mr6jZ1SP9SAPE>StUh@m4Us9xE3$pxT1ZeMVenX>VI6_>YT~>vI%|AA)%*xAN6>A$f?8agg-R}a>JlrLQxEG)(nopLUY#^-_{V_b%tIkG zy^oV#8t_Q(ed(3WDnaZYbuihmVunMH#kVqf9gjz$Mbkst&4O0O{mhO}^^pBmr$Cz* zce@SM5%SGQGI5qAghy`b<}UempCI3mW1*7)2!TsYu>|8N`&oD;&gR5>;m#NW$Wnv` zo;rzN#wJIK{^8w>Qr5^D^o}svNmN%T-w3JM0D75NMC>)mt9ih~2zqT9c+OdUMt9ca ze0%nmvr1xNme?gWUPjkCl9r4xG7MNBO0cGfWY*<_i^mmFIFu%@W+*SG{Bp#^Du}A9*`Q?EN=WlzjrW2 z%lmj;ghxiNk-c>+y{u_S0`@Etn%2@dRLm-Fy^^bW=j}6+g9#3yU=BUa6~aDDbd&OXzmZG5rjcKd$f>0PH2cg>o6)o1QCwg@aXip1;2irh$2AxKq<5piBfA!;U`~;WO z-J9iZ<3?1Su!jcNfc1%mI+Lca7Fkoh{-Q&hjfg|Y`z_ejQhthqPj&AV^d|%sa;WfK z)x@KAg2VbOpsprv4cPye3B*kdWzoN^65(d^fO8&xgu+iqV%X*?iY3Cygz6rsg%;NH z`RMZ&|6J~iRc4(r4+BB+R=m9V*7!6&aK#YBr?PpNy+c6 zRHWIg50IHo%qrSL^7C^H3mZn^<-DAd4 zKP?_LYo=C)MH&!&On2?sOBD37yb^bVquveNoc+@@nR8Z@BKDfn>)87IHr+E{K*WyOg|Q(RY>- zv#quG?@P3WUET<7(Ur@(e@9v(pJ?#zaC`cOG(p(H`1FQFhc%OP@XEc0apr5Y+^1%K zlQG|CBT5(Lc>XoWPe%$X4##t8_iw&Q_@{%Byrcy~D2Y7x4B$#ekqiUw<;%I;a<^XZ zJK}b3+pKbTUrIif;)>^vA`H9M3;IQZ*9-a6b(mufip2m^Ful8`vL~&u-{}g3Y9PBV zp5-X86Vi-o-fHou&b37fYeiefkn@p3C{6uRE z^V-;|rUYu#BZdnyQAbXyIs&&SbID1Mlkst1q0?5;+;2CMVh6W*j!&^$>aR@3F9jj- z>^f?~%jkMmacn8*Ad6vyA-Gzsx3uuuQp$&!KWFqk^ zyOs8_X~j#{r=BU$*^f;}(b*m%%WQ6M_Rscvxl7=XEvJj~~_J!!BGVi7+ zC?J-VvdI1h)5F032{CTQtwjUZ=zKe2Js-hpx>i99^L}~t3;R%lW3wRjLNhmjHex(% zDqTls?=67qO~e`V zFv&}A@{zf-sx^PnzOf{IzlN&JkRHD3q#+>Q@Bc?pSC`t~4HB;};ygU|Z(;-(EIuZ9 zV~xUo)CpqDDNsG(gT5zMuXM0` z%B?T(M8jUpeQq!XLK_TP!4%gJK`qSigdlDDpVW@&LaCKkz+@FYw@N3S}a(SCDz=<}8?s51_h7SrHIx!|~oTG0Jy z?RWoGR9TlMZ|Z2q>Ded&b2Y1(3;GUki^$uM8iQ4D^8=b{r>l4Dnk*mXEqEdt80{#( z*54DtLgsA94CvvfK{HLHYC zW?tp)oO9mNT!i4x^{IZ?F9q;EdgJBcHCr(lMj&TioQZAtikjGgrlYeaIN1pT>;YfN z|7G`Ss(}I@0zWsG{W;+yDeq`*N}@m9&s{!@Iu^fjxnx_8CY_6#<7r%)Kd%tFRk{pF zH#Ah;CsNjBE|-^Ff|guov}*&8DSBlHV^mj?)S3xO7<983o0alSOeN*An zmI@OoBU7fGr;bS705C^0b3K9=;H1?mSeOEMdY1$_4K zPlPK^;jYzI?(Oma4DkxwV%{?xjP;jx+Gf)?hTt$OKG!n zx?x2W+dUx$+kXU^wtdh$-m18q@3*1(?XB_=8CU^~`Z?RKxt((KY(uPX7Ir9t`miSV z&gRx4iR4GO`0I{MzzVHiRZwGk2LI=@y7ynVzOPL$9jQ28;PIXtZiVPdBokaJF^C zrvT~fH#8W_5kobbnYv@n#(~?R%L7RbA!&Rvr*o*^N!S7dCh-{ z!b&M?jenPHV-A+U>7G{ANFbFFVEBu_mR(T&PcxRj{!A0Qh8qsm3XPpxi$5d99M3@U z_;b`GDBTMJE8bK<)D4U5JJ0?{J+&1DGU~abNdkX?c@*Q+x@Mobzv5V=f#HWEs}j|I zkWGbbqghD}eplNn zwet6@W&M3o<<->`NhD3NnvxN9Ir8(U4tvWwL7L2BU?6Fr?7sG#m)0w*mpK^5PQ~&9 z5>+yEJ*8(2N)Y*U_UbCxg)hNh{$vnv!S*%ohi_=F427eGiMQj2m=4YI!LyupDTfN` zx<38ql)N1`uj~^m`dtXHYoKBqi`kz0>CL}MzYH8-d<;jNbm#-$JfF0$wn4bTE=MSR zYr9u85A#+>$TqLhYii;ZY;ViROd{61v3{{C+j`i0#X+32ss%Zgde?EPy0H(h65MXy zDeW5Ui~jFbM^knyl@kmPe{8b#F~8TfFJHXv3Y!FtjBcBFJO9w+`(-GEErk0^FTE+; z;VkC^r^6RI&m+SRfX(;BvkJw1bvC~01TecsL};yS1_I%yfQeS7gPq)?7^<%H%J&6e zjHCdJk!ZuVnIH{!672MH>7}NPS2)Z2w>%ZDT%TufDQNmzU!^j#r8eG{tRuexLT#K~ zSk$>;uZ3A8J^7a4%k>^U>Vz7)-;m*UP!Z3^-Mwu(J8cCl_8^*S``j}?rT--OX z{@mIsWcg3f9Z`L%7L~XJSW`v}a_6mwtFCHJ@xnzIq1ZWI$0^Y1Q`FHL0zp19u z_FgO-gNQLL{x2#tld2G~(fgtwei2ZPoQ*A17r51W-FXd1ykd{_bh|_gixA!2?tSC| zR?`*p({r~aw)LRvVapreqT(X8rY2J~c1*3uBv|E*hm+7-W)-B`iwP@Q4J`cRlFSg> zH-q>U{6*FXh3%6dyL;2;77ys>Re{T2$S=6JomJ@B&DS6`A0s$rlr|~Hi24alD;rBN zQ3bf8IXQX($Pb8JVZ{EZ7x|7epsx%+kWp3uwUT8zstcL2`}zI8^6q}U!<^-K@W3X&uE{BtdE+*b{3@>#1Sc_cc33Rh#hgr?8>!P zKguvxx(#>Zwh8=2|FS}4R^|2yKsw?V3#@>(P-MS`rPUDo+~wP{1T7j>%(gjIN-&&w zj*Mz-GTIGT6;WHu6%-@bhYL83U+bQg?dqtL5iw;uA>!u~OC3jH#KA6?7~zTKa8vUy zmvx1wp~?jwE$p4N!X!rfSVigGb7u(yq~}XB-``%E@zP4hmnB@BuFH-QotmrLD2N~p zeB7xTc6kHjzZ&w`ka1*)sM4Adl)nC#&FQ<{U2paGJ}%-81dHY!VRaRXKaFGo2h@4v*UY{B$u{~k z4a4JBp~coz+Fp(wvF?7fp=|Si0A7R?!D{5}t-w}uTC;;k;9;yhR^>TuvpQiRCv@MYdDC}z^Ro1Nnbmi&&&tEJCr_S* z53q=>D@`N9$THTgen^f~aAR9_&-CnRT71+6`m>{FW8rBQ7}|lSoQ_Za(g(O7J}n_i z0vRT`HGf@^NmaI&L&MJJ=?|-?xtOx&ci6>~-pVny)7G^XB2eY*f*&(|VNnZiWdy?j zv|dNM^5rj=+}>%;kXoT8`+k$IrNM}UenW(CEd@*%r%NX#zsdH4Wv_QZBBQ+lWuK8v zMf%^q922WQ2CP~@AvoD*O|ZPiyn{%*YkxDiiWSM`gLi}E`))XXN9Qk(P4dgBnBCnC zmIt0hx`|8VC~y3Mw_*0*G7IV5yY5LS4%2tcZQhVZZRBndKNb_qW`(UGl`1LO`3Evb z${j+ySL*vw>K|1y(^pJkGd)>O;zSj~+Zai!P&*!oLVyP`r7f)?frU;P{`hPD0UYP4 z>iWgg3Hd{EH%GCS4cUH$>a3Yt$7QC#K(v#1{%Gwa-92n7iX|Xt$>Qz7GW+u$cxhh{ zX{yKRz!T(^^SsXPd?;u6(Xe*^=$diaUMqeJC@tvZjGbnN-{6{w_JB_F0sZD%H!8Sc z|3>`B3)TUp4x3A87e?YFxn--Ljsgu$G8HpT6PPPXsc{9xon3fyB=18zF;6W(O~WMTxHc2#dBTRq_y+Cu$41*klye& z2!5cC#!tn}CH4tfGrfXS;zV$csY>81U(6Wx9I25 zE6$TOBaHWw9wIOgG2aJPz#&S$D|#hZ>Dw!s{;{6!EIC$?u;IMpc`!1GUwge`m&JcO z^5x$1NVhN6eavUrlGEgGeh2&Uh4I7*?&v;F1P-borJ&o+XvG^D?%9{39?|enugqDK zli+iEY06s{B|zD+s8<0^&ijbDB7APX z)T7ICEFYz-$E>6=>9t!KUCSi{-OME}9O?@r&! znJW`BdU8`y z(&xV#3)!?N{1XJ_AbxjeE~TjrVL1~R*VhNh>a6(ZZPbip{84I4Q&5E#ZJt@Do7X+< zjKpSuB^DghWN$Z!6SMId8F5Mdcq5kdxp4Qodcls=^2YeU#g6J_RCOtxgp7nWuBOO!-aTw7Fk|ZS1$kZZ>(b8O*HC z;#cDr9x)JadKAY@w2LD7^+kf$jPIC@5kWa6@a%JJS8L3GB z{@O2oHhrz4^4l5}mGfDbWV2ZOPCWR;W#PFhe?%E0O4i{YPmv9iRGC&-1?ya>i0uK9 zp;Mhv3wr7b;gy4W9F#Oe!@FDirK5T`%a>Wd12(z0(W}^kI5CcT7^$tF8(-y!R;5|x z@RYl`Kdh}`q?n&3Ng@_T*^(2;F*0J}xP;N;IU=peq+_qytEy0rtX5G0ak=Z@Mc9ZG z^l+cd*VGXzUCjC7sEgmgCUIgrk^TZUNydbXe}I0Ne~xX97#6(Jed);bp~Zz{b=nlx za=Bi$?8MlqTMCA~ajntGuB3%px_Fmq-8!1A?{rHPWV6=j=cn)UUl`lan;9h^(A}Kv zm&nnyPF(GuSj4`iD8(X4asC6wb_ZBoseGzw0PiikALdPRIH#4_rpkP4^DX;i7u=KH zrbnt`eS`q>tp+3^Knt}X3}RYU{9l4r#c+2=b;Yzmq|bmZ4|>i5PXi@xY5tESoI97! ztd$VOp{VyY@;dow1-_3SVvL+S)Cz8|jNvaEWg8%;4gC+W)^er3;Y(NF+~(U?e0;y( z6N?RP1)&@Eq4cB$(W+OwD)Pbi^EB(YcIeFB%FlqW%0^~c}Z&mJG2e-XnMdj!0 zXAz2daJLo7NI^EXbiSqn;o17%@A-&*G*CPYWKcqw9L8F)UM+l>>9-(|&+&1oYk6-h zX2RcbpTxz~n6Qon$xeh2A`9+K2RfY7ai+1fX!WbbhyvVL6*`XVQrW>`Cr_>$%4oiXbRufY4hQIPVGou1@lb#s(ccBzF=S8apL@3QJ@p4y`TOvofXJ z|Ed*K>@W=-Xw#!vsh8{e{9QW)1I5Yju3xUV0AGq)v?>u-7+K zXK#eEBE`&P4=0_ug<)e%Y~PqnK#9N0y+Vc;2>JJfMKe)l)9J5QuezIi9$X|ynEejq zU5h8_w5;Wo^cnw%x5n)tc@-U&NeOoHnlMn!UiThveI0iiulT*IZtR3KA#O)s=f>l^X5l=p7cw2|YSZYIOCqH)8cgid6CpjR!xiI4=#EI{scu`O?oX8- z@&A~jn{2jwBMYxd%Y#q|5rwZgUiCRf4J#{M*N0uL%`+3DN-&}@bHXFOEHub z)edXLyFpi!l|(npJN79rP^zW5&-_>w1L{xUxZdJ6bd5=SZAb_4bFZ)zLGDf1*nHA` zyXfj8vbWAM1>4$T!J=_ZzGp|#O4If%1r z_IiG}r)fvY0KB2=7fLHN!>Lo`7AtEpZSET5GvK49@BnLwQD~;57%QnOg{;i;KCnGq z5TV<_nz5&QB|S*{)(54x;t}}&6dFd)YTD`)T;ts>(%1LooqRma(4tupn1^J7w1&Wf zfV+HSEQj~ufICm}dT<^w7XQIk|IsOkz(qkU`TQ$(n{20J@om1N*@a>dQoul6l^L|Y zN{YJW>$rR2O|M0I%tnsxnyPa*w)3&xed1s#+xi;jR(|%#7J&;yKrE48)A?#S#VSjW z64X;!#=Xr1u9b4V&RT2BpU=Scc)JO#%S;jHJoz`o4R4=& ztzKO6%#qJyJ%H!bjBoQxpa^`4Cdys9=Wn-W$K7MtOx$LtnwdvOP;W5_HNB^aEmoS; zTReTMbx}d;6}l8vV?F-Z9ZcZlhZB84#lI^f?Jv{5yF#RVn02VDG>0JE@52hql$AJD z|6wFQE=!$Fi+&Y@q)U>xt?tB-8OduO-B+~!sFVHx5pNa7^i?Y$^|diJrZ-br62%XT zs)1;*j?5z+MaJBx<_oQ@;rtfU)5K|=$kApGv%1bZAs0#wu*&U-J`doF_qE}2=fxbY z7|qo!C`)H5LwF^wm@zQuQ{;%Ij31}|<($BhB#G{I%G`-+g0Me^qI&mg^bey>s2aTB6j&t;wuMvH#&a@85$Gdk~% zb>kb@Bg7k1|8V?XKSx|P{n}OpmkQeuVrwLz&Yl2wQH-jX%pCt+<)0Hk%-jW)^v;fT zcIj#0GNXC{M^W{PN{nI`SKHO8>pQUkq%kL5!;iDB+eUP-Z#V81{Re+?SkxY>fgt^f za=9qZ%H%E@pwdbWuG2 zpKu{IYlQ0M(n~;yJ35lx&_A7Yl`!%|4t)32#na?71Fu_h+Y6X$h=;9s|ouw6BZ5A=oyS$ueY>l=3E)FHQG`i%1@a?Y>%|XcUo( zyT1Qy5#^p!VCKqA``TROq%xCJi)841p&$De%NA3|^;w5)jr!PB$-QCiMBnhBnbwIb zW&RgXT7^;Dg+-m>3Nd0vD*hi*`1;BQW=lifjvnYc+~dROMK1G$rG(yJ$AitztWiZOtua4v3UvV`l#JynLA1e@{VY?T_5a~P*0+^0<7)+0Bdx_^-40>6&i6ugfGCtsWg{+npk9{#-?CEs;(g0) z-%HauixF@Nkbfxi#h=g0CAp+DocvpEGbINaD!}qJ_fp7kvL6Z8vSWFTn zp&q6Ns@8&B1*@j7U-dNhr`koLRd!wP|4DwHvajh>ZQ4>q4Ri`?2DDU~q|wr`_!E%5XJjml8Zhp^njh;`y`#g_M_f$r!GPr+)t`K&9qQd`~W@l>3C|sX?yys zU}z1AJ(0in=#E@;kFX^wwJa1<(G)IS)1_Elpt$Dp<-b?$xqe$msv^QC`ojf6Vm-I} z5n5VaqSFOo69d=~#+S>xzfIonU+*EQg0fPOV4R~o3Q{b^p%X11%EG?rQZ$!sfxcOe z1|ae~7NXw>g-q*+cigv$t7vvjQwqCx7%5``GGzyohi`;VF8_tn9JlN;3H;4vUOm}| zu4bu>+NbnO6Wu#v+rN5kjNGvxpi9w}_Af?Mw*GAgg72}D9$V1J`SSAg0$Gcu%f^1Z zawUpVHm#`}k`b;b*{U?S04P{cxCatCMoiT<3%LszueFJK%Rr;%8_wl!>nNG{bEyVU z2bVP2m}>yH3l+lowPG1={=TPrg=WlL}?e6zbX);`~XzA-AZtx@2 zoOQCeIv%re_4`YN-~Ywm-}l1JaLlFpnEr7cZwob*M6qq~zj#*jE&a}SCioE}jL0+t zmWrjGiW;BRzUdQuwHo^l!mG1yK@sn`u2u4g;jx!07Wq*-y+5J)(zsV5{rO6^Zfb*K z5Gwt7T>a%g2NZ+!{ZVCC{KpKMQZTvrS(kd&B8}4JI%8H@lu$yLt)TN(fJR}n1lyhbTbQ=`EoGL1(roN&FX$; zP#79i)Q~T>5W3(JpS*P~7 z#e6%>8$Zq_^BTeDpa{?(Au99)`^NJw&zY_*hN@@IgAPGejt)VhIfg}+x2Zv^VJ|ttF06( z#qP7^tv&UA50ZWG;<=AFWD;Py$Kzss@^1zw@8a;on^~=4%}3Mt%ro90oc`IddN#of z-Cs&(2clj!MI_q$3~S_?HhN7&ku&DqAEs2NNTcQ3o$QtoW7tvVQ7Go==m}}T3ve2s z&+$zz=Ej^G@ZnGyFw2%2Dpno(FyWaUv5(`E?+y#9lAAEy%Kceh>!0$_-oxQEu_(!Y z*1_2(ZS~MUww8+T>~&SPzMCId;?}JXL!_iXREgSZD%JIHfjUU#>v41 z=5zl57amMF8@jkGT{ z0IGY#^6Km5wg*^6s<1V@H-;#Uo5iL|7cW<6-Nw6dI}h}^qe?w_z|S0emUTQ3=L1Wc zJJUKc1ck28F{84n2?c3Azcl}8Br?KM+v1<*ucyaSav+Uf;)0D+x32B^q>3zJ%B?nO zza+11*b+Yk+F`SqDs)8{F-S|-w;Vs4#JS!E?5u-P(uEx~$uRH(89P!0%H=6(J+Hb? z3~{7>j&`b;>eH;|zeekAu;PdF3%{a{u828EcnLsQc=K%^gVGz^X>)g$P5G&%QK zL4Zar?2nd>mH+oiv!&qNNcXGbJwQX-;9`zf6@ct17xl!-K<6AQMC;wS%B}tpl&wdw|tg=6UWSMYQ>P^Q}ZbmhD#G@;UyUx%oHG5yBM& z4RS#cbbx!Y`LBFK>Kvu6@2_A3tu>MA{&rO6NMsp9@SwpX(rx-mFM-Ov?Q z9_+?XBuFvgoWInM=w>kE*tfgw0q=!{*@gsGL#^P7TL2pGj_ld@*j_)O0KxFQau_X= zH4Z*#k(xAC4QjmK_P^n&+8cU+yOp03s@!|`+@D8YZfXf-*q2bh))Du*U#+=nTY+MF zeg!-$hRwJ-ZqbmdqNpe8QR^zlj_4a!G{oNzhjk42_>(k6aUsPAr^?B;?}oJ-kaoL< zL1PGNU202OPdW8^|MBIkM6&!zYzu#v5qYU^Fq|V%toOl_< z_N}E+jVEk(xWtb-p`$;mLlGNAaPHcHypkb(^ZTr*ImV>Y5(wkVdO2zq=2na~L!@VkN5LEibZu`NyV`#wJzA5y9iRYu z`dq=1Per0mu1bz2(TGen&@TH?t5|Ua>UWu&mJ`NtiW9AEWbZ_(|9~)jXHG*c6w|P2 zBoZ%!Z+Ipj$<S`b?tW>(`D_sE{+fBCPE`v`5WOq2tmKpTzBGQzl7s5rKJtK>xPeY`YFbW&bnr z$Ktd9Sm})FpBhCh)WhkN1|YFYewS?M&Z|JV-iFB%F>Smae>Jy-iBRYHKc6=Q5G@$+c1MT}RVj z4%&=wp?2!$KyT=NSvt*uyoT{Lee(oE8uaXpL`-<}ZqRC;ys<=Zq;vB4;))c_TSbh9rJ(P45dZF3Un(!EPL`CgzzE5 z8It(o>R>g9Tc^m{KxQapL**LZ7&}oE$Euq9znX-sQzkSA>pO{Bm|DyFS~0(YLHdW|g+&VgG;UY3 z6WyinWAziF2=u>;Lz3uF?=CR02K00^XEVkpIzrm^T6qSiPiGk7^eXX`sJNUaiS@Ia zD7ILT5?y!_h(e=aE>m%;zDwYNiqwLDOLEo9&`37BYZt93l6CcQz|Phd)7`7JbUnLn zJEF<(?db0SxUN?%F&hAv(D323Z1~4X$X6qDML{anUx7;{g30?&9OA?WbMrPhjwlJ2 zF+(AHS%?wHBz)E(lTvYN3AgQhSmllnD`U|FhB0i9Ke&&zj6^DWXD>a>EveCRg{b* zAw4X50n#f&Y&HBH(Nk~!f`ZtG@5(T@y zRM}s;s`|e%4nM%HQnfgyiva20BZ{tQ05~|(8by$w4v?qCQaF&Bx3FWNkSQxihH`!Z z+V2O7AAt8dw0hQPTlNnbK6*8CLm!7CXRQfb!rnQny#5Ou zm--OVWnWrNu&ohOw2J{#S-w87V0^#&<5!u0>m}!NUNjUvw{PPqt4*7`IK8XFP0KI; z4vCA-fQ2Cc3QiUezr5ofM$3=Vj2$td#wO9>FXQ(6jq{Ksj{8z#U`Fr2OCQJ^iQV5} z)t58XaB=4NX89VKum!W$_v_L++@Tp;ZAIOdF_bKlN2Lmr#SWTl}f>~E~Ek#@lrNdu!!oLO16Btp|`W3m# z)FSBMVFWq0Bjh9of#I93o0I!2>ALO@58|5z^Hpzkc@3+UjV;Ru@U;FsWs);UDDNcW zn;i22$%0oaaGNc6b;NWHRTd`By|K`F7|9$wW8kH)EZLxikyIgRgAX5&oZS-;fu1hW z1G+c1R~R<*R)oWTBmxi2v!k#HK^m>`nN10u)oE=eC&gW~gkU33#%|Ns9c8^J0eGSH z`=>V|pkrM6=tS4Wmuayub%>lcExqp(sv(@V6VWHU93I1F&?4rZ@64i((G1et(irgF zVqoK($aHdNK`qTE;>IFy#mq2&b~>(;Dj@i$eH_N3Y0(I3#aM%CODIeBwNCG;%&7Hh zf+)qK)hbhFpI(T362<>4Z6j<9%uD53ga;#g9v zIE)sBYp7P#n+fnb+18b&1qT(>Z4IaGS(7VZSl{W+x`xFSWSE)HCzZnv~DS(zosu0C!?!h}S$8@ix2GaNw>6 zmt}xlc>cG=Okfhymm;&8irF*UPZ zp+UGO&&^3qUEgz#fthTU7Dnl~P%v+Vk(K!w9}XPIEDH1?Y|msR>#gfPo*(M?`8Yx; z{0!D@+*cUPLeSzUmLsiUBZJ_x1*q5v9p^dV{_3lVs_ckDZ~w9ePhvbA>WW3_aD)LaC5_i?yN*`l zbD>`(DJXmZ9*EHet@Sqi=U^gN(kUGS71)q?QJd30d6U^xOArkhX8V(J>&iZRV(_t0 zH_47K4Vi;D$CSeLp$q<;Tg|O+hqXPjg37Zc{TDvv-x0w1W zz*Ju{2KRmION_E5P|Jg4mq|O3|@B!Wj!6kG|alpG_Dax}BRNwtqVV~eXxwr+Jt#oX^cjC|1Ty{lHb=d|LZ z<*0a^`tIuQIK^y(&W5GgcZQ(did&7uZ1tIqtm&;J*hIa)K9)Ep9TpR;3CutK=e{pY z7%-2HF~J)e|J@xi`Lz_7eog~nwC2^faI139UbyHHvH|(jpmQLu&%EG;Rv6RAvn#7+ z4amK+;JlR3sx_zc%J>Z-Sjd39(XAIaT+zTjw40aoMO`B|y^ofgcjd7~C-cP_eD0~w z=4RkI#fT|ydt~W{+4#pw(gsh9N@jT(;5~)XUTyX5%E69H9E&d$q|BZYq*d<-|Errk zmU}TB+iuE?#R1|lb8QtO8*DT6Y$cM@5_}r`&MRXQKCnMoo$mFK%>b_`6?SnBHI1CYfca&1Zu`=@Z)X6M%W zOBDiC;PBxhvq(#L1vd|3#8b-=tHljFuKcJ1qQgfVMTBYCfbrP3F-OnFoQMhz0WWV= eO!n^Ex7WmEAJen;Iru*nhoeqMaEE^V Date: Thu, 31 Dec 2020 22:00:56 +0100 Subject: [PATCH 51/91] [README] Added all_emeralds picture, updated Physics, UI, Content, added Controls section stub --- README.md | 45 +++++++++++++------- doc/all_emeralds.png | Bin 0 -> 1101 bytes {screenshots => doc}/picosonic_showcase.png | Bin 3 files changed, 30 insertions(+), 15 deletions(-) create mode 100644 doc/all_emeralds.png rename {screenshots => doc}/picosonic_showcase.png (100%) diff --git a/README.md b/README.md index 9bb0197f..01670309 100644 --- a/README.md +++ b/README.md @@ -6,34 +6,41 @@ develop [![Build Status](https://travis-ci.org/hsandt/sonic-pico8.svg?branch=develop)](https://travis-ci.org/hsandt/sonic-pico8) [![codecov](https://codecov.io/gh/hsandt/sonic-pico8/branch/develop/graph/badge.svg)](https://codecov.io/gh/hsandt/sonic-pico8) -# PICO-Sonic +# pico-sonic -A partial clone of classic Sonic the Hedgehog games made with PICO-8. It is inspired by the 16-bit games for mechanics, and by a mix of the 8-bit and 16-bit games for graphics and audio. +*1-stage demake of Sonic 3 made with PICO-8* -This is a fan game distributed for free and is not endorsed by Sega Games Co., Ltd, which owns the Sonic the Hedgehog trademark and retains all copyrights on the original assets. +*The 8 Pico Emeralds have been scattered on Pico Island! Sonic fe* -It is currently under development. +![The 8 Pico Emeralds displayed in circle, each color corresponding to a color on the PICO-8 logo](doc/all_emeralds.png?raw=true) -## Compatibility +*pico-sonic* is a partial demake of Sonic the Hedgehog 3's Angel Island act 1 made with PICO-8. Various classic Sonic games were used as reference, including the 8-bit games (Game Gear and Master System) which have sprites closer to what PICO-8's resolution and color palette. -Works with PICO-8 0.2.0i ~ 0.2.1b. +The project was started as a personal challenge and was meant to be a fully-fledged fan game, but I eventually dropped many features to focus on Sonic's main movements and the exploration of the stage. + +*pico-sonic* is a fan game distributed for free and is not endorsed by Sega. Sega Games Co., Ltd owns the Sonic the Hedgehog trademark and copyrights on the original assets. ## Screenshots -![Sonic running toward emerald](screenshots/picosonic_showcase.png?raw=true) +![Sonic running toward emerald](doc/picosonic_showcase.png?raw=true) + +## Compatibility + +Works with PICO-8 0.2.0i ~ 0.2.1b. ## Features -Version: 5.1 +Version: 5.1+ ### Physics -* Character runs on flat ground, slopes, and through loops. Accelerates, decelerates and brakes. -* Character falls from steep slopes and ceiling if running speed is too low. -* Character is blocked by walls when running, walls and ceiling when jumping -* Character jumps with variable height orthogonally to current ground +* Character can run on flat ground, slopes, and through loops with acceleration, deceleration and braking +* Character can roll with reduced friction +* Character is blocked by walls and ceiling +* Character falls from steep slopes and ceiling if running speed is too low +* Character can jump with variable height orthogonally to current ground * Character preserves momentum on jumping and landing -* Character can roll +* One-way platforms * Spring bounce ### Rendering @@ -51,6 +58,8 @@ Version: 5.1 * Zone splash screen on stage start * Ingame HUD: list of picked emeralds shown in top-left corner * Result screen on stage clear +* Retry screen to restart stage keeping or dropping emeralds picked so far +* Fade-in/out effects using gradual color palette swapping ### Audio @@ -60,7 +69,7 @@ Version: 5.1 ### Content -One demo stage, a reproduction of the environment of the first part of Angel Island Act 1 (before it is set on fire) almost at scale 1:1, but using tiles of 8x8. It uses a custom map streaming system to allow a bigger map than PICO-8 standard tilemap. There are no enemies, hazards, rings nor item boxes, but some items have been replaced with emeralds that can be collected to make the stage more interesting. +One demo stage, a reproduction of the environment of the first part of Angel Island Act 1 (before it is set on fire) at a scale close to 1:1 (Sonic is slightly smaller relatively to the environment), but half the resolution horizontally and vertically. It uses a custom map "streaming" system to allow a bigger map than PICO-8 standard tilemap. There are no enemies, hazards, rings nor item boxes. Instead, some enemies have been replaced by static obstacles, and most importantly some items have been replaced by emeralds that can be collected to make the stage more interesting. Gimmicks: @@ -68,6 +77,10 @@ Gimmicks: * Loop * Launch ramp +## Controls + +TODO + ## Known technical issues * When jumping diagonally, Sonic can go past the ceiling in a vertical motion @@ -211,7 +224,9 @@ The `npm` folder has its own MIT license because I adapted a script from the `lu ### Assets -Most assets are derivative works of Sonic the Hedgehog, especially the Master System and Mega Drive games. They have been created, either manually or with a conversion tool, for demonstration purpose. +Sega Games Co., Ltd owns the Sonic the Hedgehog trademark and copyrights on the original assets. + +Most assets are derivative works of classic Sonic the Hedgehog games. They have been made with a combination or automated conversion and manual work, depending on the asset's complexity. for demonstration purpose. #### Sprites diff --git a/doc/all_emeralds.png b/doc/all_emeralds.png new file mode 100644 index 0000000000000000000000000000000000000000..74f3207e5286e26dd008b008c949a68d5a6abc1b GIT binary patch literal 1101 zcmeAS@N?(olHy`uVBq!ia0vp^bAWgT2OE$S)NF_bQjEnx?oJHr&dI!FU|?bQba4!+ znDh4T-fo#v8MY6xg4ze&3?4*JR$lRT;-O==l)Q>OeT27sVBNX@z#(qS8pr<()t*Z$ zot1AybUghkbm&$G$2K8t-E-G|>YXt<_r|!|dcObmn@`J&H}l`G{c+bX&5W7h!IGK% zwG6YL{Wx#mSFQWuh;?uK=bN_Af7r`O{$`jV)~M|Dprfkf4YNe_`*Yi}{@&bq>{syn z5;d7XCif`^1WkYPmFJf4|N8TF_x{~$w||cK%`G-}+HEUj`5im%-VnFGaZ|b^e%(6z zQkc}%Z*>=c=TE&qFUw|5{Mowp^<}$u>4$&6d+qMcs`=-S`UBl@_w>cjnTG^b3tTv5 z0-NjW`2YRgbm`%$jn;RMm2X>BvLoy6+;c4;2MSp%V*9^mU**G`?B_cw{;jxNwf)_^ z+}eMa<}P0QFZ$`n-pK0q>W8oMxbN-1^Xtnl>y4Zs#{6uFe$9r!>z;nG@4tVaEHBOfe12?ZZGG%k zSBPVDYwz*Zdv)tw z-RsZS^Jzj?#^>)*An+QLA`fr9JRo#oT4 zV((7Z{+(xa`(%(ckh%JI-fFApRG9v~zppO4f4j3TYx(P2H?^SpV18m?u!{QqmA&@A z|IMF=uh07b;mpM)>u0{GwzL2BWB0Mg{Py+MRd%(Hr$a20`t!N^#cu|k(j8f+t?yoV zzPSt~-hjTkaQRR7_uKIWU%vw*sN&au`7>e_ukU`3ns5L2&rZMBe?Pt6YuA1m9xJ@} z9;V6P$$j^@rmF77@8WA;x8Io_UwipS?fsg^Umee<$JXD4Muf7~{r0TotA7`j-p@^% zyLR<0Pz2sZiNIr^2<*Lc^H<&fH%H}xX>s3v^ZtK!K(V`Ue|OwkasIm@PwiQUw*{fQ zF6T9oXSR3d*JkElsNK2i z-<6r*#0< Date: Fri, 1 Jan 2021 19:02:10 +0100 Subject: [PATCH 52/91] [EXPORT] Added missing regions 30 and 31 causing reload to fail and scanning of last region (21) multiple times spawning 9 emeralds and crashing on release export only Also added more logs --- export_cartridge_release.p8 | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/export_cartridge_release.p8 b/export_cartridge_release.p8 index df957853..77a9bc94 100644 --- a/export_cartridge_release.p8 +++ b/export_cartridge_release.p8 @@ -11,9 +11,10 @@ __lua__ -- PICO-8 cannot read data/version.txt, so exceptionally set the version manually here local version = "5.1+" +local export_folder = "picosonic/v"..version.."_release" local cartridge_basename = "picosonic_v"..version.."_release" -cd("picosonic/v"..version.."_release") +cd(export_folder) local entry_cartridge = "picosonic_titlemenu.p8" @@ -22,8 +23,8 @@ local additional_cartridges_list = { "picosonic_ingame.p8", "picosonic_stage_clear.p8", "data_bgm1.p8", - "data_stage1_00.p8", "data_stage1_10.p8", "data_stage1_20.p8", - "data_stage1_01.p8", "data_stage1_11.p8", "data_stage1_21.p8", + "data_stage1_00.p8", "data_stage1_10.p8", "data_stage1_20.p8", "data_stage1_30.p8", + "data_stage1_01.p8", "data_stage1_11.p8", "data_stage1_21.p8", "data_stage1_31.p8", "data_stage1_runtime.p8" } @@ -48,6 +49,7 @@ load(entry_cartridge) -- save as png cartridge cd(cartridge_basename..".png") save(entry_cartridge..".png") +printh("Exported PNG cartridges in carts/"..export_folder.."/"..cartridge_basename..".png") cd("..") -- concatenate cartridge names with space separator with a very simplified version @@ -64,8 +66,10 @@ end -- on pink (color 14) background => -c 14 -- and most importantly we pass additional logic and data files as additional cartridges export(cartridge_basename..".bin "..additional_cartridges_string.." -i 160 -s 2 -c 14") +printh("Exported binaries in carts/"..export_folder.."/"..cartridge_basename..".bin") mkdir(cartridge_basename..".web") cd(cartridge_basename..".web") export(cartridge_basename..".html "..additional_cartridges_string.." -i 160 -s 2 -c 14") +printh("Exported HTML in carts/"..export_folder.."/"..cartridge_basename..".html") cd("..") From 54e654bbe1008dd5dfceb2527d206debf7750f1e Mon Sep 17 00:00:00 2001 From: huulong Date: Sat, 2 Jan 2021 19:16:02 +0100 Subject: [PATCH 53/91] [README] Added controls, more features info and other things to be in sync with itch.io page --- README.md | 69 ++++++++++++++++++++++++++++++++++++------------------- 1 file changed, 45 insertions(+), 24 deletions(-) diff --git a/README.md b/README.md index 01670309..a52047bf 100644 --- a/README.md +++ b/README.md @@ -8,15 +8,15 @@ develop # pico-sonic -*1-stage demake of Sonic 3 made with PICO-8* +[itch.io page](https://komehara.itch.io/pico-sonic) -*The 8 Pico Emeralds have been scattered on Pico Island! Sonic fe* +*The 8 Pico Emeralds have been scattered! Sonic arrives on Pico Island, ready to collect them all!* ![The 8 Pico Emeralds displayed in circle, each color corresponding to a color on the PICO-8 logo](doc/all_emeralds.png?raw=true) *pico-sonic* is a partial demake of Sonic the Hedgehog 3's Angel Island act 1 made with PICO-8. Various classic Sonic games were used as reference, including the 8-bit games (Game Gear and Master System) which have sprites closer to what PICO-8's resolution and color palette. -The project was started as a personal challenge and was meant to be a fully-fledged fan game, but I eventually dropped many features to focus on Sonic's main movements and the exploration of the stage. +The project was started as a personal challenge and was meant to be a fully-fledged fan game, but I eventually dropped many features to focus on Sonic's main movements and the exploration of the stage. This is now closer to a technical demo with some tweaks in the stage to make it more interesting to explore. *pico-sonic* is a fan game distributed for free and is not endorsed by Sega. Sega Games Co., Ltd owns the Sonic the Hedgehog trademark and copyrights on the original assets. @@ -35,7 +35,7 @@ Version: 5.1+ ### Physics * Character can run on flat ground, slopes, and through loops with acceleration, deceleration and braking -* Character can roll with reduced friction +* Character can roll from run * Character is blocked by walls and ceiling * Character falls from steep slopes and ceiling if running speed is too low * Character can jump with variable height orthogonally to current ground @@ -45,7 +45,7 @@ Version: 5.1+ ### Rendering -* Character sprites: *idle*, *walk* cycle, *run* cycle, *spin* cycle, *brake* animation +* Character sprites: *idle*, *walk* cycle, *run* cycle, *spin* cycle, *brake* animation, spring *jump* * Foreground plane: grass and leaves, loop entrance * Midground plane: general collision tiles, loop exit, some decorations * Background planes: sky, ocean, trees and forest holes moving with parallax @@ -64,14 +64,24 @@ Version: 5.1+ ### Audio * BGM: Sonic 3 Angel Island BGM demake -* Jingles: Sonic 3 intro, stage clear -* SFX: brake, roll, jump, spring jump +* Jingles: Sonic 3 intro, pick emerald, stage clear +* SFX: brake, roll, jump, spring jump, rotating goal plate, menu confirm -### Content +### Notable features missing -One demo stage, a reproduction of the environment of the first part of Angel Island Act 1 (before it is set on fire) at a scale close to 1:1 (Sonic is slightly smaller relatively to the environment), but half the resolution horizontally and vertically. It uses a custom map "streaming" system to allow a bigger map than PICO-8 standard tilemap. There are no enemies, hazards, rings nor item boxes. Instead, some enemies have been replaced by static obstacles, and most importantly some items have been replaced by emeralds that can be collected to make the stage more interesting. +* Crouching and looking up +* Spin dash +* When you collect all emeralds... -Gimmicks: +## Content + +There is a single demo stage which covers the first part of Angel Island Act 1. Scale is close to 1:1, but Sonic is slightly smaller (relatively to the environment) than in the original game. + +The game uses a custom map "streaming" system to allow a bigger map than PICO-8's standard tilemap. There are no enemies, hazards, rings nor item boxes. Rocks are not destructible. + +Some enemies have been replaced by static obstacles, and most importantly some items have been replaced by emeralds that can be collected to make the stage more interesting. + +Stage gimmicks: * Spring * Loop @@ -79,31 +89,40 @@ Gimmicks: ## Controls -TODO +You can play with keyboard or gamepad with those inputs: + +| Keyboard | Gamepad | Action | +|-------------------|------------------------|-------------------| +| Left/right arrows | D-pad left/right | Move | +| Down arrow | D-pad down | Roll (during run) | +| Z/C/N | Face button up/down | Jump | +| X/V/M | Face button left/right | Cancel (menu) | +| Enter | Start | Open pause menu | + +If you gamepad mapping is not correct, you can customize it with [SDL2 Gamepad Tool](https://www.generalarcade.com/gamepadtool) and copy-paste the configuration line into sdl_controllers.txt in PICO-8's [configuration directory](https://pico-8.fandom.com/wiki/Configuration). For instance, the Logicool Gamepad F310 had Open PICO-8 menu mapped to Right Trigger, so I remapped it to Start instead. ## Known technical issues -* When jumping diagonally, Sonic can go past the ceiling in a vertical motion -* Sonic can walk up low slopes with too much ease, and some slopes behave differently than in Sonic 3 -* After running up a vertical wall and falling, Sonic may fall *into* the wall and get stuck -* The game slows down in some areas, and tends to run at 30 FPS (instead of 60) in others +* The player cannot control the character until the stage intro is over +* The game slows down in some areas, reaching 30 FPS instead of 60 FPS +* The game pauses to switch to another cartridge, esp. at the end of the stage intro (only memory reload has been patched on PICO-8 to be instant) ## Known design issues -* The stage feels very empty and too big due to the lack of items and hazards, despite a reproduction 1:1 of the original map -* Scaling is slightly inconsistent as the tilemap is 1:1, but the Sonic sprites are slightly smaller than they should be, and the rocks even smaller +* The stage feels a bit empty and too big due to the lack of items, enemies and hazards +* Scaling is slightly inconsistent as the tilemap is 1:1, but the Sonic sprites are slightly smaller than they should be (rocks in particular look very big) ## Releases -You can directly download a released version of the game on the [releases](Releases) page. If you download the binary export for your platform, you're good to go. +You can directly download a released version of the game on the [releases](Releases) page, or on the [itch.io page](https://komehara.itch.io/pico-sonic). If you download the binary export for your platform, you're good to go. However, if you download the cartridges or compressed cartridges (png) archive to run them directly in PICO-8, there are a few caveats: 1. This game uses multiple cartridges, therefore you need to unzip the archive in your local PICO-8 *carts* folder so it can properly detect and load neighbor cartridges on game state transition (if you only want to play the core game and without title menu, you can just run picosonic_ingame.p8 anywhere, but note that it will freeze when the stage has been finished) -2. The ingame cartridge (in .p8 or .p8.png form) cannot be run with a vanilla PICO-8 as it exceeds the maximum token limit (8192). To play it, you need to patch your PICO-8 executable by following the procedure I described in [this thread](https://www.lexaloffle.com/bbs/?pid=71689#p). +2. The ingame cartridge (in .p8 or .p8.png form) cannot be run with a vanilla PICO-8 as it exceeds the maximum token limit (8192). To play it, you need to patch your PICO-8 executable to support more tokens, by either following the procedure I described in [this thread](https://www.lexaloffle.com/bbs/?pid=71689#p) or applying the patches provided in [pico-boots/scripts/patches](pico-boots/scripts/patches) (currently only provided for Linux editor as 'linux_amd64', and Linux and OSX runtime binaries, but if you are playing with Splore then you need an editor patch). You will need xdelta to apply the patches. -3. I recommend using a fast reload patch (I will write a post to explain how to do it later) to instantly stream stage data. Otherwise, the game will pause half a second every time the character is approaching a different 128x32-tiles region of the map, and also in the transition area between two regions. +3. I also recommend using a fast reload patch to instantly stream stage data. Otherwise, the game will pause half a second every time the character is approaching a different 128x32-tiles region of the map, and also in the transition area between two regions. Similarly to 2., you should apply the patch from the patches folder using xdelta. ## Build @@ -226,14 +245,16 @@ The `npm` folder has its own MIT license because I adapted a script from the `lu Sega Games Co., Ltd owns the Sonic the Hedgehog trademark and copyrights on the original assets. -Most assets are derivative works of classic Sonic the Hedgehog games. They have been made with a combination or automated conversion and manual work, depending on the asset's complexity. for demonstration purpose. +Most assets are derivative works of classic Sonic the Hedgehog games. They have been made with a combination or automated conversion and manual work (depending on the asset's complexity). + +Because of this, I only consider original assets and the manual work of adaptation to be under CC BY NC 4.0. #### Sprites -I drew the sprites based on the Mega Drive and GBA games. Original sprites are under CC BY 4.0. +I drew most sprites based on the Master System/Game Gear, Mega Drive and GBA games. #### Audio -For the BGMs, I used 8-bit remixes of Sonic 3 & Knuckles by danooct1 with the author's permission, converted FamiTracker Music (FTM) files to MIDI, then to PICO-8 format using [midi2pico](https://github.com/gamax92/midi2pico), an automated music format converter. Finally, I merged the sound channels and reworked some notes to make them sound better in PICO-8. +For the BGMs, I used 8-bit remixes of Sonic 3 & Knuckles by danooct1 with the author's permission. I had to go from 8 channels to only 3 or 4 (PICO-8 has 4 channels but during in-game I need to keep one channel for SFX) by picking the notes I considered the most important. Then I exported the modified FamiTracker Music (FTM) files to MIDI, and converted each MIDI channel to PICO-8 format using [midi2pico](https://github.com/gamax92/midi2pico). Finally, I merged the channels manually and reworked some notes to make them sound better in PICO-8. -For the SFX, I listened to the original ones and tried to reproduce them with PICO-8's sound editor. +For the SFX, I listened to the original ones and tried to reproduce them manually with PICO-8's sound editor. From 4fa9d092ba8dc0f7d541dfe30792f7faf2499570 Mon Sep 17 00:00:00 2001 From: huulong Date: Sat, 2 Jan 2021 19:17:16 +0100 Subject: [PATCH 54/91] [EXPORT] Replaced export script with export_and_patch_cartridge_release.sh which exports and patches pico8 runtime binary for Linux, using new patch_pico8_runtime.sh + patch files in pico-boots --- export_and_patch_cartridge_release.sh | 21 +++++++++++++++++++++ export_cartridge_release.sh | 2 -- install_single_cartridge.sh | 1 + pico-boots | 2 +- sonic-2d-tech-demo.sublime-project | 5 ++--- 5 files changed, 25 insertions(+), 6 deletions(-) create mode 100755 export_and_patch_cartridge_release.sh delete mode 100755 export_cartridge_release.sh diff --git a/export_and_patch_cartridge_release.sh b/export_and_patch_cartridge_release.sh new file mode 100755 index 00000000..bc192bc8 --- /dev/null +++ b/export_and_patch_cartridge_release.sh @@ -0,0 +1,21 @@ +#!/bin/bash + +# Configuration: paths +picoboots_scripts_path="$(dirname "$0")/pico-boots/scripts" +data_path="$(dirname "$0")/data" +# Linux only +carts_dirpath="$HOME/.lexaloffle/pico-8/carts" + +# Configuration: cartridge +version=`cat "$data_path/version.txt"` +export_folder="picosonic/v${version}_release" +cartridge_basename="picosonic_v${version}_release" +bin_folder="$carts_dirpath/$export_folder/${cartridge_basename}.bin" + +# Export via PICO-8 editor +pico8 -x export_cartridge_release.p8 + +# Patch the runtime binaries with 4x_token and fast_reload +patch_cmd="\"$picoboots_scripts_path/patch_pico8_runtime.sh\" \"$bin_folder/linux/$cartridge_basename\"" +echo "> $patch_cmd" +bash -c "$patch_cmd" diff --git a/export_cartridge_release.sh b/export_cartridge_release.sh deleted file mode 100755 index b55f260c..00000000 --- a/export_cartridge_release.sh +++ /dev/null @@ -1,2 +0,0 @@ -#!/bin/bash -pico8 -x export_cartridge_release.p8 diff --git a/install_single_cartridge.sh b/install_single_cartridge.sh index 3f7cbd5d..5dd02981 100755 --- a/install_single_cartridge.sh +++ b/install_single_cartridge.sh @@ -45,6 +45,7 @@ fi output_path="build/v${version}_${config}" cartridge_filepath="${output_path}/${cartridge_stem}_${cartridge_suffix}.p8${suffix}" +# Linux only carts_dirpath="$HOME/.lexaloffle/pico-8/carts" install_dirpath="${carts_dirpath}/picosonic/v${version}_${config}" diff --git a/pico-boots b/pico-boots index e574b0a3..191615ec 160000 --- a/pico-boots +++ b/pico-boots @@ -1 +1 @@ -Subproject commit e574b0a3e6aa2509e0b8fa4de34c7e24c4ae8730 +Subproject commit 191615ec05f4f4e72d71279b3433d74f0a369e6a diff --git a/sonic-2d-tech-demo.sublime-project b/sonic-2d-tech-demo.sublime-project index b11ae66a..da281262 100644 --- a/sonic-2d-tech-demo.sublime-project +++ b/sonic-2d-tech-demo.sublime-project @@ -759,11 +759,10 @@ ] }, { - "name": "Game: export cartridge release", + "name": "Game: export and patch cartridge release", "working_dir": "${project_path}", "shell": true, - "cmd": ["./export_cartridge_release.sh"], - "syntax": "Packages/Python/Python.sublime-syntax" + "cmd": ["./export_and_patch_cartridge_release.sh"], }, { "name": "Game: test", From d7776c1c0a181ce9e2bfbefef7c813396965bf69 Mon Sep 17 00:00:00 2001 From: huulong Date: Sat, 2 Jan 2021 19:34:45 +0100 Subject: [PATCH 55/91] [EXPORT] Patch runtime binary with new option --inplace --- export_and_patch_cartridge_release.sh | 4 ++-- pico-boots | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/export_and_patch_cartridge_release.sh b/export_and_patch_cartridge_release.sh index bc192bc8..cd71877e 100755 --- a/export_and_patch_cartridge_release.sh +++ b/export_and_patch_cartridge_release.sh @@ -15,7 +15,7 @@ bin_folder="$carts_dirpath/$export_folder/${cartridge_basename}.bin" # Export via PICO-8 editor pico8 -x export_cartridge_release.p8 -# Patch the runtime binaries with 4x_token and fast_reload -patch_cmd="\"$picoboots_scripts_path/patch_pico8_runtime.sh\" \"$bin_folder/linux/$cartridge_basename\"" +# Patch the runtime binaries in-place with 4x_token and fast_reload +patch_cmd="\"$picoboots_scripts_path/patch_pico8_runtime.sh\" --inplace \"$bin_folder/linux/$cartridge_basename\"" echo "> $patch_cmd" bash -c "$patch_cmd" diff --git a/pico-boots b/pico-boots index 191615ec..5f6b7f93 160000 --- a/pico-boots +++ b/pico-boots @@ -1 +1 @@ -Subproject commit 191615ec05f4f4e72d71279b3433d74f0a369e6a +Subproject commit 5f6b7f935c4f6462b85e11e33be8c4735cd62bfa From f11b90fe0fe0e1654d21fb026d9c6f8e1ce68a1b Mon Sep 17 00:00:00 2001 From: huulong Date: Sat, 2 Jan 2021 20:31:06 +0100 Subject: [PATCH 56/91] [EXPORT] Adapted script to picob-boots patch script now patching for all OSes (doesn't work yet as not all OSes have patches in repository) Changed naming convention: 'game' instead of 'cartridge' when dealing with final export since it's technically a multi-cartridge game --- export_and_patch_cartridge_release.sh | 7 ++++-- ...ridge_release.p8 => export_game_release.p8 | 22 +++++++++---------- install_all_cartridges.sh | 2 +- install_single_cartridge.sh | 2 +- install_single_cartridge_with_data.sh | 2 +- pico-boots | 2 +- 6 files changed, 20 insertions(+), 17 deletions(-) rename export_cartridge_release.p8 => export_game_release.p8 (78%) diff --git a/export_and_patch_cartridge_release.sh b/export_and_patch_cartridge_release.sh index cd71877e..8d151004 100755 --- a/export_and_patch_cartridge_release.sh +++ b/export_and_patch_cartridge_release.sh @@ -12,10 +12,13 @@ export_folder="picosonic/v${version}_release" cartridge_basename="picosonic_v${version}_release" bin_folder="$carts_dirpath/$export_folder/${cartridge_basename}.bin" +# Cleanup bin folder as a bug in PICO-8 makes it accumulate files in .zip for each export (even homonymous files!) +rm -rf "$bin_folder" + # Export via PICO-8 editor -pico8 -x export_cartridge_release.p8 +pico8 -x export_game_release.p8 # Patch the runtime binaries in-place with 4x_token and fast_reload -patch_cmd="\"$picoboots_scripts_path/patch_pico8_runtime.sh\" --inplace \"$bin_folder/linux/$cartridge_basename\"" +patch_cmd="\"$picoboots_scripts_path/patch_pico8_runtime.sh\" --inplace \"$bin_folder\" \"$cartridge_basename\"" echo "> $patch_cmd" bash -c "$patch_cmd" diff --git a/export_cartridge_release.p8 b/export_game_release.p8 similarity index 78% rename from export_cartridge_release.p8 rename to export_game_release.p8 index 77a9bc94..cc69471d 100644 --- a/export_cartridge_release.p8 +++ b/export_game_release.p8 @@ -12,7 +12,7 @@ __lua__ -- PICO-8 cannot read data/version.txt, so exceptionally set the version manually here local version = "5.1+" local export_folder = "picosonic/v"..version.."_release" -local cartridge_basename = "picosonic_v"..version.."_release" +local game_basename = "picosonic_v"..version.."_release" cd(export_folder) @@ -29,7 +29,7 @@ local additional_cartridges_list = { } -- prepare folder for png cartridges -mkdir(cartridge_basename..".png") +mkdir(game_basename..".png") -- load each additional cartridge to save it as png cartridge -- in folder created above @@ -37,7 +37,7 @@ mkdir(cartridge_basename..".png") for cartridge_name in all(additional_cartridges_list) do load(cartridge_name) - cd(cartridge_basename..".png") + cd(game_basename..".png") save(cartridge_name..".png") cd("..") end @@ -47,9 +47,9 @@ end load(entry_cartridge) -- save as png cartridge -cd(cartridge_basename..".png") +cd(game_basename..".png") save(entry_cartridge..".png") -printh("Exported PNG cartridges in carts/"..export_folder.."/"..cartridge_basename..".png") +printh("Exported PNG cartridges in carts/"..export_folder.."/"..game_basename..".png") cd("..") -- concatenate cartridge names with space separator with a very simplified version @@ -65,11 +65,11 @@ end -- with top-left at sprite 160 (run1) => -i 160 -- on pink (color 14) background => -c 14 -- and most importantly we pass additional logic and data files as additional cartridges -export(cartridge_basename..".bin "..additional_cartridges_string.." -i 160 -s 2 -c 14") -printh("Exported binaries in carts/"..export_folder.."/"..cartridge_basename..".bin") +export(game_basename..".bin "..additional_cartridges_string.." -i 160 -s 2 -c 14") +printh("Exported binaries in carts/"..export_folder.."/"..game_basename..".bin") -mkdir(cartridge_basename..".web") -cd(cartridge_basename..".web") -export(cartridge_basename..".html "..additional_cartridges_string.." -i 160 -s 2 -c 14") -printh("Exported HTML in carts/"..export_folder.."/"..cartridge_basename..".html") +mkdir(game_basename..".web") +cd(game_basename..".web") +export(game_basename..".html "..additional_cartridges_string.." -i 160 -s 2 -c 14") +printh("Exported HTML in carts/"..export_folder.."/"..game_basename..".html") cd("..") diff --git a/install_all_cartridges.sh b/install_all_cartridges.sh index f51a9bcd..d4086de3 100755 --- a/install_all_cartridges.sh +++ b/install_all_cartridges.sh @@ -12,7 +12,7 @@ # png option is legacy for p8tool. It works in theory but in practice, # since p8tool fails to build .p8.png properly, png will be directly -# saved from PICO-8 with export_cartridge_release.p8 into PICO-8 carts folder +# saved from PICO-8 with export_game_release.p8 into PICO-8 carts folder # Configuration: paths game_scripts_path="$(dirname "$0")" diff --git a/install_single_cartridge.sh b/install_single_cartridge.sh index 5dd02981..e488be0f 100755 --- a/install_single_cartridge.sh +++ b/install_single_cartridge.sh @@ -16,7 +16,7 @@ # png option is legacy for p8tool. It works in theory but in practice, # since p8tool fails to build .p8.png properly, png will be directly -# saved from PICO-8 with export_cartridge_release.p8 into PICO-8 carts folder +# saved from PICO-8 with export_game_release.p8 into PICO-8 carts folder # Configuration: paths data_path="$(dirname "$0")/data" diff --git a/install_single_cartridge_with_data.sh b/install_single_cartridge_with_data.sh index 454415d6..7e716d22 100755 --- a/install_single_cartridge_with_data.sh +++ b/install_single_cartridge_with_data.sh @@ -13,7 +13,7 @@ # png option is legacy for p8tool. It works in theory but in practice, # since p8tool fails to build .p8.png properly, png will be directly -# saved from PICO-8 with export_cartridge_release.p8 into PICO-8 carts folder +# saved from PICO-8 with export_game_release.p8 into PICO-8 carts folder # Configuration: paths game_scripts_path="$(dirname "$0")" diff --git a/pico-boots b/pico-boots index 5f6b7f93..dd3c78c8 160000 --- a/pico-boots +++ b/pico-boots @@ -1 +1 @@ -Subproject commit 5f6b7f935c4f6462b85e11e33be8c4735cd62bfa +Subproject commit dd3c78c89e0342663665acba1c80083f097c0c53 From a2b7c461c7340e665cafc26e2bd80fd67f59ec68 Mon Sep 17 00:00:00 2001 From: huulong Date: Sat, 2 Jan 2021 20:57:21 +0100 Subject: [PATCH 57/91] [PATCH] Update pico-boots to switch to xdelta3 --- README.md | 4 ++-- pico-boots | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index a52047bf..2490a17f 100644 --- a/README.md +++ b/README.md @@ -120,9 +120,9 @@ However, if you download the cartridges or compressed cartridges (png) archive t 1. This game uses multiple cartridges, therefore you need to unzip the archive in your local PICO-8 *carts* folder so it can properly detect and load neighbor cartridges on game state transition (if you only want to play the core game and without title menu, you can just run picosonic_ingame.p8 anywhere, but note that it will freeze when the stage has been finished) -2. The ingame cartridge (in .p8 or .p8.png form) cannot be run with a vanilla PICO-8 as it exceeds the maximum token limit (8192). To play it, you need to patch your PICO-8 executable to support more tokens, by either following the procedure I described in [this thread](https://www.lexaloffle.com/bbs/?pid=71689#p) or applying the patches provided in [pico-boots/scripts/patches](pico-boots/scripts/patches) (currently only provided for Linux editor as 'linux_amd64', and Linux and OSX runtime binaries, but if you are playing with Splore then you need an editor patch). You will need xdelta to apply the patches. +2. The ingame cartridge (in .p8 or .p8.png form) cannot be run with a vanilla PICO-8 as it exceeds the maximum token limit (8192). To play it, you need to patch your PICO-8 executable to support more tokens, by either following the procedure I described in [this thread](https://www.lexaloffle.com/bbs/?pid=71689#p) or applying the patches provided in [pico-boots/scripts/patches](pico-boots/scripts/patches) (currently only provided for Linux editor as 'linux_amd64', and Linux and OSX runtime binaries, but if you are playing with Splore then you need an editor patch). You will need xdelta3 to apply the patches. -3. I also recommend using a fast reload patch to instantly stream stage data. Otherwise, the game will pause half a second every time the character is approaching a different 128x32-tiles region of the map, and also in the transition area between two regions. Similarly to 2., you should apply the patch from the patches folder using xdelta. +3. I also recommend using a fast reload patch to instantly stream stage data. Otherwise, the game will pause half a second every time the character is approaching a different 128x32-tiles region of the map, and also in the transition area between two regions. Similarly to 2., you should apply the patch from the patches folder using xdelta3. ## Build diff --git a/pico-boots b/pico-boots index dd3c78c8..dfa5b7d2 160000 --- a/pico-boots +++ b/pico-boots @@ -1 +1 @@ -Subproject commit dd3c78c89e0342663665acba1c80083f097c0c53 +Subproject commit dfa5b7d2a01c3e697530260dce5670ded8dbe364 From e757aa5cb4775a03cd9231374373aa10c40c9650 Mon Sep 17 00:00:00 2001 From: huulong Date: Wed, 6 Jan 2021 20:33:20 +0100 Subject: [PATCH 58/91] [RETRY] Fixed picked emeralds appearing for a second before missed emeralds fade in --- src/stage_clear/stage_clear_state.lua | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/stage_clear/stage_clear_state.lua b/src/stage_clear/stage_clear_state.lua index 6d526cd6..51f2cef7 100644 --- a/src/stage_clear/stage_clear_state.lua +++ b/src/stage_clear/stage_clear_state.lua @@ -367,11 +367,11 @@ function stage_clear_state:zigzag_fade_out_async() -- and the fadeout_rect fully covers the screen, ready to be used as background ui_animation.move_drawables_on_coord_async("x", {fadeout_rect, zigzag_drawable}, {-128, 0}, - visual.fadeout_zigzag_width, 128, stage_clear_data.zigzag_fadeout_duration) - -- at the end of the zigzag, clear the emerald assessment widgets which are now completely hidden - -- no need to clear result_show_emerald_set_by_number as we'll set result_show_emerald_set_by_number - -- again in show_retry_screen_async to show missing emeralds + -- at the end of the zigzag fade-out, clear the emerald assessment widgets which are now completely hidden + -- also hide the emeralds until we show them again (but it will be the missed ones) -- no need to preserve fadeout_rect either because in phase 2, we cls() on render start anyway self.result_overlay:clear_drawables() + clear_table(self.result_show_emerald_set_by_number) end function stage_clear_state:show_retry_screen_async() From 7c74b2ae5244f32b6acd35779842c704273d0f4a Mon Sep 17 00:00:00 2001 From: huulong Date: Wed, 6 Jan 2021 21:05:42 +0100 Subject: [PATCH 59/91] [STAGE] Fixed all(nil) assert in headless itests stage_intro with render due to palm_tree_leaves_core_global_locations being uninitialized, by moving initialization to base_stage_state --- src/ingame/base_stage_state.lua | 7 +++++++ src/ingame/base_stage_state_utest.lua | 2 ++ src/ingame/stage_state.lua | 4 ---- src/ingame/stage_state_utest.lua | 2 -- 4 files changed, 9 insertions(+), 6 deletions(-) diff --git a/src/ingame/base_stage_state.lua b/src/ingame/base_stage_state.lua index e3de5621..e7e6096f 100644 --- a/src/ingame/base_stage_state.lua +++ b/src/ingame/base_stage_state.lua @@ -12,6 +12,10 @@ function base_stage_state:init() -- create camera, but wait for player character to spawn before assigning it a target -- see on_enter for how we warp it to a good place first self.camera = camera_class() + + -- palm trees: list of global locations of palm tree leaves core sprites detected + -- used to draw the palm tree extension sprites on foreground + self.palm_tree_leaves_core_global_locations = {} end @@ -156,6 +160,9 @@ function base_stage_state:render_environment_foreground() sprite_masks.midground) end + -- CARTRIDGE NOTE: currently objects are not scanned in stage_intro, but we could imagine + -- that intro will show some future object waterfall later, so we don't strip object-related code + -- from #stage_intro from now -- draw palm tree extension sprites on the foreground, so they can hide the character and items at the top for global_loc in all(self.palm_tree_leaves_core_global_locations) do -- top has pivot at its bottom-left = the top-left of the core diff --git a/src/ingame/base_stage_state_utest.lua b/src/ingame/base_stage_state_utest.lua index 3fb7e044..91713b3d 100644 --- a/src/ingame/base_stage_state_utest.lua +++ b/src/ingame/base_stage_state_utest.lua @@ -23,9 +23,11 @@ describe('base_base_stage_state', function () local state = base_stage_state() assert.are_same({ camera_class(), + {}, }, { state.camera, + state.palm_tree_leaves_core_global_locations, }) end) diff --git a/src/ingame/stage_state.lua b/src/ingame/stage_state.lua index 12fdc41f..963ac304 100644 --- a/src/ingame/stage_state.lua +++ b/src/ingame/stage_state.lua @@ -38,10 +38,6 @@ function stage_state:init() -- list of emerald pick fxs playing (currently no pooling, just add and delete) self.emerald_pick_fxs = {} - -- palm trees: list of global locations of palm tree leaves core sprites detected - -- used to draw the palm tree extension sprites on foreground - self.palm_tree_leaves_core_global_locations = {} - --#if itest -- set to false in itest setup to disable object spawning, which relies on very slow map scan self.enable_spawn_objects = true diff --git a/src/ingame/stage_state_utest.lua b/src/ingame/stage_state_utest.lua index 25fd5b64..c69e8d8b 100644 --- a/src/ingame/stage_state_utest.lua +++ b/src/ingame/stage_state_utest.lua @@ -78,7 +78,6 @@ describe('stage_state', function () {}, {}, {}, - {}, nil, -- itest only true, @@ -93,7 +92,6 @@ describe('stage_state', function () state.emeralds, state.picked_emerald_numbers_set, state.emerald_pick_fxs, - state.palm_tree_leaves_core_global_locations, state.loaded_map_region_coords, -- itest only state.enable_spawn_objects, From f5492d8f5c4589f3553700c972a7e38a2128cdb2 Mon Sep 17 00:00:00 2001 From: huulong Date: Wed, 6 Jan 2021 21:07:43 +0100 Subject: [PATCH 60/91] [README] Added Pause menu and note on ugly sprites/SFX. Improved text about Angel Island. --- README.md | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 2490a17f..ae83a393 100644 --- a/README.md +++ b/README.md @@ -14,11 +14,11 @@ develop ![The 8 Pico Emeralds displayed in circle, each color corresponding to a color on the PICO-8 logo](doc/all_emeralds.png?raw=true) -*pico-sonic* is a partial demake of Sonic the Hedgehog 3's Angel Island act 1 made with PICO-8. Various classic Sonic games were used as reference, including the 8-bit games (Game Gear and Master System) which have sprites closer to what PICO-8's resolution and color palette. +**pico-sonic** is a partial demake of Sonic the Hedgehog 3 made with PICO-8. It features a simplified version of Angel Island Act 1 with some tweaks. Various classic Sonic games were used as reference, including the 8-bit games (Game Gear and Master System) which have sprites closer to what PICO-8's resolution and color palette. -The project was started as a personal challenge and was meant to be a fully-fledged fan game, but I eventually dropped many features to focus on Sonic's main movements and the exploration of the stage. This is now closer to a technical demo with some tweaks in the stage to make it more interesting to explore. +The project was started as a personal challenge and was meant to be a fully-fledged fan game, but I eventually dropped many features to focus on Sonic's main movements and the exploration of the stage. Consider it a technical demo with some exploration challenge. -*pico-sonic* is a fan game distributed for free and is not endorsed by Sega. Sega Games Co., Ltd owns the Sonic the Hedgehog trademark and copyrights on the original assets. +pico-sonic is a fan game distributed for free and is not endorsed by Sega. Sega Games Co., Ltd owns the Sonic the Hedgehog trademark and copyrights on the original assets. ## Screenshots @@ -101,6 +101,14 @@ You can play with keyboard or gamepad with those inputs: If you gamepad mapping is not correct, you can customize it with [SDL2 Gamepad Tool](https://www.generalarcade.com/gamepadtool) and copy-paste the configuration line into sdl_controllers.txt in PICO-8's [configuration directory](https://pico-8.fandom.com/wiki/Configuration). For instance, the Logicool Gamepad F310 had Open PICO-8 menu mapped to Right Trigger, so I remapped it to Start instead. +### Pause menu + +In the pause menu (toggled with Enter/Start), if you are in-game, you can select the following options: + +* Warp to start: restart stage from beginning keeping collected emeralds +* Retry from zero: restart stage losing emeralds collected so far +* Back to title: go back to title menu + ## Known technical issues * The player cannot control the character until the stage intro is over @@ -111,6 +119,7 @@ If you gamepad mapping is not correct, you can customize it with [SDL2 Gamepad T * The stage feels a bit empty and too big due to the lack of items, enemies and hazards * Scaling is slightly inconsistent as the tilemap is 1:1, but the Sonic sprites are slightly smaller than they should be (rocks in particular look very big) +* Some ugly sprite / sprite transitions and SFX too far from the original sounds ## Releases From c346adaf7181c25f3af3521eb55cb66e33588291 Mon Sep 17 00:00:00 2001 From: huulong Date: Wed, 6 Jan 2021 21:10:37 +0100 Subject: [PATCH 61/91] [README] Added outline to emeralds to make them more visible on GitHub's light theme --- doc/all_emeralds.png | Bin 1101 -> 1276 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/doc/all_emeralds.png b/doc/all_emeralds.png index 74f3207e5286e26dd008b008c949a68d5a6abc1b..c3f17f967a5fd05041e64b868c41968b1de2d69e 100644 GIT binary patch literal 1276 zcmeAS@N?(olHy`uVBq!ia0vp^bAWgT2OE$S)NF_bQjEnx?oJHr&dI!FU|`wi>Eakt zG3V{wz1bp;GRHnfbA~nbE_e`~EU=YV%4vH_E|CAh~E>BYb*6% zzs_e`%OIi0!WRTYU$`&d{8nGH-u%|Z-{(#*+M2pu;#KiE9(GM85L!8L8UF&tLhI=3 zb9=4VcVC+p`&@dJ-*LEf;+ulL){U0$&x@bFpZK^}?puBU8?Sl6jE4%Y4?t+?i-Mi8 z@z4JJFS}XTmtR-1>e+{NWj8;*+t0pp?ss`ssaz}1muK+RmNTzC`rW3lt>%e~ z--lVvQZ7|bgKO(~{cz6tA2YwcUEn9as`1>id-IL!45e?pbSj@#CwuuI%;TD_|38*l z?bb`be%I(l&d!hJAojJpSLU8gf*U1Vd*b%X7dZ#_%w6~LTpGgR?R6$s>OY$`t~sx6 zEVG0Ign%gl26(McYz?aYo1&4A8N5XEce!Z`#alePO7ym%%UZ$&S$>(RCDRt*3XI6 z74a`KGA*$+uNMLAkf>gIT9Y-T^HAH zs@o=<)&1+A5i~v-A^tzm5Lom6WU<_%SFb;xH0MQ19$)iMiP^8;{l6e<_a@J^vQPA1 z{jB-*X71--Cs$XOO_7Ns z`v%78{@>~hm>;}rZofBwzO}WS$-U|8qf7ago26NJ2!hek7X|OE&OYY-UbR+SSg`D2 z&-?rJ|BLJ9FP-Z9yyo7{Qsr0YriGd9{oUUScfqe}shs7TUl*0#^}TF+b`ej zniE?dTyEA4SLC}U@uzj{;kj$yeN7j)mnn~X?-$A!f9vI}cR6D4=qmal7xZ^d`;zZG zH~7+5TE{kh%ua;I^p|R>stXy%Es9gm{r;@{cI$$Vb!YvrTh5W#^a|OC7X?{<$8X+# zY4JL2x&F@c73;I}m)|pf#Lf%1rEQo0pLS@Db4&jW&qN}B7g^^oU%a^$Zl_4Ca?G{w fJWu-I-l%8%(7^pBbY}fiPz-vy`njxgN@xNAWG-H% literal 1101 zcmeAS@N?(olHy`uVBq!ia0vp^bAWgT2OE$S)NF_bQjEnx?oJHr&dI!FU|?bQba4!+ znDh4T-fo#v8MY6xg4ze&3?4*JR$lRT;-O==l)Q>OeT27sVBNX@z#(qS8pr<()t*Z$ zot1AybUghkbm&$G$2K8t-E-G|>YXt<_r|!|dcObmn@`J&H}l`G{c+bX&5W7h!IGK% zwG6YL{Wx#mSFQWuh;?uK=bN_Af7r`O{$`jV)~M|Dprfkf4YNe_`*Yi}{@&bq>{syn z5;d7XCif`^1WkYPmFJf4|N8TF_x{~$w||cK%`G-}+HEUj`5im%-VnFGaZ|b^e%(6z zQkc}%Z*>=c=TE&qFUw|5{Mowp^<}$u>4$&6d+qMcs`=-S`UBl@_w>cjnTG^b3tTv5 z0-NjW`2YRgbm`%$jn;RMm2X>BvLoy6+;c4;2MSp%V*9^mU**G`?B_cw{;jxNwf)_^ z+}eMa<}P0QFZ$`n-pK0q>W8oMxbN-1^Xtnl>y4Zs#{6uFe$9r!>z;nG@4tVaEHBOfe12?ZZGG%k zSBPVDYwz*Zdv)tw z-RsZS^Jzj?#^>)*An+QLA`fr9JRo#oT4 zV((7Z{+(xa`(%(ckh%JI-fFApRG9v~zppO4f4j3TYx(P2H?^SpV18m?u!{QqmA&@A z|IMF=uh07b;mpM)>u0{GwzL2BWB0Mg{Py+MRd%(Hr$a20`t!N^#cu|k(j8f+t?yoV zzPSt~-hjTkaQRR7_uKIWU%vw*sN&au`7>e_ukU`3ns5L2&rZMBe?Pt6YuA1m9xJ@} z9;V6P$$j^@rmF77@8WA;x8Io_UwipS?fsg^Umee<$JXD4Muf7~{r0TotA7`j-p@^% zyLR<0Pz2sZiNIr^2<*Lc^H<&fH%H}xX>s3v^ZtK!K(V`Ue|OwkasIm@PwiQUw*{fQ zF6T9oXSR3d*JkElsNK2i z-<6r*#0< Date: Thu, 7 Jan 2021 01:25:16 +0100 Subject: [PATCH 62/91] [LOAD] Prefer passing cartridge basename to load() to avoid ".p8" load failing in PNG version --- src/application/picosonic_app_ingame.lua | 9 ++++++--- src/application/picosonic_app_ingame_utest.lua | 2 +- src/ingame/stage_state.lua | 3 ++- src/menu/titlemenu.lua | 3 ++- src/stage_clear/stage_clear_state.lua | 6 ++++-- src/stage_intro/stage_intro_state.lua | 5 +++-- 6 files changed, 18 insertions(+), 10 deletions(-) diff --git a/src/application/picosonic_app_ingame.lua b/src/application/picosonic_app_ingame.lua index 73d72264..589fada3 100644 --- a/src/application/picosonic_app_ingame.lua +++ b/src/application/picosonic_app_ingame.lua @@ -20,15 +20,18 @@ function picosonic_app_ingame:on_post_start() -- override (optional) menuitem(3, "warp to start", function() assert(flow.curr_state.type == ':stage') flow.curr_state:store_picked_emerald_data() - load('picosonic_ingame.p8') + -- prefer passing basename for compatibility with .p8.png + load('picosonic_ingame') end) menuitem(4, "retry from zero", function() - load('picosonic_ingame.p8') + -- prefer passing basename for compatibility with .p8.png + load('picosonic_ingame') end) menuitem(5, "back to title", function() - load('picosonic_titlemenu.p8') + -- prefer passing basename for compatibility with .p8.png + load('picosonic_titlemenu') end) end diff --git a/src/application/picosonic_app_ingame_utest.lua b/src/application/picosonic_app_ingame_utest.lua index ed82d078..1f6c32f4 100644 --- a/src/application/picosonic_app_ingame_utest.lua +++ b/src/application/picosonic_app_ingame_utest.lua @@ -43,7 +43,7 @@ describe('picosonic_app_ingame', function () assert.spy(picosonic_app_base.on_post_start).was_called_with(match.ref(app)) end) - it('should load cartridge: picosonic_titlemenu.p8', function () + it('should load cartridge: picosonic_titlemenu', function () app:on_post_start() assert.spy(menuitem).was_called(3) -- no reference to lambda passed to menuitem, so don't test was_called_with diff --git a/src/ingame/stage_state.lua b/src/ingame/stage_state.lua index 963ac304..7b8eacc0 100644 --- a/src/ingame/stage_state.lua +++ b/src/ingame/stage_state.lua @@ -782,7 +782,8 @@ function stage_state:on_reached_goal_async() self:store_picked_emerald_data() -- finally advance to stage clear sequence on new cartridge - load('picosonic_stage_clear.p8') + -- prefer passing basename for compatibility with .p8.png + load('picosonic_stage_clear') end function stage_state:restore_picked_emerald_data() diff --git a/src/menu/titlemenu.lua b/src/menu/titlemenu.lua index e5914359..62a0f73f 100644 --- a/src/menu/titlemenu.lua +++ b/src/menu/titlemenu.lua @@ -18,7 +18,8 @@ titlemenu.type = ':titlemenu' -- sequence of menu items to display, with their target states local menu_item_params = { {"start", function(app) - load('picosonic_stage_intro.p8') + -- prefer passing basename for compatibility with .p8.png + load('picosonic_stage_intro') end}, {"credits", function(app) flow:query_gamestate_type(':credits') diff --git a/src/stage_clear/stage_clear_state.lua b/src/stage_clear/stage_clear_state.lua index 51f2cef7..5d973a3a 100644 --- a/src/stage_clear/stage_clear_state.lua +++ b/src/stage_clear/stage_clear_state.lua @@ -38,7 +38,8 @@ local retry_menu_item_params = { function stage_clear_state.retry_stage_async() -- zigzag fadeout will also give time to player to hear confirm SFX flow.curr_state:zigzag_fade_out_async() - load('picosonic_ingame.p8') + -- prefer passing basename for compatibility with .p8.png + load('picosonic_ingame') end function stage_clear_state.retry_from_zero_async() @@ -53,7 +54,8 @@ function stage_clear_state.back_to_titlemenu_async() -- zigzag fadeout will also give time to player to hear confirm SFX flow.curr_state:zigzag_fade_out_async() - load('picosonic_titlemenu.p8') + -- prefer passing basename for compatibility with .p8.png + load('picosonic_titlemenu') end function stage_clear_state:init() diff --git a/src/stage_intro/stage_intro_state.lua b/src/stage_intro/stage_intro_state.lua index dfa92e78..63feb45f 100644 --- a/src/stage_intro/stage_intro_state.lua +++ b/src/stage_intro/stage_intro_state.lua @@ -162,10 +162,11 @@ function stage_intro_state:show_stage_splash_async() self.overlay:remove_drawable("zone") -- splash is over, load ingame cartridge and give control to player - load('picosonic_ingame.p8') + -- prefer passing basename for compatibility with .p8.png + load('picosonic_ingame') --[[#pico8 - assert(false, "could not load picosonic_ingame.p8") + assert(false, "could not load picosonic_ingame") --#pico8]] end From 9fb9f8df1a693034f7b733cc255fa51beb7ae1a8 Mon Sep 17 00:00:00 2001 From: huulong Date: Thu, 7 Jan 2021 01:25:44 +0100 Subject: [PATCH 63/91] [EXPORT] Fixed web export and apply WASM patch --- export_and_patch_cartridge_release.sh | 15 +++++++++++---- export_game_release.p8 | 5 +++-- pico-boots | 2 +- sonic-2d-tech-demo.sublime-project | 8 ++++++++ 4 files changed, 23 insertions(+), 7 deletions(-) diff --git a/export_and_patch_cartridge_release.sh b/export_and_patch_cartridge_release.sh index 8d151004..62aa63cb 100755 --- a/export_and_patch_cartridge_release.sh +++ b/export_and_patch_cartridge_release.sh @@ -11,6 +11,7 @@ version=`cat "$data_path/version.txt"` export_folder="picosonic/v${version}_release" cartridge_basename="picosonic_v${version}_release" bin_folder="$carts_dirpath/$export_folder/${cartridge_basename}.bin" +web_folder="$carts_dirpath/$export_folder/${cartridge_basename}.web" # Cleanup bin folder as a bug in PICO-8 makes it accumulate files in .zip for each export (even homonymous files!) rm -rf "$bin_folder" @@ -18,7 +19,13 @@ rm -rf "$bin_folder" # Export via PICO-8 editor pico8 -x export_game_release.p8 -# Patch the runtime binaries in-place with 4x_token and fast_reload -patch_cmd="\"$picoboots_scripts_path/patch_pico8_runtime.sh\" --inplace \"$bin_folder\" \"$cartridge_basename\"" -echo "> $patch_cmd" -bash -c "$patch_cmd" +# Patch the runtime binaries in-place with 4x_token, fast_reload, fast_load (experimental) if available +patch_bin_cmd="\"$picoboots_scripts_path/patch_pico8_runtime.sh\" --inplace \"$bin_folder\" \"$cartridge_basename\"" +echo "> $patch_bin_cmd" +bash -c "$patch_bin_cmd" + +# Patch the html export in-place with 4x_token, fast_reload +js_filepath="${web_folder}/${cartridge_basename}.js" +patch_js_cmd="python3.6 \"$picoboots_scripts_path/patch_pico8_js.py\" \"$js_filepath\" \"${js_filepath}\"" +echo "> $patch_js_cmd" +bash -c "$patch_js_cmd" diff --git a/export_game_release.p8 b/export_game_release.p8 index cc69471d..968fced6 100644 --- a/export_game_release.p8 +++ b/export_game_release.p8 @@ -69,7 +69,8 @@ export(game_basename..".bin "..additional_cartridges_string.." -i 160 -s 2 -c 14 printh("Exported binaries in carts/"..export_folder.."/"..game_basename..".bin") mkdir(game_basename..".web") -cd(game_basename..".web") -export(game_basename..".html "..additional_cartridges_string.." -i 160 -s 2 -c 14") +-- Do not cd into game_basename..".web" because we want the additional cartridges to be accessible +-- in current path. Instead, export directly into the .web folder +export(game_basename..".web/"..game_basename..".html "..additional_cartridges_string.." -i 160 -s 2 -c 14") printh("Exported HTML in carts/"..export_folder.."/"..game_basename..".html") cd("..") diff --git a/pico-boots b/pico-boots index dfa5b7d2..73ae463b 160000 --- a/pico-boots +++ b/pico-boots @@ -1 +1 @@ -Subproject commit dfa5b7d2a01c3e697530260dce5670ded8dbe364 +Subproject commit 73ae463b3fa71571516ab7525d149c65856b5a1c diff --git a/sonic-2d-tech-demo.sublime-project b/sonic-2d-tech-demo.sublime-project index da281262..99d6eb51 100644 --- a/sonic-2d-tech-demo.sublime-project +++ b/sonic-2d-tech-demo.sublime-project @@ -1148,6 +1148,14 @@ "file_regex": "\"(.*\\.py)\"(?:, line )([\\d]+)(?:\\:)?([\\d]+)?(.*)", "syntax": "Packages/Python/Python.sublime-syntax" }, + { + "name": "Engine: Postbuild: test patch_pico8_js", + "working_dir": "${project_path}/pico-boots", + "shell": true, + "cmd": ["python3 -m scripts.test_patch_pico8_js"], + "file_regex": "\"(.*\\.py)\"(?:, line )([\\d]+)(?:\\:)?([\\d]+)?(.*)", + "syntax": "Packages/Python/Python.sublime-syntax" + }, { "name": "Python (legacy): test replace_strings", "working_dir": "${project_path}", From 1ff46f8c601e7971f4fa1947583871fc071ceb0d Mon Sep 17 00:00:00 2001 From: huulong Date: Thu, 7 Jan 2021 23:05:22 +0100 Subject: [PATCH 64/91] [EXPORT] Added archiving step to export and patch script Renaming .png folder to _png_cartridges to match Travis _cartridges (.p8) release Project: added command to install all cartridges --- export_and_patch_cartridge_release.sh | 63 +++++++++++++++++++++++---- export_game_release.p8 | 10 ++--- sonic-2d-tech-demo.sublime-project | 30 +++++++++++++ 3 files changed, 90 insertions(+), 13 deletions(-) diff --git a/export_and_patch_cartridge_release.sh b/export_and_patch_cartridge_release.sh index 62aa63cb..750f7aee 100755 --- a/export_and_patch_cartridge_release.sh +++ b/export_and_patch_cartridge_release.sh @@ -1,31 +1,78 @@ #!/bin/bash +# Export and patch cartridge releases, then update existing archives with patched executables +# Make sure to first build full game in release + # Configuration: paths picoboots_scripts_path="$(dirname "$0")/pico-boots/scripts" +game_scripts_path="$(dirname "$0")" data_path="$(dirname "$0")/data" # Linux only carts_dirpath="$HOME/.lexaloffle/pico-8/carts" # Configuration: cartridge version=`cat "$data_path/version.txt"` -export_folder="picosonic/v${version}_release" +export_folder="$carts_dirpath/picosonic/v${version}_release" cartridge_basename="picosonic_v${version}_release" -bin_folder="$carts_dirpath/$export_folder/${cartridge_basename}.bin" -web_folder="$carts_dirpath/$export_folder/${cartridge_basename}.web" +rel_bin_folder="${cartridge_basename}.bin" +rel_web_folder="${cartridge_basename}.web" # Cleanup bin folder as a bug in PICO-8 makes it accumulate files in .zip for each export (even homonymous files!) -rm -rf "$bin_folder" +# and we want to remove any extraneous files too +rm -rf "${export_folder}/${rel_bin_folder}" -# Export via PICO-8 editor -pico8 -x export_game_release.p8 +# Export via PICO-8 editor: PNG cartridges, binaries, HTML +pico8 -x "$game_scripts_path/export_game_release.p8" # Patch the runtime binaries in-place with 4x_token, fast_reload, fast_load (experimental) if available -patch_bin_cmd="\"$picoboots_scripts_path/patch_pico8_runtime.sh\" --inplace \"$bin_folder\" \"$cartridge_basename\"" +patch_bin_cmd="\"$picoboots_scripts_path/patch_pico8_runtime.sh\" --inplace \"$rel_bin_folder\" \"$cartridge_basename\"" echo "> $patch_bin_cmd" bash -c "$patch_bin_cmd" # Patch the html export in-place with 4x_token, fast_reload -js_filepath="${web_folder}/${cartridge_basename}.js" +js_filepath="${export_folder}/${rel_web_folder}/${cartridge_basename}.js" patch_js_cmd="python3.6 \"$picoboots_scripts_path/patch_pico8_js.py\" \"$js_filepath\" \"${js_filepath}\"" echo "> $patch_js_cmd" bash -c "$patch_js_cmd" + +pushd "${export_folder}" + + # PNG cartridges archive + zip -r "${cartridge_basename}_png_cartridges.zip" "${cartridge_basename}_png_cartridges" + + # HTML archive + zip -r "${cartridge_basename}.web.zip" "${cartridge_basename}.web" + + # Bin archives + pushd "${rel_bin_folder}" + + # Linux archive + + # Rename linux folder with full game name so our archive contains a self-explanatory folder as the initial archive + mv "linux" "${cartridge_basename}_linux" + + # To minimize operations, do not recreate the archive, just replace the executable in the archive generated by PICO-8 export + # with the patched executable. We still get some warnings about "Local Version Needed To Extract does not match CD" + # on other files, so make the operation quiet (-q) + zip -q "${cartridge_basename}_linux.zip" "${cartridge_basename}_linux/${cartridge_basename}" + + + # OSX archive + + # Replace the executable in the archive generated by PICO-8 export with the patched executable + zip -q "${cartridge_basename}_osx.zip" "${cartridge_basename}.app/Contents/MacOS/${cartridge_basename}" + + + # Windows archive + + # Rename linux folder with full game name so our archive contains a self-explanatory folder as the initial archive + mv "windows" "${cartridge_basename}_windows" + + # To minimize operations, do not recreate the archive, just replace the executable in the archive generated by PICO-8 export + # with the patched executable. We still get some warnings about "Local Version Needed To Extract does not match CD" + # on other files, so make the operation quiet (-q) + zip -q "${cartridge_basename}_windows.zip" "${cartridge_basename}_windows/${cartridge_basename}.exe" + + popd + +popd diff --git a/export_game_release.p8 b/export_game_release.p8 index 968fced6..72d646ab 100644 --- a/export_game_release.p8 +++ b/export_game_release.p8 @@ -29,7 +29,7 @@ local additional_cartridges_list = { } -- prepare folder for png cartridges -mkdir(game_basename..".png") +mkdir(game_basename.."_png_cartridges") -- load each additional cartridge to save it as png cartridge -- in folder created above @@ -37,7 +37,7 @@ mkdir(game_basename..".png") for cartridge_name in all(additional_cartridges_list) do load(cartridge_name) - cd(game_basename..".png") + cd(game_basename.."_png_cartridges") save(cartridge_name..".png") cd("..") end @@ -46,10 +46,10 @@ end -- just after load(entry_cartridge) --- save as png cartridge -cd(game_basename..".png") +-- save as png cartridge as we only did it for other cartridges earlier +cd(game_basename.."_png_cartridges") save(entry_cartridge..".png") -printh("Exported PNG cartridges in carts/"..export_folder.."/"..game_basename..".png") +printh("Exported PNG cartridges in carts/"..export_folder.."/"..game_basename.."_png_cartridges") cd("..") -- concatenate cartridge names with space separator with a very simplified version diff --git a/sonic-2d-tech-demo.sublime-project b/sonic-2d-tech-demo.sublime-project index 99d6eb51..238a67ca 100644 --- a/sonic-2d-tech-demo.sublime-project +++ b/sonic-2d-tech-demo.sublime-project @@ -758,6 +758,36 @@ } ] }, + { + "name": "Game: install all cartridges (with data)", + "working_dir": "${project_path}", + "shell": true, + "cmd": ["./install_all_cartridges.sh debug"], + "syntax": "Packages/Python/Python.sublime-syntax", + "variants": + [ + { + "name": "debug", + "cmd": ["./install_all_cartridges.sh debug"] + }, + { + "name": "cheat", + "cmd": ["./install_all_cartridges.sh cheat"] + }, + { + "name": "release", + "cmd": ["./install_all_cartridges.sh release"] + }, + { + "name": "cheat (png)", + "cmd": ["./install_all_cartridges.sh cheat png"] + }, + { + "name": "release (png)", + "cmd": ["./install_all_cartridges.sh release png"] + } + ] + }, { "name": "Game: export and patch cartridge release", "working_dir": "${project_path}", From aa887464c5adc7360dfc13400de56260f41e4cdf Mon Sep 17 00:00:00 2001 From: huulong Date: Thu, 7 Jan 2021 23:05:44 +0100 Subject: [PATCH 65/91] [TRAVIS] Take build version number from version.txt --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index be4ddcd8..49adbe67 100644 --- a/.travis.yml +++ b/.travis.yml @@ -8,7 +8,7 @@ language: python # Can use any language here, but if it's not 'python' python: "3.6" env: - - LUA_VERSION=5.3 BUILD_VERSION=5.0 ENABLE_RENDER=1 + - LUA_VERSION=5.3 BUILD_VERSION=`cat data/version.txt` ENABLE_RENDER=1 cache: directories: From cf05da9c9684b34866c08530179542c506247bb5 Mon Sep 17 00:00:00 2001 From: huulong Date: Thu, 7 Jan 2021 23:41:55 +0100 Subject: [PATCH 66/91] [UPLOAD] Added upload script to butler push release files to itch.io (except .p8) Renamed .web -> _web for consistency --- export_and_patch_cartridge_release.sh | 4 +- export_game_release.p8 | 8 ++-- sonic-2d-tech-demo.sublime-project | 6 +++ upload_cartridge_release.sh | 59 +++++++++++++++++++++++++++ 4 files changed, 71 insertions(+), 6 deletions(-) create mode 100755 upload_cartridge_release.sh diff --git a/export_and_patch_cartridge_release.sh b/export_and_patch_cartridge_release.sh index 750f7aee..19847415 100755 --- a/export_and_patch_cartridge_release.sh +++ b/export_and_patch_cartridge_release.sh @@ -15,7 +15,7 @@ version=`cat "$data_path/version.txt"` export_folder="$carts_dirpath/picosonic/v${version}_release" cartridge_basename="picosonic_v${version}_release" rel_bin_folder="${cartridge_basename}.bin" -rel_web_folder="${cartridge_basename}.web" +rel_web_folder="${cartridge_basename}_web" # Cleanup bin folder as a bug in PICO-8 makes it accumulate files in .zip for each export (even homonymous files!) # and we want to remove any extraneous files too @@ -41,7 +41,7 @@ pushd "${export_folder}" zip -r "${cartridge_basename}_png_cartridges.zip" "${cartridge_basename}_png_cartridges" # HTML archive - zip -r "${cartridge_basename}.web.zip" "${cartridge_basename}.web" + zip -r "${cartridge_basename}_web.zip" "${cartridge_basename}_web" # Bin archives pushd "${rel_bin_folder}" diff --git a/export_game_release.p8 b/export_game_release.p8 index 72d646ab..cec483a3 100644 --- a/export_game_release.p8 +++ b/export_game_release.p8 @@ -68,9 +68,9 @@ end export(game_basename..".bin "..additional_cartridges_string.." -i 160 -s 2 -c 14") printh("Exported binaries in carts/"..export_folder.."/"..game_basename..".bin") -mkdir(game_basename..".web") --- Do not cd into game_basename..".web" because we want the additional cartridges to be accessible --- in current path. Instead, export directly into the .web folder -export(game_basename..".web/"..game_basename..".html "..additional_cartridges_string.." -i 160 -s 2 -c 14") +mkdir(game_basename.."_web") +-- Do not cd into game_basename.."_web" because we want the additional cartridges to be accessible +-- in current path. Instead, export directly into the _web folder +export(game_basename.."_web/"..game_basename..".html "..additional_cartridges_string.." -i 160 -s 2 -c 14") printh("Exported HTML in carts/"..export_folder.."/"..game_basename..".html") cd("..") diff --git a/sonic-2d-tech-demo.sublime-project b/sonic-2d-tech-demo.sublime-project index 238a67ca..15c944e6 100644 --- a/sonic-2d-tech-demo.sublime-project +++ b/sonic-2d-tech-demo.sublime-project @@ -794,6 +794,12 @@ "shell": true, "cmd": ["./export_and_patch_cartridge_release.sh"], }, + { + "name": "Game: upload cartridge release", + "working_dir": "${project_path}", + "shell": true, + "cmd": ["./upload_cartridge_release.sh"], + }, { "name": "Game: test", "working_dir": "${project_path}", diff --git a/upload_cartridge_release.sh b/upload_cartridge_release.sh new file mode 100755 index 00000000..5dae28f5 --- /dev/null +++ b/upload_cartridge_release.sh @@ -0,0 +1,59 @@ +#!/bin/bash + +# Update exported cartridge release to itch.io via butler +# Make sure to first build, export and patch game, and also to have pushed a tag for release. +# Travis generates picosonic_v${BUILD_VERSION}_release_cartridges.zip containing .p8 files +# for release, but we need pico8 to build _png_cartridges, .bin and _web, so we always +# export those from a local computer. + +# Dependencies: +# - butler + +# Configuration: paths +data_path="$(dirname "$0")/data" +# Linux only +carts_dirpath="$HOME/.lexaloffle/pico-8/carts" + +# Configuration: cartridge +version=`cat "$data_path/version.txt"` +export_folder="$carts_dirpath/picosonic/v${version}_release" +cartridge_basename="picosonic_v${version}_release" +rel_bin_folder="${cartridge_basename}.bin" + +help() { + echo "Push build with specific version for all platforms to itch.io with butler." + usage +} + +usage() { + echo "Usage: upload_cartridge_release.sh +" +} + +if [[ $# -ne 0 ]]; then + echo "Wrong number of arguments: found $#, expected 0." + echo "Passed arguments: $@" + usage + exit 1 +fi + +# Arg $1: platform/format ('linux', 'osx', 'windows', 'web', 'png') +# Arg $2: path to archive corresponding to platform/format +function butler_push_game_for_platform { + platform="$1" + filepath="$2" + + butler push --fix-permissions --userversion="$version" \ + "$filepath" "komehara/pico-sonic:$platform" +} + +pushd "${export_folder}" + + # Travis builds and releases .p8 cartridges packed in .zip, so focus on other platforms/formats + butler_push_game_for_platform linux "${rel_bin_folder}/${cartridge_basename}_linux.zip" + butler_push_game_for_platform osx "${rel_bin_folder}/${cartridge_basename}_osx.zip" + butler_push_game_for_platform windows "${rel_bin_folder}/${cartridge_basename}_windows.zip" + butler_push_game_for_platform web "${cartridge_basename}_web.zip" + butler_push_game_for_platform png "${cartridge_basename}_png_cartridges.zip" + +popd From 7c0bb66cb48c94b7c3c1e73deaa6c1b942ed47c8 Mon Sep 17 00:00:00 2001 From: huulong Date: Fri, 8 Jan 2021 00:08:32 +0100 Subject: [PATCH 67/91] [TRAVIS] Deployed release zip now contains wrapping folder for cleaner extraction --- .travis.yml | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index 49adbe67..f7a69d43 100644 --- a/.travis.yml +++ b/.travis.yml @@ -83,15 +83,18 @@ script: before_deploy: # zip multiple cartridges (including data) into one distributable archive - cp data/data_*.p8 "build/v${BUILD_VERSION}_release" - - pushd "build/v${BUILD_VERSION}_release" - - zip -r "picosonic_v${BUILD_VERSION}_release_cartridges.zip" . + - pushd build + # we are going to zip the whole folder, folder itself included, to make it easier + # for user to extract in a proper folder; so rename the folder to make it meaningful + - mv "v${BUILD_VERSION}_release" "picosonic_v${BUILD_VERSION}_release_cartridges" + - zip -r "picosonic_v${BUILD_VERSION}_release_cartridges.zip" "picosonic_v${BUILD_VERSION}_release_cartridges" - popd deploy: provider: releases api_key: secure: bfXQQ0AXGHgXiq0xOxhYQ2AXX/flQnxJh/+eA/HUGfwdoPDq0QTdqFA/3jEMWkJSsFKEBVKDjJGCt24QPxUIjTu91r1wyCNdL2KlNfnogRjWAVutRZxB/OC2HWR3kJtPjkFQBCsOXHBxGI3hMJL7LWr5WfNsSGMbcRMfvphxFT3ER8XBHAUEJY6roITm6noHroqQt8Uye+0+rkGqJ8QslKRqq8qBZMZeOiOrh6SBdlhsGw0KqNno/dMXQxx2ZCrh/VUeWjNvxzXe/mZjfBPbhvyecN7jz+FytEdAhdt1Dy37hhyOAkDfxLGGsH1YAAfinH8uFwoSRo0MH8fuhdXpT7jUXuAgP9/RS0FEiZDdX+J/FdncCbnoDfE9B4Dt3L3srISeiNwxKK5sx2kzyWvftK30pV1+zEgnbVEKGPIIeGb5wYWSCmzHf+CfLMk+bzeznTrpo/irY/vjoRBefNaVWXqLygrNWxM1uIMJae+OA3MYeUSYd1lpCyRw98i3GC7si68M9OaDeLoDjnqOLqvhurB/RmLzCU7mCYipn2kxykAOdevWN73cyx9VhdFy2GPE5VDw6EO6ZQP04KaeYxP2pgR4ts2kYWpVvf1PGg+2yN4QMkVhrWV+6dG2jtUO0BrCqt5Tpw0I3C3aFmBjjzFBBuKsZpr2yUG3roxnu1Dhww0= - file: build/v${BUILD_VERSION}_release/picosonic_v${BUILD_VERSION}_release_cartridges.zip + file: build/picosonic_v${BUILD_VERSION}_release_cartridges.zip on: tags: 'true' skip_cleanup: 'true' From e63b7f7e52cbc719883096448f5ce4e3667addb3 Mon Sep 17 00:00:00 2001 From: huulong Date: Fri, 8 Jan 2021 00:46:40 +0100 Subject: [PATCH 68/91] [EXPORT] Fixed path for bin patching, rename html -> index.html for itch.io play-in-browser Delete previous PNG and web zips before compressing to avoid zip merge --- export_and_patch_cartridge_release.sh | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/export_and_patch_cartridge_release.sh b/export_and_patch_cartridge_release.sh index 19847415..f5d2abfd 100755 --- a/export_and_patch_cartridge_release.sh +++ b/export_and_patch_cartridge_release.sh @@ -1,6 +1,9 @@ #!/bin/bash # Export and patch cartridge releases, then update existing archives with patched executables +# Also apply small tweaks to make release work completely: +# - rename HTML file to index.html to make it playable directly in browser (esp. on itch.io) +# - (TODO) add '.png' to every occurrence of '.p8' in copy of game source before exporting to PNG # Make sure to first build full game in release # Configuration: paths @@ -25,22 +28,29 @@ rm -rf "${export_folder}/${rel_bin_folder}" pico8 -x "$game_scripts_path/export_game_release.p8" # Patch the runtime binaries in-place with 4x_token, fast_reload, fast_load (experimental) if available -patch_bin_cmd="\"$picoboots_scripts_path/patch_pico8_runtime.sh\" --inplace \"$rel_bin_folder\" \"$cartridge_basename\"" +bin_folder="${export_folder}/${rel_bin_folder}" +patch_bin_cmd="\"$picoboots_scripts_path/patch_pico8_runtime.sh\" --inplace \"$bin_folder\" \"$cartridge_basename\"" echo "> $patch_bin_cmd" bash -c "$patch_bin_cmd" -# Patch the html export in-place with 4x_token, fast_reload +# Rename HTML file to index.html for direct play-in-browser +html_filepath="${export_folder}/${rel_web_folder}/${cartridge_basename}.html" +mv "$html_filepath" "${export_folder}/${rel_web_folder}/index.html" + +# Patch the HTML export in-place with 4x_token, fast_reload js_filepath="${export_folder}/${rel_web_folder}/${cartridge_basename}.js" -patch_js_cmd="python3.6 \"$picoboots_scripts_path/patch_pico8_js.py\" \"$js_filepath\" \"${js_filepath}\"" +patch_js_cmd="python3.6 \"$picoboots_scripts_path/patch_pico8_js.py\" \"$js_filepath\" \"$js_filepath\"" echo "> $patch_js_cmd" bash -c "$patch_js_cmd" pushd "${export_folder}" - # PNG cartridges archive + # PNG cartridges archive (delete existing one to be safe) + rm -f "${cartridge_basename}_png_cartridges.zip" zip -r "${cartridge_basename}_png_cartridges.zip" "${cartridge_basename}_png_cartridges" - # HTML archive + # HTML archive (delete existing one to be safe) + rm -f "${cartridge_basename}_web.zip" zip -r "${cartridge_basename}_web.zip" "${cartridge_basename}_web" # Bin archives From 003790f602a864acfa64accc2ca24c40ecb15f63 Mon Sep 17 00:00:00 2001 From: huulong Date: Fri, 8 Jan 2021 00:47:05 +0100 Subject: [PATCH 69/91] [PROJECT] Added command to build, export and upload at once This will avoid forgetting one step and uploading an old version --- sonic-2d-tech-demo.sublime-project | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/sonic-2d-tech-demo.sublime-project b/sonic-2d-tech-demo.sublime-project index 15c944e6..a8546a73 100644 --- a/sonic-2d-tech-demo.sublime-project +++ b/sonic-2d-tech-demo.sublime-project @@ -800,6 +800,12 @@ "shell": true, "cmd": ["./upload_cartridge_release.sh"], }, + { + "name": "Game: build full game release, export and patch it, upload it", + "working_dir": "${project_path}", + "shell": true, + "cmd": ["./build_and_install_all_cartridges.sh release && ./export_and_patch_cartridge_release.sh && ./upload_cartridge_release.sh"], + }, { "name": "Game: test", "working_dir": "${project_path}", From 1819344a3161a83f5c593b84089ad93cd87b5148 Mon Sep 17 00:00:00 2001 From: huulong Date: Fri, 8 Jan 2021 00:47:36 +0100 Subject: [PATCH 70/91] [README] Added notes on GBA, pico-boots and Audio, added Credits --- README.md | 32 ++++++++++++++++++++------------ 1 file changed, 20 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index ae83a393..38ed2016 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ develop ![The 8 Pico Emeralds displayed in circle, each color corresponding to a color on the PICO-8 logo](doc/all_emeralds.png?raw=true) -**pico-sonic** is a partial demake of Sonic the Hedgehog 3 made with PICO-8. It features a simplified version of Angel Island Act 1 with some tweaks. Various classic Sonic games were used as reference, including the 8-bit games (Game Gear and Master System) which have sprites closer to what PICO-8's resolution and color palette. +**pico-sonic** is a partial demake of Sonic the Hedgehog 3 made with PICO-8. It features a simplified version of Angel Island Act 1 with some tweaks. Various classic Sonic games were used as reference, including the 8-bit games (Game Gear and Master System) which have sprites closer to what PICO-8's resolution and color palette, and the GBA titles which have more clear-cut graphics. The project was started as a personal challenge and was meant to be a fully-fledged fan game, but I eventually dropped many features to focus on Sonic's main movements and the exploration of the stage. Consider it a technical demo with some exploration challenge. @@ -232,12 +232,30 @@ Alternatively, to edit the spritesheet in your favorite editor: * [Sonic Physics Guide](http://info.sonicretro.org/Sonic_Physics_Guide) * [TASVideos Resources for Sonic the Hedgehog](http://tasvideos.org/GameResources/Genesis/SonicTheHedgehog.html) -## Tools +## Tools and process * Tilemap and audio editing made with PICO-8 * Sprites made with Aseprite * Code written with Sublime Text +I used my own PICO-8 framework, [pico-boots](https://github.com/hsandt/pico-boots). + +#### Audio + +For the BGMs, I used 8-bit remixes of Sonic 3 & Knuckles by danooct1 with the author's permission. I had to go from 8 channels to only 3 or 4 (PICO-8 has 4 channels but during in-game I need to keep one channel for SFX) by picking the notes I considered the most important. + +Then I exported the modified FamiTracker Music (FTM) files to MIDI, and converted each MIDI channel to PICO-8 format using [midi2pico](https://github.com/gamax92/midi2pico). Finally, I merged the channels manually and reworked some notes to make them sound better in PICO-8. + +For the SFX, I listened to the original ones, sometimes used Audacity to inspect wave forms, and tried to reproduce them from scratch with PICO-8's sound editor. + +## Credits + +* Original games: Sonic Team +* 8-bit remixes of Sonic 3 BGMs: danooct1 +* Programming: Leyn (komehara) +* Sprite adaptation: Leyn +* SFX/jingle adaptation, BGM adjustments: Leyn + ## License ### Code @@ -257,13 +275,3 @@ Sega Games Co., Ltd owns the Sonic the Hedgehog trademark and copyrights on the Most assets are derivative works of classic Sonic the Hedgehog games. They have been made with a combination or automated conversion and manual work (depending on the asset's complexity). Because of this, I only consider original assets and the manual work of adaptation to be under CC BY NC 4.0. - -#### Sprites - -I drew most sprites based on the Master System/Game Gear, Mega Drive and GBA games. - -#### Audio - -For the BGMs, I used 8-bit remixes of Sonic 3 & Knuckles by danooct1 with the author's permission. I had to go from 8 channels to only 3 or 4 (PICO-8 has 4 channels but during in-game I need to keep one channel for SFX) by picking the notes I considered the most important. Then I exported the modified FamiTracker Music (FTM) files to MIDI, and converted each MIDI channel to PICO-8 format using [midi2pico](https://github.com/gamax92/midi2pico). Finally, I merged the channels manually and reworked some notes to make them sound better in PICO-8. - -For the SFX, I listened to the original ones and tried to reproduce them manually with PICO-8's sound editor. From f550ea02b9bf5783e2afe76fc4a1b9da947a85fb Mon Sep 17 00:00:00 2001 From: huulong Date: Fri, 8 Jan 2021 00:54:17 +0100 Subject: [PATCH 71/91] [EXPORT] Fixed missing app/web icon by reusing Sonic run cycle (step 2 this time) Added run cycle 2 to titlemenu spritesheet and adjusted icon index in export .p8 script --- data/builtin_data_titlemenu.p8 | 32 ++++++++++++++++---------------- export_game_release.p8 | 13 +++++++------ 2 files changed, 23 insertions(+), 22 deletions(-) diff --git a/data/builtin_data_titlemenu.p8 b/data/builtin_data_titlemenu.p8 index be25e598..242e1707 100644 --- a/data/builtin_data_titlemenu.p8 +++ b/data/builtin_data_titlemenu.p8 @@ -21,22 +21,22 @@ eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee1d77777777d1eeeeeeeeeeeeeeeeee eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee1d7777dddddddd7777d1eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee1d77dd111111111111dd77d1eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee1d777d111144444444441111d777d1eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee -e1d1eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee1d7d1114919aaaaaaaaaa9194111d7d1eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee1d1eeeeeeeeeeeeeeeee -e1d7dd1eeeeeeeeeeeeeeeeeeeeeeeeeeeeeee1d711144901110aaaaaaaa011109441117d1eeeeeeeeeeeeeeeeeeeeeeeeeeeeeee1dd7d1eeeeeeeeeeeeeeeee -e0dd777dd1eeeeeeeeeeeeeeeeeeeeeeeeeeed7d11499aaa101aaaaaaaaaa101aaa99411d7deeeeeeeeeeeeeeeeeeeeeeeeeee1dd777dd0eeeeeeeeeeeeeeeee -ee1d777777dd1eeeeeeeeeeeeeeeeeeeeee11d1144aaaaaa0a0aaaaaaaaaa0a0aaaaaa4411d11eeeeeeeeeeeeeeeeeeeeee1dd777777d1eeeeeeeeeeeeeeeeee -eee1d77777777dd1eeeeeeeeeeeeeeeeee1dd1149aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa9411dd1eeeeeeeeeeeeeeeeee1dd77777777d1eeeeeeeeeeeeeeeeeee -eee0dd77777777777dd1eeeeeeeeeeeee1dd1491aaaaaaaaaaa1d7d77d7d1aaaaaaaaaaa1941dd1eeeeeeeeeeeee1dd77777777777dd0eeeeeeeeeeeeeeeeeee -eeee0dd77777777777777dd1eeeeeeee1d11401110aaaaaad17dddddddddd71daaaaaa01110411d1eeeeeeee1dd77777777777777dd0eeeeeeeeeeeeeeeeeeee -eeeee1dd77777777777777777dd1eee1d114aa101aaaa1d1ddd1111111111ddd1d1aaaa101aa411d1eee1dd77777777777777777dd1eeeeeeeeeeeeeeeeeeeee -eeeeee0dd7777777777777777777771dd14aaa0a0aaa7ddd1110000000000111ddd7aaa0a0aaa41dd1777777777777777777777dd0eeeeeeeeeeeeeeeeeeeeee -eeeeeee1dd7777777777777777777d1d14aaaaaaaa11dd11000000000000000011dd11aaaaaaaa41d1d7777777777777777777dd1eeeeeeeeeeeeeeeeeeeeeee -eeeeeeee01d77777777777777777d1d14aaaaaaaa1dd110000000000000000000011dd1aaaaaaaa41d1d77777777777777777d10eeeeeeeeeeeeeeeeeeeeeeee -eeeeeeeee10d77777777777777771d11aaaaaaaa1dd10000000000000000000000001dd1aaaaaaaa11d17777777777777777d01eeeeeeeeeeeeeeeeeeeeeeeee -e11eeeeeeee1dd7777777777777d1d19aaaaaaa1dd1000000000000000000000000001dd1aaaaaaa91d1d7777777777777dd1eeeeeeee11eeeeeeeeeeeeeeeee -e1dd10eeeeee10dd777777777771d140000000000000000000000000000000000000000000001aa1a41d177777777777dd01eeeeee01dd1eeeeeeeeeeeeeeeee -ee1d77d10eeeee1ddd777777771dd100111111111111000111111100011111111000100499941011101dd177777777ddd1eeeee01d77d1eeeeeeeeeeeeeeeeee -ee0dd7777d10eee01ddd7777771d19a019777aaaa9941001977aa100104aaa99991104a7aaaa94011a91d1777777ddd10eee01d7777dd0eeeeeeeeeeeeeeeeee +e1d1eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee1d7d1114919aaaaaaaaaa9194111d7d1eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee1d1eeeeeeecccceeeeee +e1d7dd1eeeeeeeeeeeeeeeeeeeeeeeeeeeeeee1d711144901110aaaaaaaa011109441117d1eeeeeeeeeeeeeeeeeeeeeeeeeeeeeee1dd7d1eeeeeecccccceceee +e0dd777dd1eeeeeeeeeeeeeeeeeeeeeeeeeeed7d11499aaa101aaaaaaaaaa101aaa99411d7deeeeeeeeeeeeeeeeeeeeeeeeeee1dd777dd0eeeeeeecffcccceee +ee1d777777dd1eeeeeeeeeeeeeeeeeeeeee11d1144aaaaaa0a0aaaaaaaaaa0a0aaaaaa4411d11eeeeeeeeeeeeeeeeeeeeee1dd777777d1eeeeeeeccfccc7ccee +eee1d77777777dd1eeeeeeeeeeeeeeeeee1dd1149aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa9411dd1eeeeeeeeeeeeeeeeee1dd77777777d1eeeeeeecccccc770cee +eee0dd77777777777dd1eeeeeeeeeeeee1dd1491aaaaaaaaaaa1d7d77d7d1aaaaaaaaaaa1941dd1eeeeeeeeeeeee1dd77777777777dd0eeeeeeccccccc770cee +eeee0dd77777777777777dd1eeeeeeee1d11401110aaaaaad17dddddddddd71daaaaaa01110411d1eeeeeeee1dd77777777777777dd0eeeeeeeeeecccff77f0e +eeeee1dd77777777777777777dd1eee1d114aa101aaaa1d1ddd1111111111ddd1d1aaaa101aa411d1eee1dd77777777777777777dd1eeeeeeeee9c888cfffeee +eeeeee0dd7777777777777777777771dd14aaa0a0aaa7ddd1110000000000111ddd7aaa0a0aaa41dd1777777777777777777777dd0eeeeeeeee9f888856eeeee +eeeeeee1dd7777777777777777777d1d14aaaaaaaa11dd11000000000000000011dd11aaaaaaaa41d1d7777777777777777777dd1eeeeeeeee228887766eeeee +eeeeeeee01d77777777777777777d1d14aaaaaaaa1dd110000000000000000000011dd1aaaaaaaa41d1d77777777777777777d10eeeeeeeee22888cffeeeeee2 +eeeeeeeee10d77777777777777771d11aaaaaaaa1dd10000000000000000000000001dd1aaaaaaaa11d17777777777777777d01eeeeeeeee222ccee11eeee2e2 +e11eeeeeeee1dd7777777777777d1d19aaaaaaa1dd1000000000000000000000000001dd1aaaaaaa91d1d7777777777777dd1eeeeeeee11e22eeeeee1eeee222 +e1dd10eeeeee10dd777777777771d140000000000000000000000000000000000000000000001aa1a41d177777777777dd01eeeeee01dd1e2eeee871eeee222e +ee1d77d10eeeee1ddd777777771dd100111111111111000111111100011111111000100499941011101dd177777777ddd1eeeee01d77d1eee2ee8877222222ee +ee0dd7777d10eee01ddd7777771d19a019777aaaa9941001977aa100104aaa99991104a7aaaa94011a91d1777777ddd10eee01d7777dd0eeeeee82227222eeee eee1d77777771100000dd7777d1d1aa019799999999941019a999111097a9999991097a99999aa900aa1d1d7777dd00000117777777d1eeeeeeeeeeeeeeeeeee eee0dd77777777777777777771d19aa019799999999991019a9991109799999990047a99999999940aa91d17777777777777777777dd0eeeeeeeeeeeeeeeeeee eeee0dd7777777777777777771d1aaa019799911199994119a999109799999999109a9999999999910aa1d1777777777777777777dd0eeeeeeeeeeeeeeeeeeee diff --git a/export_game_release.p8 b/export_game_release.p8 index cec483a3..24fb3b2c 100644 --- a/export_game_release.p8 +++ b/export_game_release.p8 @@ -23,7 +23,7 @@ local additional_cartridges_list = { "picosonic_ingame.p8", "picosonic_stage_clear.p8", "data_bgm1.p8", - "data_stage1_00.p8", "data_stage1_10.p8", "data_stage1_20.p8", "data_stage1_30.p8", + "data_stage1_00.p8", "data_stage1_10.p8", "data_stage1_20.p8", "data_stage1_46.p8", "data_stage1_01.p8", "data_stage1_11.p8", "data_stage1_21.p8", "data_stage1_31.p8", "data_stage1_runtime.p8" } @@ -61,16 +61,17 @@ end -- exports are done via EXPORT, and can use a custom icon -- instead of the .p8.png label --- icon is a 16x16 square => -s 2 tiles wide --- with top-left at sprite 160 (run1) => -i 160 --- on pink (color 14) background => -c 14 +-- icon is stored in builtin_data_titlemenu.p8, +-- as a 16x16 square => -s 2 tiles wide +-- with top-left cell at sprite 46 (run1) => -i 46 +-- on pink (color 14) background => -c 14 -- and most importantly we pass additional logic and data files as additional cartridges -export(game_basename..".bin "..additional_cartridges_string.." -i 160 -s 2 -c 14") +export(game_basename..".bin "..additional_cartridges_string.." -i 46 -s 2 -c 14") printh("Exported binaries in carts/"..export_folder.."/"..game_basename..".bin") mkdir(game_basename.."_web") -- Do not cd into game_basename.."_web" because we want the additional cartridges to be accessible -- in current path. Instead, export directly into the _web folder -export(game_basename.."_web/"..game_basename..".html "..additional_cartridges_string.." -i 160 -s 2 -c 14") +export(game_basename.."_web/"..game_basename..".html "..additional_cartridges_string.." -i 46 -s 2 -c 14") printh("Exported HTML in carts/"..export_folder.."/"..game_basename..".html") cd("..") From 214444e686bd1ff49399cfab9228c2007562add6 Mon Sep 17 00:00:00 2001 From: huulong Date: Fri, 8 Jan 2021 01:00:20 +0100 Subject: [PATCH 72/91] [METADATA] Changed metadata label with pico-sonic title (without menu, but with version number) --- data/metadata.p8 | 256 +++++++++++++++++++++---------------------- data/metadata.p8.png | Bin 2546 -> 7584 bytes 2 files changed, 128 insertions(+), 128 deletions(-) diff --git a/data/metadata.p8 b/data/metadata.p8 index 1430d8e8..884e4e6d 100644 --- a/data/metadata.p8 +++ b/data/metadata.p8 @@ -5,131 +5,131 @@ __lua__ -- by leyn -- (title and author above will be overwritten by add_metadata.py, so don't mind them too much) __label__ -1c1111111111111111111111111111111111c11111111111111111111111111111111111c11111111111111111111111111111111111c1111111111111111111 -1111111111111111111111111111111c1111111111111111111111111111111111c11111111111111111111111111111111111c1111111111111111111111111 -111111111111c11111111111111111111111111111111111c11111111111111111111111111111111111c111111111111111111111111111111111111c111111 -b11111111111111bb1111111111111111b111111111111111111111111111111b11111111111111bb1111111111111111b111111111111111111111111111111 -b11111111111111bb111b111111111111b111111111b11b1b111111111111111b11111111111111bb111b111111111111b111111111b11b1b111111111111111 -b1111b1111111b1bbbb1b1111111b1111b111111111b11b1b1b1111111111111b1111b1111111b1bbbb1b1111111b1111b111111111b11b1b1b1111111111111 -b1111b11111b1b1bbbbbb1111111b1111b1bb111111b11b1bbb11b1b111b11b1b1111b11111b1b1bbbbbb1111111b1111b1bb111111b11b1bbb11b1b111b11b1 -bb111b11111b1bbbbbbbb11111b1b111bb1bb111111b11b1bbb11b1bb11b11b1bb111b11111b1bbbbbbbb11111b1b111bb1bb111111b11b1bbb11b1bb11b11b1 -bbbb1b1b1bbb1bbbbbbbbb111bb1b1b1bb1bbb1111bb11b1bbbb1bbbb1bb11b1bbbb1b1b1bbb1bbbbbbbbb111bb1b1b1bb1bbb1111bb11b1bbbb1bbbb1bb11b1 -bbbbbbbb1bbb1bbbbbbbbb111bbbb1b1bb1bbb11b1bbb1bbbbbb1bbbbbbb1bb1bbbbbbbb1bbb1bbbbbbbbb111bbbb1b1bb1bbb11b1bbb1bbbbbb1bbbbbbb1bb1 -bbbbbbbb1bbbbbbbbbbbbb111bbbbbbbbbbbbb11b1bbbbbbbbbbbbbbbbbb1bbbbbbbbbbb1bbbbbbbbbbbbb111bbbbbbbbbbbbb11b1bbbbbbbbbbbbbbbbbb1bbb -bbbbbbbb3bbbbbbbbbbbbb3113bbbbbbbbbbbb1bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb3bbbbbbbbbbbbb3113bbbbbbbbbbbb1bbbbbbbbbbbbbbbbbbbbbbbbb -bbbbbbbb3bbbbb3bbbbbbb3b13bbbbbbbbbbbbbbbbbbbbbbbbbbbb3bbbbbbbbbbbbbbbbb3bbbbb3bbbbbbb3b13bbbbbbbbbbbbbbbbbbbbbbbbbbbb3bbbbbbbbb -bbb3b3bb3bbbbb3bbbbbbb3bb3bbbbb333bbbbbbbbbbbb3bbbbbbb3bbbbb3bbbbbb3b3bb3bbbbb3bbbbbbb3bb3bbbbb333bbbbbbbbbbbb3bbbbbbb3bbbbb3bbb -33b3b3b33bbbbb3bbbbb333bb33bb3b333bbbbbbbbbbbb3bbbb3bb3bbbbb33bb33b3b3b33bbbbb3bbbbb333bb33bb3b333bbbbbbbbbbbb3bbbb3bb3bbbbb33bb -3333b3b33b33bb3bbb33333bb333b3b333b3bbb3bbbbbb33bbb3bb3bbbbb33b33333b3b33b33bb3bbb33333bb333b3b333b3bbb3bbbbbb33bbb3bb3bbbbb33b3 -3333b3333b33bb3bb333333bb33333b333b3bb33bb3bbb33bbb3bb3b3b3b33b33333b3333b33bb3bb333333bb33333b333b3bb33bb3bbb33bbb3bb3b3b3b33b3 -3333b33333333b33b33333333333333333333b33333bbb33b333333b333b33b33333b33333333b33b33333333333333333333b33333bbb33b333333b333b33b3 -3333333333333333333333333333333333333b33333b3b333333333b333b33b33333333333333333333333333333333333333b33333b3b333333333b333b33b3 -33333333333b3333333333333333333333333bb3333333333333b333333b33b333333333333b3333333333333333333333333bb3333333333333b333333b33b3 -3333b33b333b3333b3b33333333333333333bbb3333333333333b333333333b33333b33b333b3333b3b33333333333333333bbb3333333333333b333333333b3 -3333b3bb333b3333b3b333333333333b3b33bbb333b3333b3333b333333b3bb33333b3bb333b3333b3b333333333333b3b33bbb333b3333b3333b333333b3bb3 -3333b3bb33bb33b3b3b33333333b33bb3b33bbb333b3333b33b3b33333bb3bb33333b3bb33bb33b3b3b33333333b33bb3b33bbb333b3333b33b3b33333bb3bb3 -b333b3bb33bbb3b3b3b333bb333bb3bb3b33bbbb33bb33bb33bbb3333bbb3bbbb333b3bb33bbb3b3b3b333bb333bb3bb3b33bbbb33bb33bb33bbb3333bbb3bbb -b333bbbb3bbbbbb3bbb3b3bb33bbb3bb3b3bbbbbb3bb3bbb33bbbbb33bbb3bbbb333bbbb3bbbbbb3bbb3b3bb33bbb3bb3b3bbbbbb3bb3bbb33bbbbb33bbb3bbb -b333bbbbbbbbbbb3bbbbbbbbb3bbb3bbbb3bbbbbb3bb3bbb33bbbbb33bbbbbbbb333bbbbbbbbbbb3bbbbbbbbb3bbb3bbbb3bbbbbb3bb3bbb33bbbbb33bbbbbbb -b333bbbbbbbbbbbbbbbbbbbbb3bbbbbbbbbbbbbbb3bbbbbb3bbbbbbb3bbbbbbbb333bbbbbbbbbbbbbbbbbbbbb3bbbbbbbbbbbbbbb3bbbbbb3bbbbbbb3bbbbbbb -b33bbbbbbb3bbbbbbbbbb3bbb3bbbbbbbbbbbbbbb3bbbbbbb3bbbb3bbbbbbbbbb33bbbbbbb3bbbbbbbbbb3bbb3bbbbbbbbbbbbbbb3bbbbbbb3bbbb3bbbbbbbbb -bbbbbbbbbb3bbbbbbbb3b3bbb3bbbbbbbbbbbbbbb3bbbbbbb3bbbb3bbbb3bbbbbbbbbbbbbb3bbbbbbbb3b3bbb3bbbbbbbbbbbbbbb3bbbbbbb3bbbb3bbbb3bbbb -bbbbbbbbbb33bbb3bbb3b3bbb33bbbbbbbbbbbbbb3b3bbbbb3bbbb33bbb3bbbbbbbbbbbbbb33bbb3bbb3b3bbb33bbbbbbbbbbbbbb3b3bbbbb3bbbb33bbb3bbbb -bbbbbbbbbb33bbb3bbb3b3b3b33b3b3bbbbb33b3b3b33bbbb3b33b33b3333bbbbbbbbbbbbb33bbb3bbb3b3b3b33b3b3bbbbb33b3b3b33bbbb3b33b33b3333bbb -bbbbbbbbbb33bbb3bbb3b333b33b3b3bbb3b33b3b3b33b3bb3333b33b3333bbbbbbbbbbbbb33bbb3bbb3b333b33b3b3bbb3b33b3b3b33b3bb3333b33b3333bbb -bbbbb3bbbb33bb33b3b3b333b33b3b3bbb3333b3b3b3333bb3333b3333333bbbbbbbb3bbbb33bb33b3b3b333b33b3b3bbb3333b3b3b3333bb3333b3333333bbb -3bbbb3bbbb333b33b3b33333b33b3b3bb33333b3b3333333b3333333333333bb3bbbb3bbbb333b33b3b33333b33b3b3bb33333b3b3333333b3333333333333bb -3b3bb3bb33333b333333333333333b3bb33333b33333333333333333333333bb3b3bb3bb33333b333333333333333b3bb33333b33333333333333333333333bb -3b33b33b333333333333333333333b3bb33333b33333333333333333333333333b33b33b333333333333333333333b3bb33333b3333333333333333333333333 -3b33333333333333333333333333333bb33333b33333333333333333333333333b33333333333333333333333333333bb33333b3333333333333333333333333 -33333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333 -33333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333 -33333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333 -33333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333 -33333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333 -33333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333 -33333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333 -33333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333 -33333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333 -33333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333 -33333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333 -33333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333 -33333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333 -3333333333333333333333333333333333333333bb33333333333333333333333333333333333333333333333333333333333333bb3333333333333333333333 -33333333333333333333333333333333333333b3bb333333333333333333333333333333333333333333333333333333333333b3bb3333333333333333333333 -33333333333333333333333333333333333333b3bb33bb33333333333333333333333333333333333333333333333333333333b3bb33bb333333333333333333 -333333333333333333333333333333b333bbb3bbbbbbbb333333333333333333333333333333333333333333333333b333bbb3bbbbbbbb333333333333333333 -33333333333333333333333333333bb333bbbbbbbbbbbb3333b333333333333333333333333333333333333333333bb333bbbbbbbbbbbb3333b3333333333333 -33333333333333333333333333b33bbb33bbbbbbbbbbbbb3b3b3333b3333333333333333333333333333333333b33bbb33bbbbbbbbbbbbb3b3b3333b33333333 -333333333333333333333ccccccbb1bbbbbbbbbbbbbbbbbbbbbbb33b3333333333333333333333333333333bb3bbbbbbbbbbbbbbbbbbbbbbbbbbb33b33333333 -33333333333333333b33cb3cccccccbbbbbbbbbbbbbbbbbbbbbbbbbbb333333333333333333333333b333b3bb3bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb3333333 -b333333bb33333333b33bccffcccccbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb333bb333333bb33333333b33bb3bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb333b -b333b33bbbb333333b33cccfcc7cccbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb3bbb333b33bbbb333333b33bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb3bb -bbbbbb3bbbbbb33bbb3cccccc770ccbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb3bbbbbb33bbb3bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb -bbbbbbbbbbbbbbbbbbbcbb1cc770cbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb -bbbbbbbbbbbbbbbbbbbbbccccf77f0bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb -bbbbbbbbbbbbbbbbbbbbbcbcccffbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb -bbbbbbbbbbbbbbbbbbbbbbb1fccf7bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb9bbb7777777777777777 -bbbbbbbbbbbbbbbbbbbbbbb777cf77bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb9bbbb9b9bbb7777777777777777 -bbbbbbbbbbbbbbbbbbbbbbb7777c77bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb9bbb9bb9bbbb9b9bbb7777777777777777 -bbbbbbbbbbbbbbbbbbbbbbbb77bcbbbbbbbbbbbbb3bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb9bbbb9bb99bbb9bbbbbbbbb7777777777777777 -bbbbbbbbbbbbbbbbbbbbbbbbbcbcbbbbbbbbbbbbb3bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb9bb9b9bb93bbb9bbbbb9bbb7777777777777777 -bbbbbbbbbbbbbbbbbbbbb9bbb7b739bbb3bbbbbbb3bbb3bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb9bb9bb9bb9bbbbb3bbbbbb3bbbbb37777777777777777 -bbbbbbbbbbbbbbbbbb9b99bb08720883bb3b9b9bb33b33b3bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb9b99b9bbbbb93b9bb3bbbbbb33b3b307777777777777777 -bbbbbbbbbbbbbbbbb9bb9bbb27888028bb39b9b9b33333b3bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb99b99bb3bbbbb3b9bbbb3b3b330303007777777777777777 -bbbbb9bbbbb9bb9bb9b39b9bb9b39b9bb9b9b9b9b93393b33bb3b9bbbbbbb9bbbbb9bb9bb9b9b9b99bbb9bbbbbbbbbb3bbbbbbb3000000047777777777777777 -bb9bb9b99bb9bb9bb9bbbb9bb9bbbb9bb9bbb9bbb9bb93393b9339b9bb9bb9b99bb9bb9bb9bbb9bbbbbbbbbbbb0bb3b3bb0bb3b3044040047777777777777777 -bb9bb9b99bbbbb9bbbbbbbbbbbbbbbbbbbbbbbbbb9bbbbb9bb9bb9b9bb9bb9b99bbbbb9bbbbbbbbbbbbb300b3003b0303003b030444444047777777777777777 -bbbbb9bbbbb93bbbbbbbbbbbbbbbbbbbbbbbbbbbbbb39bbbbbbbb9bbbbbbb9bbbbb93bbbbbbbbbbbb3bb04030040300400403004444444447777777777777777 -bbbbbbbbbbbb03bbbbb3bbbbbbb3bbbbbbbb0bbbbb30bbbbbbbbbbbbbbbbbbbbbbbb03bbbbbb0bbbb0bb44404444004444440044444444447777777777777777 -bbbb3bbbb3bb03b3bbb030b0bbb030b0b03b03bbbb30bb3bbbbb3bbbbbbb3bbbb3bb03b3b03b03bb30b344404444404444444044944444447777777777777777 -b3b30b33b0b3003033b0003033b00030000b00b0b3003b0bb3b30b33b3b30b33b0b30030000b00b000b444444444444444444449944944447777777777777777 -0300030300000000030040000300400004030030b000000003000303030003030000000004030030403444449449444444449449994444447777777777777777 -44404400000440404440440000044040444044000004404044404400000440404440440077777777777777777777777777777777777777777777777777777777 -44444440404444444444444040444444444444404044444444444440404444444444444077777777777777777777777777777777777777777777777777777777 -44444444444444444444444444444444444444444444444444444444444444444444444477777777777777777777777777777777777777777777777777777777 -94444444444449499444444444444949944444444444494994444444444449499444444477777777777777777777777777777777777777777777777777777777 -94944444444444499494444444444449949444444444444994944444444444499494444477777777777777777777777777777777777777777777777777777777 -99444444444494999944444444449499994444444444949999444444444494999944444477777777777777777777777777777777777777777777777777777777 -49494444444494994949444444449499494944444444949949494444444494994949444477777777777777777777777777777777777777777777777777777777 -49494444444494494949444444449449494944444444944949494444444494494949444477777777777777777777777777777777777777777777777777777777 -99494444444444499949444444444449994944444444444999494444444444499949444477777777777777777777777777777777777777777777777777777777 -94444444444449499444444444444949944444444444494994444444444449499444444477777777777777777777777777777777777777777777777777777777 -94444444444449499444444444444949944444444444494994444444444449499444444477777777777777777777777777777777777777777777777777777777 -99494444444444499949444444444449994944444444444999494444444444499949444477777777777777777777777777777777777777777777777777777777 -99444444444444499944444444444449994444444444444999444444444444499944444477777777777777777777777777777777777777777777777777777777 -99944444444494949994444444449494999444444444949499944444444494949994444477777777777777777777777777777777777777777777777777777777 -99494444444494949949444444449494994944444444949499494444444494949949444477777777777777777777777777777777777777777777777777777777 -99494444444494999949444444449499994944444444949999494444444494999949444477777777777777777777777777777777777777777777777777777777 -99494444444444499949444444444449994944444444444999494444444444499949444477777777777777777777777777777777777777777777777777777777 -94444444444449499444444444444949944444444444494994444444444449499444444477777777777777777777777777777777777777777777777777777777 -94444444444449499444444444444949944444444444494994444444444449499444444477777777777777777777777777777777777777777777777777777777 -99494444444444499949444444444449994944444444444999494444444444499949444477777777777777777777777777777777777777777777777777777777 -99444444444444499944444444444449994444444444444999444444444444499944444477777777777777777777777777777777777777777777777777777777 -99944444444494949994444444449494999444444444949499944444444494949994444477777777777777777777777777777777777777777777777777777777 -99494444444494949949444444449494994944444444949499494444444494949949444477777777777777777777777777777777777777777777777777777777 -99494444444494999949444444449499994944444444949999494444444494999949444477777777777777777777777777777777777777777777777777777777 -99494444444444999949444444444499994944444444449999494444444444999949444477777777777777777777777777777777777777777777777777777777 -99444444444494999944444444449499994444444444949999444444444494999944444477777777777777777777777777777777777777777777777777777777 -94494444434499999449444443449999944944444344999994494444434499999449444477777777777777777777777777777777777777777777777777777777 -994944444b343949994944444b343949994944444b343949994944444b3439499949444477777777777777777777777777777777777777777777777777777777 -999944443bb4b949999944443bb4b949999944443bb4b949999944443bb4b9499999444477777777777777777777777777777777777777777777777777777777 -999944b3bbbb3999999944b3bbbb3999999944b3bbbb3999999944b3bbbb3999999944b377777777777777777777777777777777777777777777777777777777 -993b43bbb33bb939993b43bbb33bb939993b43bbb33bb939993b43bbb33bb939993b43bb77777777777777777777777777777777777777777777777777777777 -393b3bbbbb33b3b3393b3bbbbb33b3b3393b3bbbbb33b3b3393b3bbbbb33b3b3393b3bbb77777777777777777777777777777777777777777777777777777777 -bb3b3bbbbb3bbb3bbb3b3bbbbb3bbb3bbb3b3bbbbb3bbb3bbb3bbb3bbb3b3bbbbb3bbb3b77777777777777777777777777777777777777777777777777777777 -b3bb33bbb333bb3bb3bb33bbb333bb3bb3bb33bbb333bb3bb333bb3bb3bb33bbb333bb3b77777777777777777777777777777777777777777777777777777777 -03b3bbbbb3b3b3b003b3bbbbb3b3b3b003b3bbbbb3b3b3b0b3b3b3b003b3bbbbb3b3b3b077777777777777777777777777777777777777777777777777777777 -b03bbb333bbbbbb0b03bbb333bbbbbb0b03bbb333bbbbbb03bbbbbb0b03bbb333bbbbbb077777777777777777777777777777777777777777777777777777777 -b333bbbbb33b30bbb333bbbbb33b30bbb333bbbbb33b30bbb33b30bbb333bbbbb33b30bb77777777777777777777777777777777777777777777777777777777 -b3bbb3bb30033bbbb3bbb3bb30033bbbb3bbb3bb30033bbb30033bbbb3bbb3bb30033bbb77777777777777777777777777777777777777777777777777777777 -3bb330bbbb3b33bb3bb330bbbb3b33bb3bb330bbbb3b33bbbb3b33bb3bb330bbbb3b33bb77777777777777777777777777777777777777777777777777777777 -bbbbb0bbb33bb3bbbbbbb0bbb33bb3bbbbbbb0bbb33bb3bbb33bb3bbbbbbb0bbb33bb3bb77777777777777777777777777777777777777777777777777777777 -bb3b3bbbbb3bbb3bbb3b3bbbbb3bbb3bbb3b3bbbbb3b3bbbbb3bbb3bbb3b3bbbbb3b3bbb77777777777777777777777777777777777777777777777777777777 -b3bb33bbb333bb3bb3bb33bbb333bb3bb3bb33bbb3bb33bbb333bb3bb3bb33bbb3bb33bb77777777777777777777777777777777777777777777777777777777 -03b3bbbbb3b3b3b003b3bbbbb3b3b3b003b3bbbb03b3bbbbb3b3b3b003b3bbbb03b3bbbb77777777777777777777777777777777777777777777777777777777 -b03bbb333bbbbbb0b03bbb333bbbbbb0b03bbb33b03bbb333bbbbbb0b03bbb33b03bbb3377777777777777777777777777777777777777777777777777777777 -b333bbbbb33b30bbb333bbbbb33b30bbb333bbbbb333bbbbb33b30bbb333bbbbb333bbbb77777777777777777777777777777777777777777777777777777777 -b3bbb3bb30033bbbb3bbb3bb30033bbbb3bbb3bbb3bbb3bb30033bbbb3bbb3bbb3bbb3bb77777777777777777777777777777777777777777777777777777777 -3bb330bbbb3b33bb3bb330bbbb3b33bb3bb330bb3bb330bbbb3b33bb3bb330bb3bb330bb77777777777777777777777777777777777777777777777777777777 -bbbbb0bbb33bb3bbbbbbb0bbb33bb3bbbbbbb0bbbbbbb0bbb33bb3bbbbbbb0bbbbbbb0bb77777777777777777777777777777777777777777777777777777777 +11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 +11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111100011111001111111 +11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111010077701110770110111 +11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111110707070011111070107011 +11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111110707077701111070077701 +11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111110777000701011070107011 +11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111070077700700777010111 +11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111101100011011000111111 +11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 +11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 +11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 +11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 +11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 +11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 +11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 +11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 +11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 +11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 +11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 +11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 +11111111111111111111111111111111111111111111111111111111111d77777777d11111111111111111111111111111111111111111111111111111111111 +1111111111111111111111111111111111111111111111111111111d7777dddddddd7777d1111111111111111111111111111111111111111111111111111111 +11111111111111111111111111111111111111111111111111111d77dd111111111111dd77d11111111111111111111111111111111111111111111111111111 +11111111111111111111111111111111111111111111111111d777d111144444444441111d777d11111111111111111111111111111111111111111111111111 +1111111111d11111111111111111111111111111111111111d7d1114919aaaaaaaaaa9194111d7d11111111111111111111111111111111111111d1111111111 +1111111111d7dd111111111111111111111111111111111d711144901110aaaaaaaa011109441117d111111111111111111111111111111111dd7d1111111111 +1111111110dd777dd1111111111111111111111111111d7d11499aaa101aaaaaaaaaa101aaa99411d7d1111111111111111111111111111dd777dd0111111111 +11111111111d777777dd1111111111111111111111111d1144aaaaaa0a0aaaaaaaaaa0a0aaaaaa4411d1111111111111111111111111dd777777d11111111111 +111111111111d77777777dd11111111111111111111dd1149aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa9411dd11111111111111111111dd77777777d111111111111 +111111111110dd77777777777dd111111111111111dd1491aaaaaaaaaaa1d7d77d7d1aaaaaaaaaaa1941dd111111111111111dd77777777777dd011111111111 +1111111111110dd77777777777777dd1111111111d11401110aaaaaad17dddddddddd71daaaaaa01110411d1111111111dd77777777777777dd0111111111111 +11111111111111dd77777777777777777dd11111d114aa101aaaa1d1ddd1111111111ddd1d1aaaa101aa411d11111dd77777777777777777dd11111111111111 +111111111111110dd7777777777777777777771dd14aaa0a0aaa7ddd1110000000000111ddd7aaa0a0aaa41dd1777777777777777777777dd011111111111111 +1111111111111111dd7777777777777777777d1d14aaaaaaaa11dd11000000000000000011dd11aaaaaaaa41d1d7777777777777777777dd1111111111111111 +111111111111111101d77777777777777777d1d14aaaaaaaa1dd110000000000000000000011dd1aaaaaaaa41d1d77777777777777777d101111111111111111 +1111111111111111110d77777777777777771d11aaaaaaaa1dd10000000000000000000000001dd1aaaaaaaa11d17777777777777777d0111111111111111111 +11111111111111111111dd7777777777777d1d19aaaaaaa1dd1000000000000000000000000001dd1aaaaaaa91d1d7777777777777dd11111111111111111111 +1111111111dd1011111110dd777777777771d140000000000000000000000000000000000000000000001aa1a41d177777777777dd0111111101dd1111111111 +11111111111d77d10111111ddd777777771dd100111111111111000111111100011111111000100499941011101dd177777777ddd11111101d77d11111111111 +11111111110dd7777d1011101ddd7777771d19a019777aaaa9941001977aa100104aaa99991104a7aaaa94011a91d1777777ddd1011101d7777dd01111111111 +111111111111d77777771100000dd7777d1d1aa019799999999941019a999111097a9999991097a99999aa900aa1d1d7777dd00000117777777d111111111111 +111111111110dd77777777777777777771d19aa019799999999991019a9991109799999990047a99999999940aa91d17777777777777777777dd011111111111 +1111111111110dd7777777777777777771d1aaa019799911199994119a999109799999999109a9999999999910aa1d1777777777777777777dd0111111111111 +11111111111111d777777777777777771d11aaa01979991110a999119a99910a99999119400a99999119999910aa11d177777777777777777d11111111111111 +111111111111111d77777777777777771d1aaaa019a9991110a994119a99914a99991111104a999911109999410aa1d17777777777777777d111111111111111 +111111111111110dd7777777777777771d1aaaa019a9990007a991019a99919a99911111109a99911110a999910aa1d1777777777777777dd011111111111111 +1111111111111110dd777777777777771d1aaaa019a999aa7a9991019a99919999901110104a99911109a999410aa1d177777777777777dd0111111111111111 +11111111111111110dd77777777777711d1aaaa019a99999999941019a99914999900009400a99990097a99910aaa1d11777777777777dd01111111111111111 +111111111111111110dd777777777771d1aaa1a019a99999999410019a9991099aa949aa910a9999aa7a9999101aaa1d177777777777dd011111111111111111 +11111111111111111101d77777777771d1a0111019a99911111100019a999104999a77a99104a9999999999410110a1d17777777777d10111111111111111111 +11111111111111111111dd7777777771d1aa101019a99910000000019a9991019999999994109a99999999910101aa1d1777777777dd11111111111111111111 +1111111111111111111101dd77777771d1aa0a0019a99910000000019a999101149999999910049999999410a0a0aa1d17777777dd1011111111111111111111 +11111111111111111111110ddd777771d1aaaaa0111111100000000111111100111049941000001499941001aaaaaa1d177777ddd01111111111111111111111 +11111111111111111dd000001ddd7771d1aaaaa00000000000000000000000000010000000000000000001d1aaaaaa1d1777ddd100000dd11111111111111111 +111111111111111110ddddd777777771d1aaaaaa1d10000000000000000000000000000000000000000001d1aaaaaa1d177777777ddddd011111111111111111 +1111111111111111110dd77777777771d1aaaaaa1d10000000000000000000000000000000000000000001d1aaaaaa1d17777777777dd0111111111111111111 +11111111111111111110dd7777777771d1aaaaaa122288888888888888888888888888888888888888882221aaaaaa1d1777777777dd01111111111111111111 +111111111111111111110dd7777777722888888888888888888888888888888888888888888888888888888888888882277777777dd011111111111111111111 +11111111111111111111101dd777888888888888888888888888000000888888888888888888888888888888888888888888777dd10111111111111111111111 +111111111111111111111111d88888888888800011100088888000000188800000001800000000000008888000000088888888dd111111111111111111111111 +11111111111111111111111182888888888800049994110881004999410180111111110111111111111080111111110088888821111111111111111111111111 +1111111111111111111111102288888888804a7a99999908104a7aaaa940100777aa91197aaa1977aa100104aaa9999188888828111111111111111111111111 +111111111111111111111111228888888814aa9999999001097a99999aa9000799999019a99919a999111097a999999188888828111111111111111111111111 +111111111111111111111111228888888119a9999999911047a999999999400799999919a99919a9991109799999990088888828111111111111111111111111 +11111111111111111111111120888888811a9999011111109a9999999999910799999909a99919a9991097999999991888888828111111111111111111111111 +111111111111111111111111208877777019999aa9401110a99999119999910799999999a99919a99910a9999911940777778828111111111111111111111111 +111111111111111111111111207777777719999999999004a99991110999940a99999999a99919a99914a9999111117777777788111111111111111111111111 +111111111111111111111111407777777701999999999909a99911110a99990a99999999999919a99919a9991111117777777708111111111111111111111111 +111111111111111111111111007777777771149999999904a99911109a99940a9999a999999919a9991999990111010777777709111111111111111111111111 +11111111111111111111111100777777777011100a999910a99990097a99910a99999a99999919a9991499990000940777777709111111111111111111111111 +11111111111111111111111140777777777049a7a9999910a9999aa7a999910a99994799999919a9991099aa949aa91777777709111111111111111111111111 +1111111111111111111111114077777778009999999999104a9999999999410a999909a9999919a999104999a77a991077777709111111111111111111111111 +11111111111111111111111840778888800499999999911109a999999999100a999914a9999919a9991019999999994188887709811111111111111111111111 +111111111111111111118822408888888001999999941008004999999941000a99991099999919a9991011499999999188888809228111111111111111111111 +11111111111111111888822242888888880001111110088880014999410080111111001111111111111081110499410088888809228888111111111111111111 +11111111111111888888822222888888888800000088888888800000008880000000080000000000000088810000008888888828228888888111111111111111 +11111111118888888888822222888888888888888888888888888888888888888888888888888888888888888888888888888828228888888888811111111111 +11111111188888888888829482888888888888888888244444444444444444444444444444444444442888888888888888888224928888888888881111111111 +111111111188888888877994828888888888211101dd1449494999999999999999999999999994949441dd101112888888888224997778888888811111111111 +1111111111188888777779948288828111111111101dd11a9a9a9a9a9a9a9a9aa9a9a9a9a9a9a9a9a11dd1011111111112088224997777778888111111111111 +111111111111877777777994480002811111111111011d11aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa11d110111111111112000024997777777771111111111111 +1111111111111877777779944000028111111111111001dd1aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa1dd1001111111111112000084997777777711111111111111 +111111111111117777777994408002811111111111111011d111aaaaaaaaaaaaaaaaaaaaaaaa111d110111111111111112008844997777777811111111111111 +1111111111111877777779942000028111111111111111001ddd111aaaaaaaaaaaaaaaaaa111ddd1001111111111111112000024997777777711111111111111 +1111111111111777777779222000008111111111111111110111ddd1111aaaaaaaaaa1111ddd1110111111111111111110000022227777777781111111111111 +111111111111877777788222200000111111111111111111100011dddd111111111111dddd110001111111111111111118000022228887777771111111111111 +1111111111117778888882222000811111111111111111111111001111dddddddddddd1111001111111111111111111111800022228888887778111111111111 +11111111111888888888822220811111111111111111111111111100001111111111110000111111111111111111111111118022228888888888111111111111 +11111111111888888888822281111111111111111111111111111111110000000000001111111111111111111111111111111182228888888888811111111111 +11111111118888888888888111111111111111111111111111111111111111111111111111111111111111111111111111111111188888888888811111111111 +11111111118888888888111111111111111111111111111111111111111111111111111111111111111111111111111111111111111188888888881111111111 +11111111188888881111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111118888881111111111 +11111111188111111111111111111111111111ddd111111111111111111111111111111111111111111111111111111111111111111111111111888111111111 +1111111111111111111111111111111111111d6766d1111111111111111111111111111111111111111111111111111111111111111111111111111111111111 +111111111111111111111111111111111111d677776d111111111111111111111111111111111111111111111111111111111111111111111111111111111111 +11111111111111111111111111111111111d67777776dd1111111111111111111111111111111111111111111111111111111111111111111111111111111111 +11111111111111111111111111111111111d67777776666d11111111111111111111111111111111111111111111111111111111111111111111111111111111 +1111111111111111111111111111111dd666677777777776d1111111111111111111111111111119711111111111111111111111111111111111111111111111 +111111111111111111111111111111d67777777777777776d1111111111111111111111111111124911111111111111111111111111111111111111111111111 +111111111111111111111111111111d77777777777777777d1111111111111111111111111111124911111111111111111111111111111111111111111111111 +11111111111111111111111111111d677777777777777777666d1111111111111111111111111124911111111111111111111111111111111111111111111111 +11111111111111111111111111111d6777777777777777777776d111111111111111111111111224911111111111111111111111111111111111111111111111 +11111111dd6611111111111111111d67777777777777777777776d11111111111111111111111224911111111111d66d11111111111111111111111111111111 +1111111d67776dddd11111111111d667772477777777777777777d1111111111111111111111122449111111ddd67776d1241111111111111111111111111111 +11111111777776776d11111dd1dd6667772477777777777777777d11111111111111111111111224491111166776777761241111111111111111111111111111 +1111111d7777777776d111d666677777772277777777777777777d11111111111111111111112224249749167777777766221111111111111111111111111111 +111111666777777777d6666666777777772277777777777777777661111111111111111111121224224449677777777766221111111111111111111111111111 +1111d66777777777776777766677777777224977777777777777776d11111111111119911112212222442467777777777622491111111ddddddd11111111ddd6 +111166777777777777777777666777777722497777777777777777661111111111111491112212222244249649777777662249111111d666666dd11111166776 +11166777777777777777777766677777272229729777777777777776d666d1111111129494212122122424492967777626222912966666776666666d11677777 +11166777777777777777777777777677242224229777777777777776666666dd66d1424494b3bb121b2224442496776924222422966667777766776766777777 +11d66677777777777777777777777677222222229477777777777777666666d6666442bbb1333bb1b3b2b2442249696422222222947777767777777777777777 +1d677777777777777777777777777774222b2222224774677777777776777776643422b3b331333bb3bbbbb222444964222b2222224977776777776676777777 +d677777777777777777777777777244421313b22322424467777777667777744bb3b3333b33313333bb3333b1324444223113b22322429777777777667777777 +66777777777777777777777777772333131313bb333332244966677767777744333333341221313331333b1113b32224111313b1333332244977777767777777 +7777777777777777777777777442233331111113311131322224ddddddddd43b31122424242211311111331b3333113331111113311131322224777766777777 +ddddddddddddddddddddddddddddddd11dd11dd13bdddd11dddddd67dddddddd1ddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd +dddddddddddddddd6d676d6dddddddddddddddddddddddddddd76dddd67dd1ddddd67ddddddddd6676dddddddddddddddddd676d6ddddddddd666ddddddd66dd +1ddddddd666666dddddddddddd6d6776d6ddddddddddddddddddddddddddddddddddddddd1dddddddddddddddddd666dddddddddddddddd66666ddd1ddd66666 +11dd1ddddd666ddddddddddddddddddddddddddddd666ddddddd1ddd11dd111111111dd1111ddddddd1dddd6dd666666d6ddddddddddd666666dd1111dddd666 +11111111dddddddddddd11111111dddddd676ddd6666ddddddd1111111111111111111111111dd67dd111d666666666666ddd1111111dddddddd11111111dddd +1111111111ddd111dd11111111111dddddddddd6666dd1ddd1111111111111111111111111111dddd1111dd66666767766ddd1111111111111111111111111dd +11111111111111111111111111111111dddddd66dddd111111116d11111111111d61111111111dddd11111dd666666666dd111111d6111111111111111111111 +11111111111111111111111116d1111111ddddddddd11111111111111111111111111111d61111dd1111111ddddd6666dd11111111111166d111111111111111 +11111d6d11111111111111111111111111111111111111111111111111111111111111111111111111111111111ddddd11111111111111111111111111111111 +1111111111111111111111111111111111111111111111111111111111dd611111111111111111111111111111111111111111111111111111116dd611111111 +111111111111111116d611111111111111111111111d611111111111111111111111111111111d6111111d6d1111111111116d11111111111111111111111111 +11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 diff --git a/data/metadata.p8.png b/data/metadata.p8.png index 36914c194a09b49ce8bb6fd8e710cb92cd5e941f..573ebee7462361423d3b4f7b31092dac7f0f3beb 100644 GIT binary patch literal 7584 zcmZ{JbyQSe)b^cW=nm2~1%I*SrQZZ2agEkKXwESZ=d#V{W81I)&lS zX1>K5wufWs2?*d*K82dVqVPCuA2S=)Kz}=}#&TEbz(7!wLTaZ7%!EpE%;s=~Up5C6 zuaHKgM0O7dBzsAAwSo?tovmF+=U7nZS8Jm(W?F)nkjrOk_8PUuylJ;g_rlaf!4=*t zUs&tCF)J}ihQV@$#|(#TFW=y9Q=$6|lFK$IxT0cP3ZSC~G*|Iqi5EqHyBN9+nSeSF zpH`AG6(*d$cQzQGo_|rhdU*xavRMq&$mIGY6^wR(fgInzD_-X+UdNu8hedzm+E+TP zI<>R%*Juo?6X&xzKi(hoMvnP~eAvm&Q#ETJJN1R-r_QY2_21-(REq$(1Z8_#Q-$SY z|3U+J4%+l)KdqCBn&pS2S1q^uw;jv-i{~+Q`=9u%dCr{K|30p&OPpe%eD7Js(p74z z&$XSGJzBDCe8J+p4UYmo{^%AnP4XTYz%e@n zjywA%M0*MM<>_VprS(8n+o)7ZLCEr(ujB|P#S$lh_QgaDJls8z@Tf=#ToJ7B_%Wy; z8SNM~fU7>^wRolswOK44UbkcJE|vl?ep;=BEobGQI@{){P>4;G+ z0^M8&mFbU=@Pzu*L6%Vt5Rn5{o~zvZgv!SN#V}#Md|OxmHn~5i`;m!OMbEVkuv($&a8>h$cj!(UdT)R~H)nO_;=C^PQUZ;5`#2{6vL-o>N9( zg3q$J8w0*>fo1VXV2sd2JNT_)?WU<~X8tBe+rAKSA>nfvjxW zm?!O8nc9LRYyCB~xZVe+TRQKOczJGAiObeS^l!kcf0Y;dhG3N&E=%8q9^4Y7sqo&E z65Pqfmf4A2>+?H@klL|N$OZe7&Ilt*IAxz|lt>qB%x^YgXS!OyB66Tn={rl@gR#9; zv-ZgM4v7w1MgN55Y-OCP9POg!yE#eU_-pIh+89IIDZ-PS>URM?HwOEgf2Keh*fr%v zHkc_<)si2hT-RH~>c70-nm&u2MEKX!tzm}$eC%|T*Sl|N%}}feHP{;lba>`oiAXU)s;==Klt`E_ACcXpmgRbg80w;LxO=w=`GB&w3ePiTG5x)@Qyv@A_;9xoSMF~l=EW! z5w(2AM{r2uRvSJ*we0e*TWxV%D82^npj=I#7ySbRZWT!X{<8g5rkMDS#1i!QD$PiB zlHeQ{_%EIC%e6U{v(i*tUa`7tWyO@mN!e;dNX&jJ+~JvF4fgl?FGXsAFOz~jhGg;o zX4XM4v-Yh_LTafg&afNJG3Ty!Bcfxv33V+2SL@k{ezN4WmNVrBkx3DNt&JnzoB6yArYW2lwyOPE|Z_6$RYSL*@(e1nrh#wo1P zR0oAOkFmN49J8$I$0C}M9(h1{wo>!-41HYJ)$3;^9icWeFlT<&fDo~ja16VK=zpGN zsRTwTp6Od50CNUzfq`i6Ld%M=@{`d!>Je5%JM^r=%#pmdM|G4z$~jh}Hp7xJV>|W7 zWxU-Rst(t`Pak#s0u_I1=F%t9ey$3Y#xkB*7aJ+rDef5!OTr4Yp3Q){Zkk-K@F`}k z8WL&aS6IzWv>d9b1{6=YM>~?(OrM;$6+hnc7!#N?K7kLpZybi zZ=@r}9}2Lm=QL4?Irc9f3CFmw-ARt<%g#p2n~WuhhgB~$`H+1t+&S1CqKN!+4xm9^ zYhX~c)5EXzzW2FbUe)v=!gQmRoG@1&qS6>+MJ3TVaESfNH#=|6a`*Z$hYL5I&}Z~zFI&pus{w66lC;_KvgEo#Q~AvP0MduULl!RXbG zG4S75d@_3i22D@cdkKMrN(>R~fYTdl&h-{p$tw-7UY1I(a64Cb(8z9}h^ zYbGx}T9A?vUJkZc-h5Rw2N$Oohe`6aKC#P3iAI;p^&Ua1oJ6Wl<7tAw;$caKP9SDh zG1}vBHG0M1S{f50eM#|&>VPov{&I)J3Ax$u@Fuw7Iw%X5;Ad`Hm+EM(jIJDj>vyvr z6o}9KM5oEEaDg!r^Cun)>|KFn7*cOk6BV?t+|U5TWoyt%`SzUq13$SvNs$CR+V|HSnZ4ljs{DX#4pdW6p%Qm?Q`BLI^$>rT_4L^F{Fu5w8+J8M%~H; zD*pR+EAbv1iM$+t6whO0Y?+mSU8B~$XO}ls><1dVDLv!-p*|64(D0H{xg2JFhzKE8 zxp4h@xxlXu=K;k+aDTPt%U;aVjqq=KmYp@liUWL>}(U5LkbBp*k@FdeSoZqgsMf>7#dZR->=DCiz`&@Sc{c+pcm z&eJ;>`zwLddRXq~-nKZ=Ayq6vLIWQQx8enBchCW> z_k{z66?kGNjtoea9EGHL3d7HSV*Sh>3_xT5)tza4%HQ?BslxX9!mXSvc4U}$GTT2Z zORJbnqGe+j(LUJex7Q~hv<)iP({KOsny@avpE5FPjAFmmJ_R82s9_gbljn|&%x3p6R%Zj>HMtT5v9{-qnHuv zR46mv^ch;n}1kXFpM zZMcQTf5I_q)r4e_AzJc++y_#fGP??9o6iIDeD-Mg-Mm9HMc{JR*84uJVHVLw z314UyY!M#;@jIXIVxhU_bhwU%=!jqtxo@w_Pg598S>112a%>yRZ9`f5Hk_O~1?-_( zrsGa|PqLk<5`{%>j3Kh_wnP)-hO7+9j^BQoo@J$yTFyRD;^*cGdzHf^d&P`K7pWsg zZ-GCsf3{%veAl1fa+jPPmcxrJS=;g)_Ud7IPQ6T3GpsO13@2B?u2FuBFm&1P55NQ{jPf3)RqzzS;f%jnlS_t(>XnJUM^%j!hf9* zCGIi8H0Q1G-ME4gfp579FYIN)5G zS#cavl)8wWr~>u~5=)3ZIt2|Yv{mknP;z#+x` zu5ANjAbm@TlKU%h+wJQLb4F;hYz?rjj6!awr=4`k_E3+KWP9DFly9h4WN|02e;#M2`hw;n3(9A&Rq{5bpgrsn)pZd!R6t+;Yq}wza842E~lj)XX%Cs-BS2m9M*ki#qd~f}~<>$B2-uE^fP}YF9dHlC?Agf}_ zR{A*boyCup6YjwyEo*`#SlD<9{~ku&3?yb7V7q+Rw2tjyUP4-JCQF4O)CY%#$I0j_ z;i!FkSF^pzF@6jR7N%|*QGKjE3N|-DiRl|#rQoI5xTeFJBxhb!&#mtiZzWYReO8@N z;5d5q0CRFqG*lRf9iuBsOG;=Gkw)1-b~jqs1Cl2mE3 zTL)VQVBa^I!dQV5%Hr4hbfP3VlLBW)mBLSFQzRGp$DFhKIm zOhxv$UE;2kqbTtsef$ea_Z;^FC0?d)$s9AqRv7Ce$8i|k4dx56CI-iiEaE4Hwh?RG z8-)20BOUga0NRb0sZLfXi3WK?vBzH>_#)Q>#ZL~ z*792_Ds)J5_{o`ZMfrQKjt^fYj@4=n(#vf1$N!}B+ABkVeUs^*RNhxQE{T7TnHC~I zbBZ(-!pVvs89QC7_4_8zRZCvQ?c#RbMOiK*XMQRta44uIWf#qk_Hvb4q$tYR+yR!L_-ixf1nSS)7jJA%%xD(_1jlLE3rZnYR579cvM z1CFy~jSCYX)a!204l|j8CWSy93@ED{5~+3G{4wW(7Y8wQ>I0Z5V53*o>IgP=YLL1C zg*w<;IgCIdoK0PaO&#$VnM{HCkDU2Q?CFNS?p3w?@8QtBcthR`&n6JCknx+<>6kQo)?(y&7Nhjjr?i#xmqHMs1GDs;< zFtAomw;t4?FiK~~Jbp{F6^cd1tRcj#Q41MNVR??QF~YK@fwLgVtp77Cof%mRR_22+ zq0gu~rlpLWjxGFfH6NZ{w4=`q{(;VQy#h7FH#NiU-?|0ZM7+`HlOlevL>7k#wu^V1 zUrU+c89r0$8pdX{-q$5 zW#kU6Nl_Xr>Hd_j4+;7w^yQsddh`5}`5}!Yd&wLiA;T+6sDUkj=FD7@v8EJ3G9)<6 z&T-I!*KXpgdDef53at1)9oWJJF#A96P?;@!2fJ@yJ3Uj4Fq}JP#ASA_gvqcP^0;Ei zP%08~lvd?4^oeSsETt{9ygB8f^*wh`>@6(B&@+}g*T1(>F33tcQ(Qk zDLkPbY`b(i+3;qqJ!E=&QWPtH;h(Acmlhfzfh`3u7{qI>rsAM(TSoF1 z-YTT8g_i)(bC)^-3{{m!xmt%I{u7kUu1~7Or#~TOz~-x&M8LB2L>CNV6IiT@$&Am$ z3t9>J@&RXwlYvH`6xjoc^OOPWQy%5OGF&BO>p&;y;;9QlS;a2I6r>ZQD{z({UKa)F zElIyg3x^xs+K=A1J@;uoT8 za4tf3ngiR$zQ410U8yyou$r0JFDx4`b*-;->$6iv-5{2vlgH|+i{9gIW#;;s9k;gI zt2+8y>-AnY1QnQ{LEz3Gz7euV?RXJ*Nr7I%NPfXn9{dkw4NT4ISsPYN(__q%zN)y> zLq$HozNz-AEKM$MT^m359K&BaHoTB|a0{@-SSM8B#nAMOA8yHna#K!;8^}YQDj0$l z05h}t119&gkQ6*Ev|UY!bfa_LB8)Wp^fdZ8Ip_E>Zsq;YaRU7<`T6Hd@~$QTHEF{< z@F?AAVrPK-&|T09vw+)-+;jA&6&K{tXj0f(WmewY#X;YX6=s6&)vn9awxB@vZNu;H zBG%P8#G-z!|2MX5=yy{7W3fdmhJlrYvd%Zy{Y9fGH)Oxhes8WxGB+_!VpT0^3pmoi z??ik|mRd<7P~U%{+R>h-Q!cYAn0(LFBn+rWrqeL|xqc!)I_QQwg7kRI)7!PCEFHuy z`tHy3&TY#8TdRAmUh!g&nA@?|<-sZ|qQ_Tx*z@$`=((TYdXAUb`B*>HKdT95VLl!W z@SiZt)K$vI_w6cDCd&O6{mFnL7GY`*s^g+!BdjH01#>!3h9!7+FTEjN8d|wapbD$G z5c}90ksOVXyO_SgrJckAQ0vMf)v7+S6E;TgR^)u8kS8ukJv@x$4qlr9_J!s&6(RV# zcQ=naBh55PhrdmdITrt!Kbl%&M90bUF5yNl(GgQSK92m}#CRb?ibGYU1!zh?jKm$h z=J&{HJIe7m0P6pX?zF%`7jy#g{OVIvy7*ZTHud`zaxh5!w@R{MS)Iwj`VU`;R=gsC zUqRpn^}|9XY!mj8}?r;9FI%lWz9N)a+ixTeNm?7PUX{J`>KvWM6=s#za?t$>5&G& z4Y=}9XC zoq!veXnd~=reMZ&MD%jelWzM0MiKH8pY}^4RsbEl z6~m8a&tAL96B1&KSA2^(FNsbcGoheWgrLUrC-Ay@*F3&2EiiwPhPVR_Suu?jQKQ3o z=7AlN$#U`m9OG}y{*_7+h5g)4R#p$cj&f_*oV1%o1MNev*G?B{Q=yy8SAltn#NK-? z3OOTzDQCd)Q*NJ$H);h&U z&VuXWaLvm(>aKlXehuHQ!z(Qv9wZL17JRM+R^94&7ycu0O)?OjCuaQfBuk7dGne#J z!AE;4z;K%!@cxkXQ>@m&yCAJp8*0S*iJY%pKVDG#H_x(y{D&FX^UJ{{sGkvh4(707 zJ7%IMdoqJq(w@KMv3ikUfi4sipcS#38JH*4`PbFxQKglzpbX#dwYjZa8O1v-Tbcgf z3umn_M(9FMZ?9Zgp_84)?`6%t#qlhP(GBd&^Q}+jWQvR7Dw_S-j7#%EoS1cq=&CJ< z%Z9oEFP&5vo1k{GEAf4gR%3Iq{x&L~YoBYp%}Y3I7aWk9ZM_?O^ROT1c0s4G)Iaxj z!zIL^d=(Yv*;3oLFFy+NBT3Pi@hcrm@(Nu(2B4=jj0A8S(bT9U@Amq26SMjww9V%4 zT0WtX5+5W$*i$*tvVVU(nB5+73)qIS#BrzZd8v<#t=zT?v$q5+Wu;tYPucK;*p&aC zx+`ue?6-DggUem5bTJ0}y-*W~<^(RJ@oep{(#pEwoAE_toET3pj+JFL$=4eKkI%98 zYV&e~2QmXlW2CpY$2|XOrNjIS2!62Kygwu`b+!m!(aDJwm}NItd#6UpPV)cJ5dM=F z=1ZqEIXXih%=C0{#`0vziMZHUp#P~Rr!~ct4dZPU;>!;)2^-w`TY|9s)tP?@NjJP> z*NRC$L0c-R4-V8sbYq5coSJnXlNCy|UT7`1$Q{{p})%kWHw8ZKyLATBmf-}g-} zYX;sY(f<1xHbKA8uL;7PQ~fo|IyE9#Tx5?kgS8hT##s|B{!?wnH)y&Ks+N?B4*pDM z4@$eq+y&1E9k;#HUxt61r%3GWKj{;-Cy9cPTY3Li+NxFi6Lp4% zOBkI|W6Y~BQ7}G=A}8gk*S$n*UeZz8Xszr2=>iIk#=PDF?G$NbPZ>JKL70(>ZE#|s z2Ly_IZvJ8aB&Arb`OdHHu;|U;@YmBVR#$f1<}_=}%hOi~=4FF!PTbm{o2|*Okqg>P zZZ_(#CU8cm=D8Z#_-)gUAh;|4S8F8taUNy0lP+MuL z4T_4TrJ>=LDyk)-#L^CG392L_vbevwbLQN0&z<|6^Z(!To%j9T@BhB@zt8*LzT{xN zUtUcf1On~H*q|>1<9*;6klhQs%^oIFK_Iyj4BEow26&}JKaw2@>**i`K@1GmI&D=h zy&FD!NLo=*@sOe-`h04M%c7L7FXQ7&bB4Kv#V2zMa|ib|TZLa7WcJ9&?AZgB0n6m+ z=^O^!xs*L)JUYG5yQfLLhZy)sK%l%y5N##oNJ>c^W)VK|?ywSUJmGBe2!rX;CdRhg zwPb9aZ<%bS55{YppIrSA_2usR&M0DIl8MC$e?|^=XzuJ-&bGxzX_jA~^u;souATKp+MutxM{P;ZpesKbn?rd~Boeo-_KTOdnrj zj~5$>S!Z$F*rHtY%U4f@lC)(GIfs(U4;`nQ&LP}gUq0v(Q#&3fGKAMTxrFq#Y3Aoe zeYHv5M})d&zkVFAZbQ=9ElG5mc_!}s+}VAdqpW>jr)!l>U%a`VNgtGicC8NBqo&W3 z=F8&T4Np5b48jPTwZPhiS?)69++tGF*yl-|{c*4&G}0XwD0DttIc&u7>hKBoyICVw zkdcgm>T;48Ou`+_0ks~a+dXc-Gv%_^wH&mfd`#>!4SaO|>i~`oT_~Iy$?e?{)oiK~ z&a9Y*8#Hi&TId)2QqzVPVJf-Fpoh)=AZY;R06@|=Z39C+Cl+DO7Fq16W7g&vFK=S6 zT#bP{xh#xP{{sFyKvDp9*&oE@%5u%oZ?Ha?oMM*$Qc~S?vD!Rp`drv`JmTLjl&-g} z40c9SFVFv{OhWC)1UMKB+&=^a2JpYhe;X@)n0RJs?r}wc=&ih)bkQEB?Mg8 zZ+%YtB{wrum34SFb1`12?_$We=5*i~n{`)9a|F75 zNCs^=TeQ!Zv)AEAIA)1gq=bn4rtw@aGH=k>o%B&KHBiQ^CMP4l?RA#}XF~+GD!YLX z0RHqO=bYKbz2$r{^vnUT;#jM>B3#n@jjp9XqQ8u=4Z9PpO1&B!>=sVg>Zu+gl3cr$ zj?jgnX$Nl3p5XDwz8Y;SB`z7plR=rrJ9!A+_5k$<;52IK17{C-xne9ZE!i~3&BStk z^k#8e!k3MP*DCR*7l+CY7MCH$u)dxm&l?I3!@p*bs5b=I&EiXp(q+EpobER6n4zGe zI@>I6Oh)kSry7J$utn$Px4DrY3VhC5e+^eDdt}wFHxk))o%b3ErYReTL-VuYjGG&? zN3q1}S%ut-?G?uMJ}5&S|GH_B*u0np=eGCMBoLu)NQF~vJd}Pu7PBg;*mCUm8B?#k(`-!AphD|yEJPTOxRe`1GP#$?p+nmW znROkuWYn0-&rhOeg+&>WZ6VDQKG6Dwv)y)`#}Tg=`m%4ar@xQnejmAoeqoZUH^}@T zI2|uohamI>Rm=b=(%&cL+>^f}sH4%hruYT%?Y|)GwWifM`i%+OeZDA3{RSs>UTrYW zju>DfXkeLr6=6`gbWCw_2>n!eu0fshjLMrGfuol7gPBo$!}RdN7H*D>&GW({z1B=* zFj@hn`t~zp*szAEU%%Pk(h(jri#DY#sAyNMxKZb{q4g2b_Tz~DjkK@h%LAnkis-iq z_@#)2M%(D;*VpsZ;k0_1GG#Q{7RUnN>a1A;4d5T?RAL|6%J=i-o@P}~6KAdoi_{X$ zbV^%uIXLSFk80J9mOu3IVDm-?ZMF+y#1H1v1v>t&(*=(aJ~kN%OH+3IouKH*4@0=n zr7Nm((0qa`_IbIK+mpHc$zR1h5B64Jt66Z{YFO;4cJkU-ZDh$^H$qTzD>e>HX7}aS;B(9f3d>KdsT*(vq`x`s#walF_$ifS&e9A zCtE51v#PMSjB+ocyK3Z)EA38*Y7q6RHXmnJ3rvlwJacq^jG!a?n7Tpz!Ecx@M*020 zW?IfhX=PSO>bgQu@Mw=GertTY&(&o3;rz9kh~2Mup1IOd7$)KMUh)(jLxRD!Q4El* zY~IUC>FQoUy%xVRxLb^k?2QXhNZ77LRyG;HUy%Q6{EKz8r!?#}oZ$9x%O9j#SJaQo zHu~?1@}KJbJ9(@Zm;Y*he%=Mr*wkbtC3W%$2psUdeq&;}Z{8U*M4M+Tbvwm44O*|L zPv~RCCPGGz&PVd#Bm5>IacM1e-%;(rHNiPmQ4Ech{xfkRX-)Q^L9~dP7ZT+m-vzkV zaE}X(Uo-_AlPAH!T|@e{`|*Q9?|Bosd%@zGGxk!Zn&8xZ>ZcD}nPK(Qh-*{2w=o^o ztQ0`U-CAUb0>b%`sdj>CN$V|?MlpYLZFL`iqvh@6Q!BW%g9d$!M9po!>?#>;KkNNo znrn9`{~xr=F>Y00xq|P&V(5VF1OM?fBrGdA2I)V<&G0fN#Uc<=7b{(dxC&Q72dq(! zfkV4n{aGHWGD}7;Mo+zXE);$0O5Yc>W3*dxe`_~d7OtxaJB(L__od_(%w)gN{F;?C zOihmHOzPX4?j!{a_J~U<8>L}R)9Hl6(066>;Ve8coUdSNX19*ew{Y6gKXf-aT#4y@ zlpMB!xaLuls1mCweq5%9a(8Yhp~<|%>8T?7!we-OtU zU7*JS%ENIP1@ad?p#Hqca?DwU71(@GcWxc&v}qL!dJDW&?LHHyiBoUPp@O8fqX$i? vs~AUoA<(ZuVy!%4?W2Q3fTj*)XmIM3fuZ548P8@0U;|;S9MH9v{we Date: Fri, 8 Jan 2021 01:06:24 +0100 Subject: [PATCH 73/91] [README] Follow new convention "pico sonic" without hyphen Also updated comments in code --- README.md | 6 +++--- pico-boots | 2 +- src/ingame/playercharacter.lua | 2 +- src/menu/credits.lua | 2 +- src/menu/menu.lua | 4 ++-- src/menu/menu_utest.lua | 6 +++--- 6 files changed, 11 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index 38ed2016..c4d47b6e 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ develop [![Build Status](https://travis-ci.org/hsandt/sonic-pico8.svg?branch=develop)](https://travis-ci.org/hsandt/sonic-pico8) [![codecov](https://codecov.io/gh/hsandt/sonic-pico8/branch/develop/graph/badge.svg)](https://codecov.io/gh/hsandt/sonic-pico8) -# pico-sonic +# pico sonic [itch.io page](https://komehara.itch.io/pico-sonic) @@ -14,11 +14,11 @@ develop ![The 8 Pico Emeralds displayed in circle, each color corresponding to a color on the PICO-8 logo](doc/all_emeralds.png?raw=true) -**pico-sonic** is a partial demake of Sonic the Hedgehog 3 made with PICO-8. It features a simplified version of Angel Island Act 1 with some tweaks. Various classic Sonic games were used as reference, including the 8-bit games (Game Gear and Master System) which have sprites closer to what PICO-8's resolution and color palette, and the GBA titles which have more clear-cut graphics. +**pico sonic** is a partial demake of Sonic the Hedgehog 3 made with PICO-8. It features a simplified version of Angel Island Act 1 with some tweaks. Various classic Sonic games were used as reference, including the 8-bit games (Game Gear and Master System) which have sprites closer to what PICO-8's resolution and color palette, and the GBA titles which have more clear-cut graphics. The project was started as a personal challenge and was meant to be a fully-fledged fan game, but I eventually dropped many features to focus on Sonic's main movements and the exploration of the stage. Consider it a technical demo with some exploration challenge. -pico-sonic is a fan game distributed for free and is not endorsed by Sega. Sega Games Co., Ltd owns the Sonic the Hedgehog trademark and copyrights on the original assets. +pico sonic is a fan game distributed for free and is not endorsed by Sega. Sega Games Co., Ltd owns the Sonic the Hedgehog trademark and copyrights on the original assets. ## Screenshots diff --git a/pico-boots b/pico-boots index 73ae463b..aef6c8c3 160000 --- a/pico-boots +++ b/pico-boots @@ -1 +1 @@ -Subproject commit 73ae463b3fa71571516ab7525d149c65856b5a1c +Subproject commit aef6c8c38f129cbc6b45d652b529ce8a9b96c5ed diff --git a/src/ingame/playercharacter.lua b/src/ingame/playercharacter.lua index 1168c961..cdaf03d0 100644 --- a/src/ingame/playercharacter.lua +++ b/src/ingame/playercharacter.lua @@ -349,7 +349,7 @@ function player_char:handle_input() -- this caused delayed lock such as jumping out of lock situation to escape but still being locked for -- a moment on ground, or falling off a ceiling and still not being able to move freely for a moment -- this contributes to the feel of lack of control after falling off and may be desirable, - -- but in pico-sonic we prefer decrementing timer when airborne, so after a long fall or jump you + -- but in pico sonic we prefer decrementing timer when airborne, so after a long fall or jump you -- can immediately get control back -- to restore original game behavior, uncomment the line below and comment out the 2nd line below -- if self.horizontal_control_lock_timer > 0 and self:is_grounded() then diff --git a/src/menu/credits.lua b/src/menu/credits.lua index 1fc35e67..29799fea 100644 --- a/src/menu/credits.lua +++ b/src/menu/credits.lua @@ -60,7 +60,7 @@ function credits:draw_credits_text() -- top local y = 2 - text_helper.print_aligned("pico-sonic - credits", 64, y, alignments.horizontal_center, text_color) + text_helper.print_aligned("pico sonic - credits", 64, y, alignments.horizontal_center, text_color) y = y + line_dy + paragraph_margin + 2 api.print("sonic team", margin_x, y, text_color) diff --git a/src/menu/menu.lua b/src/menu/menu.lua index 12df0d42..d74755a9 100644 --- a/src/menu/menu.lua +++ b/src/menu/menu.lua @@ -120,7 +120,7 @@ function menu:update() end -- visual - -- no vertical arrow graphics in pico-sonic, and no paginated menu for now anyway, + -- no vertical arrow graphics in pico sonic, and no paginated menu for now anyway, -- so replaced visual_data.menu_arrow_anim_period with some number 2 self.anim_time = (self.anim_time + self.app.delta_time) % 2 local anim_time_ratio = self.anim_time / 2 @@ -243,7 +243,7 @@ function menu:draw(x, top) y = y + character_height + self.interval_y end - -- no vertical arrow graphics in pico-sonic, and no paginated menu for now anyway + -- no vertical arrow graphics in pico sonic, and no paginated menu for now anyway --[=[ diff --git a/src/menu/menu_utest.lua b/src/menu/menu_utest.lua index 99a44738..15b3f5da 100644 --- a/src/menu/menu_utest.lua +++ b/src/menu/menu_utest.lua @@ -245,7 +245,7 @@ describe('menu', function () end) it('should increase anim_time and loop around period', function () - -- no vertical arrow graphics in pico-sonic, and no paginated menu for now anyway, + -- no vertical arrow graphics in pico sonic, and no paginated menu for now anyway, -- so replaced visual_data.menu_arrow_anim_period with some number 2 m.anim_time = 2 - 1/120 @@ -267,7 +267,7 @@ describe('menu', function () it('should set arrow extra y to 1 on second half', function () -- impossible situation, but simple enough to check that extra y is reset - -- no vertical arrow graphics in pico-sonic, and no paginated menu for now anyway, + -- no vertical arrow graphics in pico sonic, and no paginated menu for now anyway, -- so replaced visual_data.menu_arrow_anim_period with some number 2 m.anim_time = 2 / 2 m.prev_page_arrow_extra_y = 0 @@ -647,7 +647,7 @@ describe('menu', function () s.was_called_with("> extra3", 60, 48, alignments.left, colors.red) end) - -- no vertical arrow graphics in pico-sonic, and no paginated menu for now anyway, + -- no vertical arrow graphics in pico sonic, and no paginated menu for now anyway, -- so not drawing page arrows right now --[[ From 5749f6b004faadd6391f1d78d1a8793b4315e1bc Mon Sep 17 00:00:00 2001 From: huulong Date: Fri, 8 Jan 2021 01:27:21 +0100 Subject: [PATCH 74/91] [ITEST][TRAVIS] Headless itests don't log trace and trace2 to avoid exceeding max log length in Travis Reduced itest_stage_clear/intro wait times to avoid itest being too long, and Travis test being too long --- pico-boots | 2 +- src/itests/stage_clear/iteststage_clear.lua | 3 ++- src/itests/stage_intro/iteststage_intro.lua | 3 ++- src/tests/headless_itests_utest.lua | 4 ++-- 4 files changed, 7 insertions(+), 5 deletions(-) diff --git a/pico-boots b/pico-boots index aef6c8c3..f9b495be 160000 --- a/pico-boots +++ b/pico-boots @@ -1 +1 @@ -Subproject commit aef6c8c38f129cbc6b45d652b529ce8a9b96c5ed +Subproject commit f9b495be7eed82ef96c160ee2e6d9d0dea3669e2 diff --git a/src/itests/stage_clear/iteststage_clear.lua b/src/itests/stage_clear/iteststage_clear.lua index 52d5d39d..1d526716 100644 --- a/src/itests/stage_clear/iteststage_clear.lua +++ b/src/itests/stage_clear/iteststage_clear.lua @@ -23,7 +23,8 @@ itest_manager:register_itest('player waits', end) -- let stage clear sequence play and see if nothing crashes - wait(750, true) + -- reduced frames from 750 to 100 to make it shorter, although we won't test the whole sequence + wait(100, true) -- we should still be in stage clear (because even if we load() titlemenu cartridge in headless, -- it won't do anything) diff --git a/src/itests/stage_intro/iteststage_intro.lua b/src/itests/stage_intro/iteststage_intro.lua index 733d7743..04bd026e 100644 --- a/src/itests/stage_intro/iteststage_intro.lua +++ b/src/itests/stage_intro/iteststage_intro.lua @@ -16,7 +16,8 @@ itest_manager:register_itest('player waits', end) -- let stage intro sequence play and see if nothing crashes - wait(750, true) + -- reduced frames from 750 to 100 to make it shorter, although we won't test the whole sequence + wait(100, true) -- we should still be in stage intro (because even if we load() titlemenu cartridge in headless, -- it won't do anything) diff --git a/src/tests/headless_itests_utest.lua b/src/tests/headless_itests_utest.lua index 1f5838d3..d43c8abf 100644 --- a/src/tests/headless_itests_utest.lua +++ b/src/tests/headless_itests_utest.lua @@ -60,8 +60,8 @@ logging.logger.active_categories = { -- ['log'] = nil, -- ['ui'] = nil, ['frame'] = true, - ['trace'] = true, - ['trace2'] = true, + -- ['trace'] = true, + -- ['trace2'] = true, ['spring'] = true, -- game From 3d555b4ae863e2be32cbc23cb602ee704cde8d0c Mon Sep 17 00:00:00 2001 From: huulong Date: Fri, 8 Jan 2021 01:34:43 +0100 Subject: [PATCH 75/91] [EXPORT] Fixed 30 in cartridge name changed to 46 during batch number change --- export_game_release.p8 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/export_game_release.p8 b/export_game_release.p8 index 24fb3b2c..0d3f3d07 100644 --- a/export_game_release.p8 +++ b/export_game_release.p8 @@ -23,7 +23,7 @@ local additional_cartridges_list = { "picosonic_ingame.p8", "picosonic_stage_clear.p8", "data_bgm1.p8", - "data_stage1_00.p8", "data_stage1_10.p8", "data_stage1_20.p8", "data_stage1_46.p8", + "data_stage1_00.p8", "data_stage1_10.p8", "data_stage1_20.p8", "data_stage1_30.p8", "data_stage1_01.p8", "data_stage1_11.p8", "data_stage1_21.p8", "data_stage1_31.p8", "data_stage1_runtime.p8" } From bdd81fe244f04fb49a23ab98895282fdec0602ea Mon Sep 17 00:00:00 2001 From: huulong Date: Fri, 8 Jan 2021 01:58:06 +0100 Subject: [PATCH 76/91] [EXPORT] Remove existing png folder as PICO-8 will not overwrite existing cartridges with same name without permission --- export_and_patch_cartridge_release.sh | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/export_and_patch_cartridge_release.sh b/export_and_patch_cartridge_release.sh index f5d2abfd..b2e98a26 100755 --- a/export_and_patch_cartridge_release.sh +++ b/export_and_patch_cartridge_release.sh @@ -17,9 +17,14 @@ carts_dirpath="$HOME/.lexaloffle/pico-8/carts" version=`cat "$data_path/version.txt"` export_folder="$carts_dirpath/picosonic/v${version}_release" cartridge_basename="picosonic_v${version}_release" +rel_png_folder="${cartridge_basename}_png_cartridges" rel_bin_folder="${cartridge_basename}.bin" rel_web_folder="${cartridge_basename}_web" +# Cleanup png folder as PICO-8 will prompt before overwriting an existing cartridge with the same name, +# and we cannot reply "y" to prompt in headless script +rm -rf "${export_folder}/${rel_png_folder}" + # Cleanup bin folder as a bug in PICO-8 makes it accumulate files in .zip for each export (even homonymous files!) # and we want to remove any extraneous files too rm -rf "${export_folder}/${rel_bin_folder}" @@ -47,7 +52,7 @@ pushd "${export_folder}" # PNG cartridges archive (delete existing one to be safe) rm -f "${cartridge_basename}_png_cartridges.zip" - zip -r "${cartridge_basename}_png_cartridges.zip" "${cartridge_basename}_png_cartridges" + zip -r "${cartridge_basename}_png_cartridges.zip" "$rel_png_folder" # HTML archive (delete existing one to be safe) rm -f "${cartridge_basename}_web.zip" From 87c5d8eab3edcc6a2b332861c47aff5dcf9bce68 Mon Sep 17 00:00:00 2001 From: huulong Date: Fri, 8 Jan 2021 20:02:16 +0100 Subject: [PATCH 77/91] [METADATA] Removed version number from label since we add version to title at export time --- data/metadata.p8 | 16 ++++++++-------- data/metadata.p8.png | Bin 7584 -> 7489 bytes 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/data/metadata.p8 b/data/metadata.p8 index 884e4e6d..0b146a94 100644 --- a/data/metadata.p8 +++ b/data/metadata.p8 @@ -3,16 +3,16 @@ version 16 __lua__ -- pico sonic -- by leyn --- (title and author above will be overwritten by add_metadata.py, so don't mind them too much) +-- (title and author above will be overwritten by add_metadata.py, also adding version, so don't mind them too much) __label__ 11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111100011111001111111 -11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111010077701110770110111 -11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111110707070011111070107011 -11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111110707077701111070077701 -11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111110777000701011070107011 -11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111070077700700777010111 -11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111101100011011000111111 +11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 +11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 +11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 +11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 +11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 +11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 +11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 diff --git a/data/metadata.p8.png b/data/metadata.p8.png index 573ebee7462361423d3b4f7b31092dac7f0f3beb..8e41377ff1cd60e42c483d7446a21a408ab08137 100644 GIT binary patch delta 7413 zcmY+JbyQSe)c5bu9nzfw(jW~|l7a|II7sP$bO}g%2PKpS1wy?@J4Skn8?`B|oCuiD6lH~>$A>g;hdDKjg8sCowZ%6;y%lNBNoFa2K z^Yl^`dsz>5$vkS8VWzrw{Vs7N)xO@6?0n`LCdnT3a$Cz(*J(Sy%!K4NB**xaDqy)- zEbK5H{>Vw!C_taXSkKAg7C=S{U?#d3&3%ml1Rrh--%hTrd$VdIAE>m)HCnVCbX?Z5 zic{G{8<7D6W6mce{X679Ti^*r$n~yUR%$ue?7{76LXd+Qy~MeP!#VPjpQNFJDzXc#{Emndxu~y&G#;i z8Y3_FTvk1g%Uf06{&(jyc@!{pSY8wViI?$%PdRVfnzfnGMs~*V>XPLNuiF+p0{9Ge zx64^e?0k7d`?VIWA72+A%t-pnh*u$oFER|dal+ebfUoG1?bWh+vXj-`mGCGAy+7rL zf$9?UJ7)h@qD2arHWGCX>?-6Yju`Ph)zbrn_yPJ`{}Y&qa00ky@EwPbKi@4CmgLe5 zPX6=&2mrHlP2WXjQU618t;&%VvJ}Ah{|-uKV@qaZ0{;aRzQ1LkR=U06`>#u?B|9$6 zJ>~@|(134Mkw#%rOqaz=lNrD;7U8>G1Z0=WA50ysu?s_?GDj?yoI6)C1S3V*cv~#{hY*<)%V(ELNCJe z4ddSeM#bV%G!Xn&Kq%H-S9^7zzNq{!g%&5MBrr)=e=P`EY^KGZIi?=0HDC_h2XYlZ zL!J^2`ju?oL6`L{tcY9v$fG()Ede?{iAGCVJf7DgI5CI@TLaeoS6O8<85){%0laURrJy2ZyF5|M1l>_OoyB@xtQ&ziQXflAhV;%f{c{4rY z&BRfLZ5I?;`)I)ycQp8F6PnQfc=Nw#H128?u|YrG|I*0m)dF*6f6vUAo`hCX>ujr+ z3I&sbym92FiYkR3;A>7roj^G#_`8#hZzD2r*h1@GQ|Mj)e;!-#Lj0$uqt$pUkrj%c zPd(O@7{|YbZ5|Cpk5vUWv#k=o9eVDneNV7CDk(6L>^Zbf1Yf<-v~_iSH)Eroc;jd% zj{N5uc3YD8#q!dM^`pR-kj2?Y$jDNOf<#Zl$$_xbg#*p)d;q@u=h^U(+b@bZMhdi(9o1<^$XAY#a%bcr6rh~ABz4Y0ANxK7q0O!LMdh92M}{=@Y%Yt28I#4BnoEOL zR~(=|eM+U7G`l^DDH>J2mcM5ZSt^R_;I1Cf;Q5-wUcApDS4!!F3I!^Bi7Dij%1I1p zoSasX2TL1fmVl)GXDN*af{lFcKRMr3$ZfRNqEmDz$6Rwgkzqo^;1fC`QJQlVfDa8uw?vh6mLGhvkfn z)UefknMKwRE58|hu0=#XEW50cv4wOyaG5ishqbAu0K?z>T>n@4u8oPuuA6 zBtl5>VFkwpu1;Hb1Bh~Kr#NMTim;36D_bMG8A(R|*L2M+{ncXLH9cLD? zpR{xge^KW=L~Rryfj{*2M_TUdP;>PFh?VfE}QD$mU1>qwzXu=QgOx$fJYY0o*+ z`fIvzv5wD}P#z1S45(j_KLrROGpa|Sx=)pfEZf)Q;tIFFc5p(tsE_-;Nvv35c$Z?D zNSxzs6;;H_;vIDV4g-Ivt!l}QDm<6Lf2`>k77%(gF69}!A*G_1;)CxV)f1R{AoHzY z?laPEnFZt9%1gO43iMe$cQwgeI^9_v^1yGcSe|!?l>gomF5$SRHeINg=gwR=kdwu0rqdA2js@NcvYpY`;l#I z1yr7T9E^>TMN&p?(mpS^P|SjBXqgri*xpwEeCt$v2i4x-LrihMk1QEOdfBOxfpZ;+ z?Cy10RcXXQ7oT}+5XF_}Ig5&$5E$}dfuS(Q3)P+v1+$1>CAS-&GvbKqDKS;Yc&HXg zqDCK+3kOL{0;@13nMo?%dYIUQ8{pGr(oKKVOFT1Ua%Wbk7Y*p?kb-533%>y;zAd$S z;@O)1AcXTo6!1vAALRMun9-hMcC#Iq-wd)ObsoZEkC6|^=key5iJb%_CH>8bxReJD z4Nn34#oY|-t{A*beGc`(BY2TXQsid1Q)kJ*jqWXm>}l840Wlc^tV;eJ$BKk&aaHeE zY4q9-HQArRN`{3I?FTE`pqK}qqh;^GRN=fl?wCmZNwJsT`OB|tUmCO7x_Dp64Oq!V z%AQQI`jo{nRcr+I8o>=f6=iIjjb<%Xz~E7b<7Pea)!(MnT8XLwvd-4#-(p37B}V=$ z^MvMvX?*6n^SWhEH6FMYkPe5Jo&WZGQ!{#zuj;R4n{2dR{B;aGjW)dAeH&17EiiZ7 zn&@$E@%0=F*{laoB~dfi#Vu16wcC_Hc*#U7HGMckW#+nCXoQ4>L24+?|1Wz&Zg=t@8=kii}&1o3VHroY6 zd~BEs&slXoQFoaLc)#Ki47Wia^Tdki4dAN`^!P4A(BG0=jc5t2o}NQ&ac_OY`h1eQ zk&W4L=8>}4HjCG?WROa|Ydf052r|S{7>k?n5@FsD*}(lqzO>b1H+L@ca)HmI1WWB~ zQ*u_l)6gA(i2@oRMqEy0{J+Et0xR1Td6r}Llu~UIuVVmVD%Cm{VE9-1a!*y&lU@UM zx?;&c`mS9z@3nsf?K)6)`b|wl{APvEG)rZnBFc*h^6!X*+>qr9Q3Mt6UwfllxWYc* z`J(U}&g+-r$_RU-_&bpwc`4uU6n?^ZPf$~I!Kq|QYGgDHRSOC<_Xc`ENtpjY?5rjr zy4{0_k0?)V`9S7IHeyF4gn?rKPu`p46A-d`;7S^^{d86(V;JW1kLAlTGvD`k*nnoC z3qUoOA>U*@`EIC6uq2a>e)2z%<8Pa>)~%kLNcF$DQP1IA^SH(A&pY}w|FF4tqh$S5 zf>@4)UXep8%h(FcK}zn7gS^LP`$he-gF&3n5+y_C_vbHS2?J(TY;Vd{^_?T|z;&}^ zM{y|#apHBhw=S!>XgrdSa`Z7jrpK!=RIeY3s+CA;`Y2(gb|3~iM9Oli*Mzn|HI z!Q)kX;Y&HM3)Dv>ne|r5+UUUWYOx^2_~vh6J?K_dJ^7X;f5^of9rG+a)h6pK&~DG~ zPdVYDoQEN}_fQwUE+70lrhjSEMMI(TOAm8)*!dFUS~((i4C+>s{x0(QmW4IDa_%rR z_nrN3>hqb5-yi(8UUBcGgxP;swAK}`upAmcnnsh%)3>4X-rlonW1Z6Pb+}`DWAo5l7a+*t49Pp%eJfp-QVtF zSOG}SQiW?D(vmK!E`0v_wsNhOgW(2A4`Otj=;>W1-iHSVy=?c}7+{;1@HDOr9Kt*z z5F+eA1a*ZC9d{m&R|ph$sOoy~!_VoF&s(Ibkt}1)iZ?Ppw#^n$^DKa?&QPjvdnSPu z%yOK?v*E*BB$*L^+OFH8MbI=7ZLl`;{0NgLnEQCc+}J#GOw3y7U7FRZIcD?3wrpxc zp_K4jI$yJWu(y#wX}&L6uzP1LM+{ zuKwxqjSPUiUt>Ar?4+72k>rQgpY;4}GG7M!)?6UtBVdlB7Pc!4PJtu&|HORzSTd!5 z$WbK2TnBgW1gSmulficigx9mg&a4$A3Oa^Kh?I#BaN_=m6T1Bt+o20w*%;>ZeX|Y)D86I* z5ZV~#f|HHSLnX^0ZEcgwP%5{X1?49L_eH;ckllO=_inmaZZq7))bq-VIzLOw zI5n)2_;tQ7Doj)kRR!xD{8xW}#dxZ^D5 z+rfzOvFZPGCZ?>ih80Y<<@j7&qSlUQO z&Da0BzQqk#5IE{1urt{wuLN?ycK~XyLhJ8EhT-{x|~8I zd@FLwAUCl zr_fKm%}d8Smd>3lN{6RcO+gAkcnG9)U`;{I2JZ17nfqtrXLWA{3@%WQ zpql@F@&|reQtssN?s`)DbWY-Af1pwX>m_cW0DU}egIi%*{JeA@^pg31CX1eK&e*K@Px<$htXYdY59M1WXvt$8<1CztQ zObVK!HU+=t4%a3Nz=*?BY$IS__}M%kK0muFZFZ6fV*|v7S(AaNC_as&OZMTSx3Zm) zpCD(X8Ekv;Hu43%ao~E|ZK4;LgOQ2Dh6Xn#(Ovx~y#s_CB5Rd@^rqx<20ZacE7@K%Ov1Ne}p&Jmi0IMNb1%hyw8n$t>K6G&{Xo97k)Pkjr*~0 zn)xdIyFAPnQ=ffo<@rr8Esz4$Lrw`?_!ot_llLF%JtAw93~45Ke_r~SHz3FNtA(~G zIkD*Q0kbKTel@nqw{tU{kh!6RJ(~>3c|5C1RHtHhL!vGpAoBfUFIe($DY$H(3^?h! zj2=m{>0Z@7U+U(y*3;TIvi7x`d6rwElkBK|)4=jSQHc1p#<^+6 z-V)M8EAdNi7RJ#V&58yasA9FVZlBz)+^QO7TZJbbeU|gOeE!zNfVgbEYUOY;f<jl!fzF#!u9CN*^8lk)zYgaxRt^2o(=6-9|J!omYG%iZ~gmX#AWF z{oIP(r$o*^2czb^>?fq65<|HNr`bpRU#cBn_>2@gn%iABYlNS7smCg9uWAQ77}17` z&q87tm3@=h;w?3Vbj(8u2lK5GW&6gDfXp%|NKs!&eq3&a5R5tT_W(NP8M5dId8qlo zy=`hbnIcM{ZTGvKO+r~v{y-#8^!e(qHPt;Xc2X@^6Lh7Rx&1nj82Ql#-k_WF72As z4Ab^Uh0!+Ks9G_mEWYVx8}bbbrXjGI`0tvI_79yr&?A|fV2d9>X@|fC zGlwUn1#9xytDnIvW3b_C|H`dOB+L+k>W(F6>qWySanw!~+L(v;)ZiKOcN#fvntwX$ z?DoLR=hYMQ9u(YM_Z>D7D7Q~r1Z>}J@WfM-BA4>-_{ta;4|O$jRG`{P)WI?4mBv>b zn?a=wIZ*-xBNlU`+ zJb%y$*^qy!Oi1V0Q0^KfdlOz`RqP-mc(%hzA+Z#i>A|6}CU+6HzN-5yd?g%Qhb(6N zWZiNqE?`LWB{pE8;?ebnhUOtOR~`}E+AGd{yBc6jxA92cRV8tF@dccwX0qCzYf8HQ}Z4Bkr1i`17&~Q zp!URFPma^euW@MUqHz-QI*D5x+#WR+^&gumE$Ua4>0*eFt>=&WUvK4GC0DE$ImT~M zw!RElFj+~kLCv}oe4l0#sbVtQ;U#Yv>WMhvV!}EPv&+hnc9b+8Fo|sPuD8IQixTTS z-(1ZDpC@BgJgSDlph1>&ksbzRN|bxdZq9C6cZk9yw#RA;Ik~Glf97u++Oh!XxT{$0 zG!LQ4{MbXh#Vt}h&J%nxg-Cwic5NP6(+xs+1p=aH6bVFa)n8a5C3XUwhmg%qEB&uu z*}g{>WMv+cLnJ9X%0`iUSBHIB&7o(&%P`&;keg&^I+*a`oW0@YI1aIJSSInWi#^d(<~$>6sq)~}s?V(v=amBW*x>9DI{&sx zat35JTu;I7C9x%!Or`HM*-LW>`GpP7mV_?}=*nhYi&I9|9!;(9 z2TyZ`XL}Kl|F=9;2JOYwnKk#>&Gh8Wx^e5Rk2272n5u-?CCxKByr%-v5k%Dai8s#3 z_f@}9zcq4FVhSw>8{I#>6?bhn4)pI#L$C-YYFAv1ml|YodXM2q@h=X620IwE|967_ z_x(4q+3`;#OhX zYIB}1I*?|3;=r4T(b5vQN`%q-AB){mV1Phn5~wEUVmnGEMTCnD$2uJ0B?hOw2*DnLg!~ zFo)^_gfLvQ2~G`MKK@fXN+U-y_PzP5XtAC^PV5#&)voW+3TCj^7YqMO}`#F%Qm!s74--g-lQB7{pRW# WKPAJG?KAPA3+lU)^l}>)xlc{gfhOJ>ZdO|{k)DMkJ;gJMfZ|<`iRvGNI2lY9Iny3Cc9s2+r>5U_wU0dF{%@C3MqzAy6fg z?URr<+y(`*eE%+aovL^pdSdT`1%G2&m)fk`wKH;8=nN|3XEV7!-tKirjQE9o*vigP zHE$g`_J!vrPc7f{US)|^iURmVB|BOZ`K2TOzyQ9z7QN|DtK?$lxgn_)i>>}Ghw}as zIV_$2M?Nc_Qz!Pn4=ZZoC)lXpdseV@6r1VuZ02MR7cH8cu{m!dB7u+Kk4|y31n;3f zyqB|U9DOEzvN13RZt|=j<1Jhf>rCDLVR&7%njIvumOaTI{j z<4Pr5Icxvq=@w6gd|Z$u!AUT#(6RS+_{v3)&1iiQQldXZ#s~V;z!uRCFws3%zKiVJ zxbph|)gW=Md`oBmE~P)W`+=!eS;nH#0=@DR9^m@t56>nJ#6VU7>jz~*Ic+qaR}aKt z)Q>IxBeuaJLh&0gcA(MSuflipc*jqszE@b{eX(B*fp@qq>`)5uXHYaDsEUUQ8`K^X}Ma0b}No1~`5x-Q(`^Ju4Zjw2p+M zrbi)rDV~((sHnU9bc!bfFq4#w<=OqW*0I|G=y?ZWt)RN7Z-{3B!-rU?_OHzQ8z)-{Uy zCPL-_uD?^$?!RThMjWXSF>=cYjR{y4c48q`EpaUG35^h&Y6rje1QS}^=cQus+~`F* z2-?`<`=^SZuNx6FmQbDVo`=Iu2>&`}my7uewMG7&TzwcvUQ|2WNaB&xh&ZI--aX<`F}C2IXOv%K7erdl~~lX-crRY=Izg!ir? zDu0#c`-b3@8ZJJ*0r#$nQdIb_iivLIqD$<=FZBhTL&)to$K-;2$)`k6MclFvHHseR ztsn$4J-JCh^)Y?>8n-qQ{Z`wG1oRVL$IX z9prTHTG=oaDjFH=3<5g*lWq^Zuc(`MAOW&=%Zf=C$I?Ba@9fX*r*(6G13YKCm6miU zof@U*4>fC@b{y@-@xy z1s(0F|4TZ1_Nm_9Z-Ts9kp_3HRR5Yj$dcA#sHL-T+ovMtB2l(dFdt@5~(6fF!8b6*Zz0?8Po#BxFd( zYAfi_fQ~&PGKxatclkVx$#NLKJPy$E+wdi30s0^Rg-{NBH{YKX4Oj!mYivFah9zJl zP2+(s)}-a09IND6vb8zf{b#-k^%znN2^FFY}%;*ukP z{5`J$z1Z_gMB9#XfuuI5iC5Kgl5|?AKctq+d=CMO-)JEOXck@mb*jyeK8meEI4D;# z=0yF#LRbefzCUk$kuEN=CAna9c#&ePI!<(o5ByhEgr(YCiy0{@E-%0bj<2y9_B}|4pqz;O4Cx>BO|s-~@N*mF9?ZN2@W(A;Xxu zmXNE>^jI%NVoFntzn3#bo_V^rC~i%PST~^ua~AI-18=rK2YM0FY+)52roPi^>RVhU zcW1rfH1(u=GWEqW>%W`S@lPxvg=TU0h^<%lMVIKkOugsA$S-FP{{(fwog{XF>LW5b>ojEW5g>f1V}DM8-*;VCqH)z?z1iXCT%+*R*7!{9yQo zc8DF>YIIU&?nqhPr8>+c?HsL9oo2U)oHYCv|EVG^&YY_o9uxNI|#>=+OS#DHT zy;XcHUy7b6>{`VbOw#GDuPnMo^HR$5<4dU~6cXYso zzBikT7rl7pB^HM+c^r-4ZL76Jm#t(@mOq;Bz6q|( zQoy!x^Ujm=s|wCNjqRkaQ9;lr977&n(kkbJ?04ZIq$+2wU(e?R)e(Gm1uf6@K zLd>7*F^7K=`Vh;#g@$-z)A9UHM|r8KtERoBpK-_-jnXA{0}tsl_iJQ!nAOTM&fxPd zq@!C>_aorg4i^Zw#~-zV(CR5It5h;*qaEC!ZR`!`yr+7Mr*|;!S0bm?(8_H`DySv$ z_6cht;XQaa7Vm6c7a@gcOiF^6ssKuRVD^5wo~95XMFfr;T5{hs(C%BNUV(>^GkxMkqSe@Rsm^9t zMQxP9mxpMsgvH(9Z`_A`#GOIRtedaf;jEYS;a0uiZYo_kgkaUa$2h1AJoL*~8b&hH#j8@CGLQN`=5NFbS8rj`SnmrL8Yl3`P68E>C^ZaA z@f1Ov{KWa0*&l$>fr}fn*rdO!f0IS*^+lSw+3YB=Z)7%qmKRsBm`2G)&tp*7>b2LW z1pD6vmFnrYetAh;liN!j5jn!Lke=HFBxKMGj*f}G+1FHX;b%&bdHeOPFRtBsNJuz@ z8;CoTO|Plu#%X;7nGt6>Y*X6hVr!6z)hf1fep2g**6FcTOpA7^6~PAsZKrh4mPx1O zW7cNRC0pm}{V7eOKFWUnYPl`Ppk@WQf&~4=*c+OUsAoqF>BW6p2Ak;o#~d@3O$Ek9 z`_3Wo2CBrV&=D4B1FY?X*S^&@wo>Lk@~)yMge!An&q%v-jiZ$qDWz$ z7i)l`vnAftq%I>(s_nO*re{g9l$NuP5$RKlxSjGrvYkR^Rk&kVkb18spo)H+7eUm3WrYlKhs>`r zBO3m z$9P`%C-uz6j*YlD6!4DVX5m6hGc#pF!xx`AP`_HaN@JXq9>F9Gc zF(212gfE>|zN)UwE46R(|H=APETwXl6`G=3Qc+Q`g7pV{pYTlVRSU{%;w#YjdEEPC zz5Z5RB2@ialSIJ--6TF?fL4^j-j7}EHcAh4PRMU6i%STj`KiCCP?k|Dx@t~kPQFQ} zkrwBTKALPQq)zz~ePQdEi#rl*EAZO?TW)R({cU&M9(5IDlh1!M3$`r2XswTj*qZ-X zKIR@g)U*N$li}bJDEzw{b~BKiu7ht2T+%zXLivg5wOOnb2GAcI>h33EB*Rhr`lf1g zoon`H%LuAtDITgD%?n@VEL>%roeUZ;u&l$ zQX9G-6CZ~(QymX!rWnX9Eh{UXY^8Y0m03|K<^^(JORs3szN|$0hgV3cG}x^|Z31v_ z>&@Woz!7!fOZ}QBk__`S|7cFf;{b-2C5NUACX*4iFs(;kjK?(XVnc91>cm_{_P1U9 zwzQ)d={bJ3;-hxKd{NBM8US9sTmbHj() z?7?#YgXDR#lQmlMOu-~76qD#Xj);8R(HDEZsMSD;<2+`m)v6JWEA=optzDg!+@`WJ z9r7$eN>+R^!LEzL{TJ~g)mr_GG8?_IKN-AsN{~?BM8*f@x8;rt5+7tH9}y8T;-;t& zkC**O+v-rQ-8FrxTJ$1j8^7Zw(rOU}PW@Dl<5Eyf$YuE~HW`CauXITpTp_ofLPbAI zWs;nFpPV@+ZWqX+8Jks=y(FMV-*LPdP=;i96{F(vMUbJRL0$F{ z#E2@09M5z3z{&{ll=iqz z5;e|D0nqEF-wr#GiY|#r9SSI`?31XqU;VM*K@{V_SW!x#f@?1azLAEE3@(-;cpN- zI8!=caeH2Qu=4j>CyB!t2~y!F74RgnsbikeFjhZZD&-`IT66)A4xpaOp`K)`7R@2p ztCqy4e|xHWUVZTNLJ+)W|M93THN-LK)AjLIWl^Ua$bPqR)*f~C*5UzpYgkM~7tMi+ zChAYLRZcDZK~Y`6Qz+0`Y(rNKO~<3KF^(z^vhet~_n;klcXJ8iR>PDHIM4=3c?t$L z%BeQ}I#kA~oY;r2={7(d3RaCrtQys@{v@`iNLynZ8#)9Vio)i<$WmER)lg*t7z^gM zs%=u**y+&H4`1`{;aTfOOUysWsjgR`hQzvNnEh+F0Ne0a8a>ja@0BQGkimAbj=rY8C8%B86K;1 zj8Wc8H;LsOn?D74)`Fk*-XMgqdp~c{n9qHOx^G@OflpM!4QCFS@mZbA;WF%oe6CnB z)QZGh#TB_sJz|<@tH+jF-rRCg`kq^8&L%d}($*ss1?1b-<@)Bu+^c%R)YHPxTWewQ zRGvm29NP>!nTSTM9aL&-LL|Fj{-253=aw1}p$!EuILvFMtC93XU=lLZ?N$S%T15!v zUn``7D`7=|(Wy%f5tgdTy=<-h5dSf1R@Vm=5|f`$GEj@Qf(N!P8tNWosZu7%A0-psl0l-sYu}VgxzEkD!B! z{FF5)h$uz@%DP8d9lC3n&kdP4(=@DC}#Jk*Pf##c0M+DwNI*o z*;o&U1N_I#({+_{34J>Xlu5GxMSU`$ibk4QKx%ksIEbr>*rD7G)S+?SoeQr>7Y3GY z;%Gvv&cr`Sl-(x%^YBidcYbxK)( zGqydN_?lZ_5JMeMilaG|%`e}dcPz#EOpSbvOYDwm276z(WPvQmqQ98%PCakq90a)!c!IY+~ zJYs9%K{FGBkQ;?rPKW6Jw2*eS3Ra6qPz+3R2N4!qB6+V+0-DD<0n$=2*WxhPeZE z8E@;$B8LZaECSmi66NFrxJF-@|0|Xv3H`a5sH`4#8R=HHK5jRS;oQ4!udNR9hI}`f zuR^nu@!hvtSPBx!q7#$ax@WrCrm(cUdkRmcxbr3*7rGoi?ZFfSSkwbg7hqdQ!}O3w zu;WPLt=1~lNAA4K!Z6MA8QP9rUqKDuj{OTQ9X=Euuo8T#1y$W>dl&X2enlz}Q!VC# zvt$d*OH=2J6Tt^ND!^ci9Poah{Zq77-@72KWLsL~>XDqUT`xgU>o?DmyxhAf_|xYiQsVYnYzlE#!@@koLfxOfN{9n&!rA#BBxBNMz^ zYgbLpYY#9%TfA%fgyBp=h!AmC`B>BL?O}grYsfY5CX_9PH+9ELeQ0Fqx>ba;DPSQZ z=^}H&RuICW{P);haYJF(thp@{TIy=8i`D1vg&spT#_^y`rmKGym)8tljn1QD#QD1M ztgSN1zFrY|e2%tPo0aR|lj%d5puD|3W(AMS9cG`w2!rKj{b7O0(*=ZzPEPE=G^d5y zJ2h%fvg8L)25`1`LX)cyNL@GDQYNu@_cm^gH+MrA zp1VBtFD~JVf8vgtEuMq>dj2Saxe2RUK<{4wlx-1_&Q!%? z6cLDz%h&ULoyUfW|3Q@hZklb-FU)Jg2K%azRlOcWpUKSr$8It=!Lvb!E${Rf5#MI1;=6l~dc^F>B4LOoQ~t`B3K0u6NlT80 zN78JvA=wv-q}q;SBp>^}+Z)Y{blt!ZPAxQho(l)?+(_PNJZcIGfB+x(|0WTbH`@Kd zWJ2+8$d$|a9V$)GP;T6MNsjw#+VS&+qv*@$@{c$C6$8--FZTXBQD;GmHq(nNZ&~J` z!=_} zB80L(@@j_;(W-*<{pCEh_={2gA#qND>AOE}S)UF~U)cw%>$LBGofx0u})rF%w76w#x9bA`; K6-cIM;{G4**<67D From a1aa01e252902013e98e893aabaa6c96f455b335 Mon Sep 17 00:00:00 2001 From: huulong Date: Fri, 8 Jan 2021 20:29:38 +0100 Subject: [PATCH 78/91] [TITLE] Fixed top of O in SONIC in pico sonic logo (removed extra line) --- data/builtin_data_titlemenu.p8 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data/builtin_data_titlemenu.p8 b/data/builtin_data_titlemenu.p8 index 242e1707..a40256b7 100644 --- a/data/builtin_data_titlemenu.p8 +++ b/data/builtin_data_titlemenu.p8 @@ -55,7 +55,7 @@ eeeeeeeee0ddddd777777771d1aaaaaa1d10000000000000000000000000000000000000000001d1 eeeeeeeeee0dd77777777771d1aaaaaa1d10000000000000000000000000000000000000000001d1aaaaaa1d17777777777dd0eeeeeeeeeeeeeeeeeeeeeeeeee eeeeeeeeeee0dd7777777771d1aaaaaa122288888888888888888888888888888888888888882221aaaaaa1d1777777777dd0eeeeeeeeeeeeeeeeeeeeeeeeeee eeeeeeeeeeee0dd7777777722888888888888888888888888888888888888888888888888888888888888882277777777dd0eeeeeeeeeeeeeeeeeeeeeeeeeeee -eeeeeeeeeeeee01dd777888888888888888888888888000000888888888888888888888888888888888888888888777dd10eeeeeeeeeeeeeeeeeeeeeeeeeeeee +eeeeeeeeeeeee01dd777888888888888888888888888888888888888888888888888888888888888888888888888777dd10eeeeeeeeeeeeeeeeeeeeeeeeeeeee eeeeeeeeeeeeeee1d88888888888800011100088888000000188800000001800000000000008888000000088888888dd1eeeeeeeeeeeeeeeeeeeeeeeeeeeeeee eeeeeeeeeeeeeeee8288888888880004999411088100499941018011111111011111111111108011111111008888882eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee eeeeeeeeeeeeeee02288888888804a7a99999908104a7aaaa940100777aa91197aaa1977aa100104aaa9999188888828eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee From 2fb3d6c6b5cfb130e1522d80fed8f044d212136c Mon Sep 17 00:00:00 2001 From: huulong Date: Sat, 9 Jan 2021 19:38:52 +0100 Subject: [PATCH 79/91] [EXPORT] Use new png export features: - replace ".p8" with cartridge_ext constant - call adapt_for_png.py in export and patch script - adapted export script to load main cartridges from the adapted folder when saving them as png --- export_and_patch_cartridge_release.sh | 29 ++++++ export_game_release.p8 | 139 ++++++++++++++++---------- pico-boots | 2 +- sonic-2d-tech-demo.sublime-project | 8 ++ src/ingame/base_stage_state.lua | 2 +- src/ingame/stage_state.lua | 6 +- src/stage_intro/stage_intro_state.lua | 2 +- 7 files changed, 128 insertions(+), 60 deletions(-) diff --git a/export_and_patch_cartridge_release.sh b/export_and_patch_cartridge_release.sh index b2e98a26..476651a2 100755 --- a/export_and_patch_cartridge_release.sh +++ b/export_and_patch_cartridge_release.sh @@ -29,15 +29,38 @@ rm -rf "${export_folder}/${rel_png_folder}" # and we want to remove any extraneous files too rm -rf "${export_folder}/${rel_bin_folder}" +# Create a variant of each non-data cartridge for PNG export, that reloads .p8.png instead of .p8 +adapt_for_png_cmd="python3.6 \"$picoboots_scripts_path/adapt_for_png.py\" "${export_folder}/picosonic_*.p8 +echo "> $adapt_for_png_cmd" +bash -c "$adapt_for_png_cmd" + +if [[ $? -ne 0 ]]; then + echo "" + echo "Adapt for PNG step failed, STOP." + exit 1 +fi + # Export via PICO-8 editor: PNG cartridges, binaries, HTML pico8 -x "$game_scripts_path/export_game_release.p8" +if [[ $? -ne 0 ]]; then + echo "" + echo "Export game release via PICO-8 step failed, STOP." + exit 1 +fi + # Patch the runtime binaries in-place with 4x_token, fast_reload, fast_load (experimental) if available bin_folder="${export_folder}/${rel_bin_folder}" patch_bin_cmd="\"$picoboots_scripts_path/patch_pico8_runtime.sh\" --inplace \"$bin_folder\" \"$cartridge_basename\"" echo "> $patch_bin_cmd" bash -c "$patch_bin_cmd" +if [[ $? -ne 0 ]]; then + echo "" + echo "Patch bin step failed, STOP." + exit 1 +fi + # Rename HTML file to index.html for direct play-in-browser html_filepath="${export_folder}/${rel_web_folder}/${cartridge_basename}.html" mv "$html_filepath" "${export_folder}/${rel_web_folder}/index.html" @@ -48,6 +71,12 @@ patch_js_cmd="python3.6 \"$picoboots_scripts_path/patch_pico8_js.py\" \"$js_file echo "> $patch_js_cmd" bash -c "$patch_js_cmd" +if [[ $? -ne 0 ]]; then + echo "" + echo "Patch JS step failed, STOP." + exit 1 +fi + pushd "${export_folder}" # PNG cartridges archive (delete existing one to be safe) diff --git a/export_game_release.p8 b/export_game_release.p8 index 0d3f3d07..08823efb 100644 --- a/export_game_release.p8 +++ b/export_game_release.p8 @@ -13,65 +13,96 @@ __lua__ local version = "5.1+" local export_folder = "picosonic/v"..version.."_release" local game_basename = "picosonic_v"..version.."_release" +local rel_png_folder = game_basename.."_png_cartridges" cd(export_folder) -local entry_cartridge = "picosonic_titlemenu.p8" - -local additional_cartridges_list = { - "picosonic_stage_intro.p8", - "picosonic_ingame.p8", - "picosonic_stage_clear.p8", - "data_bgm1.p8", - "data_stage1_00.p8", "data_stage1_10.p8", "data_stage1_20.p8", "data_stage1_30.p8", - "data_stage1_01.p8", "data_stage1_11.p8", "data_stage1_21.p8", "data_stage1_31.p8", - "data_stage1_runtime.p8" -} - --- prepare folder for png cartridges -mkdir(game_basename.."_png_cartridges") - --- load each additional cartridge to save it as png cartridge --- in folder created above --- the metadata label is used automatically for each -for cartridge_name in all(additional_cartridges_list) do - load(cartridge_name) - - cd(game_basename.."_png_cartridges") - save(cartridge_name..".png") + local entry_cartridge = "picosonic_titlemenu.p8" + + local additional_main_cartridges_list = { + "picosonic_stage_intro.p8", + "picosonic_ingame.p8", + "picosonic_stage_clear.p8", + } + + -- all main cartridges, including entry cartridge + local main_cartridges_list = {entry_cartridge} + for additional_main_cartridge in all(additional_main_cartridges_list) do + add(main_cartridges_list, additional_main_cartridge) + end + + local data_cartridges_list = { + "data_bgm1.p8", + "data_stage1_00.p8", "data_stage1_10.p8", "data_stage1_20.p8", "data_stage1_30.p8", + "data_stage1_01.p8", "data_stage1_11.p8", "data_stage1_21.p8", "data_stage1_31.p8", + "data_stage1_runtime.p8" + } + + -- PNG + + -- prepare folder for png cartridges + mkdir(rel_png_folder) + + -- data do not contain any code, so no need to adapt reload ".p8" -> ".p8.png" + -- so just save them directly as png + for cartridge_name in all(data_cartridges_list) do + load(cartridge_name) + save(rel_png_folder.."/"..cartridge_name..".png") + end + + -- main cartridges need to be *adapted for PNG* for reload, so load those adapted versions + -- to resave them as PNG + -- (export_and_patch_cartridge_release.sh must have called pico-boots/scripts/adapt_for_png.py) + -- the metadata label is used automatically for each + cd("p8_for_png") + + for cartridge_name in all(main_cartridges_list) do + load(cartridge_name) + -- save as png (make sure to go one level up first since we are one level down) + save("../"..rel_png_folder.."/"..cartridge_name..".png") + end + cd("..") -end --- load the entry cartridge (titlemenu) last, since we're going to use it for export --- just after -load(entry_cartridge) + printh("Resaved (adapted) cartridges as PNG in carts/"..export_folder.."/"..rel_png_folder) --- save as png cartridge as we only did it for other cartridges earlier -cd(game_basename.."_png_cartridges") -save(entry_cartridge..".png") -printh("Exported PNG cartridges in carts/"..export_folder.."/"..game_basename.."_png_cartridges") -cd("..") --- concatenate cartridge names with space separator with a very simplified version --- of string.lua > joinstr_table that doesn't mind about adding an initial space -local additional_cartridges_string = "" -for cartridge_name in all(additional_cartridges_list) do - additional_cartridges_string = additional_cartridges_string.." "..cartridge_name -end - --- exports are done via EXPORT, and can use a custom icon --- instead of the .p8.png label --- icon is stored in builtin_data_titlemenu.p8, --- as a 16x16 square => -s 2 tiles wide --- with top-left cell at sprite 46 (run1) => -i 46 --- on pink (color 14) background => -c 14 --- and most importantly we pass additional logic and data files as additional cartridges -export(game_basename..".bin "..additional_cartridges_string.." -i 46 -s 2 -c 14") -printh("Exported binaries in carts/"..export_folder.."/"..game_basename..".bin") - -mkdir(game_basename.."_web") --- Do not cd into game_basename.."_web" because we want the additional cartridges to be accessible --- in current path. Instead, export directly into the _web folder -export(game_basename.."_web/"..game_basename..".html "..additional_cartridges_string.." -i 46 -s 2 -c 14") -printh("Exported HTML in carts/"..export_folder.."/"..game_basename..".html") + -- BIN & WEB + + -- load the original (not adapted for PNG) entry cartridge (titlemenu) + -- this will serve as main entry point for the whole game + load(entry_cartridge) + + -- concatenate cartridge names with space separator with a very simplified version + -- of string.lua > joinstr_table that doesn't mind about adding an initial space + local additional_cartridges_string = "" + for cartridge_name in all(additional_main_cartridges_list) do + additional_cartridges_string = additional_cartridges_string.." "..cartridge_name + end + for cartridge_name in all(data_cartridges_list) do + additional_cartridges_string = additional_cartridges_string.." "..cartridge_name + end + + + -- BIN + + -- exports are done via EXPORT, and can use a custom icon + -- instead of the .p8.png label + -- icon is stored in builtin_data_titlemenu.p8, + -- as a 16x16 square => -s 2 tiles wide + -- with top-left cell at sprite 46 (run1) => -i 46 + -- on pink (color 14) background => -c 14 + -- and most importantly we pass additional logic and data files as additional cartridges + export(game_basename..".bin "..additional_cartridges_string.." -i 46 -s 2 -c 14") + printh("Exported binaries in carts/"..export_folder.."/"..game_basename..".bin") + + + -- WEB + + mkdir(game_basename.."_web") + -- Do not cd into game_basename.."_web" because we want the additional cartridges to be accessible + -- in current path. Instead, export directly into the _web folder + export(game_basename.."_web/"..game_basename..".html "..additional_cartridges_string.." -i 46 -s 2 -c 14") + printh("Exported HTML in carts/"..export_folder.."/"..game_basename..".html") + cd("..") diff --git a/pico-boots b/pico-boots index f9b495be..adeace31 160000 --- a/pico-boots +++ b/pico-boots @@ -1 +1 @@ -Subproject commit f9b495be7eed82ef96c160ee2e6d9d0dea3669e2 +Subproject commit adeace31fc9c58cd85e0b3a1725c3ce7b171c8e6 diff --git a/sonic-2d-tech-demo.sublime-project b/sonic-2d-tech-demo.sublime-project index a8546a73..854d0f64 100644 --- a/sonic-2d-tech-demo.sublime-project +++ b/sonic-2d-tech-demo.sublime-project @@ -1190,6 +1190,14 @@ "file_regex": "\"(.*\\.py)\"(?:, line )([\\d]+)(?:\\:)?([\\d]+)?(.*)", "syntax": "Packages/Python/Python.sublime-syntax" }, + { + "name": "Engine: Postbuild: test adapt_for_png", + "working_dir": "${project_path}/pico-boots", + "shell": true, + "cmd": ["python3 -m scripts.test_adapt_for_png"], + "file_regex": "\"(.*\\.py)\"(?:, line )([\\d]+)(?:\\:)?([\\d]+)?(.*)", + "syntax": "Packages/Python/Python.sublime-syntax" + }, { "name": "Engine: Postbuild: test patch_pico8_js", "working_dir": "${project_path}/pico-boots", diff --git a/src/ingame/base_stage_state.lua b/src/ingame/base_stage_state.lua index e7e6096f..3cfc9f6e 100644 --- a/src/ingame/base_stage_state.lua +++ b/src/ingame/base_stage_state.lua @@ -24,7 +24,7 @@ end -- return map filename for current stage and given region coordinates (u: int, v: int) -- do not try this with transitional regions, instead we'll patch them from individual regions function base_stage_state:get_map_region_filename(u, v) - return "data_stage"..self.curr_stage_id.."_"..u..v..".p8" + return "data_stage"..self.curr_stage_id.."_"..u..v..cartridge_ext end diff --git a/src/ingame/stage_state.lua b/src/ingame/stage_state.lua index 7b8eacc0..10179b2e 100644 --- a/src/ingame/stage_state.lua +++ b/src/ingame/stage_state.lua @@ -115,7 +115,7 @@ function stage_state:reload_runtime_data() -- containing all the sprites of interest, then copy-paste the __gff__ lines into the builtin .p8 cartridge -- Later, you can move all mask tiles to another spritesheet to reload -- on start instead. - local runtime_data_path = "data_stage"..self.curr_stage_id.."_runtime.p8" + local runtime_data_path = "data_stage"..self.curr_stage_id.."_runtime"..cartridge_ext reload(0x0, 0x0, 0x600, runtime_data_path) -- the runtime spritesheet also contains 45-degree rotated sprite variants @@ -968,7 +968,7 @@ function stage_state:reload_bgm() -- => 40 * 4 = 160 = 0xa0 bytes -- the bgm should start at pattern 0 on both source and -- current cartridge, so use copy memory from the start of music section - reload(0x3100, 0x3100, 0xa0, "data_bgm"..self.curr_stage_id..".p8") + reload(0x3100, 0x3100, 0xa0, "data_bgm"..self.curr_stage_id..cartridge_ext) -- we also need the music sfx referenced by the patterns self:reload_bgm_tracks() @@ -981,7 +981,7 @@ function stage_state:reload_bgm_tracks() -- => 50 * 68 = 3400 = 0xd48 bytes -- the bgm sfx should start at index 0 on both source and -- current cartridge, so use copy memory from the start of sfx section - reload(0x3200, 0x3200, 0xd48, "data_bgm"..self.curr_stage_id..".p8") + reload(0x3200, 0x3200, 0xd48, "data_bgm"..self.curr_stage_id..cartridge_ext) end function stage_state:play_bgm() diff --git a/src/stage_intro/stage_intro_state.lua b/src/stage_intro/stage_intro_state.lua index 63feb45f..cd4dfdc9 100644 --- a/src/stage_intro/stage_intro_state.lua +++ b/src/stage_intro/stage_intro_state.lua @@ -43,7 +43,7 @@ function stage_intro_state:on_enter() -- tile_collision_data.from_raw_tile_collision_data in outer scope, so early enough to be before -- loading runtime spritesheet...). Or we could have a custom intro cinematics that doesn't use physics -- at all and so no tile collision data is needed. - local runtime_data_path = "data_stage"..self.curr_stage_id.."_runtime.p8" + local runtime_data_path = "data_stage"..self.curr_stage_id.."_runtime"..cartridge_ext reload(0x0, 0x0, 0x600, runtime_data_path) self.camera:setup_for_stage(self.curr_stage_data) From 10987e59cab79a15d4acbe7eb455127b72c43ae8 Mon Sep 17 00:00:00 2001 From: huulong Date: Sat, 9 Jan 2021 19:57:45 +0100 Subject: [PATCH 80/91] [METADATA] Build main cartridges with suffix included in title --- build_single_cartridge.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build_single_cartridge.sh b/build_single_cartridge.sh index 4d8a9fd6..4677b023 100755 --- a/build_single_cartridge.sh +++ b/build_single_cartridge.sh @@ -125,7 +125,7 @@ symbols+="$cartridge_suffix" "$game_src_path" main_${cartridge_suffix}.lua \ -d "$data_path/builtin_data_${cartridge_suffix}.p8" \ -M "$data_path/metadata.p8" \ - -a "$author" -t "$title" \ + -a "$author" -t "$title ($cartridge_suffix)" \ -p "$build_output_path" \ -o "${cartridge_stem}_${cartridge_suffix}" \ -c "$config" \ From 231d7f3c1059f85951290caf33cbb4e37ea3a16b Mon Sep 17 00:00:00 2001 From: huulong Date: Sat, 9 Jan 2021 20:00:17 +0100 Subject: [PATCH 81/91] [BUILD] Clear folder content, not folders, to avoid having file browser/terminal point to deleted dir --- build_legacy.sh | 4 ++-- export_and_patch_cartridge_release.sh | 9 +++++---- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/build_legacy.sh b/build_legacy.sh index 298c968c..3e0dabc4 100755 --- a/build_legacy.sh +++ b/build_legacy.sh @@ -124,7 +124,7 @@ then exit 1 else echo "Build succeeded: $OUTPUT_FILEPATH" - # Clean intermediate folder now. If the build failed, the intermediate folder will remain for debugging. - # rm -rf "intermediate/${config}" + # Clean intermediate folder content now. If the build failed, the intermediate folder will remain for debugging. + # rm -rf "intermediate/${config}/"* exit 0 fi diff --git a/export_and_patch_cartridge_release.sh b/export_and_patch_cartridge_release.sh index 476651a2..8bbc3b1a 100755 --- a/export_and_patch_cartridge_release.sh +++ b/export_and_patch_cartridge_release.sh @@ -22,12 +22,13 @@ rel_bin_folder="${cartridge_basename}.bin" rel_web_folder="${cartridge_basename}_web" # Cleanup png folder as PICO-8 will prompt before overwriting an existing cartridge with the same name, -# and we cannot reply "y" to prompt in headless script -rm -rf "${export_folder}/${rel_png_folder}" - +# and we cannot reply "y" to prompt in headless script (and png tends to keep old label when overwritten) +# Note that we prefer deleting folder content than folder, to avoid file browser/terminal sometimes +# continuing to show old folder in system bin. Make sure to place blob * outside "" +rm -rf "${export_folder}/${rel_png_folder}/"* # Cleanup bin folder as a bug in PICO-8 makes it accumulate files in .zip for each export (even homonymous files!) # and we want to remove any extraneous files too -rm -rf "${export_folder}/${rel_bin_folder}" +rm -rf "${export_folder}/${rel_bin_folder}/"* # Create a variant of each non-data cartridge for PNG export, that reloads .p8.png instead of .p8 adapt_for_png_cmd="python3.6 \"$picoboots_scripts_path/adapt_for_png.py\" "${export_folder}/picosonic_*.p8 From 38e9db7323c287e4a0ddaf60ca2729b1947671a3 Mon Sep 17 00:00:00 2001 From: huulong Date: Sat, 9 Jan 2021 20:01:32 +0100 Subject: [PATCH 82/91] [METADATA] Improved data cartridges first comments to fit into cartridge short description (where we should put title and author) --- data/builtin_data_ingame.p8 | 7 ++++--- data/builtin_data_stage_clear.p8 | 7 ++++--- data/builtin_data_stage_intro.p8 | 7 ++++--- data/builtin_data_titlemenu.p8 | 6 +++--- data/data_bgm1.p8 | 3 ++- data/data_stage1_00.p8 | 4 ++-- data/data_stage1_01.p8 | 4 ++-- data/data_stage1_10.p8 | 4 ++-- data/data_stage1_11.p8 | 4 ++-- data/data_stage1_20.p8 | 4 ++-- data/data_stage1_21.p8 | 4 ++-- data/data_stage1_30.p8 | 4 ++-- data/data_stage1_31.p8 | 4 ++-- data/data_stage1_runtime.p8 | 3 ++- pico-boots | 2 +- 15 files changed, 36 insertions(+), 31 deletions(-) diff --git a/data/builtin_data_ingame.p8 b/data/builtin_data_ingame.p8 index d872d506..ccfcf9fd 100644 --- a/data/builtin_data_ingame.p8 +++ b/data/builtin_data_ingame.p8 @@ -1,8 +1,10 @@ pico-8 cartridge // http://www.pico-8.com version 29 __lua__ --- this is the built-in data cartridge --- for the ingame cartridge +-- picosonic builtin data: ingame +-- by leyn + +-- this section will be overwritten during build -- the collision masks at the top will be overwritten by runtime sprites -- via reload @@ -278,4 +280,3 @@ __music__ 00 41424344 00 41424344 04 39777879 - diff --git a/data/builtin_data_stage_clear.p8 b/data/builtin_data_stage_clear.p8 index abaa4e86..b30726ad 100644 --- a/data/builtin_data_stage_clear.p8 +++ b/data/builtin_data_stage_clear.p8 @@ -1,8 +1,10 @@ pico-8 cartridge // http://www.pico-8.com version 29 __lua__ --- this is the built-in data cartridge --- for the ingame cartridge +-- picosonic builtin data: stage_clear +-- by leyn + +-- this section will be overwritten during build -- the collision masks at the top will be overwritten by runtime sprites -- via reload @@ -227,4 +229,3 @@ __sfx__ __music__ 00 08090c0d 04 0a0b4040 - diff --git a/data/builtin_data_stage_intro.p8 b/data/builtin_data_stage_intro.p8 index d945d3a9..41fdcfbd 100644 --- a/data/builtin_data_stage_intro.p8 +++ b/data/builtin_data_stage_intro.p8 @@ -1,8 +1,10 @@ pico-8 cartridge // http://www.pico-8.com version 29 __lua__ --- this is the built-in data cartridge --- for the ingame cartridge +-- picosonic builtin data: stage_intro +-- by leyn + +-- this section will be overwritten during build -- the collision masks at the top will be overwritten by runtime sprites -- via reload @@ -278,4 +280,3 @@ __music__ 00 41424344 00 41424344 04 39777879 - diff --git a/data/builtin_data_titlemenu.p8 b/data/builtin_data_titlemenu.p8 index a40256b7..63e54488 100644 --- a/data/builtin_data_titlemenu.p8 +++ b/data/builtin_data_titlemenu.p8 @@ -1,9 +1,10 @@ pico-8 cartridge // http://www.pico-8.com version 29 __lua__ +-- picosonic builtin data: titlemenu +-- by leyn --- this is the built-in data cartridge --- for the titlemenu cartridge +-- this section will be overwritten during build __gfx__ 00000000eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee5eeeeeee77778eeeeeeeeeeee8eeeeeeeeeeeeeeeeee8eeeeeeeeee 00000000ee7eeeeeeeeee7eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee575eeeee7777788eeeeeeeeee272eeeeee2eeeeeeeee272eeeeee2ee @@ -192,4 +193,3 @@ __music__ 00 0a0f1318 00 0b0e1419 04 0c10151a - diff --git a/data/data_bgm1.p8 b/data/data_bgm1.p8 index d9aa9387..92661e90 100644 --- a/data/data_bgm1.p8 +++ b/data/data_bgm1.p8 @@ -1,6 +1,8 @@ pico-8 cartridge // http://www.pico-8.com version 29 __lua__ +-- picosonic data: pico island +-- bgm by leyn __sfx__ 010700001f1301c1301f1301c1301f1301c1301f1301c1301f1301c1301f1301c1301f1301c1301f1301c1301f1301c1301f1301c1301f1301c1301f1301c130211301d130211301d130211301d130211301d130 @@ -86,4 +88,3 @@ __music__ 00 20222940 00 23242840 02 25262d40 - diff --git a/data/data_stage1_00.p8 b/data/data_stage1_00.p8 index dabbfe0e..c8e570f4 100644 --- a/data/data_stage1_00.p8 +++ b/data/data_stage1_00.p8 @@ -1,8 +1,8 @@ pico-8 cartridge // http://www.pico-8.com version 29 __lua__ --- this is a data cartridge which only contains map region data for: --- Angel Island (0, 0) +-- picosonic data: pico island +-- region (0, 0) by leyn -- we only need __map__, but we kept __gfx__ only to visualize tiles when editing -- (and __gff__ because it's not too big) diff --git a/data/data_stage1_01.p8 b/data/data_stage1_01.p8 index 56aeec33..decfe554 100644 --- a/data/data_stage1_01.p8 +++ b/data/data_stage1_01.p8 @@ -1,8 +1,8 @@ pico-8 cartridge // http://www.pico-8.com version 29 __lua__ --- this is a data cartridge which only contains map region data for: --- Angel Island (0, 1) +-- picosonic data: pico island +-- region (0, 1) by leyn -- we only need __map__, but we kept __gfx__ only to visualize tiles when editing -- (and __gff__ because it's not too big) diff --git a/data/data_stage1_10.p8 b/data/data_stage1_10.p8 index 049e7724..ed05aea9 100644 --- a/data/data_stage1_10.p8 +++ b/data/data_stage1_10.p8 @@ -1,8 +1,8 @@ pico-8 cartridge // http://www.pico-8.com version 29 __lua__ --- this is a data cartridge which only contains map region data for: --- Angel Island (1, 0) +-- picosonic data: pico island +-- region (1, 0) by leyn -- we only need __map__, but we kept __gfx__ only to visualize tiles when editing -- (and __gff__ because it's not too big) diff --git a/data/data_stage1_11.p8 b/data/data_stage1_11.p8 index eb3c8f80..f161aa15 100644 --- a/data/data_stage1_11.p8 +++ b/data/data_stage1_11.p8 @@ -1,8 +1,8 @@ pico-8 cartridge // http://www.pico-8.com version 29 __lua__ --- this is a data cartridge which only contains map region data for: --- Angel Island (1, 1) +-- picosonic data: pico island +-- region (1, 1) by leyn -- we only need __map__, but we kept __gfx__ only to visualize tiles when editing -- (and __gff__ because it's not too big) diff --git a/data/data_stage1_20.p8 b/data/data_stage1_20.p8 index dc2831b4..6368f9d5 100644 --- a/data/data_stage1_20.p8 +++ b/data/data_stage1_20.p8 @@ -1,8 +1,8 @@ pico-8 cartridge // http://www.pico-8.com version 29 __lua__ --- this is a data cartridge which only contains map region data for: --- Angel Island (2, 0) +-- picosonic data: pico island +-- region (2, 0) by leyn -- we only need __map__, but we kept __gfx__ only to visualize tiles when editing -- (and __gff__ because it's not too big) diff --git a/data/data_stage1_21.p8 b/data/data_stage1_21.p8 index 2f4b08d6..67f3e117 100644 --- a/data/data_stage1_21.p8 +++ b/data/data_stage1_21.p8 @@ -1,8 +1,8 @@ pico-8 cartridge // http://www.pico-8.com version 29 __lua__ --- this is a data cartridge which only contains map region data for: --- Angel Island (2, 1) +-- picosonic data: pico island +-- region (2, 1) by leyn -- we only need __map__, but we kept __gfx__ only to visualize tiles when editing -- (and __gff__ because it's not too big) diff --git a/data/data_stage1_30.p8 b/data/data_stage1_30.p8 index 4ea2bad6..c755c90c 100644 --- a/data/data_stage1_30.p8 +++ b/data/data_stage1_30.p8 @@ -1,8 +1,8 @@ pico-8 cartridge // http://www.pico-8.com version 29 __lua__ --- this is a data cartridge which only contains map region data for: --- Angel Island (3, 0) +-- picosonic data: pico island +-- region (3, 0) by leyn -- we only need __map__, but we kept __gfx__ only to visualize tiles when editing -- (and __gff__ because it's not too big) diff --git a/data/data_stage1_31.p8 b/data/data_stage1_31.p8 index 009ea611..d566a5ed 100644 --- a/data/data_stage1_31.p8 +++ b/data/data_stage1_31.p8 @@ -1,8 +1,8 @@ pico-8 cartridge // http://www.pico-8.com version 29 __lua__ --- this is a data cartridge which only contains map region data for: --- Angel Island (3, 1) +-- picosonic data: pico island +-- region (3, 1) by leyn -- we only need __map__, but we kept __gfx__ only to visualize tiles when editing -- (and __gff__ because it's not too big) diff --git a/data/data_stage1_runtime.p8 b/data/data_stage1_runtime.p8 index 54500466..b27ed9d4 100644 --- a/data/data_stage1_runtime.p8 +++ b/data/data_stage1_runtime.p8 @@ -1,7 +1,8 @@ pico-8 cartridge // http://www.pico-8.com version 29 __lua__ - +-- picosonic data: +-- runtime spritesheet by leyn __gfx__ 00000000eeeeebbbeeeeeeeeeeeeeeeeeeed65eeeeeeeeeeeeeeeccccced65eeeeeeeeeee5eeeeeeeeeeeeeeeeeeeeeeee8eeeeeeeeeeeeeeeeee8eeeeeeeeee 00000000eeeebbbeeeeeeeeeed66666666666666666666deed66cccccccc6666666666de575eeeeeee7eeeeeeeeee7eee272eeeeee2eeeeeeeee272eeeeee2ee diff --git a/pico-boots b/pico-boots index adeace31..9a45fa0f 160000 --- a/pico-boots +++ b/pico-boots @@ -1 +1 @@ -Subproject commit adeace31fc9c58cd85e0b3a1725c3ce7b171c8e6 +Subproject commit 9a45fa0fa37d60ea27082be3d4d0a839c776b542 From 705156c8ef3e05f99ddfd46d2e0fd7e54c98deb6 Mon Sep 17 00:00:00 2001 From: huulong Date: Sat, 9 Jan 2021 20:21:12 +0100 Subject: [PATCH 83/91] [README] Added technical issue: high-pitched sounds on web version --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index c4d47b6e..2bf1da1e 100644 --- a/README.md +++ b/README.md @@ -114,6 +114,7 @@ In the pause menu (toggled with Enter/Start), if you are in-game, you can select * The player cannot control the character until the stage intro is over * The game slows down in some areas, reaching 30 FPS instead of 60 FPS * The game pauses to switch to another cartridge, esp. at the end of the stage intro (only memory reload has been patched on PICO-8 to be instant) +* Web version: high-pitched sounds in BGM do not convey the same as in desktop/cartridge versions ## Known design issues From b4e584965e400d9da6e12e39427c541d6e4ed58a Mon Sep 17 00:00:00 2001 From: huulong Date: Tue, 12 Jan 2021 19:37:26 +0100 Subject: [PATCH 84/91] [RETRY] If player got all emeralds, message reads "Congratulations!" --- src/stage_clear/stage_clear_state.lua | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/src/stage_clear/stage_clear_state.lua b/src/stage_clear/stage_clear_state.lua index 5d973a3a..a41d9d22 100644 --- a/src/stage_clear/stage_clear_state.lua +++ b/src/stage_clear/stage_clear_state.lua @@ -377,17 +377,26 @@ function stage_clear_state:zigzag_fade_out_async() end function stage_clear_state:show_retry_screen_async() - -- at the end of the zigzag, clear the emerald assessment widgets which are now completely hidden, - -- but keep the full black screen rectangle as background for retry screen - local try_again_label = label("try again?", vector(45, 30), colors.white) - self.result_overlay:add_drawable("try again", try_again_label) + + local has_missed_any_emeralds = false -- display missed emeralds for num = 1, 8 do -- not nil is true, and not true is false, so we are effectively filling the set, -- just setting false for picked emeralds instead of the usual nil, but works the same - self.result_show_emerald_set_by_number[num] = not self.picked_emerald_numbers_set[num] + local has_missed_this_emerald = not self.picked_emerald_numbers_set[num] + self.result_show_emerald_set_by_number[num] = has_missed_this_emerald + has_missed_any_emeralds = has_missed_any_emeralds or has_missed_this_emerald + end + + -- change text if player has got all emeralds + local result_label + if has_missed_any_emeralds then + result_label = label("try again?", vector(45, 30), colors.white) + else + result_label = label("congratulations!", vector(35, 45), colors.white) end + self.result_overlay:add_drawable("result text", result_label) self.retry_menu = menu(self.app, alignments.left, 1, colors.white, visual.sprite_data_t.menu_cursor, 7) self.retry_menu:show_items(self.retry_menu_items) From 74b39e3980ff492d87ee618f6049378a744d0b68 Mon Sep 17 00:00:00 2001 From: huulong Date: Tue, 12 Jan 2021 19:52:34 +0100 Subject: [PATCH 85/91] [UPLOAD] Added note: you need to be me! --- upload_cartridge_release.sh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/upload_cartridge_release.sh b/upload_cartridge_release.sh index 5dae28f5..86a01a98 100755 --- a/upload_cartridge_release.sh +++ b/upload_cartridge_release.sh @@ -9,6 +9,8 @@ # Dependencies: # - butler +# You also need to be signed in on itch.io as a collaborator on this project! + # Configuration: paths data_path="$(dirname "$0")/data" # Linux only From 1741e3de8df7998c9ed977f0ffc09866a58b1f83 Mon Sep 17 00:00:00 2001 From: huulong Date: Tue, 12 Jan 2021 19:55:09 +0100 Subject: [PATCH 86/91] [VERSION] Bumped to v5.2 with changes in CHANGELOG --- CHANGELOG.md | 18 +++++++++++++++--- README.md | 2 +- data/version.txt | 2 +- export_game_release.p8 | 2 +- src/menu/titlemenu.lua | 2 +- 5 files changed, 19 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 714d075d..4834d824 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,17 +6,28 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## [Unreleased] + +## [5.2] - 2021-01-12 ### Added +- Titlemenu: added version number in top-right corner +- Stage intro: stage fades in with gradual color darkness (palette swap) +- System: Added menu entries to retry with and without emeralds - Physics: added one-way platform system. Added one-way platform tiles and integrated them in stage - Stage clear: added retry screen with missed emeralds and options to retry with or without emerald, or go back to title menu - Stage clear: retry screen fades in and out with a zigzag swipe animation and/or gradual color darkness (palette swap) -- Stage intro: stage fades in with gradual color darkness (palette swap) -- Titlemenu: added version number in top-right corner +- Export: improved export script to patch standalone and web automatically (supports > 8192 tokens, no lag on reload()) +- Export: always load cartridge via basename (without extension) and modify loading path in PNG cartridges (".p8.png") to fix load/reload +- Export: generate .zip archives after export +- Upload: added upload script to immediately upload to itch.io via butler ### Changed +- Titlemenu: fixed top of 'O' in "SONIC" in title logo +- Stage intro: extracted stage intro into its own cartridge (so ingame cartridge has fewer characters ad can be exported). This introduced a small loading lag after stage intro, and player character cannot move during intro - Sprite: fixed top row of some Sonic rotated run sprites missing - Sprite: reduced emerald size by 2px in both directions, adjusted HUD - Stage clear: removed emerald cross, arranged emerald position to match retry screen +- Export: fixed export scripts to take all stage regions into account +- Export: fixed app icon, changed label to show title screen (without menu) ## [5.1] - 2020-12-17 ### Changed @@ -214,7 +225,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Game: in-game: debug character flies X/Y on directional input, go back to title menu on reach goal - Test: all busted unit tests in separator folder tests -[Unreleased]: https://github.com/hsandt/sonic-pico8/compare/v5.1...HEAD +[Unreleased]: https://github.com/hsandt/sonic-pico8/compare/v5.2...HEAD +[5.2]: https://github.com/hsandt/sonic-pico8/compare/v5.1...v5.2 [5.1]: https://github.com/hsandt/sonic-pico8/compare/v5.0...v5.1 [5.0]: https://github.com/hsandt/sonic-pico8/compare/v4.2...v5.0 [4.2]: https://github.com/hsandt/sonic-pico8/compare/v4.1...v4.2 diff --git a/README.md b/README.md index 2bf1da1e..4f988d35 100644 --- a/README.md +++ b/README.md @@ -30,7 +30,7 @@ Works with PICO-8 0.2.0i ~ 0.2.1b. ## Features -Version: 5.1+ +Version: 5.2 ### Physics diff --git a/data/version.txt b/data/version.txt index 34dd4fc6..ef425ca9 100644 --- a/data/version.txt +++ b/data/version.txt @@ -1 +1 @@ -5.1+ +5.2 diff --git a/export_game_release.p8 b/export_game_release.p8 index 08823efb..ad6fd538 100644 --- a/export_game_release.p8 +++ b/export_game_release.p8 @@ -10,7 +10,7 @@ __lua__ -- Paths are relative to PICO-8 carts directory. -- PICO-8 cannot read data/version.txt, so exceptionally set the version manually here -local version = "5.1+" +local version = "5.2" local export_folder = "picosonic/v"..version.."_release" local game_basename = "picosonic_v"..version.."_release" local rel_png_folder = game_basename.."_png_cartridges" diff --git a/src/menu/titlemenu.lua b/src/menu/titlemenu.lua index 62a0f73f..5c0366db 100644 --- a/src/menu/titlemenu.lua +++ b/src/menu/titlemenu.lua @@ -121,7 +121,7 @@ function titlemenu:draw_version() -- PICO-8 cannot access data/version.txt and we don't want to preprocess substitute some $version -- tag in build script just for this, so we exceptionally hardcode version number -- coords correspond to top-right corner with a small margin - text_helper.print_aligned("V5.1+", 126, 2, alignments.right, colors.white, colors.black) + text_helper.print_aligned("V5.2", 126, 2, alignments.right, colors.white, colors.black) end return titlemenu From 393ac12b52ab03cae53582f8179d172cfbdc1e4e Mon Sep 17 00:00:00 2001 From: huulong Date: Tue, 12 Jan 2021 20:12:02 +0100 Subject: [PATCH 87/91] [UPLOAD] Upload web version first so it gets detected as embedded HTML on initial upload --- upload_cartridge_release.sh | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/upload_cartridge_release.sh b/upload_cartridge_release.sh index 86a01a98..cbd8ec05 100755 --- a/upload_cartridge_release.sh +++ b/upload_cartridge_release.sh @@ -52,10 +52,12 @@ function butler_push_game_for_platform { pushd "${export_folder}" # Travis builds and releases .p8 cartridges packed in .zip, so focus on other platforms/formats + # Upload web first, it matters for the initial upload as first one will be considered as web version + # when using embedded web game on itch.io + butler_push_game_for_platform web "${cartridge_basename}_web.zip" butler_push_game_for_platform linux "${rel_bin_folder}/${cartridge_basename}_linux.zip" butler_push_game_for_platform osx "${rel_bin_folder}/${cartridge_basename}_osx.zip" butler_push_game_for_platform windows "${rel_bin_folder}/${cartridge_basename}_windows.zip" - butler_push_game_for_platform web "${cartridge_basename}_web.zip" butler_push_game_for_platform png "${cartridge_basename}_png_cartridges.zip" popd From 135db9e4e7bbcfbd8db868e6739584e6c4069046 Mon Sep 17 00:00:00 2001 From: huulong Date: Tue, 12 Jan 2021 20:21:14 +0100 Subject: [PATCH 88/91] [README] Fixed Releases hyperlink and notes on missing editor patches --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 4f988d35..0e8bb953 100644 --- a/README.md +++ b/README.md @@ -124,15 +124,15 @@ In the pause menu (toggled with Enter/Start), if you are in-game, you can select ## Releases -You can directly download a released version of the game on the [releases](Releases) page, or on the [itch.io page](https://komehara.itch.io/pico-sonic). If you download the binary export for your platform, you're good to go. +You can directly download a released version of the game on the [releases](https://github.com/hsandt/sonic-pico8/releases) page, or on the [itch.io page](https://komehara.itch.io/pico-sonic). If you download the binary export for your platform, you're good to go. However, if you download the cartridges or compressed cartridges (png) archive to run them directly in PICO-8, there are a few caveats: 1. This game uses multiple cartridges, therefore you need to unzip the archive in your local PICO-8 *carts* folder so it can properly detect and load neighbor cartridges on game state transition (if you only want to play the core game and without title menu, you can just run picosonic_ingame.p8 anywhere, but note that it will freeze when the stage has been finished) -2. The ingame cartridge (in .p8 or .p8.png form) cannot be run with a vanilla PICO-8 as it exceeds the maximum token limit (8192). To play it, you need to patch your PICO-8 executable to support more tokens, by either following the procedure I described in [this thread](https://www.lexaloffle.com/bbs/?pid=71689#p) or applying the patches provided in [pico-boots/scripts/patches](pico-boots/scripts/patches) (currently only provided for Linux editor as 'linux_amd64', and Linux and OSX runtime binaries, but if you are playing with Splore then you need an editor patch). You will need xdelta3 to apply the patches. +2. The ingame cartridge (in .p8 or .p8.png form) cannot be run with a vanilla PICO-8 as it exceeds the maximum token limit (8192). To play it, you need to patch your PICO-8 executable to support more tokens, by either following the procedure I described in [this thread](https://www.lexaloffle.com/bbs/?pid=71689#p) or applying the patches provided in [pico-boots/scripts/patches](pico-boots/scripts/patches) (currently only provided for Linux, OSX and Windows runtime binaries; I will try to push patches for the editor, which you are probably using if you own PICO-8). You will need xdelta3 to apply the patches. -3. I also recommend using a fast reload patch to instantly stream stage data. Otherwise, the game will pause half a second every time the character is approaching a different 128x32-tiles region of the map, and also in the transition area between two regions. Similarly to 2., you should apply the patch from the patches folder using xdelta3. +3. I also recommend using a fast reload patch to instantly stream stage data. Otherwise, the game will pause half a second every time the character is approaching a different 128x32-tiles region of the map, and also in the transition area between two regions. Similarly to 2., you should apply the patch from the patches folder using xdelta3 (editor patches not available yet). ## Build From e2ce780404c4e1912554a391d498444fa5fa1e67 Mon Sep 17 00:00:00 2001 From: huulong Date: Tue, 12 Jan 2021 20:26:43 +0100 Subject: [PATCH 89/91] [README] Improved Credits formatting --- README.md | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 0e8bb953..1c8404f4 100644 --- a/README.md +++ b/README.md @@ -251,11 +251,9 @@ For the SFX, I listened to the original ones, sometimes used Audacity to inspect ## Credits -* Original games: Sonic Team -* 8-bit remixes of Sonic 3 BGMs: danooct1 -* Programming: Leyn (komehara) -* Sprite adaptation: Leyn -* SFX/jingle adaptation, BGM adjustments: Leyn +* Sonic Team - Original games +* danooct1 - 8-bit remixes of Sonic 3 BGMs +* Leyn (komehara) - Programming, Sprite/SFX/jingle adaptation, BGM adjustments ## License From 4ad731d2e42370d3c7a60e1af49fe2a5607dd666 Mon Sep 17 00:00:00 2001 From: huulong Date: Tue, 12 Jan 2021 20:57:53 +0100 Subject: [PATCH 90/91] [README] Added that Timer is missing --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 1c8404f4..266e8edc 100644 --- a/README.md +++ b/README.md @@ -69,6 +69,7 @@ Version: 5.2 ### Notable features missing +* Timer * Crouching and looking up * Spin dash * When you collect all emeralds... From e4d4f2dda5fb22ff0e075f533d947edae486111e Mon Sep 17 00:00:00 2001 From: huulong Date: Tue, 12 Jan 2021 20:58:42 +0100 Subject: [PATCH 91/91] [UPLOAD] butler push on folder content not zip containing folder to avoid extra folder under itch.io's own generated folder (and make sure file diff is efficient) --- export_and_patch_cartridge_release.sh | 11 ++++++++++- upload_cartridge_release.sh | 17 +++++++++++------ 2 files changed, 21 insertions(+), 7 deletions(-) diff --git a/export_and_patch_cartridge_release.sh b/export_and_patch_cartridge_release.sh index 8bbc3b1a..6a37c89b 100755 --- a/export_and_patch_cartridge_release.sh +++ b/export_and_patch_cartridge_release.sh @@ -78,6 +78,15 @@ if [[ $? -ne 0 ]]; then exit 1 fi +# Archiving +# The archives we create here keep all the files under a folder with the full game name +# to avoid extracting files "in the wild". They are meant for manual distribution and preservation. +# itch.io creates its own folder from the game name + channel, and count on the distributable structure +# to be stable across versions so butler can only upload differences. So do not upload those archives +# to itch.io, as the folder inside contain the version number which will change and prevent efficient +# diffing. For itch.io, just upload the folder directly, and it will generate the zip. +# The exception is OSX, when the .app folder is at the top of the PICO-8 archive, +# and we can upload either the .app folder directly or the .zip containing it. pushd "${export_folder}" # PNG cartridges archive (delete existing one to be safe) @@ -93,7 +102,7 @@ pushd "${export_folder}" # Linux archive - # Rename linux folder with full game name so our archive contains a self-explanatory folder as the initial archive + # Rename linux folder with full game name so our archive contains a self-explanatory folder () mv "linux" "${cartridge_basename}_linux" # To minimize operations, do not recreate the archive, just replace the executable in the archive generated by PICO-8 export diff --git a/upload_cartridge_release.sh b/upload_cartridge_release.sh index cbd8ec05..6d7a686a 100755 --- a/upload_cartridge_release.sh +++ b/upload_cartridge_release.sh @@ -53,11 +53,16 @@ pushd "${export_folder}" # Travis builds and releases .p8 cartridges packed in .zip, so focus on other platforms/formats # Upload web first, it matters for the initial upload as first one will be considered as web version - # when using embedded web game on itch.io - butler_push_game_for_platform web "${cartridge_basename}_web.zip" - butler_push_game_for_platform linux "${rel_bin_folder}/${cartridge_basename}_linux.zip" - butler_push_game_for_platform osx "${rel_bin_folder}/${cartridge_basename}_osx.zip" - butler_push_game_for_platform windows "${rel_bin_folder}/${cartridge_basename}_windows.zip" - butler_push_game_for_platform png "${cartridge_basename}_png_cartridges.zip" + # when using embedded web game on itch.io + # Note that we do *not* want the folder containing game name + version inside the .zip + # as itch.io already generates a top-level folder inside the distributed zip, and butler + # is more efficient when distributable structure is stable (while version number changes). + # So we don't upload our custom .zip but the folders directly (OSX ist just an .app folder + # so we can upload either). + butler_push_game_for_platform web "${cartridge_basename}_web" + butler_push_game_for_platform linux "${rel_bin_folder}/${cartridge_basename}_linux" + butler_push_game_for_platform osx "${rel_bin_folder}/${cartridge_basename}_osx" + butler_push_game_for_platform windows "${rel_bin_folder}/${cartridge_basename}_windows" + butler_push_game_for_platform png "${cartridge_basename}_png_cartridges" popd