Funkin/source/funkin/ui/story/Level.hx
Eric 42d8d55067 Unit Test Suite (#119)
* Initial test suite

* Fix some build warnings

* Implemented working unit tests with coverage

* Reduced some warnings

* Fix a mac-specific issue

* Add 2 additional unit test classes.

* Multiple new unit tests

* Some fixins

* Remove auto-generated file

* WIP on hiding ignored tests

* Added list of debug hotkeys

* Remove old website

* Remove empty file

* Add more unit tests

* Fix bug where arrows would nudge BF

* Fix bug where ctrl/alt would flash capsules

* Fixed bug where bf-old easter egg broke

* Remove duplicate lines

* More test-related stuff

* Some code cleanup

* Add mocking and a test assets folder

* More TESTS!

* Update Hmm...

* Update artist on Monster

* More minor fixes to individual functions

* 1.38% unit test coverage!

* Even more tests? :O

* More unit test work

* Rework migration for BaseRegistry

* gameover fix

* Fix an issue with Lime

* Fix issues with version parsing on data files

* 100 total unit tests!

* Added even MORE unit tests!

* Additional test tweaks :3

* Fixed tests on windows by updating libraries.

* Set versions for flixel-ui and hamcrest

---------

Co-authored-by: Cameron Taylor <cameron.taylor.ninja@gmail.com>
2023-08-22 04:27:30 -04:00

186 lines
4.5 KiB
Haxe

package funkin.ui.story;
import flixel.FlxSprite;
import flixel.util.FlxColor;
import funkin.play.song.Song;
import funkin.data.IRegistryEntry;
import funkin.data.level.LevelRegistry;
import funkin.data.level.LevelData;
/**
* An object used to retrieve data about a story mode level (also known as "weeks").
* Can be scripted to override each function, for custom behavior.
*/
class Level implements IRegistryEntry<LevelData>
{
/**
* The ID of the story mode level.
*/
public final id:String;
/**
* Level data as parsed from the JSON file.
*/
public final _data:LevelData;
/**
* @param id The ID of the JSON file to parse.
*/
public function new(id:String)
{
this.id = id;
_data = _fetchData(id);
if (_data == null)
{
throw 'Could not parse level data for id: $id';
}
}
/**
* Get the list of songs in this level, as an array of IDs.
* @return Array<String>
*/
public function getSongs():Array<String>
{
// Copy the array so that it can't be modified on accident
return _data.songs.copy();
}
/**
* Retrieve the title of the level for display on the menu.
*/
public function getTitle():String
{
// TODO: Maybe add localization support?
return _data.name;
}
public function buildTitleGraphic():FlxSprite
{
var result = new FlxSprite().loadGraphic(Paths.image(_data.titleAsset));
return result;
}
/**
* Get the list of songs in this level, as an array of names, for display on the menu.
* @return Array<String>
*/
public function getSongDisplayNames(difficulty:String):Array<String>
{
var songList:Array<String> = getSongs() ?? [];
var songNameList:Array<String> = songList.map(function(songId) {
var song:Song = funkin.play.song.SongData.SongDataParser.fetchSong(songId);
if (song == null) return 'Unknown';
var songDifficulty:SongDifficulty = song.getDifficulty(difficulty);
if (songDifficulty == null) songDifficulty = song.getDifficulty();
var songName:String = songDifficulty?.songName;
return songName ?? 'Unknown';
});
return songNameList;
}
/**
* Whether this level is unlocked. If not, it will be greyed out on the menu and have a lock icon.
* TODO: Change this behavior in a later release.
*/
public function isUnlocked():Bool
{
return true;
}
/**
* Whether this level is visible. If not, it will not be shown on the menu at all.
*/
public function isVisible():Bool
{
return true;
}
public function buildBackground():FlxSprite
{
if (_data.background.startsWith('#'))
{
// Color specified
var color:FlxColor = FlxColor.fromString(_data.background);
return new FlxSprite().makeGraphic(FlxG.width, 400, color);
}
else
{
// Image specified
return new FlxSprite().loadGraphic(Paths.image(_data.background));
}
}
public function getDifficulties():Array<String>
{
var difficulties:Array<String> = [];
var songList = getSongs();
var firstSongId:String = songList[0];
var firstSong:Song = funkin.play.song.SongData.SongDataParser.fetchSong(firstSongId);
if (firstSong != null)
{
for (difficulty in firstSong.listDifficulties())
{
difficulties.push(difficulty);
}
}
// Filter to only include difficulties that are present in all songs
for (songIndex in 1...songList.length)
{
var songId:String = songList[songIndex];
var song:Song = funkin.play.song.SongData.SongDataParser.fetchSong(songId);
if (song == null) continue;
for (difficulty in difficulties)
{
if (!song.hasDifficulty(difficulty))
{
difficulties.remove(difficulty);
}
}
}
if (difficulties.length == 0) difficulties = ['normal'];
return difficulties;
}
public function buildProps():Array<LevelProp>
{
var props:Array<LevelProp> = [];
if (_data.props.length == 0) return props;
for (propIndex in 0..._data.props.length)
{
var propData = _data.props[propIndex];
var propSprite:Null<LevelProp> = LevelProp.build(propData);
if (propSprite == null) continue;
propSprite.x += FlxG.width * 0.25 * propIndex;
props.push(propSprite);
}
return props;
}
public function destroy():Void {}
public function toString():String
{
return 'Level($id)';
}
public function _fetchData(id:String):Null<LevelData>
{
return LevelRegistry.instance.parseEntryDataWithMigration(id, LevelRegistry.instance.fetchEntryVersion(id));
}
}