diff --git a/.travis.yml b/.travis.yml index 41ef6542..f7a69d43 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: @@ -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) @@ -81,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' diff --git a/CHANGELOG.md b/CHANGELOG.md index a1bc40b8..4834d824 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,30 @@ 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] + +## [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) +- 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 - Audio: fixed Travis release for GitHub forgetting to upload BGM cartridge (a glitchy version of Green Hill Zone was played instead of Angel Island) @@ -201,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 403dd453..266e8edc 100644 --- a/README.md +++ b/README.md @@ -6,39 +6,46 @@ 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. +[itch.io page](https://komehara.itch.io/pico-sonic) -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! Sonic arrives on Pico Island, ready to collect them all!* -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 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. -Works with PICO-8 0.2.0i and 0.2.1b. +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.2 ### 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 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 * Character preserves momentum on jumping and landing -* Character can roll +* One-way platforms * Spring bounce ### 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 @@ -51,46 +58,82 @@ 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 * 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 + +### Notable features missing + +* Timer +* Crouching and looking up +* Spin dash +* When you collect all emeralds... -### Content +## 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. +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. -Gimmicks: +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 * Launch ramp +## Controls + +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. + +### 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 -* 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) +* Web version: high-pitched sounds in BGM do not convey the same as in desktop/cartridge versions ## 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) +* Some ugly sprite / sprite transitions and SFX too far from the original sounds ## 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](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 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, 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 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 xdelta3 (editor patches not available yet). ## Build @@ -191,12 +234,28 @@ 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 + +* Sonic Team - Original games +* danooct1 - 8-bit remixes of Sonic 3 BGMs +* Leyn (komehara) - Programming, Sprite/SFX/jingle adaptation, BGM adjustments + ## License ### Code @@ -211,14 +270,8 @@ 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. - -#### Sprites - -I drew the sprites based on the Mega Drive and GBA games. Original sprites are under CC BY 4.0. - -#### Audio +Sega Games Co., Ltd owns the Sonic the Hedgehog trademark and copyrights on the original assets. -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. +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 the SFX, I listened to the original ones and tried to reproduce them with PICO-8's sound editor. +Because of this, I only consider original assets and the manual work of adaptation to be under CC BY NC 4.0. 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_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 79% rename from build_and_install_single_cartridge.sh rename to build_and_install_single_cartridge_with_data.sh index 68975cf5..9a886706 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,11 +14,11 @@ 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 - 'titlemenu', 'ingame' or 'stage_clear' + 'titlemenu', 'stage_intro', ingame' or 'stage_clear' CONFIG Build config. Determines defined preprocess symbols. (default: 'debug') @@ -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/build_itest.sh b/build_itest.sh index 248f316e..08510451 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" @@ -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_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/build_pico8_utests.sh b/build_pico8_utests.sh index aad24187..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 6a619945..4677b023 100755 --- a/build_single_cartridge.sh +++ b/build_single_cartridge.sh @@ -11,10 +11,10 @@ game_src_path="$(dirname "$0")/src" data_path="$(dirname "$0")/data" # Configuration: cartridge +version=`cat "$data_path/version.txt"` author="leyn" -title="pico sonic" cartridge_stem="picosonic" -version="5.1" +title="pico sonic v$version" help() { echo "Build a PICO-8 cartridge with the passed config." @@ -26,7 +26,9 @@ usage() { ARGUMENTS CARTRIDGE_SUFFIX Cartridge to build for the multi-cartridge game - 'titlemenu', 'ingame' or 'stage_clear' + '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,23 +106,33 @@ 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 +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 # 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 \ -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" \ --no-append-config \ -s "$symbols" \ - --minify-level 3 + --minify-level 3 \ + --unify "_${cartridge_suffix}" if [[ $? -ne 0 ]]; then echo "" diff --git a/data/builtin_data_ingame.p8 b/data/builtin_data_ingame.p8 index 0e48c7f1..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 @@ -127,17 +129,17 @@ eeeea994eeeeeeeeeeed65eeeeee999999999999aa944400d65694cdc0ca9a155194cdc0ca9a656d eeeaaaa94eeeeeeeeeed65eeeee99999999999aa99444000d656dcacac75471551dcacac7547656d3beb3ebe44444494b00bb00bbbbbbbab3333b33beeeeeeee eeaaaaaaa999eeeeeeed65eeee9999999999aa9449444400d6569ac7ca7a7a15519ac7ca7a7a656db3b33eb3449444443b0330b33b3bbbbbb33bee3beeeeeeee eaaaaaaaaaaa999eeeed65eee4949494999a944444444000d6564c000c094715514c000c0947656deebeebee444444440b0330b0eeebbebbbbeeeeeeeeeeeeee -eeeeeeeeeeeeeeeeeeeeeee949494449a99494444444440ed656c0ca7a77701551c0ca7a7770656deee888eeeeeeeeeeeeeeeeeeeeeeebbbbbeeeeeeeeeeeeee -eeeeeeeeeeeeeeeeeeeee99444449a9944444444444440eed65649a04f4977155149a04f4977656de7777788eeeeeeeeeeeeeeeeeeeeeebbbbaeeeeeeeeeeeee -eeeeeeeeeeeeeeeeeeee44449a994444444444994440eeeed6569afaf00a7a15519afaf00a7a656d777777888eeeeeeeeeeeeeeeeeeeeeeebbbbeeeeeeeeeeee -eeeeeeeeeeeeeeeeee4499994444494444944494444eeeeed656dcafaf05071551dcafaf0507656d222888222eeeeeeeeeeeeeeeeeeeeeeeebbbeeeeeeeeeeee -eeeeeeeeeeeeeeeea9994494499449444494444440eeeeeed65690cdf09a5d155190cdf09a5d656de2288822eeeeeeeeeeeeeeeeeeeeeeeeebbbbeeeeeeeeeee -eeeeeeeeeeee99994494444444944444449444444eeeeeeed656dc09d9d9d61551dc09d9d9d6656dee28882eeeeeeeeeeeeeeeeeeeeeeeeeeebbaeeeeeeeeeee -eeeeeeee44994494449444444494444444444400eeeeeeeed60d777777777775577777777777d06deee282eeeeeeeeeeeeeeeeeeeeeeeeeeeeebaeeeeeeeeeee -eeee44994444444444444444444444444440eeeeeeeeeeeeed6000000000000ee0000000000006deeeee2eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee +eeeeeeeeeeeeeeeeeeeeeee949494449a99494444444440ed656c0ca7a77701551c0ca7a7770656de77778ee4444444444444444eeeeebbbbbeeeeeeeeeeeeee +eeeeeeeeeeeeeeeeeeeee99444449a9944444444444440eed65649a04f4977155149a04f4977656d7777788e0449449949944444eeeeeebbbbaeeeeeeeeeeeee +eeeeeeeeeeeeeeeeeeee44449a994444444444994440eeeed6569afaf00a7a15519afaf00a7a656d2288822e0449444994444000eeeeeeeebbbbeeeeeeeeeeee +eeeeeeeeeeeeeeeeee4499994444494444944494444eeeeed656dcafaf05071551dcafaf0507656de28882ee4444444999444044eeeeeeeeebbbeeeeeeeeeeee +eeeeeeeeeeeeeeeea9994494499449444494444440eeeeeed65690cdf09a5d155190cdf09a5d656dee282eee0444499444444444eeeeeeeeebbbbeeeeeeeeeee +eeeeeeeeeeee99994494444444944444449444444eeeeeeed656dc09d9d9d61551dc09d9d9d6656deee2eeee0444444444444440eeeeeeeeeebbaeeeeeeeeeee +eeeeeeee44994494449444444494444444444400eeeeeeeed60d777777777775577777777777d06deeeeeeee004499440e004440eeeeeeeeeeebaeeeeeeeeeee +eeee44994444444444444444444444444440eeeeeeeeeeeeed6000000000000ee0000000000006deeeeeeeeee0049eeeeeee000eeeeeeeeeeeeeeeeeeeeeeeee __gff__ -0041414141414141414141414141414141414141414141414141414141410000414141414141414141414141410000004141414141414141414141414141414141414141414141414141616180808000414040414141414141414040414141414545454343434343454541414141414145455149434343434545414141418080 -0000000000000000000000000000000000000000000000000000000000000000000000000000000041418080800000000000000000000000414141800080414100000000000000000000008080804141000000000000000000004141808080804040404041410000000080418080800041414141414100000000000000008000 +0041414141414141414141414141414141414141414141414141414141410000414141434341414141414141410000004141414141414141414141414141414141414141414141414141616180808000414040414141414141414040414141414141414141414141414141414141414141414141414141414141414141418080 +0000000000000000000000000000000000000000000000000000000000000000000000000000000041418080800000000000000000000000414141800080414100000000000000000000008080804141000000000000000000004141808080804040404043430000000080418080800041414141434300000000004040008000 __map__ 000000000000000000006e6f6e6f6e6f6e6f6f6e6f6e6f6e6f6e6f6e6f6e49466e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f497e7f7e7f7e7f7e7f7e7f7e7f7e7f7e7f7e7f7e7f000000000000000000007c7d7c7d7c7d7c7d1e1f1e1f1e1f1e1f5e5f5e5f5f 000000000000000000006e6f6e6f6e6f6e6f6f585945434443454244455554307e7f7e7f7e7f7e7f7e7f7e7f7e2e6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6f6f6e6f6e6f6e6f6e6f6e6f44417000000000000000000000000000000000000000000000000000000000000000000000000000007c7d5e5f5e5f5e5f6e6f6e6f6f @@ -172,63 +174,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 +239,44 @@ __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 5b857ab5..b30726ad 100644 --- a/data/builtin_data_stage_clear.p8 +++ b/data/builtin_data_stage_clear.p8 @@ -1,36 +1,38 @@ 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 __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,16 +129,16 @@ eeeea994eeeeeeeeeeed65eeeeee999999999999aa944400d65694cdc0ca9a155194cdc0ca9a656d eeeaaaa94eeeeeeeeeed65eeeee99999999999aa99444000d656dcacac75471551dcacac7547656d3beb3ebe44444494b00bb00bbbbbbbab3333b33beeeeeeee eeaaaaaaa999eeeeeeed65eeee9999999999aa9449444400d6569ac7ca7a7a15519ac7ca7a7a656db3b33eb3449444443b0330b33b3bbbbbb33bee3beeeeeeee eaaaaaaaaaaa999eeeed65eee4949494999a944444444000d6564c000c094715514c000c0947656deebeebee444444440b0330b0eeebbebbbbeeeeeeeeeeeeee -eeeeeeeeeeeeeeeeeeeeeee949494449a99494444444440ed656c0ca7a77701551c0ca7a7770656deee888eeeeeeeeeeeeeeeeeeeeeeebbbbbeeeeeeeeeeeeee -eeeeeeeeeeeeeeeeeeeee99444449a9944444444444440eed65649a04f4977155149a04f4977656de7777788eeeeeeeeeeeeeeeeeeeeeebbbbaeeeeeeeeeeeee -eeeeeeeeeeeeeeeeeeee44449a994444444444994440eeeed6569afaf00a7a15519afaf00a7a656d777777888eeeeeeeeeeeeeeeeeeeeeeebbbbeeeeeeeeeeee -eeeeeeeeeeeeeeeeee4499994444494444944494444eeeeed656dcafaf05071551dcafaf0507656d222888222eeeeeeeeeeeeeeeeeeeeeeeebbbeeeeeeeeeeee -eeeeeeeeeeeeeeeea9994494499449444494444440eeeeeed65690cdf09a5d155190cdf09a5d656de2288822eeeeeeeeeeeeeeeeeeeeeeeeebbbbeeeeeeeeeee -eeeeeeeeeeee99994494444444944444449444444eeeeeeed656dc09d9d9d61551dc09d9d9d6656dee28882eeeeeeeeeeeeeeeeeeeeeeeeeeebbaeeeeeeeeeee -eeeeeeee44994494449444444494444444444400eeeeeeeed60d777777777775577777777777d06deee282eeeeeeeeeeeeeeeeeeeeeeeeeeeeebaeeeeeeeeeee -eeee44994444444444444444444444444440eeeeeeeeeeeeed6000000000000ee0000000000006deeeee2eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee +eeeeeeeeeeeeeeeeeeeeeee949494449a99494444444440ed656c0ca7a77701551c0ca7a7770656de77778ee4444444444444444eeeeebbbbbeeeeeeeeeeeeee +eeeeeeeeeeeeeeeeeeeee99444449a9944444444444440eed65649a04f4977155149a04f4977656d7777788e0449449949944444eeeeeebbbbaeeeeeeeeeeeee +eeeeeeeeeeeeeeeeeeee44449a994444444444994440eeeed6569afaf00a7a15519afaf00a7a656d2288822e0449444994444000eeeeeeeebbbbeeeeeeeeeeee +eeeeeeeeeeeeeeeeee4499994444494444944494444eeeeed656dcafaf05071551dcafaf0507656de28882ee4444444999444044eeeeeeeeebbbeeeeeeeeeeee +eeeeeeeeeeeeeeeea9994494499449444494444440eeeeeed65690cdf09a5d155190cdf09a5d656dee282eee0444499444444444eeeeeeeeebbbbeeeeeeeeeee +eeeeeeeeeeee99994494444444944444449444444eeeeeeed656dc09d9d9d61551dc09d9d9d6656deee2eeee0444444444444440eeeeeeeeeebbaeeeeeeeeeee +eeeeeeee44994494449444444494444444444400eeeeeeeed60d777777777775577777777777d06deeeeeeee004499440e004440eeeeeeeeeeebaeeeeeeeeeee +eeee44994444444444444444444444444440eeeeeeeeeeeeed6000000000000ee0000000000006deeeeeeeeee0049eeeeeee000eeeeeeeeeeeeeeeeeeeeeeeee __gff__ -0041414141414141414141414141414141414141414141414141414141410000414141414141414141414141410000004141414141414141414141414141414141414141414141414141616180808000414040414141414141414040414141414545454343434343454541414141414145455149434343434545414141418080 +0000000000000000000000000000000040000000000000000000000000000000400000434300000000000000000000004141414141414141414141414141414141414141414141414141616180808000414040414141414141414040414141414141414141414141414141414141414141414141414141414141414141418080 0000000000000000000000000000000000000000000000000000000000000000000000000000000041418080800000000000000000000000414141800080414100000000000000000000008080804141000000000000000000004141808080804040404041410000000080418080800041414141414100000000000000008000 __map__ 000000000000000000006e6f6e6f6e6f6e6f6f6e6f6e6f6e6f6e6f6e6f6e49466e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f497e7f7e7f7e7f7e7f7e7f7e7f7e7f7e7f7e7f7e7f000000000000000000007c7d7c7d7c7d7c7d1e1f1e1f1e1f1e1f5e5f5e5f5f @@ -174,109 +176,56 @@ __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/data/builtin_data_stage_intro.p8 b/data/builtin_data_stage_intro.p8 new file mode 100644 index 00000000..41fdcfbd --- /dev/null +++ b/data/builtin_data_stage_intro.p8 @@ -0,0 +1,282 @@ +pico-8 cartridge // http://www.pico-8.com +version 29 +__lua__ +-- 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 +__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/data/builtin_data_titlemenu.p8 b/data/builtin_data_titlemenu.p8 index aca2fec3..63e54488 100644 --- a/data/builtin_data_titlemenu.p8 +++ b/data/builtin_data_titlemenu.p8 @@ -1,18 +1,19 @@ 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__ -00000000eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee5eeeeeeeee888eeeeeeeeeeee8eeeeeeeeeeeeeeeeee8eeeeeeeeee -00000000ee7eeeeeeeeee7eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee575eeeeee7777788eeeeeeeee272eeeeee2eeeeeeeee272eeeeee2ee -00700700eed7eee87eee7deeeeeeeee0000eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee5775eeee777777888eeeeeee877782eee272e2eeee287778ee2e272e -00077000eee7ee8008ee7eeeeeeeee0222700eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee57775eee222888222eeeeeeee272eeeeee2ee8eeeeee272eee8ee2ee -00077000edd7780970877ddeeeeee028887720eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee577775eee2288822eeeeeeeeee8ee2eeeeee272eee2ee8eee272eeee -00700700eedd78049087ddeeeeeee0888877820eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee57755eeeee28882eeeeeeeeeee2e272eee287778e272e2ee877782ee -00000000eeedde8008eddeeeeeeee0000000000eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee575eeeeeeee282eeeeeeeeeeeeeee2eeeeee272eee2eeeeee272eeee -00000000eeeeeee88eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee5eeeeeeeeee2eeeeeeeeeeeeeeeeeeeeeeee8eeeeeeeeeeee8eeeee +00000000eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee5eeeeeee77778eeeeeeeeeeee8eeeeeeeeeeeeeeeeee8eeeeeeeeee +00000000ee7eeeeeeeeee7eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee575eeeee7777788eeeeeeeeee272eeeeee2eeeeeeeee272eeeeee2ee +00700700eed7eee87eee7deeeeeeeee0000eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee5775eeee2288822eeeeeeeee877782eee272e2eeee287778ee2e272e +00077000eee7ee8008ee7eeeeeeeee0222700eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee57775eeee28882eeeeeeeeeee272eeeeee2ee8eeeeee272eee8ee2ee +00077000edd7780970877ddeeeeee028887720eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee577775eeee282eeeeeeeeeeeee8ee2eeeeee272eee2ee8eee272eeee +00700700eedd78049087ddeeeeeee0888877820eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee57755eeeeee2eeeeeeeeeeeeee2e272eee287778e272e2ee877782ee +00000000eeedde8008eddeeeeeeee0000000000eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee575eeeeeeeeeeeeeeeeeeeeeeeeee2eeeeee272eee2eeeeee272eeee +00000000eeeeeee88eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee5eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee8eeeeeeeeeeee8eeeee eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee @@ -21,22 +22,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 @@ -55,7 +56,7 @@ eeeeeeeee0ddddd777777771d1aaaaaa1d10000000000000000000000000000000000000000001d1 eeeeeeeeee0dd77777777771d1aaaaaa1d10000000000000000000000000000000000000000001d1aaaaaa1d17777777777dd0eeeeeeeeeeeeeeeeeeeeeeeeee eeeeeeeeeee0dd7777777771d1aaaaaa122288888888888888888888888888888888888888882221aaaaaa1d1777777777dd0eeeeeeeeeeeeeeeeeeeeeeeeeee eeeeeeeeeeee0dd7777777722888888888888888888888888888888888888888888888888888888888888882277777777dd0eeeeeeeeeeeeeeeeeeeeeeeeeeee -eeeeeeeeeeeee01dd777888888888888888888888888000000888888888888888888888888888888888888888888777dd10eeeeeeeeeeeeeeeeeeeeeeeeeeeee +eeeeeeeeeeeee01dd777888888888888888888888888888888888888888888888888888888888888888888888888777dd10eeeeeeeeeeeeeeeeeeeeeeeeeeeee eeeeeeeeeeeeeee1d88888888888800011100088888000000188800000001800000000000008888000000088888888dd1eeeeeeeeeeeeeeeeeeeeeeeeeeeeeee eeeeeeeeeeeeeeee8288888888880004999411088100499941018011111111011111111111108011111111008888882eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee eeeeeeeeeeeeeee02288888888804a7a99999908104a7aaaa940100777aa91197aaa1977aa100104aaa9999188888828eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee @@ -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 6b2e8dde..c8e570f4 100644 --- a/data/data_stage1_00.p8 +++ b/data/data_stage1_00.p8 @@ -1,36 +1,36 @@ 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) __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 -eeeeeeeeeeeeeeeeeeeeeee949494449a99494444444440ed656c0ca7a77701551c0ca7a7770656deee888eeeeeeeeeeeeeeeeeeeeeeebbbbbeeeeeeeeeeeeee -eeeeeeeeeeeeeeeeeeeee99444449a9944444444444440eed65649a04f4977155149a04f4977656de7777788eeeeeeeeeeeeeeeeeeeeeebbbbaeeeeeeeeeeeee -eeeeeeeeeeeeeeeeeeee44449a994444444444994440eeeed6569afaf00a7a15519afaf00a7a656d777777888eeeeeeeeeeeeeeeeeeeeeeebbbbeeeeeeeeeeee -eeeeeeeeeeeeeeeeee4499994444494444944494444eeeeed656dcafaf05071551dcafaf0507656d222888222eeeeeeeeeeeeeeeeeeeeeeeebbbeeeeeeeeeeee -eeeeeeeeeeeeeeeea9994494499449444494444440eeeeeed65690cdf09a5d155190cdf09a5d656de2288822eeeeeeeeeeeeeeeeeeeeeeeeebbbbeeeeeeeeeee -eeeeeeeeeeee99994494444444944444449444444eeeeeeed656dc09d9d9d61551dc09d9d9d6656dee28882eeeeeeeeeeeeeeeeeeeeeeeeeeebbaeeeeeeeeeee -eeeeeeee44994494449444444494444444444400eeeeeeeed60d777777777775577777777777d06deee282eeeeeeeeeeeeeeeeeeeeeeeeeeeeebaeeeeeeeeeee -eeee44994444444444444444444444444440eeeeeeeeeeeeed6000000000000ee0000000000006deeeee2eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee +eeeeeeeeeeeeeeeeeeeeeee949494449a99494444444440ed656c0ca7a77701551c0ca7a7770656de77778ee4444444444444444eeeeebbbbbeeeeeeeeeeeeee +eeeeeeeeeeeeeeeeeeeee99444449a9944444444444440eed65649a04f4977155149a04f4977656d7777788e0449449949944444eeeeeebbbbaeeeeeeeeeeeee +eeeeeeeeeeeeeeeeeeee44449a994444444444994440eeeed6569afaf00a7a15519afaf00a7a656d2288822e0449444994444000eeeeeeeebbbbeeeeeeeeeeee +eeeeeeeeeeeeeeeeee4499994444494444944494444eeeeed656dcafaf05071551dcafaf0507656de28882ee4444444999444044eeeeeeeeebbbeeeeeeeeeeee +eeeeeeeeeeeeeeeea9994494499449444494444440eeeeeed65690cdf09a5d155190cdf09a5d656dee282eee0444499444444444eeeeeeeeebbbbeeeeeeeeeee +eeeeeeeeeeee99994494444444944444449444444eeeeeeed656dc09d9d9d61551dc09d9d9d6656deee2eeee0444444444444440eeeeeeeeeebbaeeeeeeeeeee +eeeeeeee44994494449444444494444444444400eeeeeeeed60d777777777775577777777777d06deeeeeeee004499440e004440eeeeeeeeeeebaeeeeeeeeeee +eeee44994444444444444444444444444440eeeeeeeeeeeeed6000000000000ee0000000000006deeeeeeeeee0049eeeeeee000eeeeeeeeeeeeeeeeeeeeeeeee __gff__ -0041414141414141414141414141414141414141414141414141414141410000414141414141414141414141410000004141414141414141414141414141414141414141414141414141616180808000414040414141414141414040414141414545454343434343454541414141414145455149434343434545414141418080 -0000000000000000000000000000000000000000000000000000000000000000000000000000000041418080800000000000000000000000414141800080414100000000000000000000008080804141000000000000000000004141808080804040404041410000000080418080800041414141414100000000000000008000 +0041414141414141414141414141414141414141414141414141414141410000414141434341414141414141410000004141414141414141414141414141414141414141414141414141616180808000414040414141414141414040414141414141414141414141414141414141414141414141414141414141414141418080 +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 959a278e..decfe554 100644 --- a/data/data_stage1_01.p8 +++ b/data/data_stage1_01.p8 @@ -1,36 +1,36 @@ 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) __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 -eeeeeeeeeeeeeeeeeeeeeee949494449a99494444444440ed656c0ca7a77701551c0ca7a7770656deee888eeeeeeeeeeeeeeeeeeeeeeebbbbbeeeeeeeeeeeeee -eeeeeeeeeeeeeeeeeeeee99444449a9944444444444440eed65649a04f4977155149a04f4977656de7777788eeeeeeeeeeeeeeeeeeeeeebbbbaeeeeeeeeeeeee -eeeeeeeeeeeeeeeeeeee44449a994444444444994440eeeed6569afaf00a7a15519afaf00a7a656d777777888eeeeeeeeeeeeeeeeeeeeeeebbbbeeeeeeeeeeee -eeeeeeeeeeeeeeeeee4499994444494444944494444eeeeed656dcafaf05071551dcafaf0507656d222888222eeeeeeeeeeeeeeeeeeeeeeeebbbeeeeeeeeeeee -eeeeeeeeeeeeeeeea9994494499449444494444440eeeeeed65690cdf09a5d155190cdf09a5d656de2288822eeeeeeeeeeeeeeeeeeeeeeeeebbbbeeeeeeeeeee -eeeeeeeeeeee99994494444444944444449444444eeeeeeed656dc09d9d9d61551dc09d9d9d6656dee28882eeeeeeeeeeeeeeeeeeeeeeeeeeebbaeeeeeeeeeee -eeeeeeee44994494449444444494444444444400eeeeeeeed60d777777777775577777777777d06deee282eeeeeeeeeeeeeeeeeeeeeeeeeeeeebaeeeeeeeeeee -eeee44994444444444444444444444444440eeeeeeeeeeeeed6000000000000ee0000000000006deeeee2eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee +eeeeeeeeeeeeeeeeeeeeeee949494449a99494444444440ed656c0ca7a77701551c0ca7a7770656de77778ee4444444444444444eeeeebbbbbeeeeeeeeeeeeee +eeeeeeeeeeeeeeeeeeeee99444449a9944444444444440eed65649a04f4977155149a04f4977656d7777788e0449449949944444eeeeeebbbbaeeeeeeeeeeeee +eeeeeeeeeeeeeeeeeeee44449a994444444444994440eeeed6569afaf00a7a15519afaf00a7a656d2288822e0449444994444000eeeeeeeebbbbeeeeeeeeeeee +eeeeeeeeeeeeeeeeee4499994444494444944494444eeeeed656dcafaf05071551dcafaf0507656de28882ee4444444999444044eeeeeeeeebbbeeeeeeeeeeee +eeeeeeeeeeeeeeeea9994494499449444494444440eeeeeed65690cdf09a5d155190cdf09a5d656dee282eee0444499444444444eeeeeeeeebbbbeeeeeeeeeee +eeeeeeeeeeee99994494444444944444449444444eeeeeeed656dc09d9d9d61551dc09d9d9d6656deee2eeee0444444444444440eeeeeeeeeebbaeeeeeeeeeee +eeeeeeee44994494449444444494444444444400eeeeeeeed60d777777777775577777777777d06deeeeeeee004499440e004440eeeeeeeeeeebaeeeeeeeeeee +eeee44994444444444444444444444444440eeeeeeeeeeeeed6000000000000ee0000000000006deeeeeeeeee0049eeeeeee000eeeeeeeeeeeeeeeeeeeeeeeee __gff__ -0041414141414141414141414141414141414141414141414141414141410000414141414141414141414141410000004141414141414141414141414141414141414141414141414141616180808000414040414141414141414040414141414545454343434343454541414141414145455149434343434545414141418080 -0000000000000000000000000000000000000000000000000000000000000000000000000000000041418080800000000000000000000000414141800080414100000000000000000000008080804141000000000000000000004141808080804040404041410000000080418080800041414141414100000000000000008000 +0041414141414141414141414141414141414141414141414141414141410000414141434341414141414141410000004141414141414141414141414141414141414141414141414141616180808000414040414141414141414040414141414141414141414141414141414141414141414141414141414141414141418080 +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 f11e7c77..ed05aea9 100644 --- a/data/data_stage1_10.p8 +++ b/data/data_stage1_10.p8 @@ -1,36 +1,36 @@ 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) __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 -eeeeeeeeeeeeeeeeeeeeeee949494449a99494444444440ed656c0ca7a77701551c0ca7a7770656deee888eeeeeeeeeeeeeeeeeeeeeeebbbbbeeeeeeeeeeeeee -eeeeeeeeeeeeeeeeeeeee99444449a9944444444444440eed65649a04f4977155149a04f4977656de7777788eeeeeeeeeeeeeeeeeeeeeebbbbaeeeeeeeeeeeee -eeeeeeeeeeeeeeeeeeee44449a994444444444994440eeeed6569afaf00a7a15519afaf00a7a656d777777888eeeeeeeeeeeeeeeeeeeeeeebbbbeeeeeeeeeeee -eeeeeeeeeeeeeeeeee4499994444494444944494444eeeeed656dcafaf05071551dcafaf0507656d222888222eeeeeeeeeeeeeeeeeeeeeeeebbbeeeeeeeeeeee -eeeeeeeeeeeeeeeea9994494499449444494444440eeeeeed65690cdf09a5d155190cdf09a5d656de2288822eeeeeeeeeeeeeeeeeeeeeeeeebbbbeeeeeeeeeee -eeeeeeeeeeee99994494444444944444449444444eeeeeeed656dc09d9d9d61551dc09d9d9d6656dee28882eeeeeeeeeeeeeeeeeeeeeeeeeeebbaeeeeeeeeeee -eeeeeeee44994494449444444494444444444400eeeeeeeed60d777777777775577777777777d06deee282eeeeeeeeeeeeeeeeeeeeeeeeeeeeebaeeeeeeeeeee -eeee44994444444444444444444444444440eeeeeeeeeeeeed6000000000000ee0000000000006deeeee2eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee +eeeeeeeeeeeeeeeeeeeeeee949494449a99494444444440ed656c0ca7a77701551c0ca7a7770656de77778ee4444444444444444eeeeebbbbbeeeeeeeeeeeeee +eeeeeeeeeeeeeeeeeeeee99444449a9944444444444440eed65649a04f4977155149a04f4977656d7777788e0449449949944444eeeeeebbbbaeeeeeeeeeeeee +eeeeeeeeeeeeeeeeeeee44449a994444444444994440eeeed6569afaf00a7a15519afaf00a7a656d2288822e0449444994444000eeeeeeeebbbbeeeeeeeeeeee +eeeeeeeeeeeeeeeeee4499994444494444944494444eeeeed656dcafaf05071551dcafaf0507656de28882ee4444444999444044eeeeeeeeebbbeeeeeeeeeeee +eeeeeeeeeeeeeeeea9994494499449444494444440eeeeeed65690cdf09a5d155190cdf09a5d656dee282eee0444499444444444eeeeeeeeebbbbeeeeeeeeeee +eeeeeeeeeeee99994494444444944444449444444eeeeeeed656dc09d9d9d61551dc09d9d9d6656deee2eeee0444444444444440eeeeeeeeeebbaeeeeeeeeeee +eeeeeeee44994494449444444494444444444400eeeeeeeed60d777777777775577777777777d06deeeeeeee004499440e004440eeeeeeeeeeebaeeeeeeeeeee +eeee44994444444444444444444444444440eeeeeeeeeeeeed6000000000000ee0000000000006deeeeeeeeee0049eeeeeee000eeeeeeeeeeeeeeeeeeeeeeeee __gff__ -0041414141414141414141414141414141414141414141414141414141410000414141414141414141414141410000004141414141414141414141414141414141414141414141414141616180808000414040414141414141414040414141414545454343434343454541414141414145455149434343434545414141418080 -0000000000000000000000000000000000000000000000000000000000000000000000000000000041418080800000000000000000000000414141800080414100000000000000000000008080804141000000000000000000004141808080804040404041410000000080418080800041414141414100000000000000008000 +0041414141414141414141414141414141414141414141414141414141410000414141434341414141414141410000004141414141414141414141414141414141414141414141414141616180808000414040414141414141414040414141414141414141414141414141414141414141414141414141414141414141418080 +0000000000000000000000000000000000000000000000000000000000000000000000000000000041418080800000000000000000000000414141800080414100000000000000000000008080804141000000000000000000004141808080804040404043430000000080418080800041414141434300000000004040008000 __map__ 6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e 6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6f6f6e6f6e6f6e6f6e6f6e6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e diff --git a/data/data_stage1_11.p8 b/data/data_stage1_11.p8 index 27809ce6..f161aa15 100644 --- a/data/data_stage1_11.p8 +++ b/data/data_stage1_11.p8 @@ -1,36 +1,36 @@ 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) __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 -eeeeeeeeeeeeeeeeeeeeeee949494449a99494444444440ed656c0ca7a77701551c0ca7a7770656deee888eeeeeeeeeeeeeeeeeeeeeeebbbbbeeeeeeeeeeeeee -eeeeeeeeeeeeeeeeeeeee99444449a9944444444444440eed65649a04f4977155149a04f4977656de7777788eeeeeeeeeeeeeeeeeeeeeebbbbaeeeeeeeeeeeee -eeeeeeeeeeeeeeeeeeee44449a994444444444994440eeeed6569afaf00a7a15519afaf00a7a656d777777888eeeeeeeeeeeeeeeeeeeeeeebbbbeeeeeeeeeeee -eeeeeeeeeeeeeeeeee4499994444494444944494444eeeeed656dcafaf05071551dcafaf0507656d222888222eeeeeeeeeeeeeeeeeeeeeeeebbbeeeeeeeeeeee -eeeeeeeeeeeeeeeea9994494499449444494444440eeeeeed65690cdf09a5d155190cdf09a5d656de2288822eeeeeeeeeeeeeeeeeeeeeeeeebbbbeeeeeeeeeee -eeeeeeeeeeee99994494444444944444449444444eeeeeeed656dc09d9d9d61551dc09d9d9d6656dee28882eeeeeeeeeeeeeeeeeeeeeeeeeeebbaeeeeeeeeeee -eeeeeeee44994494449444444494444444444400eeeeeeeed60d777777777775577777777777d06deee282eeeeeeeeeeeeeeeeeeeeeeeeeeeeebaeeeeeeeeeee -eeee44994444444444444444444444444440eeeeeeeeeeeeed6000000000000ee0000000000006deeeee2eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee +eeeeeeeeeeeeeeeeeeeeeee949494449a99494444444440ed656c0ca7a77701551c0ca7a7770656de77778ee4444444444444444eeeeebbbbbeeeeeeeeeeeeee +eeeeeeeeeeeeeeeeeeeee99444449a9944444444444440eed65649a04f4977155149a04f4977656d7777788e0449449949944444eeeeeebbbbaeeeeeeeeeeeee +eeeeeeeeeeeeeeeeeeee44449a994444444444994440eeeed6569afaf00a7a15519afaf00a7a656d2288822e0449444994444000eeeeeeeebbbbeeeeeeeeeeee +eeeeeeeeeeeeeeeeee4499994444494444944494444eeeeed656dcafaf05071551dcafaf0507656de28882ee4444444999444044eeeeeeeeebbbeeeeeeeeeeee +eeeeeeeeeeeeeeeea9994494499449444494444440eeeeeed65690cdf09a5d155190cdf09a5d656dee282eee0444499444444444eeeeeeeeebbbbeeeeeeeeeee +eeeeeeeeeeee99994494444444944444449444444eeeeeeed656dc09d9d9d61551dc09d9d9d6656deee2eeee0444444444444440eeeeeeeeeebbaeeeeeeeeeee +eeeeeeee44994494449444444494444444444400eeeeeeeed60d777777777775577777777777d06deeeeeeee004499440e004440eeeeeeeeeeebaeeeeeeeeeee +eeee44994444444444444444444444444440eeeeeeeeeeeeed6000000000000ee0000000000006deeeeeeeeee0049eeeeeee000eeeeeeeeeeeeeeeeeeeeeeeee __gff__ -0041414141414141414141414141414141414141414141414141414141410000414141414141414141414141410000004141414141414141414141414141414141414141414141414141616180808000414040414141414141414040414141414545454343434343454541414141414145455149434343434545414141418080 -0000000000000000000000000000000000000000000000000000000000000000000000000000000041418080800000000000000000000000414141800080414100000000000000000000008080804141000000000000000000004141808080804040404041410000000080418080800041414141414100000000000000008000 +0041414141414141414141414141414141414141414141414141414141410000414141434341414141414141410000004141414141414141414141414141414141414141414141414141616180808000414040414141414141414040414141414141414141414141414141414141414141414141414141414141414141418080 +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 d9ba0a86..6368f9d5 100644 --- a/data/data_stage1_20.p8 +++ b/data/data_stage1_20.p8 @@ -1,36 +1,36 @@ 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) __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 -eeeeeeeeeeeeeeeeeeeeeee949494449a99494444444440ed656c0ca7a77701551c0ca7a7770656deee888eeeeeeeeeeeeeeeeeeeeeeebbbbbeeeeeeeeeeeeee -eeeeeeeeeeeeeeeeeeeee99444449a9944444444444440eed65649a04f4977155149a04f4977656de7777788eeeeeeeeeeeeeeeeeeeeeebbbbaeeeeeeeeeeeee -eeeeeeeeeeeeeeeeeeee44449a994444444444994440eeeed6569afaf00a7a15519afaf00a7a656d777777888eeeeeeeeeeeeeeeeeeeeeeebbbbeeeeeeeeeeee -eeeeeeeeeeeeeeeeee4499994444494444944494444eeeeed656dcafaf05071551dcafaf0507656d222888222eeeeeeeeeeeeeeeeeeeeeeeebbbeeeeeeeeeeee -eeeeeeeeeeeeeeeea9994494499449444494444440eeeeeed65690cdf09a5d155190cdf09a5d656de2288822eeeeeeeeeeeeeeeeeeeeeeeeebbbbeeeeeeeeeee -eeeeeeeeeeee99994494444444944444449444444eeeeeeed656dc09d9d9d61551dc09d9d9d6656dee28882eeeeeeeeeeeeeeeeeeeeeeeeeeebbaeeeeeeeeeee -eeeeeeee44994494449444444494444444444400eeeeeeeed60d777777777775577777777777d06deee282eeeeeeeeeeeeeeeeeeeeeeeeeeeeebaeeeeeeeeeee -eeee44994444444444444444444444444440eeeeeeeeeeeeed6000000000000ee0000000000006deeeee2eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee +eeeeeeeeeeeeeeeeeeeeeee949494449a99494444444440ed656c0ca7a77701551c0ca7a7770656de77778ee4444444444444444eeeeebbbbbeeeeeeeeeeeeee +eeeeeeeeeeeeeeeeeeeee99444449a9944444444444440eed65649a04f4977155149a04f4977656d7777788e0449449949944444eeeeeebbbbaeeeeeeeeeeeee +eeeeeeeeeeeeeeeeeeee44449a994444444444994440eeeed6569afaf00a7a15519afaf00a7a656d2288822e0449444994444000eeeeeeeebbbbeeeeeeeeeeee +eeeeeeeeeeeeeeeeee4499994444494444944494444eeeeed656dcafaf05071551dcafaf0507656de28882ee4444444999444044eeeeeeeeebbbeeeeeeeeeeee +eeeeeeeeeeeeeeeea9994494499449444494444440eeeeeed65690cdf09a5d155190cdf09a5d656dee282eee0444499444444444eeeeeeeeebbbbeeeeeeeeeee +eeeeeeeeeeee99994494444444944444449444444eeeeeeed656dc09d9d9d61551dc09d9d9d6656deee2eeee0444444444444440eeeeeeeeeebbaeeeeeeeeeee +eeeeeeee44994494449444444494444444444400eeeeeeeed60d777777777775577777777777d06deeeeeeee004499440e004440eeeeeeeeeeebaeeeeeeeeeee +eeee44994444444444444444444444444440eeeeeeeeeeeeed6000000000000ee0000000000006deeeeeeeeee0049eeeeeee000eeeeeeeeeeeeeeeeeeeeeeeee __gff__ -0041414141414141414141414141414141414141414141414141414141410000414141414141414141414141410000004141414141414141414141414141414141414141414141414141616180808000414040414141414141414040414141414545454343434343454541414141414145455149434343434545414141418080 -0000000000000000000000000000000000000000000000000000000000000000000000000000000041418080800000000000000000000000414141800080414100000000000000000000008080804141000000000000000000004141808080804040404041410000000080418080800041414141414100000000000000008000 +0041414141414141414141414141414141414141414141414141414141410000414141434341414141414141410000004141414141414141414141414141414141414141414141414141616180808000414040414141414141414040414141414141414141414141414141414141414141414141414141414141414141418080 +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 fbb40926..67f3e117 100644 --- a/data/data_stage1_21.p8 +++ b/data/data_stage1_21.p8 @@ -1,36 +1,36 @@ 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) __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 -eeeeeeeeeeeeeeeeeeeeeee949494449a99494444444440ed656c0ca7a77701551c0ca7a7770656deee888eeeeeeeeeeeeeeeeeeeeeeebbbbbeeeeeeeeeeeeee -eeeeeeeeeeeeeeeeeeeee99444449a9944444444444440eed65649a04f4977155149a04f4977656de7777788eeeeeeeeeeeeeeeeeeeeeebbbbaeeeeeeeeeeeee -eeeeeeeeeeeeeeeeeeee44449a994444444444994440eeeed6569afaf00a7a15519afaf00a7a656d777777888eeeeeeeeeeeeeeeeeeeeeeebbbbeeeeeeeeeeee -eeeeeeeeeeeeeeeeee4499994444494444944494444eeeeed656dcafaf05071551dcafaf0507656d222888222eeeeeeeeeeeeeeeeeeeeeeeebbbeeeeeeeeeeee -eeeeeeeeeeeeeeeea9994494499449444494444440eeeeeed65690cdf09a5d155190cdf09a5d656de2288822eeeeeeeeeeeeeeeeeeeeeeeeebbbbeeeeeeeeeee -eeeeeeeeeeee99994494444444944444449444444eeeeeeed656dc09d9d9d61551dc09d9d9d6656dee28882eeeeeeeeeeeeeeeeeeeeeeeeeeebbaeeeeeeeeeee -eeeeeeee44994494449444444494444444444400eeeeeeeed60d777777777775577777777777d06deee282eeeeeeeeeeeeeeeeeeeeeeeeeeeeebaeeeeeeeeeee -eeee44994444444444444444444444444440eeeeeeeeeeeeed6000000000000ee0000000000006deeeee2eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee +eeeeeeeeeeeeeeeeeeeeeee949494449a99494444444440ed656c0ca7a77701551c0ca7a7770656de77778ee4444444444444444eeeeebbbbbeeeeeeeeeeeeee +eeeeeeeeeeeeeeeeeeeee99444449a9944444444444440eed65649a04f4977155149a04f4977656d7777788e0449449949944444eeeeeebbbbaeeeeeeeeeeeee +eeeeeeeeeeeeeeeeeeee44449a994444444444994440eeeed6569afaf00a7a15519afaf00a7a656d2288822e0449444994444000eeeeeeeebbbbeeeeeeeeeeee +eeeeeeeeeeeeeeeeee4499994444494444944494444eeeeed656dcafaf05071551dcafaf0507656de28882ee4444444999444044eeeeeeeeebbbeeeeeeeeeeee +eeeeeeeeeeeeeeeea9994494499449444494444440eeeeeed65690cdf09a5d155190cdf09a5d656dee282eee0444499444444444eeeeeeeeebbbbeeeeeeeeeee +eeeeeeeeeeee99994494444444944444449444444eeeeeeed656dc09d9d9d61551dc09d9d9d6656deee2eeee0444444444444440eeeeeeeeeebbaeeeeeeeeeee +eeeeeeee44994494449444444494444444444400eeeeeeeed60d777777777775577777777777d06deeeeeeee004499440e004440eeeeeeeeeeebaeeeeeeeeeee +eeee44994444444444444444444444444440eeeeeeeeeeeeed6000000000000ee0000000000006deeeeeeeeee0049eeeeeee000eeeeeeeeeeeeeeeeeeeeeeeee __gff__ -0041414141414141414141414141414141414141414141414141414141410000414141414141414141414141410000004141414141414141414141414141414141414141414141414141616180808000414040414141414141414040414141414545454343434343454541414141414145455149434343434545414141418080 -0000000000000000000000000000000000000000000000000000000000000000000000000000000041418080800000000000000000000000414141800080414100000000000000000000008080804141000000000000000000004141808080804040404041410000000080418080800041414141414100000000000000008000 +0041414141414141414141414141414141414141414141414141414141410000414141434341414141414141410000004141414141414141414141414141414141414141414141414141616180808000414040414141414141414040414141414141414141414141414141414141414141414141414141414141414141418080 +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 ffde5cd9..c755c90c 100644 --- a/data/data_stage1_30.p8 +++ b/data/data_stage1_30.p8 @@ -1,36 +1,36 @@ 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) __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 -eeeeeeeeeeeeeeeeeeeeeee949494449a99494444444440ed656c0ca7a77701551c0ca7a7770656deee888eeeeeeeeeeeeeeeeeeeeeeebbbbbeeeeeeeeeeeeee -eeeeeeeeeeeeeeeeeeeee99444449a9944444444444440eed65649a04f4977155149a04f4977656de7777788eeeeeeeeeeeeeeeeeeeeeebbbbaeeeeeeeeeeeee -eeeeeeeeeeeeeeeeeeee44449a994444444444994440eeeed6569afaf00a7a15519afaf00a7a656d777777888eeeeeeeeeeeeeeeeeeeeeeebbbbeeeeeeeeeeee -eeeeeeeeeeeeeeeeee4499994444494444944494444eeeeed656dcafaf05071551dcafaf0507656d222888222eeeeeeeeeeeeeeeeeeeeeeeebbbeeeeeeeeeeee -eeeeeeeeeeeeeeeea9994494499449444494444440eeeeeed65690cdf09a5d155190cdf09a5d656de2288822eeeeeeeeeeeeeeeeeeeeeeeeebbbbeeeeeeeeeee -eeeeeeeeeeee99994494444444944444449444444eeeeeeed656dc09d9d9d61551dc09d9d9d6656dee28882eeeeeeeeeeeeeeeeeeeeeeeeeeebbaeeeeeeeeeee -eeeeeeee44994494449444444494444444444400eeeeeeeed60d777777777775577777777777d06deee282eeeeeeeeeeeeeeeeeeeeeeeeeeeeebaeeeeeeeeeee -eeee44994444444444444444444444444440eeeeeeeeeeeeed6000000000000ee0000000000006deeeee2eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee +eeeeeeeeeeeeeeeeeeeeeee949494449a99494444444440ed656c0ca7a77701551c0ca7a7770656de77778ee4444444444444444eeeeebbbbbeeeeeeeeeeeeee +eeeeeeeeeeeeeeeeeeeee99444449a9944444444444440eed65649a04f4977155149a04f4977656d7777788e0449449949944444eeeeeebbbbaeeeeeeeeeeeee +eeeeeeeeeeeeeeeeeeee44449a994444444444994440eeeed6569afaf00a7a15519afaf00a7a656d2288822e0449444994444000eeeeeeeebbbbeeeeeeeeeeee +eeeeeeeeeeeeeeeeee4499994444494444944494444eeeeed656dcafaf05071551dcafaf0507656de28882ee4444444999444044eeeeeeeeebbbeeeeeeeeeeee +eeeeeeeeeeeeeeeea9994494499449444494444440eeeeeed65690cdf09a5d155190cdf09a5d656dee282eee0444499444444444eeeeeeeeebbbbeeeeeeeeeee +eeeeeeeeeeee99994494444444944444449444444eeeeeeed656dc09d9d9d61551dc09d9d9d6656deee2eeee0444444444444440eeeeeeeeeebbaeeeeeeeeeee +eeeeeeee44994494449444444494444444444400eeeeeeeed60d777777777775577777777777d06deeeeeeee004499440e004440eeeeeeeeeeebaeeeeeeeeeee +eeee44994444444444444444444444444440eeeeeeeeeeeeed6000000000000ee0000000000006deeeeeeeeee0049eeeeeee000eeeeeeeeeeeeeeeeeeeeeeeee __gff__ -0041414141414141414141414141414141414141414141414141414141410000414141414141414141414141410000004141414141414141414141414141414141414141414141414141616180808000414040414141414141414040414141414545454343434343454541414141414145455149434343434545414141418080 -0000000000000000000000000000000000000000000000000000000000000000000000000000000041418080800000000000000000000000414141800080414100000000000000000000008080804141000000000000000000004141808080804040404041410000000080418080800041414141414100000000000000008000 +0041414141414141414141414141414141414141414141414141414141410000414141434341414141414141410000004141414141414141414141414141414141414141414141414141616180808000414040414141414141414040414141414141414141414141414141414141414141414141414141414141414141418080 +0000000000000000000000000000000000000000000000000000000000000000000000000000000041418080800000000000000000000000414141800080414100000000000000000000008080804141000000000000000000004141808080804040404043430000000080418080800041414141434300000000004040008000 __map__ 6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e6f6e0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 diff --git a/data/data_stage1_31.p8 b/data/data_stage1_31.p8 index a79b918a..d566a5ed 100644 --- a/data/data_stage1_31.p8 +++ b/data/data_stage1_31.p8 @@ -1,36 +1,36 @@ 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) __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 -eeeeeeeeeeeeeeeeeeeeeee949494449a99494444444440ed656c0ca7a77701551c0ca7a7770656deee888eeeeeeeeeeeeeeeeeeeeeeebbbbbeeeeeeeeeeeeee -eeeeeeeeeeeeeeeeeeeee99444449a9944444444444440eed65649a04f4977155149a04f4977656de7777788eeeeeeeeeeeeeeeeeeeeeebbbbaeeeeeeeeeeeee -eeeeeeeeeeeeeeeeeeee44449a994444444444994440eeeed6569afaf00a7a15519afaf00a7a656d777777888eeeeeeeeeeeeeeeeeeeeeeebbbbeeeeeeeeeeee -eeeeeeeeeeeeeeeeee4499994444494444944494444eeeeed656dcafaf05071551dcafaf0507656d222888222eeeeeeeeeeeeeeeeeeeeeeeebbbeeeeeeeeeeee -eeeeeeeeeeeeeeeea9994494499449444494444440eeeeeed65690cdf09a5d155190cdf09a5d656de2288822eeeeeeeeeeeeeeeeeeeeeeeeebbbbeeeeeeeeeee -eeeeeeeeeeee99994494444444944444449444444eeeeeeed656dc09d9d9d61551dc09d9d9d6656dee28882eeeeeeeeeeeeeeeeeeeeeeeeeeebbaeeeeeeeeeee -eeeeeeee44994494449444444494444444444400eeeeeeeed60d777777777775577777777777d06deee282eeeeeeeeeeeeeeeeeeeeeeeeeeeeebaeeeeeeeeeee -eeee44994444444444444444444444444440eeeeeeeeeeeeed6000000000000ee0000000000006deeeee2eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee +eeeeeeeeeeeeeeeeeeeeeee949494449a99494444444440ed656c0ca7a77701551c0ca7a7770656de77778ee4444444444444444eeeeebbbbbeeeeeeeeeeeeee +eeeeeeeeeeeeeeeeeeeee99444449a9944444444444440eed65649a04f4977155149a04f4977656d7777788e0449449949944444eeeeeebbbbaeeeeeeeeeeeee +eeeeeeeeeeeeeeeeeeee44449a994444444444994440eeeed6569afaf00a7a15519afaf00a7a656d2288822e0449444994444000eeeeeeeebbbbeeeeeeeeeeee +eeeeeeeeeeeeeeeeee4499994444494444944494444eeeeed656dcafaf05071551dcafaf0507656de28882ee4444444999444044eeeeeeeeebbbeeeeeeeeeeee +eeeeeeeeeeeeeeeea9994494499449444494444440eeeeeed65690cdf09a5d155190cdf09a5d656dee282eee0444499444444444eeeeeeeeebbbbeeeeeeeeeee +eeeeeeeeeeee99994494444444944444449444444eeeeeeed656dc09d9d9d61551dc09d9d9d6656deee2eeee0444444444444440eeeeeeeeeebbaeeeeeeeeeee +eeeeeeee44994494449444444494444444444400eeeeeeeed60d777777777775577777777777d06deeeeeeee004499440e004440eeeeeeeeeeebaeeeeeeeeeee +eeee44994444444444444444444444444440eeeeeeeeeeeeed6000000000000ee0000000000006deeeeeeeeee0049eeeeeee000eeeeeeeeeeeeeeeeeeeeeeeee __gff__ -0041414141414141414141414141414141414141414141414141414141410000414141414141414141414141410000004141414141414141414141414141414141414141414141414141616180808000414040414141414141414040414141414545454343434343454541414141414145455149434343434545414141418080 -0000000000000000000000000000000000000000000000000000000000000000000000000000000041418080800000000000000000000000414141800080414100000000000000000000008080804141000000000000000000004141808080804040404041410000000080418080800041414141414100000000000000008000 +0041414141414141414141414141414141414141414141414141414141410000414141434341414141414141410000004141414141414141414141414141414141414141414141414141616180808000414040414141414141414040414141414141414141414141414141414141414141414141414141414141414141418080 +0000000000000000000000000000000000000000000000000000000000000000000000000000000041418080800000000000000000000000414141800080414100000000000000000000008080804141000000000000000000004141808080804040404043430000000080418080800041414141434300000000004040008000 __map__ 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 diff --git a/data/data_stage1_runtime.p8 b/data/data_stage1_runtime.p8 index 36ab09d4..b27ed9d4 100644 --- a/data/data_stage1_runtime.p8 +++ b/data/data_stage1_runtime.p8 @@ -1,32 +1,33 @@ pico-8 cartridge // http://www.pico-8.com version 29 __lua__ - +-- picosonic data: +-- runtime spritesheet by leyn __gfx__ -00000000eeeeebbbeeeeeeeeeeeeeeeeeeed65eeeeeeeeeeeeeeeccccced65eeeeeeeeeee5eeeeeeeee000eeeeeeeeeeee8eeeeeeeeeeeeeeeeee8eeeeeeeeee -00000000eeeebbbeeeeeeeeeed66666666666666666666deed66cccccccc6666666666de575eeeeee00eee00eeeeeeeee272eeeeee2eeeeeeeee272eeeeee2ee -00700700eeebbbb7eeebeeee56000000000000000000006556000cc4ccccc0c0000000655775eeee0eeeeeee0eeeeeee877782eee272e2eeee287778ee2e272e -00077000eeeeeb77eebbbeee56ddd440dddddd4a0dd40d65569999cf4cccccc99999996557775eee0eeeeeee0eeeeeeee272eeeeee2ee8eeeeee272eee8ee2ee -00077000eeebb3b7e777eeee56dd47a0d44404aaa047a06556999ccfccccccc995995965577775eee0eeeee0eeeeeeeeee8ee2eeeeee272eee2ee8eee272eeee -00700700ebb7bb3bb3b7beee56d4aaa047aa4a7aa047a0655699ccccccccccc95759756557755eeeee0eee0eeeeeeeeeee2e272eee287778e272e2ee877782ee -00000000ebb77bbbb777bbee564aa0047aaaa700a04aa065569ccccc77cccc7c57757565575eeeeeeee0e0eeeeeeeeeeeeeee2eeeeee272eee2eeeeee272eeee -00000000eebb777777bbbbee564a0a04a004aa0d704aa06556ccccc7077cc07c95757565e5eeeeeeeeee0eeeeeeeeeeeeeeeeeeeeeeee8eeeeeeeeeeee8eeeee -eeed6deeeb773777777bbeee564a0a70a0d4aaaa707a0d655cccccc70777707c95777565eeeeeeeeeeeeccccceeeeeeeeeeeeeeeeeeeeeeeeeeeeeccccceeeee -eee777eebb77bb37777eeeee564a00a0a447aa44ad7a006556999cc7077ff07c99577565eeeeeeeeeeeeeccccceeceeeeeeeccccceeeeeeeeeeeeeeccccccece -ee66666eeebbeebb777bb7bb560aaaa0aaaa0a00a0aaa0655699ccffffffff0055757755eeeeeeeeeeeeeecffcccceeeeeeeeccccceeceeeeeeeeeeecffcccfe -eed666deeeeeee77b777b7b756d0a0a00aa00a0da00000655699cccfffffff9957757755eeeeeeeeeeeecccfcc7ccceeeeeeeecffcccceeeeeeeeecccfcc7ccc -eed666deeeba77777777777756dd00a0d00d0a0da0dddd65569cccc9fffff99957566655eeeeeeeeeeecccccc770cceeeeeecccfcc7ccceeeeeeecccccc770cc -eed666deebba77777777b77b56dddd0dddddd0dd00dddd65569cc9999999999995666655eeeeeeee77eeecccc770ceeeeeecccccc770cceeeeeeeeecccc7707c +00000000eeeeebbbeeeeeeeeeeeeeeeeeeed65eeeeeeeeeeeeeeeccccced65eeeeeeeeeee5eeeeeeeeeeeeeeeeeeeeeeee8eeeeeeeeeeeeeeeeee8eeeeeeeeee +00000000eeeebbbeeeeeeeeeed66666666666666666666deed66cccccccc6666666666de575eeeeeee7eeeeeeeeee7eee272eeeeee2eeeeeeeee272eeeeee2ee +00700700eeebbbb7eeebeeee56000000000000000000006556000cc4ccccc0c0000000655775eeeeeed7eee87eee7dee877782eee272e2eeee287778ee2e272e +00077000eeeeeb77eebbbeee56ddd440dddddd4a0dd40d65569999cf4cccccc99999996557775eeeeee7ee8008ee7eeee272eeeeee2ee8eeeeee272eee8ee2ee +00077000eeebb3b7e777eeee56dd47a0d44404aaa047a06556999ccfccccccc995995965577775eeedd7780970877ddeee8ee2eeeeee272eee2ee8eee272eeee +00700700ebb7bb3bb3b7beee56d4aaa047aa4a7aa047a0655699ccccccccccc95759756557755eeeeedd78049087ddeeee2e272eee287778e272e2ee877782ee +00000000ebb77bbbb777bbee564aa0047aaaa700a04aa065569ccccc77cccc7c57757565575eeeeeeeedde8008eddeeeeeeee2eeeeee272eee2eeeeee272eeee +00000000eebb777777bbbbee564a0a04a004aa0d704aa06556ccccc7077cc07c95757565e5eeeeeeeeeeeee88eeeeeeeeeeeeeeeeeeee8eeeeeeeeeeee8eeeee +eeed6deeeb773777777bbeee564a0a70a0d4aaaa707a0d655cccccc70777707c95777565e00000eeeeeeccccceeeeeeeeeeeeeeeeeeeeeeeeeeeeeccccceeeee +eee777eebb77bb37777eeeee564a00a0a447aa44ad7a006556999cc7077ff07c995775650eeeee0eeeeeeccccceeceeeeeeeccccceeeeeeeeeeeeeeccccccece +ee66666eeebbeebb777bb7bb560aaaa0aaaa0a00a0aaa0655699ccffffffff00557577550eeeee0eeeeeeecffcccceeeeeeeeccccceeceeeeeeeeeeecffcccfe +eed666deeeeeee77b777b7b756d0a0a00aa00a0da00000655699cccfffffff9957757755e0eee0eeeeeecccfcc7ccceeeeeeeecffcccceeeeeeeeecccfcc7ccc +eed666deeeba77777777777756dd00a0d00d0a0da0dddd65569cccc9fffff99957566655ee0e0eeeeeecccccc770cceeeeeecccfcc7ccceeeeeeecccccc770cc +eed666deebba77777777b77b56dddd0dddddd0dd00dddd65569cc9999999999995666655eee0eeee77eeecccc770ceeeeeecccccc770cceeeeeeeeecccc7707c eed666deebbb77777777bb775d77777777777777777777d55d7c777777777777775555d5eeeeeeeee77eeeccff77f0ee77eeecccc770ceeeeeeeeeffccff77f0 eed666debbbba7777777bbbbe0000000000000000000000ee0000000000000000000000eeeeeeeee777ffccccfffeeeee77eeeccff77f0eeeee77fcccccfffee -eed666debbbb7a7777777777eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeefccfeeeeeeeee7ffccccfffeeeeee7707eccffce67e -eed666de77b77777777777bbeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee1e1cffeeeeeeee7e1efcffeeeeeeeeee77cccccf11607 -eed666deebba777777777ebbeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee11cf1eeeeeeeeeee11cff11eeeeeeeeccceee611ee06 -eed666deebbba7777777eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeecc11ee8800eeeeeecccc1ee880ee77eeee227eeeee -eed666dee7b7777777777bbeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeecc1688800eeeeeeeeec6e8880e882eeee2887eeee -eed666debbbb77777b3377bbeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeec7872000eeeeeeeeec77820008772eeee268eeee -ee06660eebbe77b777bbbbebeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee788200eeeeeeeeee788820e07882eeee288eeee -eee000eeeebee77bebebeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee22000eeeeeeeeeee88200ee0888eeeee8eeeee +eed666debbbb7a7777777777bbbb3bbbbbbbbbbbeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeefccfeeeeeeeee7ffccccfffeeeeee7707eccffce67e +eed666de77b77777777777bbbbb303bbbb3bb0bbeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee1e1cffeeeeeeee7e1efcffeeeeeeeeee77cccccf11607 +eed666deebba777777777ebb3b300033b30b3003eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee11cf1eeeeeeeeeee11cff11eeeeeeeeccceee611ee06 +eed666deebbba7777777eeee0300400030030400eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeecc11ee8800eeeeeecccc1ee880ee77eeee227eeeee +eed666dee7b7777777777bbe4004444000000444eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeecc1688800eeeeeeeeec6e8880e882eeee2887eeee +eed666debbbb77777b3377bb4444444444004494eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeec7872000eeeeeeeeec77820008772eeee268eeee +ee06660eebbe77b777bbbbeb4449444444044494eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee788200eeeeeeeeee788820e07882eeee288eeee +eee000eeeebee77bebebeeee4449444444444444eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee22000eeeeeeeeeee88200ee0888eeeee8eeeee eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee @@ -83,7 +84,7 @@ eeeeeeeeeeeeeeeeeeee7777080eeeeeeeee707e66eeeeeeeeeeee7611ee00eeeeeeeee7c708eeee eeeeeeeeeeeeeeeeeeeee772662622eeeeeee7e222eeeeeeeeeeee226eeeeeeeeeeeeee777880eeeeeeeeee888eeeeeeeeee0717eeeeeeeeeeeeeeeeeeeeeeee eeeeeeeeeeeeeeeeeeeeeee000060eeeeeeeeee066eeeeeeeeeeee66eeeeeeeeeeeeeee87880eeeeeeeeeee277eeeeeeeeee02787eeeeeeeeeeeeeeeeeeeeeee eeeeeeeeeeeeeeeeeeeeeeee2220eeeeeeeeeeee022eeeeeeeeeee02eeeeeeeeeeeeeee0220eeeeeeeeeeeee288eeeeeeeeee02878eeeeeeeeeeeeeeeeeeeeee -eeeecccccceeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee +eeeecccccceeeeeeeeeccccccceeeeeeeeeecccccceeeeeeeeeccccccceeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee eeecccc70cc0eeeeecccccc70cc0eeeeeeecccc70cc0eeeeecccccc70cc0eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee eeccffc7d07feeeeccccffc7d07feeeeeeccffc7d07feeeeccccffc7d07feeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee ecccfcc777ffeeeeeeccfcc777ffeeeeecccfcc777ffeeeeeeccfcc777ffeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee @@ -131,3 +132,6 @@ eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee +__gff__ +0000000000000000000000000000000000000000000000000000000000000000000000030300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 diff --git a/data/metadata.p8 b/data/metadata.p8 index fc7c01f6..0b146a94 100644 --- a/data/metadata.p8 +++ b/data/metadata.p8 @@ -3,132 +3,133 @@ version 16 __lua__ -- pico sonic -- by leyn +-- (title and author above will be overwritten by add_metadata.py, also adding version, 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 +11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 +11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 +11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 +11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 +11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 +11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 +11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 +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 36914c19..8e41377f 100644 Binary files a/data/metadata.p8.png and b/data/metadata.p8.png differ diff --git a/data/version.txt b/data/version.txt new file mode 100644 index 00000000..ef425ca9 --- /dev/null +++ b/data/version.txt @@ -0,0 +1 @@ +5.2 diff --git a/doc/all_emeralds.png b/doc/all_emeralds.png new file mode 100644 index 00000000..c3f17f96 Binary files /dev/null and b/doc/all_emeralds.png differ diff --git a/doc/picosonic_showcase.png b/doc/picosonic_showcase.png new file mode 100644 index 00000000..d01a6ea0 Binary files /dev/null and b/doc/picosonic_showcase.png differ diff --git a/export_and_patch_cartridge_release.sh b/export_and_patch_cartridge_release.sh new file mode 100755 index 00000000..6a37c89b --- /dev/null +++ b/export_and_patch_cartridge_release.sh @@ -0,0 +1,132 @@ +#!/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 +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="$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 (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}/"* + +# 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" + +# 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\"" +echo "> $patch_js_cmd" +bash -c "$patch_js_cmd" + +if [[ $? -ne 0 ]]; then + echo "" + echo "Patch JS step failed, STOP." + 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) + rm -f "${cartridge_basename}_png_cartridges.zip" + zip -r "${cartridge_basename}_png_cartridges.zip" "$rel_png_folder" + + # 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 + pushd "${rel_bin_folder}" + + # Linux 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 + # 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_cartridge_release.p8 b/export_cartridge_release.p8 deleted file mode 100644 index 7f2cfe95..00000000 --- a/export_cartridge_release.p8 +++ /dev/null @@ -1,66 +0,0 @@ -pico-8 cartridge // http://www.pico-8.com -version 16 -__lua__ --- Run this commandline script with: --- $ pico8 -x export_cartridge.p8 - --- It will export .bin and .p8.png for the current game release --- Make sure to ./build_game release && ./install_cartridges.sh first --- Note that it will not warn if cartridge is not found. --- Paths are relative to PICO-8 carts directory. - -cd("picosonic/v5.1_release") - -local entry_cartridge = "picosonic_titlemenu.p8" - -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_runtime.p8" -} - --- prepare folder for png cartridges -mkdir("picosonic_v5.1_release.png") - --- 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("picosonic_v5.1_release.png") - save(cartridge_name..".png") - cd("..") -end - --- load the entry cartridge (titlemenu) last, since we're going to use it for export --- just after -load(entry_cartridge) - --- save as png cartridge -cd("picosonic_v5.1_release.png") -save(entry_cartridge..".png") -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 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 -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") -cd("..") 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/export_game_release.p8 b/export_game_release.p8 new file mode 100644 index 00000000..ad6fd538 --- /dev/null +++ b/export_game_release.p8 @@ -0,0 +1,108 @@ +pico-8 cartridge // http://www.pico-8.com +version 16 +__lua__ +-- Run this commandline script with: +-- $ pico8 -x export_cartridge.p8 + +-- It will export .bin and .p8.png for the current game release +-- Make sure to ./build_game release && ./install_cartridges.sh first +-- Note that it will not warn if cartridge is not found. +-- 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.2" +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_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("..") + + printh("Resaved (adapted) cartridges as PNG in carts/"..export_folder.."/"..rel_png_folder) + + + -- 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/install_all_cartridges.sh b/install_all_cartridges.sh index d24c0a7b..d4086de3 100755 --- a/install_all_cartridges.sh +++ b/install_all_cartridges.sh @@ -1,21 +1,22 @@ #!/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 # 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")" +data_path="$(dirname "$0")/data" # check that source and output paths have been provided if ! [[ $# -ge 1 && $# -le 2 ]] ; then @@ -25,20 +26,29 @@ if ! [[ $# -ge 1 && $# -le 2 ]] ; then exit 1 fi +# Configuration: cartridge +version=`cat "$data_path/version.txt"` 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" +cartridge_list="titlemenu stage_intro 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 + +# 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/install_single_cartridge.sh b/install_single_cartridge.sh index 8ff91aa4..e488be0f 100755 --- a/install_single_cartridge.sh +++ b/install_single_cartridge.sh @@ -3,48 +3,55 @@ # 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 +# 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 # 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 +# saved from PICO-8 with export_game_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 $#: - \$1: cartridge_suffix ('titlemenu', 'ingame' or 'stage_clear') - \$2: config ('debug', 'release', etc.) + \$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 fi # Configuration: cartridge cartridge_stem="picosonic" -version="5.1" +version=`cat "$data_path/version.txt"` cartridge_suffix="$1"; shift 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}" 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}" 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 +60,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..7e716d22 --- /dev/null +++ b/install_single_cartridge_with_data.sh @@ -0,0 +1,56 @@ +#!/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', 'stage_intro', '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_game_release.p8 into PICO-8 carts folder + +# 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 + echo "build.sh takes 1 or 2 params, provided $#: + \$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 +fi + +# Configuration: cartridge +version=`cat "$data_path/version.txt"` +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/pico-boots b/pico-boots index c8d59229..9a45fa0f 160000 --- a/pico-boots +++ b/pico-boots @@ -1 +1 @@ -Subproject commit c8d59229901b13af1c6e4d7979f4473c673a1266 +Subproject commit 9a45fa0fa37d60ea27082be3d4d0a839c776b542 diff --git a/run_cartridge.sh b/run_cartridge.sh index 764449f0..14d03216 100755 --- a/run_cartridge.sh +++ b/run_cartridge.sh @@ -3,15 +3,18 @@ # 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 # 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 @@ -24,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, diff --git a/run_itest.sh b/run_itest.sh index c0d7f0e6..53519fc2 100755 --- a/run_itest.sh +++ b/run_itest.sh @@ -3,13 +3,16 @@ # 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 +# 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 5f5c2c62..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 $@" diff --git a/screenshots/picosonic_showcase.png b/screenshots/picosonic_showcase.png deleted file mode 100644 index 8521609e..00000000 Binary files a/screenshots/picosonic_showcase.png and /dev/null differ diff --git a/sonic-2d-tech-demo.sublime-project b/sonic-2d-tech-demo.sublime-project index cb3bfa8b..854d0f64 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", @@ -36,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, @@ -55,9 +77,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": [ @@ -105,22 +126,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"] } ] }, @@ -128,50 +149,73 @@ "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_with_data.sh ingame release"] + } + ] + }, + { + "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.sh ingame release"] + "cmd": ["./build_and_install_single_cartridge_with_data.sh stage_intro release"] } ] }, @@ -179,22 +223,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"] } ] }, @@ -237,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}", @@ -257,26 +309,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)"] }, ] }, @@ -284,50 +336,77 @@ "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_with_data.sh ingame release && (./pico-boots/scripts/reload.sh || ./run_cartridge.sh ingame release)"] + }, + ] + }, + { + "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.sh ingame release && (./pico-boots/scripts/reload.sh || ./run_cartridge.sh ingame release)"] + "cmd": ["./build_and_install_single_cartridge_with_data.sh stage_intro release && (./pico-boots/scripts/reload.sh || ./run_cartridge.sh stage_intro release)"] }, ] }, @@ -335,22 +414,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)"] }, ] }, @@ -371,6 +454,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)"] @@ -393,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}", @@ -469,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"] @@ -477,6 +588,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"] @@ -496,129 +611,200 @@ ] }, { - "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_intro (with data)", "working_dir": "${project_path}", "shell": true, - "cmd": ["./install_single_cartridge.sh stage_clear debug"], + "cmd": ["./install_single_cartridge_with_data.sh stage_intro 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_intro debug"] }, { "name": "cheat", - "cmd": ["./install_single_cartridge.sh stage_clear cheat"] + "cmd": ["./install_single_cartridge_with_data.sh stage_intro cheat"] }, { "name": "release", - "cmd": ["./install_single_cartridge.sh stage_clear release"] + "cmd": ["./install_single_cartridge_with_data.sh stage_intro release"] }, { "name": "cheat (png)", - "cmd": ["./install_single_cartridge.sh stage_clear cheat png"] + "cmd": ["./install_single_cartridge_with_data.sh stage_intro cheat png"] }, { "name": "release (png)", - "cmd": ["./install_single_cartridge.sh stage_clear release png"] + "cmd": ["./install_single_cartridge_with_data.sh stage_intro release png"] } ] }, { - "name": "Game: export cartridge release", + "name": "Game: install cartridge stage_clear (with data)", "working_dir": "${project_path}", "shell": true, - "cmd": ["./export_cartridge_release.sh"], - "syntax": "Packages/Python/Python.sublime-syntax" + "cmd": ["./install_single_cartridge_with_data.sh stage_clear debug"], + "syntax": "Packages/Python/Python.sublime-syntax", + "variants": + [ + { + "name": "debug", + "cmd": ["./install_single_cartridge_with_data.sh stage_clear debug"] + }, + { + "name": "cheat", + "cmd": ["./install_single_cartridge_with_data.sh stage_clear cheat"] + }, + { + "name": "release", + "cmd": ["./install_single_cartridge_with_data.sh stage_clear release"] + }, + { + "name": "cheat (png)", + "cmd": ["./install_single_cartridge_with_data.sh stage_clear cheat png"] + }, + { + "name": "release (png)", + "cmd": ["./install_single_cartridge_with_data.sh stage_clear release png"] + } + ] + }, + { + "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}", + "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: 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", @@ -682,6 +868,10 @@ "name": "ingame", "cmd": ["./test.sh ingame"], }, + { + "name": "stage_intro", + "cmd": ["./test.sh stage_intro"], + }, { "name": "stage_clear", "cmd": ["./test.sh stage_clear"], @@ -792,6 +982,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"], @@ -876,7 +1111,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", @@ -915,6 +1150,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", @@ -924,10 +1167,42 @@ "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" + }, + { + "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", + "shell": true, + "cmd": ["python3 -m scripts.test_patch_pico8_js"], "file_regex": "\"(.*\\.py)\"(?:, line )([\\d]+)(?:\\:)?([\\d]+)?(.*)", "syntax": "Packages/Python/Python.sublime-syntax" }, 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..589fada3 100644 --- a/src/application/picosonic_app_ingame.lua +++ b/src/application/picosonic_app_ingame.lua @@ -15,7 +15,24 @@ 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, "warp to start", function() + assert(flow.curr_state.type == ':stage') + flow.curr_state:store_picked_emerald_data() + -- prefer passing basename for compatibility with .p8.png + load('picosonic_ingame') + end) + + menuitem(4, "retry from zero", function() + -- prefer passing basename for compatibility with .p8.png + load('picosonic_ingame') + end) + + menuitem(5, "back to title", function() + -- prefer passing basename for compatibility with .p8.png + load('picosonic_titlemenu') + 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..1f6c32f4 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,16 +23,29 @@ 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) - it('should load cartridge: picosonic_titlemenu.p8', function () + 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', 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/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_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 ffcd30fe..a103cebe 100644 --- a/src/common_stage_clear.lua +++ b/src/common_stage_clear.lua @@ -4,8 +4,17 @@ -- 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") + +--[[#pico8 +--#if unity + +-- see explanations in common_ingame.lua +require("ordered_require_stage_clear") + +--#endif +--#pico8]] diff --git a/src/common_stage_intro.lua b/src/common_stage_intro.lua new file mode 100644 index 00000000..338d9fd7 --- /dev/null +++ b/src/common_stage_intro.lua @@ -0,0 +1,29 @@ +-- 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 + +-- 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 +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/common_titlemenu.lua b/src/common_titlemenu.lua index f05af829..41b3e708 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 @@ -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]] 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/sprite_flags.lua b/src/data/sprite_flags.lua index 2abaa684..804ba533 100644 --- a/src/data/sprite_flags.lua +++ b/src/data/sprite_flags.lua @@ -1,17 +1,17 @@ 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 = { 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/data/stage_clear_data.lua b/src/data/stage_clear_data.lua new file mode 100644 index 00000000..a1e7a1ca --- /dev/null +++ b/src/data/stage_clear_data.lua @@ -0,0 +1,19 @@ +local stage_clear_data = { + + -- 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 = 2.0, + + -- 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, +} + +return stage_clear_data diff --git a/src/data/stage_data.lua b/src/data/stage_data.lua index 0dafd743..0e162892 100644 --- a/src/data/stage_data.lua +++ b/src/data/stage_data.lua @@ -1,36 +1,30 @@ local location_rect = require("engine/core/location_rect") -local sprite_data = require("engine/render/sprite_data") -local audio = require("resources/audio") +local stage_data = { -return { + -- 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 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, - - -- 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 = { @@ -119,3 +113,5 @@ return { } } + +return stage_data 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/base_stage_state.lua b/src/ingame/base_stage_state.lua new file mode 100644 index 00000000..3cfc9f6e --- /dev/null +++ b/src/ingame/base_stage_state.lua @@ -0,0 +1,178 @@ +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() + + -- 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 + + +-- 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..cartridge_ext +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 + + -- 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 + 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..91713b3d --- /dev/null +++ b/src/ingame/base_stage_state_utest.lua @@ -0,0 +1,316 @@ +-- 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, + state.palm_tree_leaves_core_global_locations, + }) + 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/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/playercharacter.lua b/src/ingame/playercharacter.lua index 20c32a4a..cdaf03d0 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,27 +249,38 @@ 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 - -- 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') +--#if ingame - -- 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 == ':stage' 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 @@ -288,12 +305,27 @@ function player_char:set_slope_angle_with_quadrant(angle, force_upward_sprite) end function player_char:update() - self:handle_input() +-- in stage_intro cartridge, we want Sonic to stay idle, so no input +-- but update physics and render as usual +--#if ingame + +--#if busted + if flow.curr_state.type == ':stage' then +--#endif + self:handle_input() +--#if busted + end +--#endif + +--(ingame) +--#endif self:update_motion() self:update_anim() 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 @@ -317,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 @@ -362,6 +394,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 @@ -371,6 +404,7 @@ function player_char:force_move_right() end --#if cheat + function player_char:toggle_debug_motion() -- 1 -> 2 (debug) -- 2 -> 1 (platformer) @@ -389,18 +423,34 @@ function player_char:set_motion_mode(val) self.debug_velocity = vector.zero() end end + +--(cheat) +--#endif + +--(ingame) --#endif -- update player position 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 + +--#if busted + if flow.curr_state.type == ':stage' 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() @@ -493,7 +543,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 @@ -502,7 +552,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) @@ -559,13 +609,30 @@ 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) + +--#if ingame - -- we now check for ignored tiles, ramp or loop - 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 - ignore_tile = true +--#if busted + if flow.curr_state.type == ':stage' 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 -- tile is on layer with disabled collision, return emptiness @@ -604,6 +671,12 @@ 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 + 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) @@ -623,8 +696,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) @@ -873,10 +948,21 @@ function player_char:update_platformer_motion() self:update_platformer_motion_airborne() end - self:check_spring() - self:check_launch_ramp() - self:check_emerald() - self:check_loop_external_triggers() +--#if ingame + +--#if busted + if flow.curr_state.type == ':stage' then +--#endif + self:check_spring() + self:check_launch_ramp() + self:check_emerald() + self:check_loop_external_triggers() +--#if busted + end +--#endif + +--(ingame) +--#endif end -- check if character is fast enough to roll and wants to roll @@ -1394,7 +1480,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) @@ -1850,6 +1936,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 @@ -1866,7 +1953,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 @@ -1876,8 +1968,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 @@ -2002,6 +2093,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 @@ -2178,6 +2271,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/playercharacter_utest.lua b/src/ingame/playercharacter_utest.lua index e3435cab..9c84437f 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 () @@ -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 () @@ -5382,7 +5460,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 +5496,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..10179b2e 100644 --- a/src/ingame/stage_state.lua +++ b/src/ingame/stage_state.lua @@ -1,10 +1,6 @@ -local gamestate = require("engine/application/gamestate") local volume = require("engine/audio/volume") -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 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") @@ -13,14 +9,13 @@ 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) +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 @@ -29,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,23 +38,18 @@ 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 = {} - - -- 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() - - -- 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 self.enable_spawn_objects = true --#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) @@ -72,12 +63,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]] @@ -95,8 +90,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 @@ -114,7 +107,15 @@ 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 - local runtime_data_path = "data_stage"..self.curr_stage_id.."_runtime.p8" + -- 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 (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"..cartridge_ext reload(0x0, 0x0, 0x600, runtime_data_path) -- the runtime spritesheet also contains 45-degree rotated sprite variants @@ -194,6 +195,10 @@ function stage_state:reload_runtime_data() -- so we just have a little margin! 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 -- we know that all coroutines belong to the stage state, so no risk clearing them from here) @@ -201,7 +206,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() @@ -209,6 +213,7 @@ function stage_state:on_exit() -- stop audio self:stop_bgm() end +--]] function stage_state:update() self:update_fx() @@ -229,55 +234,6 @@ function stage_state:render() self:render_stage_elements() self:render_fx() self:render_hud() - self:render_overlay() -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 @@ -380,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) @@ -832,19 +782,48 @@ 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() + -- Retrieve and store picked emeralds set information from memory stored in stage_clear + -- 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) + -- 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 + -- 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 - -- 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 bytes, but we can combine them in one - -- 2-byte value and store it at once with length 2 + -- + -- 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 @@ -852,7 +831,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() @@ -889,69 +868,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 - - --- ui - -function stage_state:show_stage_splash_async() - 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: @@ -971,78 +887,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() @@ -1094,7 +945,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 @@ -1108,11 +959,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 @@ -1122,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() @@ -1130,16 +976,17 @@ 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 - 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() -- 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/ingame/stage_state_utest.lua b/src/ingame/stage_state_utest.lua index e00b7eed..c69e8d8b 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,12 +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 overlay = require("engine/ui/overlay") 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") @@ -26,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 () @@ -50,21 +46,38 @@ describe('stage_state', function () state.app = app end) - describe('state', function () + describe('init', function () - it('init', 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('should initialize members', function () assert.are_same({ ':stage', 1, + stage_data.for_stage[1], nil, false, {}, {}, {}, {}, - {}, - camera_class(), - overlay(), nil, -- itest only true, @@ -72,1974 +85,1701 @@ 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, state.emeralds, state.picked_emerald_numbers_set, 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('on_enter', function () + end) - 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(camera_class, "setup_for_stage") - stub(stage_state, "check_reload_map_region") - stub(stage_state, "reload_runtime_data") - end) + describe('_tostring', function () - 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() - camera_class.setup_for_stage:revert() - stage_state.check_reload_map_region:revert() - stage_state.reload_runtime_data:revert() - end) + it('should return "stage_state(1)"', function () + assert.are_equal("stage_state(1)", state:_tostring()) + 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() - camera_class.setup_for_stage:clear() - stage_state.check_reload_map_region:clear() - stage_state.reload_runtime_data:clear() - end) + end) - before_each(function () - state:on_enter() - 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) - 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) + 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 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) + 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 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) + before_each(function () + state:on_enter() + 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 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 assign spawned player char to camera target', function () - assert.are_equal(state.player_char, state.camera.target_pc) - 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 set has_player_char_reached_goal to false', function () - assert.is_false(state.has_player_char_reached_goal) - 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 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 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 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 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 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 assign spawned player char to camera target', function () + assert.are_equal(state.player_char, state.camera.target_pc) + 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 set has_player_char_reached_goal to false', function () + assert.is_false(state.has_player_char_reached_goal) + 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) - describe('reload_runtime_data', function () + 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) - setup(function () - stub(_G, "reload") - stub(_G, "memcpy") - 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) - teardown(function () - reload:revert() - memcpy:revert() - end) + end) - after_each(function () - reload:clear() - memcpy:clear() - end) + describe('reload_runtime_data', function () - 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) + setup(function () + stub(_G, "reload") + stub(_G, "memcpy") + end) + + teardown(function () + reload:revert() + memcpy:revert() + end) - it('should copy non-rotated sprite variants into general memory', function () - state:reload_runtime_data() + after_each(function () + reload:clear() + memcpy:clear() + 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 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) + 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('spawn_emerald_at', 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 store emerald global location', function () + state:spawn_emerald_at(location(1, 33)) + assert.are_same({ + location(1, 33), + }, state.spawned_emerald_locations) end) - describe('is_tile_in_loop_entrance', function () + it('should spawn and store emerald objects for each emerald tile', function () + state:spawn_emerald_at(location(1, 33)) - 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) + assert.are_same({ + emerald(1, location(1, 33)), + }, state.emeralds) + 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 + 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) + describe('spawn_palm_tree_leaves', function () - 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 spawn and store palm tree leaves core at global location', function () + state:spawn_palm_tree_leaves_at(location(1, 33)) - 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) + assert.are_same({ + location(1, 33), + }, state.palm_tree_leaves_core_global_locations) + 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)) + + assert.are_same(goal_plate(location(1, 33)), state.goal_plate) 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('scan_current_region_to_spawn_objects', 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) + local dummy_callback = spy.new(function (self, global_loc) 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))) + setup(function () + stub(stage_state, "get_spawn_object_callback", function (self, tile_id) + if tile_id == 21 then + return dummy_callback + end end) + 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) + teardown(function () + stage_state.get_spawn_object_callback:revert() + 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) - describe('spawn_emerald_at', function () + after_each(function () + dummy_callback:clear() - it('should store emerald global location', function () - state:spawn_emerald_at(location(1, 33)) + pico8:clear_map() + end) - assert.are_same({ - location(1, 33), - }, state.spawned_emerald_locations) - end) + it('should call spawn object callbacks for recognized representative tiles', function () + state:scan_current_region_to_spawn_objects() - it('should spawn and store emerald objects for each emerald tile', function () - state:spawn_emerald_at(location(1, 33)) + assert.spy(dummy_callback).was_called(3) + assert.spy(dummy_callback).was_called_with(match.ref(state), location(1, 1 + 32), 21) + end) - assert.are_same({ - emerald(1, location(1, 33)), - }, state.emeralds) - end) + 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('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('get_region_grid_dimensions', function () - assert.are_same({ - location(1, 33), - }, state.palm_tree_leaves_core_global_locations) - 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('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('get_map_region_coords', function () - assert.are_same(goal_plate(location(1, 33)), state.goal_plate) - 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), 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) - describe('scan_current_region_to_spawn_objects', function () + 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) - local dummy_callback = spy.new(function (self, global_loc) 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) - setup(function () - stub(stage_state, "get_spawn_object_callback", function (self, tile_id) - if tile_id == 21 then - return dummy_callback - end - end) - 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) - teardown(function () - stage_state.get_spawn_object_callback:revert() - 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) - -- 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) + 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) - after_each(function () - dummy_callback:clear() + 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) - pico8:clear_map() - 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 call spawn object callbacks for recognized representative tiles', function () - state:scan_current_region_to_spawn_objects() + 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) - 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 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 (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) - describe('get_map_region_filename', function () + 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('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) + 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.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) - describe('get_region_grid_dimensions', function () + 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 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 - } + 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) - assert.are_same({2, 3}, {state:get_region_grid_dimensions()}) - 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, 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) - describe('get_map_region_coords', function () + 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) - 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) + 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) + describe('reload_map_region', function () - 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) + 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) - 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) + 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) - 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) + -- 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 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) + state.curr_stage_id = 2 + 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 call reload for map 01 for region coords (0, 1)', function () + state:reload_map_region(vector(0, 1)) - 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) + assert.spy(reload).was_called(1) + assert.spy(reload).was_called_with(0x2000, 0x2000, 0x1000, "data_stage2_01.p8") + 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 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 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) + 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 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 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 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) + 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 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 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 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) + 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 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 set loaded_map_region_coords to the passed region', function () + state.loaded_map_region_coords = vector(0, 0) - 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) + state:reload_map_region(vector(1, 0.5)) - 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) + assert.are_equal(vector(1, 0.5), state.loaded_map_region_coords) + 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) + 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) + describe('check_reload_map_region', function () - 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))) + 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) + teardown(function () + stage_state.get_map_region_coords:revert() + stage_state.reload_map_region:revert() end) - describe('reload_map_region', function () + 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) - 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) + after_each(function () + stage_state.get_map_region_coords:clear() + stage_state.reload_map_region:clear() + end) - 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) + it('should call reload_map_region with (1, 0.5)', function () + state.loaded_map_region_coords = vector(0, 0) - -- 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() + state:check_reload_map_region() - state.curr_stage_id = 2 - end) + 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) - it('should call reload for map 01 for region coords (0, 1)', function () - state:reload_map_region(vector(0, 1)) + 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(reload).was_called(1) - assert.spy(reload).was_called_with(0x2000, 0x2000, 0x1000, "data_stage2_01.p8") - end) + assert.spy(stage_state.reload_map_region).was_not_called() + 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)) + 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) + describe('get_spawn_object_callback', function () - 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 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) - 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 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 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 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) - 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) + end) - it('should set loaded_map_region_coords to the passed region', function () - state.loaded_map_region_coords = vector(0, 0) + -- 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 () - state:reload_map_region(vector(1, 0.5)) + setup(function () + stub(stage_state, "reload_map_region") + stub(stage_state, "scan_current_region_to_spawn_objects") + end) - assert.are_equal(vector(1, 0.5), state.loaded_map_region_coords) - end) + teardown(function () + stage_state.reload_map_region:revert() + stage_state.scan_current_region_to_spawn_objects:revert() + end) + after_each(function () + stage_state.reload_map_region:clear() + stage_state.scan_current_region_to_spawn_objects:clear() end) - describe('check_reload_map_region', function () + 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) - 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) + state:spawn_objects_in_all_map_regions() - teardown(function () - stage_state.get_map_region_coords:revert() - stage_state.reload_map_region:revert() - 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)) - 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) + assert.spy(stage_state.scan_current_region_to_spawn_objects).was_called(6) + end) - after_each(function () - stage_state.get_map_region_coords:clear() - stage_state.reload_map_region:clear() - end) + end) - it('should call reload_map_region with (1, 0.5)', function () - state.loaded_map_region_coords = vector(0, 0) + -- we stub restore_picked_emerald_data in (stage state entered) region, so test it outside + describe('restore_picked_emerald_data', function () - state:check_reload_map_region() + before_each(function () + -- 0b01001001 -> 73 (low-endian, so lowest bit is for emerald 1) + poke(0x5d00, 73) + end) - 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) + after_each(function () + poke(0x5d00, 0) + 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() + it('should read 1 byte in general memory representing picked emeralds bitset', function () + state:restore_picked_emerald_data() - assert.spy(stage_state.reload_map_region).was_not_called() - end) + 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) - describe('get_spawn_object_callback', function () + it('should clear picked emerald transitional memory', function () + state:restore_picked_emerald_data() - 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) + assert.are_equal(0, peek(0x5d00)) + 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) + 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) + describe('(stage states added)', function () + + before_each(function () + flow:add_gamestate(state) + end) + after_each(function () + flow:init() end) - describe('spawn_objects_in_all_map_regions', function () + describe('(stage state entered)', function () setup(function () - stub(stage_state, "reload_map_region") - stub(stage_state, "scan_current_region_to_spawn_objects") + -- 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.reload_map_region:revert() - stage_state.scan_current_region_to_spawn_objects:revert() + stage_state.spawn_objects_in_all_map_regions:revert() + stage_state.restore_picked_emerald_data:revert() end) after_each(function () - stage_state.reload_map_region:clear() - stage_state.scan_current_region_to_spawn_objects:clear() + stage_state.spawn_objects_in_all_map_regions:clear() + stage_state.restore_picked_emerald_data:clear() 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) + before_each(function () + flow:change_state(state) + end) - state:spawn_objects_in_all_map_regions() + describe('spawn_player_char', function () - 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)) + setup(function () + spy.on(player_char, "spawn_at") + end) - assert.spy(stage_state.scan_current_region_to_spawn_objects).was_called(6) - end) + teardown(function () + player_char.spawn_at:revert() + end) - end) + before_each(function () + -- clear count before test as entering stage will auto-spawn character once + player_char.spawn_at:clear() + end) - describe('(stage states added)', function () + 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() - before_each(function () - flow:add_gamestate(state) - 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) + + -- 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) - after_each(function () - flow:init() end) - describe('(stage state entered)', function () + describe('update_fx', 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") + 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) teardown(function () - stage_state.spawn_objects_in_all_map_regions:revert() + emerald_fx.update:revert() end) after_each(function () - stage_state.spawn_objects_in_all_map_regions:clear() - 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() + emerald_fx.update:clear() end) - describe('spawn_player_char', function () + 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)) + } - setup(function () - spy.on(player_char, "spawn_at") - end) + state:update_fx() - teardown(function () - player_char.spawn_at:revert() - 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) - before_each(function () - -- clear count before test as entering stage will auto-spawn character once - player_char.spawn_at:clear() - 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) - 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('render_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) - end) + 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) - describe('update_fx', function () + after_each(function () + stage_state.set_camera_with_origin:clear() + emerald_fx.render:clear() + 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) + it('render_player_char should call set_camera_with_origin', function () + state:render_fx() - teardown(function () - emerald_fx.update: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)) + end) - after_each(function () - emerald_fx.update:clear() - 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 update on each emerald fx', function () - state.emerald_pick_fxs = { - emerald_fx(1, vector(0, 0)), - emerald_fx(2, vector(12, 4)) - } + state:render_fx() - state:update_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) - 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('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) - 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) - - 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() - - 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)) - } + 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) - state:render_fx() + 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) - 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) + 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() + stage_state.check_reload_map_region:revert() end) - describe('update', function () + 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)) - 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) + state:update() - 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) + 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)) - 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) + 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) - 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() + it('should not try to update goal if no goal plate found (safety check for itests)', function () + state.goal_plate = nil - stage_state.check_reload_map_region:revert() - end) + state:update() - 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)) + assert.spy(goal_plate.update).was_not_called() + end) - 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) + end) -- update - it('should not try to update goal if no goal plate found (safety check for itests)', function () - state.goal_plate = nil + describe('render', function () - state:update() + 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) - assert.spy(goal_plate.update).was_not_called() - end) + 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) - end) -- update + 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) - describe('render', function () + 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) - 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) + end) -- state.render - 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) + describe('extend_spring', function () - 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) - - 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) + setup(function () + stub(picosonic_app, "start_coroutine") + end) - end) -- state.render + teardown(function () + picosonic_app.start_coroutine:revert() + end) - describe('extend_spring', function () + before_each(function () + picosonic_app.start_coroutine:clear() + end) - setup(function () - stub(picosonic_app, "start_coroutine") - 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) - teardown(function () - picosonic_app.start_coroutine:revert() - end) + 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) + describe('check_emerald_pick_area', function () - 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) + 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) - describe('check_emerald_pick_area', function () + 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 return nil when position is too far from all the emeralds', function () - assert.is_nil(state:check_emerald_pick_area(vector(12, 12))) - end) + describe('character_pick_emerald', function () - 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) + -- we need to stub start_coroutine on the child class, + -- not gameapp, or calls won't be monitored + setup(function () + stub(picosonic_app, "start_coroutine") end) - describe('character_pick_emerald', function () - - -- we need to stub start_coroutine on the child class, - -- not gameapp, or calls won't be monitored + teardown(function () + picosonic_app.start_coroutine:revert() + end) - setup(function () - stub(picosonic_app, "start_coroutine") - end) + -- clear in before_each as stage_state on_enter + -- will start some coroutune already + before_each(function () + picosonic_app.start_coroutine:clear() + end) - teardown(function () - picosonic_app.start_coroutine:revert() - end) + before_each(function () + state.emeralds = { + emerald(1, location(0, 0)), + emerald(2, location(1, 0)), + emerald(3, location(0, 1)), + } + 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 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) - before_each(function () - state.emeralds = { - emerald(1, location(0, 0)), - emerald(2, location(1, 0)), - emerald(3, location(0, 1)), - } - end) + it('should create a pick FX and play it', function () + state.emerald_pick_fxs = { + emerald_fx(1, vector(0, 0)) + } - 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) + state:character_pick_emerald(state.emeralds[2]) - it('should create a pick FX and play it', function () - state.emerald_pick_fxs = { - emerald_fx(1, vector(0, 0)) - } + -- 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) - state:character_pick_emerald(state.emeralds[2]) + 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) - -- 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 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 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) + 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) + 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_loop_external_triggers', 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) - 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) + 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 return nil when not entering external entrance trigger at all', function () - assert.is_nil(state:check_loop_external_triggers(vector(-20, 0), 2)) - 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 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 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) - 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 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) - 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_reached_goal', function () + setup(function () + stub(picosonic_app, "start_coroutine") end) - describe('check_reached_goal', function () + teardown(function () + picosonic_app.start_coroutine:revert() + end) - setup(function () - stub(picosonic_app, "start_coroutine") - end) + before_each(function () + picosonic_app.start_coroutine:clear() + end) - teardown(function () - picosonic_app.start_coroutine:revert() - end) + describe('(no goal)', function () - -- start_coroutine is also called on stage enter (with show_stage_splash_async) - -- so we must clear call count *before* the first test + -- should be each before_each(function () - picosonic_app.start_coroutine:clear() + state.player_char.position = vector(1000, 0) + state:check_reached_goal() end) - describe('(no goal)', function () - - -- should be each - before_each(function () - state.player_char.position = vector(1000, 0) - state:check_reached_goal() - 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) - + it('should not set has_player_char_reached_goal to true', function () + assert.is_false(state.has_player_char_reached_goal) end) - describe('(before the goal)', function () - - -- 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 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) - - end) - - describe('(just on the goal)', function () - - 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 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('(just on the goal, but already reached once)', function () - - 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) - - it('should keep has_player_char_reached_goal as true', function () - assert.is_true(state.has_player_char_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 not start on_reached_goal_async', function () + assert.spy(picosonic_app.start_coroutine).was_not_called() 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('(before the goal)', function () + -- should be each 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 - 1, 0) + 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 not set has_player_char_reached_goal to true', function () + assert.is_false(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 start on_reached_goal_async', function () + assert.spy(picosonic_app.start_coroutine).was_not_called() end) end) - describe('store_picked_emerald_data', function () - - it('should store 2 bytes 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) - - end) - - describe('feedback_reached_goal', function () - - setup(function () - stub(_G, "sfx") - stub(animated_sprite, "play") - end) - - teardown(function () - sfx:revert() - animated_sprite.play:revert() - end) - - after_each(function () - sfx:clear() - end) + describe('(just on the goal)', function () before_each(function () state.goal_plate = goal_plate(location(100, 0)) - - -- 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() + state.player_char.position = vector(804, 0) + state:check_reached_goal() 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) + it('should set has_player_char_reached_goal to true', function () + assert.is_true(state.has_player_char_reached_goal) 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") + 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('show_stage_splash_async', function () - - local corunner + describe('(just on the goal, but already reached once)', function () before_each(function () - corunner = coroutine_runner() - corunner:start_coroutine(stage_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_data.show_stage_splash_delay * state.app.fps - 1 + 160 do - corunner:update_coroutines() - end + 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) - 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)) + it('should keep has_player_char_reached_goal as true', function () + assert.is_true(state.has_player_char_reached_goal) 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)) + it('should not call on_reached_goal_async again', function () + assert.spy(picosonic_app.start_coroutine).was_not_called() 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) + end) - after_each(function () - stage_state.set_camera_with_origin:clear() - end) + describe('on_reached_goal_async', function () - it('should call set_camera_with_origin with current region topleft xy', function () - state.loaded_map_region_coords = vector(2, 1) + -- removed actual tests, too hard to maintain + -- instead, just run it and see if it crashes - state:set_camera_with_region_origin() + local corunner - 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) + 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) 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) - - 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) - - 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) - - 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) + 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 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 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) + 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 end) - describe('render_player_char', function () - - 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) - - after_each(function () - stage_state.set_camera_with_origin:clear() - player_char.render:clear() - end) - - it('should call set_camera_with_origin and player_char:render', function () - state:render_player_char() + 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) + 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(0x5d00)) end) - describe('render_emeralds', function () + end) - setup(function () - stub(stage_state, "set_camera_with_origin") - stub(emerald, "render") - end) + describe('feedback_reached_goal', function () - teardown(function () - stage_state.set_camera_with_origin:revert() - emerald.render:revert() - end) + setup(function () + stub(_G, "sfx") + stub(animated_sprite, "play") + end) - after_each(function () - stage_state.set_camera_with_origin:clear() - emerald.render:clear() - end) + teardown(function () + sfx:revert() + animated_sprite.play:revert() + end) - it('should call set_camera_with_origin and emerald:render', function () - state.emeralds = { - emerald(1, location(1, 1)), - emerald(2, location(2, 2)), - } + after_each(function () + sfx:clear() + end) - state:render_emeralds() + before_each(function () + state.goal_plate = goal_plate(location(100, 0)) - 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) + -- 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 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) - describe('render_goal_plate', function () + 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) - setup(function () - stub(stage_state, "set_camera_with_origin") - stub(goal_plate, "render") - end) + end) - teardown(function () - stage_state.set_camera_with_origin:revert() - goal_plate.render:revert() - end) + describe('render_stage_elements', function () - after_each(function () - stage_state.set_camera_with_origin:clear() - goal_plate.render: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('(no goal plate found) should do nothing', function () - state:render_goal_plate() + 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) - 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.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) - it('(goal plate found) should call set_camera_with_origin and goal_plate:render', function () - state.goal_plate = goal_plate(location(2, 33)) + 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) - state:render_goal_plate() + 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) + describe('render_player_char', function () + setup(function () + stub(stage_state, "set_camera_with_origin") + stub(player_char, "render") end) - describe('render_hud', function () - - setup(function () - stub(emerald, "draw") - stub(player_char, "debug_print_info") - end) + teardown(function () + stage_state.set_camera_with_origin:revert() + player_char.render:revert() + end) - teardown(function () - emerald.draw:revert() - player_char.debug_print_info:revert() - end) + after_each(function () + stage_state.set_camera_with_origin:clear() + player_char.render:clear() + end) - after_each(function () - emerald.draw:clear() - player_char.debug_print_info:clear() - end) + it('should call set_camera_with_origin and player_char:render', function () + state:render_player_char() - 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(6, 6)) - -- silhouette only - assert.spy(emerald.draw).was_called_with(-1, vector(16, 6)) - assert.spy(emerald.draw).was_called_with(3, vector(26, 6)) - 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) - it('should debug render character info (#debug_character only)', function () - state:render_hud() + 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) + describe('render_emeralds', function () + setup(function () + stub(stage_state, "set_camera_with_origin") + stub(emerald, "render") 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.set_camera_with_origin:revert() + emerald.render:revert() + end) - teardown(function () - stage_state.get_region_topleft_location:revert() - end) + after_each(function () + stage_state.set_camera_with_origin:clear() + emerald.render:clear() + 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) + it('should call set_camera_with_origin and emerald:render', function () + state.emeralds = { + emerald(1, location(1, 1)), + emerald(2, location(2, 2)), + } - 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) + state:render_emeralds() + 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) - 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) + 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('render_goal_plate', function () + setup(function () + stub(stage_state, "set_camera_with_origin") + stub(goal_plate, "render") end) - describe('(with tile_test_data)', function () + teardown(function () + stage_state.set_camera_with_origin:revert() + goal_plate.render:revert() + end) - setup(function () - tile_test_data.setup() + after_each(function () + stage_state.set_camera_with_origin:clear() + goal_plate.render:clear() + 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) + it('(no goal plate found) should do nothing', function () + state:render_goal_plate() - teardown(function () - tile_test_data.teardown() + assert.spy(stage_state.set_camera_with_origin).was_not_called() + assert.spy(goal_plate.render).was_not_called() + end) - stage_state.set_camera_with_origin:revert() - stage_state.set_camera_with_region_origin:revert() - sprite_data.render:revert() - spr:revert() - map:revert() - end) + it('(goal plate found) should call set_camera_with_origin and goal_plate:render', function () + state.goal_plate = goal_plate(location(2, 33)) - 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) + state:render_goal_plate() - after_each(function () - pico8:clear_map() + 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) - stage_state.set_camera_with_origin:clear() - stage_state.set_camera_with_region_origin:clear() - sprite_data.render:clear() - spr:clear() - map:clear() - end) + 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) + describe('render_hud', function () - state:render_environment_midground() + setup(function () + stub(emerald, "draw") + stub(player_char, "debug_print_info") + 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)) + teardown(function () + emerald.draw:revert() + player_char.debug_print_info:revert() + end) - 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) + after_each(function () + emerald.draw:clear() + player_char.debug_print_info:clear() + 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) + 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) - state:render_environment_foreground() + it('should debug render character info (#debug_character only)', function () + state:render_hud() - -- 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(player_char.debug_print_info).was_called(1) + assert.spy(player_char.debug_print_info).was_called_with(match.ref(state.player_char)) + end) - assert.spy(map).was_called(2) + end) - assert.spy(map).was_called_with(0, 0, 0, 0, map_region_tile_width, map_region_tile_height, sprite_masks.foreground) + describe('state audio methods', 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)) + setup(function () + stub(_G, "reload") + end) - 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) + teardown(function () + reload:revert() + 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) + -- 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) - end) -- (stage state entered) + 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) + --]] - 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/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/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/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/itests/stage_clear/iteststage_clear.lua b/src/itests/stage_clear/iteststage_clear.lua index 5a93dba3..1d526716 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) @@ -23,7 +23,8 @@ itest_manager:register_itest('player waits', end) -- let stage clear sequence play and see if nothing crashes - wait(20.0) + -- 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 new file mode 100644 index 00000000..04bd026e --- /dev/null +++ b/src/itests/stage_intro/iteststage_intro.lua @@ -0,0 +1,28 @@ +-- 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 + -- 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) + 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_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/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/credits.lua b/src/menu/credits.lua index b26099dd..29799fea 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() @@ -53,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/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/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 --[[ diff --git a/src/menu/titlemenu.lua b/src/menu/titlemenu.lua index d8025e18..5c0366db 100644 --- a/src/menu/titlemenu.lua +++ b/src/menu/titlemenu.lua @@ -16,14 +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) + -- prefer passing basename for compatibility with .p8.png + load('picosonic_stage_intro') + 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) @@ -55,12 +67,16 @@ 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() -- 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() @@ -72,6 +88,8 @@ end function titlemenu:render() self:draw_background() self:draw_title() + self:draw_version() + if self.menu then self.menu:draw(55, 101) end @@ -99,4 +117,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.2", 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 c6588e3e..b9766384 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) @@ -74,13 +73,25 @@ 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) 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 () @@ -129,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) @@ -136,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) @@ -159,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() 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 = { diff --git a/src/resources/visual_common.lua b/src/resources/visual_common.lua index d1b28818..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 @@ -67,10 +40,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 @@ -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/resources/visual_ingame_addon.lua b/src/resources/visual_ingame_addon.lua index 45b3660a..b55b4b10 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(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), @@ -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) @@ -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/resources/visual_stage_clear_addon.lua b/src/resources/visual_stage_clear_addon.lua new file mode 100644 index 00000000..deefbe3c --- /dev/null +++ b/src/resources/visual_stage_clear_addon.lua @@ -0,0 +1,21 @@ +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 + missed_emeralds_radius = 15 +} + +-- 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, stage_clear_visual) +merge(visual.sprite_data_t, menu_sprite_data_t) 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) diff --git a/src/stage_clear/stage_clear_state.lua b/src/stage_clear/stage_clear_state.lua index e79df2b4..a41d9d22 100644 --- a/src/stage_clear/stage_clear_state.lua +++ b/src/stage_clear/stage_clear_state.lua @@ -1,52 +1,153 @@ -local gamestate = require("engine/application/gamestate") +local flow = require("engine/application/flow") +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 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 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' +-- sequence of menu items to display, with their target states +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 + +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() + -- prefer passing basename for compatibility with .p8.png + load('picosonic_ingame') +end + +function stage_clear_state.retry_from_zero_async() + -- clear picked emeralds data (see stage_state:store_picked_emerald_data) in general memory + 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(0x5d00, 0) + + -- zigzag fadeout will also give time to player to hear confirm SFX + flow.curr_state:zigzag_fade_out_async() + -- prefer passing basename for compatibility with .p8.png + load('picosonic_titlemenu') +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 + -- 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 + + -- postprocessing for fade out effect + self.postproc = postprocess() -- result (stage clear) overlay self.result_overlay = overlay() - -- emerald cross variables for result UI animation - self.result_show_emerald_cross_base = false - self.result_emerald_cross_palette_swap_table = {} -- for emerald cross bright animation + -- emerald variables for result UI animation + self.picked_emerald_numbers_set = {} + self.picked_emerald_count = 0 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() + + -- 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_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") + +-- 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() + + -- 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 +-- 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,18 +159,43 @@ 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() - -- see set_camera_with_origin for value explanation (we must pass camera position) - visual_stage.render_background(vector(3392, 328)) - self:render_stage_elements() + if self.phase == 0 then + -- 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() + else + -- phase 1: retry menu + cls() + + -- 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() - self:render_emerald_cross() + + self.postproc:apply() 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!") @@ -80,16 +206,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 @@ -99,9 +215,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 @@ -109,98 +225,62 @@ 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 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) - -- hardcoded for stage 1 - return "data_stage1_"..u..v..".p8" -end +-- camera methods, also simplified versions of stage_stage equivalent -function stage_clear_state:reload_map_region() - -- hardcoded for goal region - reload(0x2000, 0x2000, 0x1000, self:get_map_region_filename(3, 1)) +-- 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 -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_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() +-- actual stage clear sequence functions - -- wait a moment and go back to titlemenu - self.app:yield_delay_s(stage_data.back_to_titlemenu_delay) - self:back_to_titlemenu() -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(0x5d00) -function stage_clear_state:back_to_titlemenu() - load('picosonic_titlemenu.p8') + -- 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) - 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) - - -- move text from right to left - ui_animation.move_drawables_on_coord_async("x", {result_label}, {0}, 128, 40, 20) - - -- show emerald cross - self.result_show_emerald_cross_base = true + local stage_label = label("angel island", vector(0, 26), colors.white, colors.black) + self.result_overlay:add_drawable("stage", stage_label) - 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) + -- 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) 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] @@ -221,66 +301,111 @@ 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 - if picked_emerald_count < 8 then - emerald_text = "got "..picked_emerald_count.." emeralds" + -- "[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 - 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) 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) +} --- 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) +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 --- 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 +function stage_clear_state:zigzag_fade_out_async() + 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) --- 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() + -- 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 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 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 --- 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) +function stage_clear_state:show_retry_screen_async() + + 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 + 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) + + -- 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 @@ -318,9 +443,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() @@ -332,44 +457,26 @@ 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) - -- 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 +-- 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) 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] + 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 diff --git a/src/stage_clear/stage_clear_state_utest.lua b/src/stage_clear/stage_clear_state_utest.lua index bbabc6b6..89ade0f6 100644 --- a/src/stage_clear/stage_clear_state_utest.lua +++ b/src/stage_clear/stage_clear_state_utest.lua @@ -5,18 +5,22 @@ 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") 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") 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") @@ -47,645 +51,637 @@ 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(), - false, {}, + 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.result_show_emerald_cross_base, - state.result_emerald_cross_palette_swap_table, + 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, "reload_runtime_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.reload_runtime_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.reload_runtime_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 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)) - 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) + before_each(function () + state:on_enter() + 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 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 hardcode set camera position', function () + assert.are_equal(vector(3392, 328), state.camera.position) end) - describe('reload_runtime_data', function () + it('should hardcode set loaded_map_region_coords', function () + assert.are_equal(vector(3, 1), state.loaded_map_region_coords) + end) - setup(function () - stub(_G, "reload") - end) + 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) - teardown(function () - reload:revert() - 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) - after_each(function () - reload:clear() - 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 reload stage runtime data into spritesheet top', function () - state:reload_runtime_data() + end) - assert.spy(reload).was_called(1) - assert.spy(reload).was_called_with(0x0, 0x0, 0x600, "data_stage1_runtime.p8") - end) + describe('spawn_goal_plate_at', function () + setup(function () + spy.on(animated_sprite, "play") end) - describe('spawn_goal_plate_at', function () + teardown(function () + animated_sprite.play:revert() + end) - setup(function () - spy.on(animated_sprite, "play") - end) + after_each(function () + animated_sprite.play:clear() + end) - teardown(function () - animated_sprite.play:revert() - end) + it('should spawn and store goal plate core at global location', function () + state:spawn_goal_plate_at(location(1, 33)) - after_each(function () - animated_sprite.play:clear() - end) + local gp = goal_plate(location(1, 33)) + gp.anim_spr:play("sonic") - it('should spawn and store goal plate core at global location', function () - state:spawn_goal_plate_at(location(1, 33)) + assert.are_same(gp, state.goal_plate) + end) - local gp = goal_plate(location(1, 33)) - gp.anim_spr:play("sonic") + it('should show sonic face of goal plate by playing corresponding animation', function () + state:spawn_goal_plate_at(location(1, 33)) - assert.are_same(gp, state.goal_plate) - 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) - it('should show sonic face of goal plate by playing corresponding animation', function () - state:spawn_goal_plate_at(location(1, 33)) + 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) + describe('scan_current_region_to_spawn_objects', function () + setup(function () + stub(stage_clear_state, "spawn_goal_plate_at") end) - describe('scan_current_region_to_spawn_objects', function () - - local dummy_callback = spy.new(function (self, global_loc) end) + teardown(function () + stage_clear_state.spawn_goal_plate_at:revert() + 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) - 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) - teardown(function () - stage_clear_state.get_spawn_object_callback:revert() - end) + after_each(function () + stage_clear_state.spawn_goal_plate_at: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_goal_plate_at global location', function () + state:scan_current_region_to_spawn_objects() - pico8:clear_map() - 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) - it('should call spawn object callbacks for recognized representative tiles', function () - state:scan_current_region_to_spawn_objects() + end) - 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) - 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) - describe('get_map_region_filename', function () + 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) + describe('(stage states added)', function () + + 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('reload_map_region', function () + describe('(stage state entered)', function () setup(function () - stub(_G, "reload") + -- 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 () - _G.reload:revert() - end) - - before_each(function () - _G.reload:clear() + stage_clear_state.scan_current_region_to_spawn_objects:revert() + stage_clear_state.restore_picked_emerald_data:revert() end) - it('should call reload for stage1, map 31 (hardcoded)', function () - state:reload_map_region() - - assert.spy(reload).was_called(1) - assert.spy(reload).was_called_with(0x2000, 0x2000, 0x1000, "data_stage1_31.p8") + after_each(function () + stage_clear_state.scan_current_region_to_spawn_objects:clear() + stage_clear_state.restore_picked_emerald_data:clear() end) - end) - - describe('(stage states added)', function () - before_each(function () - flow:add_gamestate(state) - flow:add_gamestate(titlemenu_state) -- for transition on reached goal - end) - - after_each(function () - flow:init() + 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('(stage state entered)', function () + describe('update', function () setup(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") end) teardown(function () - stage_clear_state.scan_current_region_to_spawn_objects:revert() end) - after_each(function () - stage_clear_state.scan_current_region_to_spawn_objects:clear() + before_each(function () 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() + after_each(function () end) - describe('update', function () + end) -- update - setup(function () - end) + describe('render', function () - teardown(function () - 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) - before_each(function () - 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) - after_each(function () - 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) - end) -- update + it('#solo (phase 0) should call render_background, render_stage_elements, render_overlay, render_emeralds', function () + state.phase = 0 - describe('render', function () + state:render() - 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_emerald_cross") - end) + 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) - 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() - 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() - end) + it('should call render_background, render_stage_elements, render_overlay, render_emeralds', function () + state.phase = 1 - it('should call render_background, render_stage_elements, render_overlay, render_emerald_cross', function () - 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_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)) - 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: 0b10100110 + pico8.poked_addresses[0x5d00] = 0xa6 - 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: 0b11111111 + pico8.poked_addresses[0x5d00] = 0xff + -- a time long enough to cover other async methods like assess_result_async + for i = 1, 1000 do + corunner:update_coroutines() + end end) - describe('back_to_titlemenu', function () - - setup(function () - stub(_G, "load") - end) + end) - teardown(function () - load:revert() - end) + describe('set_camera_with_origin', function () - 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) + 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_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_region_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) + setup(function () + stub(stage_clear_state, "set_camera_with_origin") + end) + teardown(function () + stage_clear_state.set_camera_with_origin:revert() end) - describe('set_camera_with_region_origin', function () + after_each(function () + stage_clear_state.set_camera_with_origin:clear() + end) - setup(function () - stub(stage_clear_state, "set_camera_with_origin") - end) + it('should call set_camera_with_origin with current region topleft xy', function () + state:set_camera_with_region_origin() - teardown(function () - stage_clear_state.set_camera_with_origin:revert() - 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) - after_each(function () - stage_clear_state.set_camera_with_origin:clear() - end) + end) - it('should call set_camera_with_origin with current region topleft xy', function () - state:set_camera_with_region_origin() + 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) - 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('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('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('render_stage_elements', 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) + setup(function () + stub(stage_clear_state, "render_environment_midground") + stub(stage_clear_state, "render_goal_plate") + stub(stage_clear_state, "render_environment_foreground") + 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) - describe('render_stage_elements', function () + 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) - setup(function () - stub(stage_clear_state, "render_environment_midground") - stub(stage_clear_state, "render_goal_plate") - stub(stage_clear_state, "render_environment_foreground") - 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) - teardown(function () - stage_clear_state.render_environment_midground:revert() - stage_clear_state.render_goal_plate:revert() - stage_clear_state.render_environment_foreground:revert() - end) + 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) + describe('render_overlay', function () - 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) + setup(function () + stub(overlay, "draw") + end) + teardown(function () + overlay.draw:revert() end) - describe('render_overlay', function () + after_each(function () + overlay.draw:clear() + end) - setup(function () - stub(overlay, "draw") - end) + it('should reset camera', function () + state:render_overlay() + assert.are_same(vector.zero(), vector(pico8.camera_x, pico8.camera_y)) + end) - teardown(function () - overlay.draw:revert() - 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) - 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_goal_plate', function () - 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) + setup(function () + stub(stage_clear_state, "set_camera_with_origin") + stub(goal_plate, "render") + end) + teardown(function () + stage_clear_state.set_camera_with_origin:revert() + goal_plate.render:revert() end) - describe('render_goal_plate', function () + after_each(function () + stage_clear_state.set_camera_with_origin:clear() + goal_plate.render:clear() + end) - setup(function () - stub(stage_clear_state, "set_camera_with_origin") - stub(goal_plate, "render") - 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)) - teardown(function () - stage_clear_state.set_camera_with_origin:revert() - goal_plate.render:revert() - end) + state:render_goal_plate() - after_each(function () - stage_clear_state.set_camera_with_origin:clear() - goal_plate.render:clear() - end) + 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) - 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)) + end) - state:render_goal_plate() + describe('(with tile_test_data)', function () - 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) + setup(function () + tile_test_data.setup() + 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) - describe('(with tile_test_data)', function () - - 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 () + end) - it('render_emerald_cross should not crash', function () - state:render_emerald_cross() - end) + describe('on exit stage state to enter titlemenu state (we actually change cartridge)', function () + before_each(function () + flow:change_state(titlemenu_state) end) - describe('on exit stage state to enter titlemenu state (we actually change cartridge)', function () - - before_each(function () - flow:change_state(titlemenu_state) - end) - - it('player character should be nil', function () - assert.is_nil(state.player_char) - end) + it('player character should be nil', function () + assert.is_nil(state.player_char) + 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('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) - end) -- on exit stage state to enter titlemenu state + end) -- on exit stage state to enter titlemenu state - -- unlike above, we test on_exit method itself here - 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 new file mode 100644 index 00000000..cd4dfdc9 --- /dev/null +++ b/src/stage_intro/stage_intro_state.lua @@ -0,0 +1,173 @@ +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 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(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"..cartridge_ext + 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 + +-- 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 +--]] + + +-- 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() + 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) + + -- 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 + -- prefer passing basename for compatibility with .p8.png + load('picosonic_ingame') + +--[[#pico8 + assert(false, "could not load picosonic_ingame") +--#pico8]] +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..d29b1c05 --- /dev/null +++ b/src/stage_intro/stage_intro_state_utest.lua @@ -0,0 +1,253 @@ +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") +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 () + + 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 () + + 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, + }) + end) + + end) + + 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('should call reload for runtime data and stage1, map 01 (hardcoded)', function () + state:on_enter() + + 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) + + 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() + + 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('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_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) + + 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) 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/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/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 diff --git a/src/tests/headless_itests_utest.lua b/src/tests/headless_itests_utest.lua index 32331f95..d43c8abf 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 @@ -58,8 +60,8 @@ logging.logger.active_categories = { -- ['log'] = nil, -- ['ui'] = nil, ['frame'] = true, - ['trace'] = true, - ['trace2'] = true, + -- ['trace'] = true, + -- ['trace2'] = true, ['spring'] = true, -- game 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 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") diff --git a/upload_cartridge_release.sh b/upload_cartridge_release.sh new file mode 100755 index 00000000..6d7a686a --- /dev/null +++ b/upload_cartridge_release.sh @@ -0,0 +1,68 @@ +#!/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 + +# 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 +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 + # 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 + # 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