mirror of
https://github.com/ninjamuffin99/Funkin.git
synced 2024-11-15 11:22:55 +00:00
Merge branch 'bugfix/various-develop-fixes' into rewrite/master
This commit is contained in:
commit
1fbca9ab6a
2
assets
2
assets
|
@ -1 +1 @@
|
|||
Subproject commit e19aaca19254ec2717a4f942005f32b42b6ecb18
|
||||
Subproject commit 7210562035c7ab6d2122606ec607b3e897a5ef20
|
|
@ -38,6 +38,17 @@ class PlayerData
|
|||
@:optional
|
||||
public var freeplayDJ:Null<PlayerFreeplayDJData> = null;
|
||||
|
||||
/**
|
||||
* Data for displaying this character in the Character Select menu.
|
||||
* If null, exclude from Character Select.
|
||||
*/
|
||||
@:optional
|
||||
public var charSelect:Null<PlayerCharSelectData> = null;
|
||||
|
||||
/**
|
||||
* Data for displaying this character in the results screen.
|
||||
*/
|
||||
@:optional
|
||||
public var results:Null<PlayerResultsData> = null;
|
||||
|
||||
/**
|
||||
|
@ -97,6 +108,9 @@ class PlayerFreeplayDJData
|
|||
@:optional
|
||||
var cartoon:Null<PlayerFreeplayDJCartoonData>;
|
||||
|
||||
@:optional
|
||||
var fistPump:Null<PlayerFreeplayDJFistPumpData>;
|
||||
|
||||
public function new()
|
||||
{
|
||||
animationMap = new Map();
|
||||
|
@ -183,6 +197,58 @@ class PlayerFreeplayDJData
|
|||
{
|
||||
return cartoon?.channelChangeFrame ?? 60;
|
||||
}
|
||||
|
||||
public function getFistPumpIntroStartFrame():Int
|
||||
{
|
||||
return fistPump?.introStartFrame ?? 0;
|
||||
}
|
||||
|
||||
public function getFistPumpIntroEndFrame():Int
|
||||
{
|
||||
return fistPump?.introEndFrame ?? 0;
|
||||
}
|
||||
|
||||
public function getFistPumpLoopStartFrame():Int
|
||||
{
|
||||
return fistPump?.loopStartFrame ?? 0;
|
||||
}
|
||||
|
||||
public function getFistPumpLoopEndFrame():Int
|
||||
{
|
||||
return fistPump?.loopEndFrame ?? 0;
|
||||
}
|
||||
|
||||
public function getFistPumpIntroBadStartFrame():Int
|
||||
{
|
||||
return fistPump?.introBadStartFrame ?? 0;
|
||||
}
|
||||
|
||||
public function getFistPumpIntroBadEndFrame():Int
|
||||
{
|
||||
return fistPump?.introBadEndFrame ?? 0;
|
||||
}
|
||||
|
||||
public function getFistPumpLoopBadStartFrame():Int
|
||||
{
|
||||
return fistPump?.loopBadStartFrame ?? 0;
|
||||
}
|
||||
|
||||
public function getFistPumpLoopBadEndFrame():Int
|
||||
{
|
||||
return fistPump?.loopBadEndFrame ?? 0;
|
||||
}
|
||||
}
|
||||
|
||||
class PlayerCharSelectData
|
||||
{
|
||||
/**
|
||||
* A zero-indexed number for the character's preferred position in the grid.
|
||||
* 0 = top left, 4 = center, 8 = bottom right
|
||||
* In the event of a conflict, the first character alphabetically gets it,
|
||||
* and others get shifted over.
|
||||
*/
|
||||
@:optional
|
||||
public var position:Null<Int>;
|
||||
}
|
||||
|
||||
typedef PlayerResultsData =
|
||||
|
@ -242,3 +308,30 @@ typedef PlayerFreeplayDJCartoonData =
|
|||
var loopFrame:Int;
|
||||
var channelChangeFrame:Int;
|
||||
}
|
||||
|
||||
typedef PlayerFreeplayDJFistPumpData =
|
||||
{
|
||||
@:default(0)
|
||||
var introStartFrame:Int;
|
||||
|
||||
@:default(4)
|
||||
var introEndFrame:Int;
|
||||
|
||||
@:default(4)
|
||||
var loopStartFrame:Int;
|
||||
|
||||
@:default(-1)
|
||||
var loopEndFrame:Int;
|
||||
|
||||
@:default(0)
|
||||
var introBadStartFrame:Int;
|
||||
|
||||
@:default(4)
|
||||
var introBadEndFrame:Int;
|
||||
|
||||
@:default(4)
|
||||
var loopBadStartFrame:Int;
|
||||
|
||||
@:default(-1)
|
||||
var loopBadEndFrame:Int;
|
||||
};
|
||||
|
|
|
@ -3,6 +3,7 @@ package funkin.data.freeplay.player;
|
|||
import funkin.data.freeplay.player.PlayerData;
|
||||
import funkin.ui.freeplay.charselect.PlayableCharacter;
|
||||
import funkin.ui.freeplay.charselect.ScriptedPlayableCharacter;
|
||||
import funkin.save.Save;
|
||||
|
||||
class PlayerRegistry extends BaseRegistry<PlayableCharacter, PlayerData>
|
||||
{
|
||||
|
@ -53,6 +54,41 @@ class PlayerRegistry extends BaseRegistry<PlayableCharacter, PlayerData>
|
|||
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.
|
||||
* @param characterId The stage character ID.
|
||||
|
|
|
@ -1979,7 +1979,7 @@ class PlayState extends MusicBeatSubState
|
|||
if (vocals == null) return;
|
||||
|
||||
// Skip this if the music is paused (GameOver, Pause menu, start-of-song offset, etc.)
|
||||
if (!FlxG.sound.music.playing) return;
|
||||
if (!(FlxG.sound.music?.playing ?? false)) return;
|
||||
var timeToPlayAt:Float = Conductor.instance.songPosition - Conductor.instance.instrumentalOffset;
|
||||
FlxG.sound.music.pause();
|
||||
vocals.pause();
|
||||
|
@ -2221,10 +2221,14 @@ class PlayState extends MusicBeatSubState
|
|||
// Calling event.cancelEvent() skips all the other logic! Neat!
|
||||
if (event.eventCanceled) continue;
|
||||
|
||||
// Skip handling the miss in botplay!
|
||||
if (!isBotPlayMode)
|
||||
{
|
||||
// Judge the miss.
|
||||
// NOTE: This is what handles the scoring.
|
||||
trace('Missed note! ${note.noteData}');
|
||||
onNoteMiss(note, event.playSound, event.healthChange);
|
||||
}
|
||||
|
||||
note.handledMiss = true;
|
||||
}
|
||||
|
@ -2321,9 +2325,16 @@ class PlayState extends MusicBeatSubState
|
|||
|
||||
playerStrumline.pressKey(input.noteDirection);
|
||||
|
||||
// Don't credit or penalize inputs in Bot Play.
|
||||
if (isBotPlayMode) continue;
|
||||
|
||||
var notesInDirection:Array<NoteSprite> = notesByDirection[input.noteDirection];
|
||||
|
||||
if (!Constants.GHOST_TAPPING && notesInDirection.length == 0)
|
||||
#if FEATURE_GHOST_TAPPING
|
||||
if ((!playerStrumline.mayGhostTap()) && notesInDirection.length == 0)
|
||||
#else
|
||||
if (notesInDirection.length == 0)
|
||||
#end
|
||||
{
|
||||
// Pressed a wrong key with no notes nearby.
|
||||
// Perform a ghost miss (anti-spam).
|
||||
|
@ -2333,16 +2344,6 @@ class PlayState extends MusicBeatSubState
|
|||
playerStrumline.playPress(input.noteDirection);
|
||||
trace('PENALTY Score: ${songScore}');
|
||||
}
|
||||
else if (Constants.GHOST_TAPPING && (!playerStrumline.mayGhostTap()) && notesInDirection.length == 0)
|
||||
{
|
||||
// Pressed a wrong key with notes visible on-screen.
|
||||
// Perform a ghost miss (anti-spam).
|
||||
ghostNoteMiss(input.noteDirection, notesInRange.length > 0);
|
||||
|
||||
// Play the strumline animation.
|
||||
playerStrumline.playPress(input.noteDirection);
|
||||
trace('PENALTY Score: ${songScore}');
|
||||
}
|
||||
else if (notesInDirection.length == 0)
|
||||
{
|
||||
// Press a key with no penalty.
|
||||
|
|
|
@ -305,6 +305,8 @@ class CharacterDataParser
|
|||
icon = "darnell";
|
||||
case "senpai-angry":
|
||||
icon = "senpai";
|
||||
case "spooky-dark":
|
||||
icon = "spooky";
|
||||
case "tankman-atlas":
|
||||
icon = "tankman";
|
||||
}
|
||||
|
|
|
@ -114,7 +114,7 @@ class ZoomCameraSongEvent extends SongEvent
|
|||
name: 'zoom',
|
||||
title: 'Zoom Level',
|
||||
defaultValue: 1.0,
|
||||
step: 0.1,
|
||||
step: 0.05,
|
||||
type: SongEventFieldType.FLOAT,
|
||||
units: 'x'
|
||||
},
|
||||
|
|
|
@ -94,6 +94,10 @@ class Strumline extends FlxSpriteGroup
|
|||
|
||||
final noteStyle:NoteStyle;
|
||||
|
||||
#if FEATURE_GHOST_TAPPING
|
||||
var ghostTapTimer:Float = 0.0;
|
||||
#end
|
||||
|
||||
/**
|
||||
* The note data for the song. Should NOT be altered after the song starts,
|
||||
* so we can easily rewind.
|
||||
|
@ -179,21 +183,36 @@ class Strumline extends FlxSpriteGroup
|
|||
super.update(elapsed);
|
||||
|
||||
updateNotes();
|
||||
|
||||
#if FEATURE_GHOST_TAPPING
|
||||
updateGhostTapTimer(elapsed);
|
||||
#end
|
||||
}
|
||||
|
||||
#if FEATURE_GHOST_TAPPING
|
||||
/**
|
||||
* Returns `true` if no notes are in range of the strumline and the player can spam without penalty.
|
||||
*/
|
||||
public function mayGhostTap():Bool
|
||||
{
|
||||
// TODO: Refine this. Only querying "can be hit" is too tight but "is being rendered" is too loose.
|
||||
// Also, if you just hit a note, there should be a (short) period where this is off so you can't spam.
|
||||
// Any notes in range of the strumline.
|
||||
if (getNotesMayHit().length > 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
// Any hold notes in range of the strumline.
|
||||
if (getHoldNotesHitOrMissed().length > 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// If there are any notes on screen, we can't ghost tap.
|
||||
return notes.members.filter(function(note:NoteSprite) {
|
||||
return note != null && note.alive && !note.hasBeenHit;
|
||||
}).length == 0;
|
||||
// Note has been hit recently.
|
||||
if (ghostTapTimer > 0.0) return false;
|
||||
|
||||
// **yippee**
|
||||
return true;
|
||||
}
|
||||
#end
|
||||
|
||||
/**
|
||||
* Return notes that are within `Constants.HIT_WINDOW` ms of the strumline.
|
||||
|
@ -492,6 +511,32 @@ class Strumline extends FlxSpriteGroup
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return notes that are within, or way after, `Constants.HIT_WINDOW` ms of the strumline.
|
||||
* @return An array of `NoteSprite` objects.
|
||||
*/
|
||||
public function getNotesOnScreen():Array<NoteSprite>
|
||||
{
|
||||
return notes.members.filter(function(note:NoteSprite) {
|
||||
return note != null && note.alive && !note.hasBeenHit;
|
||||
});
|
||||
}
|
||||
|
||||
#if FEATURE_GHOST_TAPPING
|
||||
function updateGhostTapTimer(elapsed:Float):Void
|
||||
{
|
||||
// If it's still our turn, don't update the ghost tap timer.
|
||||
if (getNotesOnScreen().length > 0) return;
|
||||
|
||||
ghostTapTimer -= elapsed;
|
||||
|
||||
if (ghostTapTimer <= 0)
|
||||
{
|
||||
ghostTapTimer = 0;
|
||||
}
|
||||
}
|
||||
#end
|
||||
|
||||
/**
|
||||
* Called when the PlayState skips a large amount of time forward or backward.
|
||||
*/
|
||||
|
@ -563,6 +608,10 @@ class Strumline extends FlxSpriteGroup
|
|||
playStatic(dir);
|
||||
}
|
||||
resetScrollSpeed();
|
||||
|
||||
#if FEATURE_GHOST_TAPPING
|
||||
ghostTapTimer = 0;
|
||||
#end
|
||||
}
|
||||
|
||||
public function applyNoteData(data:Array<SongNoteData>):Void
|
||||
|
@ -602,6 +651,10 @@ class Strumline extends FlxSpriteGroup
|
|||
|
||||
note.holdNoteSprite.sustainLength = (note.holdNoteSprite.strumTime + note.holdNoteSprite.fullSustainLength) - conductorInUse.songPosition;
|
||||
}
|
||||
|
||||
#if FEATURE_GHOST_TAPPING
|
||||
ghostTapTimer = Constants.GHOST_TAP_DELAY;
|
||||
#end
|
||||
}
|
||||
|
||||
public function killNote(note:NoteSprite):Void
|
||||
|
|
|
@ -315,7 +315,7 @@ class Bopper extends StageProp implements IPlayStateScriptedClass
|
|||
|
||||
public function isAnimationFinished():Bool
|
||||
{
|
||||
return this.animation.finished;
|
||||
return this.animation?.finished ?? false;
|
||||
}
|
||||
|
||||
public function setAnimationOffsets(name:String, xOffset:Float, yOffset:Float):Void
|
||||
|
|
|
@ -436,8 +436,9 @@ class Stage extends FlxSpriteGroup implements IPlayStateScriptedClass implements
|
|||
// Start with the per-stage character position.
|
||||
// Subtracting the origin ensures characters are positioned relative to their feet.
|
||||
// Subtracting the global offset allows positioning on a per-character basis.
|
||||
character.x = stageCharData.position[0] - character.characterOrigin.x + character.globalOffsets[0];
|
||||
character.y = stageCharData.position[1] - character.characterOrigin.y + character.globalOffsets[1];
|
||||
// We previously applied the global offset here but that is now done elsewhere.
|
||||
character.x = stageCharData.position[0] - character.characterOrigin.x;
|
||||
character.y = stageCharData.position[1] - character.characterOrigin.y;
|
||||
|
||||
@:privateAccess(funkin.play.stage.Bopper)
|
||||
{
|
||||
|
|
|
@ -121,6 +121,12 @@ class Save
|
|||
modOptions: [],
|
||||
},
|
||||
|
||||
unlocks:
|
||||
{
|
||||
// Default to having seen the default character.
|
||||
charactersSeen: ["bf"],
|
||||
},
|
||||
|
||||
optionsChartEditor:
|
||||
{
|
||||
// Reasonable defaults.
|
||||
|
@ -393,6 +399,22 @@ class Save
|
|||
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.
|
||||
*
|
||||
|
@ -471,10 +493,18 @@ class Save
|
|||
for (difficulty in difficultyList)
|
||||
{
|
||||
var score:Null<SaveScoreData> = getLevelScore(levelId, difficulty);
|
||||
// TODO: Do we need to check accuracy/score here?
|
||||
if (score != null)
|
||||
{
|
||||
return true;
|
||||
if (score.score > 0)
|
||||
{
|
||||
// Level has score data, which means we cleared it!
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Level has score data, but the score is 0.
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
|
@ -630,10 +660,18 @@ class Save
|
|||
for (difficulty in difficultyList)
|
||||
{
|
||||
var score:Null<SaveScoreData> = getSongScore(songId, difficulty);
|
||||
// TODO: Do we need to check accuracy/score here?
|
||||
if (score != null)
|
||||
{
|
||||
return true;
|
||||
if (score.score > 0)
|
||||
{
|
||||
// Level has score data, which means we cleared it!
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Level has score data, but the score is 0.
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
|
@ -956,6 +994,8 @@ typedef RawSaveData =
|
|||
*/
|
||||
var options:SaveDataOptions;
|
||||
|
||||
var unlocks:SaveDataUnlocks;
|
||||
|
||||
/**
|
||||
* The user's favorited songs in the Freeplay menu,
|
||||
* as a list of song IDs.
|
||||
|
@ -980,6 +1020,15 @@ typedef SaveApiNewgroundsData =
|
|||
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.
|
||||
*/
|
||||
|
|
|
@ -22,14 +22,26 @@ class PixelatedIcon extends FlxSprite
|
|||
|
||||
switch (char)
|
||||
{
|
||||
case 'monster-christmas':
|
||||
charPath += 'monsterpixel';
|
||||
case 'mom-car':
|
||||
charPath += 'mommypixel';
|
||||
case 'darnell-blazin':
|
||||
charPath += 'darnellpixel';
|
||||
case 'senpai-angry':
|
||||
charPath += 'senpaipixel';
|
||||
case "bf-christmas" | "bf-car" | "bf-pixel" | "bf-holding-gf":
|
||||
charPath += "bfpixel";
|
||||
case "monster-christmas":
|
||||
charPath += "monsterpixel";
|
||||
case "mom" | "mom-car":
|
||||
charPath += "mommypixel";
|
||||
case "pico-blazin" | "pico-playable" | "pico-speaker":
|
||||
charPath += "picopixel";
|
||||
case "gf-christmas" | "gf-car" | "gf-pixel" | "gf-tankmen":
|
||||
charPath += "gfpixel";
|
||||
case "dad":
|
||||
charPath += "dadpixel";
|
||||
case "darnell-blazin":
|
||||
charPath += "darnellpixel";
|
||||
case "senpai-angry":
|
||||
charPath += "senpaipixel";
|
||||
case "spooky-dark":
|
||||
charPath += "spookypixel";
|
||||
case "tankman-atlas":
|
||||
charPath += "tankmanpixel";
|
||||
default:
|
||||
charPath += '${char}pixel';
|
||||
}
|
||||
|
|
|
@ -1,27 +1,31 @@
|
|||
package funkin.ui.charSelect;
|
||||
|
||||
import funkin.ui.freeplay.FreeplayState;
|
||||
import flixel.text.FlxText;
|
||||
import funkin.ui.PixelatedIcon;
|
||||
import flixel.system.debug.watch.Tracker.TrackerProfile;
|
||||
import flixel.math.FlxPoint;
|
||||
import flixel.tweens.FlxTween;
|
||||
import openfl.display.BlendMode;
|
||||
import flixel.group.FlxGroup.FlxTypedGroup;
|
||||
import flixel.FlxObject;
|
||||
import flixel.FlxSprite;
|
||||
import flixel.group.FlxGroup;
|
||||
import flixel.group.FlxGroup.FlxTypedGroup;
|
||||
import flixel.group.FlxSpriteGroup;
|
||||
import funkin.play.stage.Stage;
|
||||
import flixel.math.FlxPoint;
|
||||
import flixel.sound.FlxSound;
|
||||
import flixel.system.debug.watch.Tracker.TrackerProfile;
|
||||
import flixel.text.FlxText;
|
||||
import flixel.tweens.FlxEase;
|
||||
import flixel.tweens.FlxTween;
|
||||
import flixel.util.FlxTimer;
|
||||
import funkin.audio.FunkinSound;
|
||||
import funkin.data.freeplay.player.PlayerData;
|
||||
import funkin.data.freeplay.player.PlayerRegistry;
|
||||
import funkin.graphics.adobeanimate.FlxAtlasSprite;
|
||||
import funkin.graphics.FunkinCamera;
|
||||
import funkin.modding.events.ScriptEvent;
|
||||
import funkin.modding.events.ScriptEventDispatcher;
|
||||
import funkin.graphics.adobeanimate.FlxAtlasSprite;
|
||||
import flixel.FlxObject;
|
||||
import openfl.display.BlendMode;
|
||||
import flixel.group.FlxGroup;
|
||||
import funkin.play.stage.Stage;
|
||||
import funkin.ui.freeplay.charselect.PlayableCharacter;
|
||||
import funkin.ui.freeplay.FreeplayState;
|
||||
import funkin.ui.PixelatedIcon;
|
||||
import funkin.util.MathUtil;
|
||||
import flixel.util.FlxTimer;
|
||||
import flixel.tweens.FlxEase;
|
||||
import flixel.sound.FlxSound;
|
||||
import funkin.audio.FunkinSound;
|
||||
import funkin.vis.dsp.SpectralAnalyzer;
|
||||
import openfl.display.BlendMode;
|
||||
|
||||
class CharSelectSubState extends MusicBeatSubState
|
||||
{
|
||||
|
@ -67,8 +71,29 @@ class CharSelectSubState extends MusicBeatSubState
|
|||
{
|
||||
super();
|
||||
|
||||
availableChars.set(4, "bf");
|
||||
availableChars.set(3, "pico");
|
||||
loadAvailableCharacters();
|
||||
}
|
||||
|
||||
function loadAvailableCharacters():Void
|
||||
{
|
||||
var playerIds:Array<String> = PlayerRegistry.instance.listEntryIds();
|
||||
|
||||
for (playerId in playerIds)
|
||||
{
|
||||
var player:Null<PlayableCharacter> = PlayerRegistry.instance.fetchEntry(playerId);
|
||||
if (player == null) continue;
|
||||
var playerData = player.getCharSelectData();
|
||||
if (playerData == null) continue;
|
||||
|
||||
var targetPosition:Int = playerData.position ?? 0;
|
||||
while (availableChars.exists(targetPosition))
|
||||
{
|
||||
targetPosition += 1;
|
||||
}
|
||||
|
||||
trace('Placing player ${playerId} at position ${targetPosition}');
|
||||
availableChars.set(targetPosition, playerId);
|
||||
}
|
||||
}
|
||||
|
||||
override public function create():Void
|
||||
|
@ -269,7 +294,6 @@ class CharSelectSubState extends MusicBeatSubState
|
|||
}
|
||||
|
||||
var grpIcons:FlxSpriteGroup;
|
||||
|
||||
var grpXSpread(default, set):Float = 107;
|
||||
var grpYSpread(default, set):Float = 127;
|
||||
|
||||
|
|
|
@ -190,8 +190,8 @@ class ChartEditorEventDataToolbox extends ChartEditorBaseToolbox
|
|||
var numberStepper:NumberStepper = new NumberStepper();
|
||||
numberStepper.id = field.name;
|
||||
numberStepper.step = field.step ?? 1.0;
|
||||
numberStepper.min = field.min ?? 0.0;
|
||||
numberStepper.max = field.max ?? 10.0;
|
||||
if (field.min != null) numberStepper.min = field.min;
|
||||
if (field.min != null) numberStepper.max = field.max;
|
||||
if (field.defaultValue != null) numberStepper.value = field.defaultValue;
|
||||
input = numberStepper;
|
||||
case FLOAT:
|
||||
|
|
|
@ -15,7 +15,7 @@ class FreeplayDJ extends FlxAtlasSprite
|
|||
{
|
||||
// Represents the sprite's current status.
|
||||
// Without state machines I would have driven myself crazy years ago.
|
||||
public var currentState:DJBoyfriendState = Intro;
|
||||
public var currentState:FreeplayDJState = Intro;
|
||||
|
||||
// A callback activated when the intro animation finishes.
|
||||
public var onIntroDone:FlxSignal = new FlxSignal();
|
||||
|
@ -99,7 +99,7 @@ class FreeplayDJ extends FlxAtlasSprite
|
|||
playFlashAnimation(animPrefix, true, false, true);
|
||||
}
|
||||
|
||||
if (getCurrentAnimation() == animPrefix && this.isLoopFinished())
|
||||
if (getCurrentAnimation() == animPrefix && this.isLoopComplete())
|
||||
{
|
||||
if (timeIdling >= IDLE_EGG_PERIOD && !seenIdleEasterEgg)
|
||||
{
|
||||
|
@ -111,18 +111,69 @@ class FreeplayDJ extends FlxAtlasSprite
|
|||
}
|
||||
}
|
||||
timeIdling += elapsed;
|
||||
case NewUnlock:
|
||||
var animPrefix = playableCharData.getAnimationPrefix('newUnlock');
|
||||
if (!hasAnimation(animPrefix))
|
||||
{
|
||||
currentState = Idle;
|
||||
}
|
||||
if (getCurrentAnimation() != animPrefix)
|
||||
{
|
||||
playFlashAnimation(animPrefix, true, false, true);
|
||||
}
|
||||
case Confirm:
|
||||
var animPrefix = playableCharData.getAnimationPrefix('confirm');
|
||||
if (getCurrentAnimation() != animPrefix) playFlashAnimation(animPrefix, false);
|
||||
timeIdling = 0;
|
||||
case FistPumpIntro:
|
||||
var animPrefix = playableCharData.getAnimationPrefix('fistPump');
|
||||
if (getCurrentAnimation() != animPrefix) playFlashAnimation('Boyfriend DJ fist pump', false);
|
||||
if (getCurrentAnimation() == animPrefix && anim.curFrame >= 4)
|
||||
var animPrefixA = playableCharData.getAnimationPrefix('fistPump');
|
||||
var animPrefixB = playableCharData.getAnimationPrefix('loss');
|
||||
|
||||
if (getCurrentAnimation() == animPrefixA)
|
||||
{
|
||||
playAnimation("Boyfriend DJ fist pump", true, false, false, 0);
|
||||
var endFrame = playableCharData.getFistPumpIntroEndFrame();
|
||||
if (endFrame > -1 && anim.curFrame >= endFrame)
|
||||
{
|
||||
playFlashAnimation(animPrefixA, true, false, false, playableCharData.getFistPumpIntroStartFrame());
|
||||
}
|
||||
}
|
||||
else if (getCurrentAnimation() == animPrefixB)
|
||||
{
|
||||
var endFrame = playableCharData.getFistPumpIntroBadEndFrame();
|
||||
if (endFrame > -1 && anim.curFrame >= endFrame)
|
||||
{
|
||||
playFlashAnimation(animPrefixB, true, false, false, playableCharData.getFistPumpIntroBadStartFrame());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
FlxG.log.warn("Unrecognized animation in FistPumpIntro: " + getCurrentAnimation());
|
||||
}
|
||||
|
||||
case FistPump:
|
||||
var animPrefixA = playableCharData.getAnimationPrefix('fistPump');
|
||||
var animPrefixB = playableCharData.getAnimationPrefix('loss');
|
||||
|
||||
if (getCurrentAnimation() == animPrefixA)
|
||||
{
|
||||
var endFrame = playableCharData.getFistPumpLoopEndFrame();
|
||||
if (endFrame > -1 && anim.curFrame >= endFrame)
|
||||
{
|
||||
playFlashAnimation(animPrefixA, true, false, false, playableCharData.getFistPumpLoopStartFrame());
|
||||
}
|
||||
}
|
||||
else if (getCurrentAnimation() == animPrefixB)
|
||||
{
|
||||
var endFrame = playableCharData.getFistPumpLoopBadEndFrame();
|
||||
if (endFrame > -1 && anim.curFrame >= endFrame)
|
||||
{
|
||||
playFlashAnimation(animPrefixB, true, false, false, playableCharData.getFistPumpLoopBadStartFrame());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
FlxG.log.warn("Unrecognized animation in FistPump: " + getCurrentAnimation());
|
||||
}
|
||||
|
||||
case IdleEasterEgg:
|
||||
var animPrefix = playableCharData.getAnimationPrefix('idleEasterEgg');
|
||||
|
@ -185,7 +236,14 @@ class FreeplayDJ extends FlxAtlasSprite
|
|||
|
||||
if (name == playableCharData.getAnimationPrefix('intro'))
|
||||
{
|
||||
currentState = Idle;
|
||||
if (PlayerRegistry.instance.hasNewCharacter())
|
||||
{
|
||||
currentState = NewUnlock;
|
||||
}
|
||||
else
|
||||
{
|
||||
currentState = Idle;
|
||||
}
|
||||
onIntroDone.dispatch();
|
||||
}
|
||||
else if (name == playableCharData.getAnimationPrefix('idle'))
|
||||
|
@ -225,9 +283,17 @@ class FreeplayDJ extends FlxAtlasSprite
|
|||
// runTvLogic();
|
||||
}
|
||||
trace('Replay idle: ${frame}');
|
||||
playAnimation(playableCharData.getAnimationPrefix('cartoon'), true, false, false, frame);
|
||||
playFlashAnimation(playableCharData.getAnimationPrefix('cartoon'), true, false, false, frame);
|
||||
// trace('Finished confirm');
|
||||
}
|
||||
else if (name == playableCharData.getAnimationPrefix('newUnlock'))
|
||||
{
|
||||
// Animation should loop.
|
||||
}
|
||||
else if (name == playableCharData.getAnimationPrefix('charSelect'))
|
||||
{
|
||||
onCharSelectComplete();
|
||||
}
|
||||
else
|
||||
{
|
||||
trace('Finished ${name}');
|
||||
|
@ -240,6 +306,15 @@ class FreeplayDJ extends FlxAtlasSprite
|
|||
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 offsetY:Float = 0.0;
|
||||
|
||||
|
@ -271,7 +346,7 @@ class FreeplayDJ extends FlxAtlasSprite
|
|||
function loadCartoon()
|
||||
{
|
||||
cartoonSnd = FunkinSound.load(Paths.sound(getRandomFlashToon()), 1.0, false, true, true, function() {
|
||||
playAnimation("Boyfriend DJ watchin tv OG", true, false, false, 60);
|
||||
playFlashAnimation(playableCharData.getAnimationPrefix('cartoon'), true, false, false, 60);
|
||||
});
|
||||
|
||||
// Fade out music to 40% volume over 1 second.
|
||||
|
@ -301,21 +376,48 @@ class FreeplayDJ extends FlxAtlasSprite
|
|||
currentState = Confirm;
|
||||
}
|
||||
|
||||
public function fistPump():Void
|
||||
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
|
||||
{
|
||||
currentState = FistPumpIntro;
|
||||
var animPrefix = playableCharData.getAnimationPrefix('fistPump');
|
||||
playFlashAnimation(animPrefix, true, false, false, playableCharData.getFistPumpIntroStartFrame());
|
||||
}
|
||||
|
||||
public function pumpFist():Void
|
||||
public function fistPump():Void
|
||||
{
|
||||
currentState = FistPump;
|
||||
playAnimation("Boyfriend DJ fist pump", true, false, false, 4);
|
||||
var animPrefix = playableCharData.getAnimationPrefix('fistPump');
|
||||
playFlashAnimation(animPrefix, true, false, false, playableCharData.getFistPumpLoopStartFrame());
|
||||
}
|
||||
|
||||
public function pumpFistBad():Void
|
||||
public function fistPumpLossIntro():Void
|
||||
{
|
||||
currentState = FistPumpIntro;
|
||||
var animPrefix = playableCharData.getAnimationPrefix('loss');
|
||||
playFlashAnimation(animPrefix, true, false, false, playableCharData.getFistPumpIntroBadStartFrame());
|
||||
}
|
||||
|
||||
public function fistPumpLoss():Void
|
||||
{
|
||||
currentState = FistPump;
|
||||
playAnimation("Boyfriend DJ loss reaction 1", true, false, false, 4);
|
||||
var animPrefix = playableCharData.getAnimationPrefix('loss');
|
||||
playFlashAnimation(animPrefix, true, false, false, playableCharData.getFistPumpLoopBadStartFrame());
|
||||
}
|
||||
|
||||
override public function getCurrentAnimation():String
|
||||
|
@ -366,13 +468,53 @@ class FreeplayDJ extends FlxAtlasSprite
|
|||
}
|
||||
}
|
||||
|
||||
enum DJBoyfriendState
|
||||
enum FreeplayDJState
|
||||
{
|
||||
/**
|
||||
* Character enters the frame and transitions to Idle.
|
||||
*/
|
||||
Intro;
|
||||
|
||||
/**
|
||||
* Character loops in idle.
|
||||
*/
|
||||
Idle;
|
||||
Confirm;
|
||||
FistPumpIntro;
|
||||
FistPump;
|
||||
|
||||
/**
|
||||
* Plays an easter egg animation after a period in Idle, then reverts to Idle.
|
||||
*/
|
||||
IdleEasterEgg;
|
||||
|
||||
/**
|
||||
* Plays an elaborate easter egg animation. Does not revert until another animation is triggered.
|
||||
*/
|
||||
Cartoon;
|
||||
|
||||
/**
|
||||
* Player has selected a song.
|
||||
*/
|
||||
Confirm;
|
||||
|
||||
/**
|
||||
* Character preps to play the fist pump animation; plays after the Results screen.
|
||||
* The actual frame label that gets played may vary based on the player's success.
|
||||
*/
|
||||
FistPumpIntro;
|
||||
|
||||
/**
|
||||
* Character plays the fist pump animation.
|
||||
* The actual frame label that gets played may vary based on the player's success.
|
||||
*/
|
||||
FistPump;
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
|
|
|
@ -177,9 +177,22 @@ class FreeplayState extends MusicBeatSubState
|
|||
|
||||
var stickerSubState:Null<StickerSubState> = null;
|
||||
|
||||
public static var rememberedDifficulty:Null<String> = Constants.DEFAULT_DIFFICULTY;
|
||||
/**
|
||||
* The difficulty we were on when this menu was last accessed.
|
||||
*/
|
||||
public static var rememberedDifficulty:String = Constants.DEFAULT_DIFFICULTY;
|
||||
|
||||
/**
|
||||
* The song we were on when this menu was last accessed.
|
||||
* NOTE: `null` if the last song was `Random`.
|
||||
*/
|
||||
public static var rememberedSongId:Null<String> = 'tutorial';
|
||||
|
||||
/**
|
||||
* The character we were on when this menu was last accessed.
|
||||
*/
|
||||
public static var rememberedCharacterId:String = Constants.DEFAULT_CHARACTER;
|
||||
|
||||
var funnyCam:FunkinCamera;
|
||||
var rankCamera:FunkinCamera;
|
||||
var rankBg:FunkinSprite;
|
||||
|
@ -209,14 +222,16 @@ class FreeplayState extends MusicBeatSubState
|
|||
|
||||
public function new(?params:FreeplayStateParams, ?stickers:StickerSubState)
|
||||
{
|
||||
currentCharacterId = params?.character ?? Constants.DEFAULT_CHARACTER;
|
||||
currentCharacterId = params?.character ?? rememberedCharacterId;
|
||||
var fetchPlayableCharacter = function():PlayableCharacter {
|
||||
var result = PlayerRegistry.instance.fetchEntry(params?.character ?? Constants.DEFAULT_CHARACTER);
|
||||
var result = PlayerRegistry.instance.fetchEntry(params?.character ?? rememberedCharacterId);
|
||||
if (result == null) throw 'No valid playable character with id ${params?.character}';
|
||||
return result;
|
||||
};
|
||||
currentCharacter = fetchPlayableCharacter();
|
||||
|
||||
rememberedCharacterId = currentCharacter?.id ?? Constants.DEFAULT_CHARACTER;
|
||||
|
||||
fromResultsParams = params?.fromResults;
|
||||
|
||||
if (fromResultsParams?.playRankAnim == true)
|
||||
|
@ -629,8 +644,8 @@ class FreeplayState extends MusicBeatSubState
|
|||
speed: 0.3
|
||||
});
|
||||
|
||||
var diffSelLeft:DifficultySelector = new DifficultySelector(20, grpDifficulties.y - 10, false, controls);
|
||||
var diffSelRight:DifficultySelector = new DifficultySelector(325, grpDifficulties.y - 10, true, controls);
|
||||
var diffSelLeft:DifficultySelector = new DifficultySelector(this, 20, grpDifficulties.y - 10, false, controls);
|
||||
var diffSelRight:DifficultySelector = new DifficultySelector(this, 325, grpDifficulties.y - 10, true, controls);
|
||||
diffSelLeft.visible = false;
|
||||
diffSelRight.visible = false;
|
||||
add(diffSelLeft);
|
||||
|
@ -743,10 +758,7 @@ class FreeplayState extends MusicBeatSubState
|
|||
var tempSongs:Array<Null<FreeplaySongData>> = songs;
|
||||
|
||||
// Remember just the difficulty because it's important for song sorting.
|
||||
if (rememberedDifficulty != null)
|
||||
{
|
||||
currentDifficulty = rememberedDifficulty;
|
||||
}
|
||||
currentDifficulty = rememberedDifficulty;
|
||||
|
||||
if (filterStuff != null) tempSongs = sortSongs(tempSongs, filterStuff);
|
||||
|
||||
|
@ -901,7 +913,15 @@ class FreeplayState extends MusicBeatSubState
|
|||
changeSelection();
|
||||
changeDiff();
|
||||
|
||||
if (dj != null) dj.fistPump();
|
||||
if (fromResultsParams?.newRank == SHIT)
|
||||
{
|
||||
if (dj != null) dj.fistPumpLossIntro();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (dj != null) dj.fistPumpIntro();
|
||||
}
|
||||
|
||||
// rankCamera.fade(FlxColor.BLACK, 0.5, true);
|
||||
rankCamera.fade(0xFF000000, 0.5, true, null, true);
|
||||
if (FlxG.sound.music != null) FlxG.sound.music.volume = 0;
|
||||
|
@ -1083,11 +1103,11 @@ class FreeplayState extends MusicBeatSubState
|
|||
|
||||
if (fromResultsParams?.newRank == SHIT)
|
||||
{
|
||||
if (dj != null) dj.pumpFistBad();
|
||||
if (dj != null) dj.fistPumpLoss();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (dj != null) dj.pumpFist();
|
||||
if (dj != null) dj.fistPump();
|
||||
}
|
||||
|
||||
rankCamera.zoom = 0.8;
|
||||
|
@ -1190,7 +1210,7 @@ class FreeplayState extends MusicBeatSubState
|
|||
/**
|
||||
* If true, disable interaction with the interface.
|
||||
*/
|
||||
var busy:Bool = false;
|
||||
public var busy:Bool = false;
|
||||
|
||||
var originalPos:FlxPoint = new FlxPoint();
|
||||
|
||||
|
@ -1233,7 +1253,32 @@ class FreeplayState extends MusicBeatSubState
|
|||
|
||||
if (controls.FREEPLAY_CHAR_SELECT && !busy)
|
||||
{
|
||||
FlxG.switchState(new funkin.ui.charSelect.CharSelectSubState());
|
||||
// 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());
|
||||
}
|
||||
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)
|
||||
|
@ -1326,6 +1371,8 @@ class FreeplayState extends MusicBeatSubState
|
|||
}
|
||||
|
||||
handleInputs(elapsed);
|
||||
|
||||
if (dj != null) FlxG.watch.addQuick('dj-anim', dj.getCurrentAnimation());
|
||||
}
|
||||
|
||||
function handleInputs(elapsed:Float):Void
|
||||
|
@ -1483,7 +1530,7 @@ class FreeplayState extends MusicBeatSubState
|
|||
generateSongList(currentFilter, true);
|
||||
}
|
||||
|
||||
if (controls.BACK)
|
||||
if (controls.BACK && !busy)
|
||||
{
|
||||
busy = true;
|
||||
FlxTween.globalManager.clear();
|
||||
|
@ -1891,7 +1938,7 @@ class FreeplayState extends MusicBeatSubState
|
|||
intendedCompletion = 0.0;
|
||||
diffIdsCurrent = diffIdsTotal;
|
||||
rememberedSongId = null;
|
||||
rememberedDifficulty = null;
|
||||
rememberedDifficulty = Constants.DEFAULT_DIFFICULTY;
|
||||
albumRoll.albumId = null;
|
||||
}
|
||||
|
||||
|
@ -2000,10 +2047,13 @@ class DifficultySelector extends FlxSprite
|
|||
var controls:Controls;
|
||||
var whiteShader:PureColor;
|
||||
|
||||
public function new(x:Float, y:Float, flipped:Bool, controls:Controls)
|
||||
var parent:FreeplayState;
|
||||
|
||||
public function new(parent:FreeplayState, x:Float, y:Float, flipped:Bool, controls:Controls)
|
||||
{
|
||||
super(x, y);
|
||||
|
||||
this.parent = parent;
|
||||
this.controls = controls;
|
||||
|
||||
frames = Paths.getSparrowAtlas('freeplay/freeplaySelector');
|
||||
|
@ -2019,8 +2069,8 @@ class DifficultySelector extends FlxSprite
|
|||
|
||||
override function update(elapsed:Float):Void
|
||||
{
|
||||
if (flipX && controls.UI_RIGHT_P) moveShitDown();
|
||||
if (!flipX && controls.UI_LEFT_P) moveShitDown();
|
||||
if (flipX && controls.UI_RIGHT_P && !parent.busy) moveShitDown();
|
||||
if (!flipX && controls.UI_LEFT_P && !parent.busy) moveShitDown();
|
||||
|
||||
super.update(elapsed);
|
||||
}
|
||||
|
|
|
@ -88,6 +88,11 @@ class PlayableCharacter implements IRegistryEntry<PlayerData>
|
|||
return _data.freeplayDJ.getFreeplayDJText(index);
|
||||
}
|
||||
|
||||
public function getCharSelectData():PlayerCharSelectData
|
||||
{
|
||||
return _data.charSelect;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param rank Which rank to get info for
|
||||
* @return An array of animations. For example, BF Great has two animations, one for BF and one for GF
|
||||
|
|
|
@ -98,14 +98,7 @@ class MainMenuState extends MusicBeatState
|
|||
add(menuItems);
|
||||
menuItems.onChange.add(onMenuItemChange);
|
||||
menuItems.onAcceptPress.add(function(_) {
|
||||
if (_.name == 'freeplay')
|
||||
{
|
||||
magenta.visible = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
FlxFlicker.flicker(magenta, 1.1, 0.15, false, true);
|
||||
}
|
||||
FlxFlicker.flicker(magenta, 1.1, 0.15, false, true);
|
||||
});
|
||||
|
||||
menuItems.enabled = true; // can move on intro
|
||||
|
@ -117,10 +110,7 @@ class MainMenuState extends MusicBeatState
|
|||
FlxTransitionableState.skipNextTransIn = true;
|
||||
FlxTransitionableState.skipNextTransOut = true;
|
||||
|
||||
openSubState(new FreeplayState(
|
||||
{
|
||||
character: FlxG.keys.pressed.SHIFT ? 'pico' : 'bf',
|
||||
}));
|
||||
openSubState(new FreeplayState());
|
||||
});
|
||||
|
||||
#if CAN_OPEN_LINKS
|
||||
|
@ -355,6 +345,7 @@ class MainMenuState extends MusicBeatState
|
|||
|
||||
if (FlxG.keys.pressed.CONTROL && FlxG.keys.pressed.ALT && FlxG.keys.pressed.SHIFT && FlxG.keys.justPressed.W)
|
||||
{
|
||||
FunkinSound.playOnce(Paths.sound('confirmMenu'));
|
||||
// Give the user a score of 1 point on Weekend 1 story mode.
|
||||
// This makes the level count as cleared and displays the songs in Freeplay.
|
||||
funkin.save.Save.instance.setLevelScore('weekend1', 'easy',
|
||||
|
@ -375,6 +366,29 @@ class MainMenuState extends MusicBeatState
|
|||
});
|
||||
}
|
||||
|
||||
if (FlxG.keys.pressed.CONTROL && FlxG.keys.pressed.ALT && FlxG.keys.pressed.SHIFT && FlxG.keys.justPressed.L)
|
||||
{
|
||||
FunkinSound.playOnce(Paths.sound('confirmMenu'));
|
||||
// Give the user a score of 0 points on Weekend 1 story mode.
|
||||
// This makes the level count as uncleared and no longer displays the songs in Freeplay.
|
||||
funkin.save.Save.instance.setLevelScore('weekend1', 'easy',
|
||||
{
|
||||
score: 1,
|
||||
tallies:
|
||||
{
|
||||
sick: 0,
|
||||
good: 0,
|
||||
bad: 0,
|
||||
shit: 0,
|
||||
missed: 0,
|
||||
combo: 0,
|
||||
maxCombo: 0,
|
||||
totalNotesHit: 0,
|
||||
totalNotes: 0,
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (FlxG.keys.pressed.CONTROL && FlxG.keys.pressed.ALT && FlxG.keys.pressed.SHIFT && FlxG.keys.justPressed.R)
|
||||
{
|
||||
// Give the user a hypothetical overridden score,
|
||||
|
|
|
@ -16,7 +16,7 @@ class LevelProp extends Bopper
|
|||
this.propData = value;
|
||||
|
||||
this.visible = this.propData != null;
|
||||
danceEvery = this.propData?.danceEvery ?? 0.0;
|
||||
danceEvery = this.propData?.danceEvery ?? 1.0;
|
||||
|
||||
applyData();
|
||||
}
|
||||
|
@ -32,7 +32,7 @@ class LevelProp extends Bopper
|
|||
|
||||
public function playConfirm():Void
|
||||
{
|
||||
playAnimation('confirm', true, true);
|
||||
if (hasAnimation('confirm')) playAnimation('confirm', true, true);
|
||||
}
|
||||
|
||||
function applyData():Void
|
||||
|
|
|
@ -314,25 +314,28 @@ class LoadingState extends MusicBeatSubState
|
|||
FunkinSprite.cacheTexture(Paths.image('ui/popup/pixel/num7'));
|
||||
FunkinSprite.cacheTexture(Paths.image('ui/popup/pixel/num8'));
|
||||
FunkinSprite.cacheTexture(Paths.image('ui/popup/pixel/num9'));
|
||||
|
||||
FunkinSprite.cacheTexture(Paths.image('notes', 'shared'));
|
||||
FunkinSprite.cacheTexture(Paths.image('noteSplashes', 'shared'));
|
||||
FunkinSprite.cacheTexture(Paths.image('noteStrumline', 'shared'));
|
||||
FunkinSprite.cacheTexture(Paths.image('NOTE_hold_assets'));
|
||||
|
||||
FunkinSprite.cacheTexture(Paths.image('ui/countdown/funkin/ready', 'shared'));
|
||||
FunkinSprite.cacheTexture(Paths.image('ui/countdown/funkin/set', 'shared'));
|
||||
FunkinSprite.cacheTexture(Paths.image('ui/countdown/funkin/go', 'shared'));
|
||||
FunkinSprite.cacheTexture(Paths.image('ui/countdown/pixel/ready', 'shared'));
|
||||
FunkinSprite.cacheTexture(Paths.image('ui/countdown/pixel/set', 'shared'));
|
||||
FunkinSprite.cacheTexture(Paths.image('ui/countdown/pixel/go', 'shared'));
|
||||
FunkinSprite.cacheTexture(Paths.image('ui/popup/normal/sick'));
|
||||
FunkinSprite.cacheTexture(Paths.image('ui/popup/normal/good'));
|
||||
FunkinSprite.cacheTexture(Paths.image('ui/popup/normal/bad'));
|
||||
FunkinSprite.cacheTexture(Paths.image('ui/popup/normal/shit'));
|
||||
|
||||
FunkinSprite.cacheTexture(Paths.image('ui/popup/funkin/sick'));
|
||||
FunkinSprite.cacheTexture(Paths.image('ui/popup/funkin/good'));
|
||||
FunkinSprite.cacheTexture(Paths.image('ui/popup/funkin/bad'));
|
||||
FunkinSprite.cacheTexture(Paths.image('ui/popup/funkin/shit'));
|
||||
|
||||
FunkinSprite.cacheTexture(Paths.image('ui/popup/pixel/sick'));
|
||||
FunkinSprite.cacheTexture(Paths.image('ui/popup/pixel/good'));
|
||||
FunkinSprite.cacheTexture(Paths.image('ui/popup/pixel/bad'));
|
||||
FunkinSprite.cacheTexture(Paths.image('ui/popup/pixel/shit'));
|
||||
FunkinSprite.cacheTexture(Paths.image('miss', 'shared')); // TODO: remove this
|
||||
|
||||
// List all image assets in the level's library.
|
||||
// This is crude and I want to remove it when we have a proper asset caching system.
|
||||
|
|
|
@ -524,12 +524,16 @@ class Constants
|
|||
* OTHER
|
||||
*/
|
||||
// ==============================
|
||||
#if FEATURE_GHOST_TAPPING
|
||||
// Hey there, Eric here.
|
||||
// This feature is currently still in development. You can test it out by creating a special debug build!
|
||||
// lime build windows -DFEATURE_GHOST_TAPPING
|
||||
|
||||
/**
|
||||
* If true, the player will not receive the ghost miss penalty if there are no notes within the hit window.
|
||||
* This is the thing people have been begging for forever lolol.
|
||||
* Duration, in seconds, after the player's section ends before the player can spam without penalty.
|
||||
*/
|
||||
public static final GHOST_TAPPING:Bool = false;
|
||||
public static final GHOST_TAP_DELAY:Float = 3 / 8;
|
||||
#end
|
||||
|
||||
/**
|
||||
* The maximum number of previous file paths for the Chart Editor to remember.
|
||||
|
|
Loading…
Reference in a new issue