mirror of
https://github.com/ninjamuffin99/Funkin.git
synced 2025-11-26 06:09:02 +00:00
Logic + animations for new unlocks
This commit is contained in:
parent
1fb5c31c22
commit
e7fca119f8
|
|
@ -53,6 +53,41 @@ class PlayerRegistry extends BaseRegistry<PlayableCharacter, PlayerData>
|
||||||
log('Loaded ${countEntries()} playable characters with ${ownedCharacterIds.size()} associations.');
|
log('Loaded ${countEntries()} playable characters with ${ownedCharacterIds.size()} associations.');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function countUnlockedCharacters():Int
|
||||||
|
{
|
||||||
|
var count = 0;
|
||||||
|
|
||||||
|
for (charId in listEntryIds())
|
||||||
|
{
|
||||||
|
var player = fetchEntry(charId);
|
||||||
|
if (player == null) continue;
|
||||||
|
|
||||||
|
if (player.isUnlocked()) count++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function hasNewCharacter():Bool
|
||||||
|
{
|
||||||
|
var characters = Save.instance.charactersSeen.clone();
|
||||||
|
|
||||||
|
for (charId in listEntryIds())
|
||||||
|
{
|
||||||
|
var player = fetchEntry(charId);
|
||||||
|
if (player == null) continue;
|
||||||
|
|
||||||
|
if (!player.isUnlocked()) continue;
|
||||||
|
if (characters.contains(charId)) continue;
|
||||||
|
|
||||||
|
// This character is unlocked but we haven't seen them in Freeplay yet.
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fallthrough case.
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the playable character associated with a given stage character.
|
* Get the playable character associated with a given stage character.
|
||||||
* @param characterId The stage character ID.
|
* @param characterId The stage character ID.
|
||||||
|
|
|
||||||
|
|
@ -121,6 +121,12 @@ class Save
|
||||||
modOptions: [],
|
modOptions: [],
|
||||||
},
|
},
|
||||||
|
|
||||||
|
unlocks:
|
||||||
|
{
|
||||||
|
// Default to having seen the default character.
|
||||||
|
charactersSeen: ["bf"],
|
||||||
|
},
|
||||||
|
|
||||||
optionsChartEditor:
|
optionsChartEditor:
|
||||||
{
|
{
|
||||||
// Reasonable defaults.
|
// Reasonable defaults.
|
||||||
|
|
@ -393,6 +399,22 @@ class Save
|
||||||
return data.optionsChartEditor.playbackSpeed;
|
return data.optionsChartEditor.playbackSpeed;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public var charactersSeen(get, never):Array<String>;
|
||||||
|
|
||||||
|
function get_charactersSeen():Array<String>
|
||||||
|
{
|
||||||
|
return data.unlocks.charactersSeen;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* When we've seen a character unlock, add it to the list of characters seen.
|
||||||
|
* @param character
|
||||||
|
*/
|
||||||
|
public function addCharacterSeen(character:String):Void
|
||||||
|
{
|
||||||
|
data.unlocks.charactersSeen.push(character);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the score the user achieved for a given level on a given difficulty.
|
* Return the score the user achieved for a given level on a given difficulty.
|
||||||
*
|
*
|
||||||
|
|
@ -471,11 +493,19 @@ class Save
|
||||||
for (difficulty in difficultyList)
|
for (difficulty in difficultyList)
|
||||||
{
|
{
|
||||||
var score:Null<SaveScoreData> = getLevelScore(levelId, difficulty);
|
var score:Null<SaveScoreData> = getLevelScore(levelId, difficulty);
|
||||||
// TODO: Do we need to check accuracy/score here?
|
|
||||||
if (score != null)
|
if (score != null)
|
||||||
{
|
{
|
||||||
|
if (score.score > 0)
|
||||||
|
{
|
||||||
|
// Level has score data, which means we cleared it!
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Level has score data, but the score is 0.
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
@ -630,11 +660,19 @@ class Save
|
||||||
for (difficulty in difficultyList)
|
for (difficulty in difficultyList)
|
||||||
{
|
{
|
||||||
var score:Null<SaveScoreData> = getSongScore(songId, difficulty);
|
var score:Null<SaveScoreData> = getSongScore(songId, difficulty);
|
||||||
// TODO: Do we need to check accuracy/score here?
|
|
||||||
if (score != null)
|
if (score != null)
|
||||||
{
|
{
|
||||||
|
if (score.score > 0)
|
||||||
|
{
|
||||||
|
// Level has score data, which means we cleared it!
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Level has score data, but the score is 0.
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
@ -956,6 +994,8 @@ typedef RawSaveData =
|
||||||
*/
|
*/
|
||||||
var options:SaveDataOptions;
|
var options:SaveDataOptions;
|
||||||
|
|
||||||
|
var unlocks:SaveDataUnlocks;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The user's favorited songs in the Freeplay menu,
|
* The user's favorited songs in the Freeplay menu,
|
||||||
* as a list of song IDs.
|
* as a list of song IDs.
|
||||||
|
|
@ -980,6 +1020,15 @@ typedef SaveApiNewgroundsData =
|
||||||
var sessionId:Null<String>;
|
var sessionId:Null<String>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
typedef SaveDataUnlocks =
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Every time we see the unlock animation for a character,
|
||||||
|
* add it to this list so that we don't show it again.
|
||||||
|
*/
|
||||||
|
var charactersSeen:Array<String>;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An anoymous structure containing options about the user's high scores.
|
* An anoymous structure containing options about the user's high scores.
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -290,7 +290,6 @@ class CharSelectSubState extends MusicBeatSubState
|
||||||
}
|
}
|
||||||
|
|
||||||
var grpIcons:FlxSpriteGroup;
|
var grpIcons:FlxSpriteGroup;
|
||||||
|
|
||||||
var grpXSpread(default, set):Float = 107;
|
var grpXSpread(default, set):Float = 107;
|
||||||
var grpYSpread(default, set):Float = 127;
|
var grpYSpread(default, set):Float = 127;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -99,7 +99,7 @@ class FreeplayDJ extends FlxAtlasSprite
|
||||||
playFlashAnimation(animPrefix, true, false, true);
|
playFlashAnimation(animPrefix, true, false, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (getCurrentAnimation() == animPrefix && this.isLoopFinished())
|
if (getCurrentAnimation() == animPrefix && this.isLoopComplete())
|
||||||
{
|
{
|
||||||
if (timeIdling >= IDLE_EGG_PERIOD && !seenIdleEasterEgg)
|
if (timeIdling >= IDLE_EGG_PERIOD && !seenIdleEasterEgg)
|
||||||
{
|
{
|
||||||
|
|
@ -111,6 +111,16 @@ class FreeplayDJ extends FlxAtlasSprite
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
timeIdling += elapsed;
|
timeIdling += elapsed;
|
||||||
|
case NewUnlock:
|
||||||
|
var animPrefix = playableCharData.getAnimationPrefix('newUnlock');
|
||||||
|
if (!hasAnimation(animPrefix))
|
||||||
|
{
|
||||||
|
currentState = Idle;
|
||||||
|
}
|
||||||
|
if (getCurrentAnimation() != animPrefix)
|
||||||
|
{
|
||||||
|
playFlashAnimation(animPrefix, true, false, true);
|
||||||
|
}
|
||||||
case Confirm:
|
case Confirm:
|
||||||
var animPrefix = playableCharData.getAnimationPrefix('confirm');
|
var animPrefix = playableCharData.getAnimationPrefix('confirm');
|
||||||
if (getCurrentAnimation() != animPrefix) playFlashAnimation(animPrefix, false);
|
if (getCurrentAnimation() != animPrefix) playFlashAnimation(animPrefix, false);
|
||||||
|
|
@ -225,8 +235,15 @@ class FreeplayDJ extends FlxAtlasSprite
|
||||||
// var name = anim.curSymbol.name;
|
// var name = anim.curSymbol.name;
|
||||||
|
|
||||||
if (name == playableCharData.getAnimationPrefix('intro'))
|
if (name == playableCharData.getAnimationPrefix('intro'))
|
||||||
|
{
|
||||||
|
if (PlayerRegistry.instance.hasNewCharacter())
|
||||||
|
{
|
||||||
|
currentState = NewUnlock;
|
||||||
|
}
|
||||||
|
else
|
||||||
{
|
{
|
||||||
currentState = Idle;
|
currentState = Idle;
|
||||||
|
}
|
||||||
onIntroDone.dispatch();
|
onIntroDone.dispatch();
|
||||||
}
|
}
|
||||||
else if (name == playableCharData.getAnimationPrefix('idle'))
|
else if (name == playableCharData.getAnimationPrefix('idle'))
|
||||||
|
|
@ -266,9 +283,17 @@ class FreeplayDJ extends FlxAtlasSprite
|
||||||
// runTvLogic();
|
// runTvLogic();
|
||||||
}
|
}
|
||||||
trace('Replay idle: ${frame}');
|
trace('Replay idle: ${frame}');
|
||||||
playAnimation(playableCharData.getAnimationPrefix('cartoon'), true, false, false, frame);
|
playFlashAnimation(playableCharData.getAnimationPrefix('cartoon'), true, false, false, frame);
|
||||||
// trace('Finished confirm');
|
// trace('Finished confirm');
|
||||||
}
|
}
|
||||||
|
else if (name == playableCharData.getAnimationPrefix('newUnlock'))
|
||||||
|
{
|
||||||
|
// Animation should loop.
|
||||||
|
}
|
||||||
|
else if (name == playableCharData.getAnimationPrefix('charSelect'))
|
||||||
|
{
|
||||||
|
onCharSelectComplete();
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
trace('Finished ${name}');
|
trace('Finished ${name}');
|
||||||
|
|
@ -281,6 +306,15 @@ class FreeplayDJ extends FlxAtlasSprite
|
||||||
seenIdleEasterEgg = false;
|
seenIdleEasterEgg = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dynamic function, it's actually a variable you can reassign!
|
||||||
|
* `dj.onCharSelectComplete = function() {};`
|
||||||
|
*/
|
||||||
|
public dynamic function onCharSelectComplete():Void
|
||||||
|
{
|
||||||
|
trace('onCharSelectComplete()');
|
||||||
|
}
|
||||||
|
|
||||||
var offsetX:Float = 0.0;
|
var offsetX:Float = 0.0;
|
||||||
var offsetY:Float = 0.0;
|
var offsetY:Float = 0.0;
|
||||||
|
|
||||||
|
|
@ -342,6 +376,22 @@ class FreeplayDJ extends FlxAtlasSprite
|
||||||
currentState = Confirm;
|
currentState = Confirm;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function toCharSelect():Void
|
||||||
|
{
|
||||||
|
if (hasAnimation('charSelect'))
|
||||||
|
{
|
||||||
|
currentState = CharSelect;
|
||||||
|
var animPrefix = playableCharData.getAnimationPrefix('charSelect');
|
||||||
|
playFlashAnimation(animPrefix, true, false, false, 0);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
currentState = Confirm;
|
||||||
|
// Call this immediately; otherwise, we get locked out of Character Select.
|
||||||
|
onCharSelectComplete();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public function fistPumpIntro():Void
|
public function fistPumpIntro():Void
|
||||||
{
|
{
|
||||||
currentState = FistPumpIntro;
|
currentState = FistPumpIntro;
|
||||||
|
|
@ -456,6 +506,15 @@ enum FreeplayDJState
|
||||||
* The actual frame label that gets played may vary based on the player's success.
|
* The actual frame label that gets played may vary based on the player's success.
|
||||||
*/
|
*/
|
||||||
FistPump;
|
FistPump;
|
||||||
IdleEasterEgg;
|
|
||||||
Cartoon;
|
/**
|
||||||
|
* Plays an animation to indicate that the player has a new unlock in Character Select.
|
||||||
|
* Overrides all idle animations as well as the fist pump. Only Confirm and CharSelect will override this.
|
||||||
|
*/
|
||||||
|
NewUnlock;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Plays an animation to transition to the Character Select screen.
|
||||||
|
*/
|
||||||
|
CharSelect;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1253,8 +1253,33 @@ class FreeplayState extends MusicBeatSubState
|
||||||
|
|
||||||
if (controls.FREEPLAY_CHAR_SELECT && !busy)
|
if (controls.FREEPLAY_CHAR_SELECT && !busy)
|
||||||
{
|
{
|
||||||
|
// Check if we have ACCESS to character select!
|
||||||
|
trace('Is Pico unlocked? ${PlayerRegistry.instance.fetchEntry('pico')?.isUnlocked()}');
|
||||||
|
trace('Number of characters: ${PlayerRegistry.instance.countUnlockedCharacters()}');
|
||||||
|
|
||||||
|
if (PlayerRegistry.instance.countUnlockedCharacters() > 1)
|
||||||
|
{
|
||||||
|
if (dj != null)
|
||||||
|
{
|
||||||
|
busy = true;
|
||||||
|
// Transition to character select after animation
|
||||||
|
dj.onCharSelectComplete = function() {
|
||||||
FlxG.switchState(new funkin.ui.charSelect.CharSelectSubState());
|
FlxG.switchState(new funkin.ui.charSelect.CharSelectSubState());
|
||||||
}
|
}
|
||||||
|
dj.toCharSelect();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Transition to character select immediately
|
||||||
|
FlxG.switchState(new funkin.ui.charSelect.CharSelectSubState());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
trace('Not enough characters unlocked to open character select!');
|
||||||
|
FunkinSound.playOnce(Paths.sound('cancelMenu'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (controls.FREEPLAY_FAVORITE && !busy)
|
if (controls.FREEPLAY_FAVORITE && !busy)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue