2023-11-07 04:04:22 -05:00
|
|
|
package funkin.ui.freeplay;
|
2021-10-21 17:40:53 -04:00
|
|
|
|
2023-11-07 04:04:22 -05:00
|
|
|
import funkin.ui.freeplay.FreeplayState.FreeplaySongData;
|
|
|
|
import funkin.graphics.shaders.HSVShader;
|
|
|
|
import funkin.graphics.shaders.GaussianBlurShader;
|
2023-08-12 00:31:43 -04:00
|
|
|
import flixel.group.FlxGroup;
|
2021-10-21 17:40:53 -04:00
|
|
|
import flixel.FlxSprite;
|
|
|
|
import flixel.graphics.frames.FlxAtlasFrames;
|
|
|
|
import flixel.group.FlxSpriteGroup.FlxTypedSpriteGroup;
|
|
|
|
import flixel.group.FlxSpriteGroup;
|
|
|
|
import flixel.math.FlxMath;
|
|
|
|
import flixel.math.FlxPoint;
|
|
|
|
import flixel.text.FlxText;
|
2023-08-06 16:24:34 -04:00
|
|
|
import flixel.util.FlxTimer;
|
2023-11-07 04:04:22 -05:00
|
|
|
import funkin.util.MathUtil;
|
|
|
|
import funkin.graphics.shaders.Grayscale;
|
2021-10-21 17:40:53 -04:00
|
|
|
|
|
|
|
class SongMenuItem extends FlxSpriteGroup
|
|
|
|
{
|
2023-09-19 19:10:30 -04:00
|
|
|
public var capsule:FlxSprite;
|
|
|
|
|
2023-08-08 14:35:07 -04:00
|
|
|
var pixelIcon:FlxSprite;
|
2023-01-22 22:25:45 -05:00
|
|
|
|
2023-10-16 20:12:40 -04:00
|
|
|
/**
|
|
|
|
* Modify this by calling `init()`
|
|
|
|
* If `null`, assume this SongMenuItem is for the "Random Song" option.
|
|
|
|
*/
|
|
|
|
public var songData(default, null):Null<FreeplaySongData> = null;
|
2023-01-22 22:25:45 -05:00
|
|
|
|
2023-10-16 20:12:40 -04:00
|
|
|
public var selected(default, set):Bool;
|
2023-01-22 22:25:45 -05:00
|
|
|
|
2023-08-14 02:49:18 -04:00
|
|
|
public var songText:CapsuleText;
|
2023-01-22 22:25:45 -05:00
|
|
|
public var favIcon:FlxSprite;
|
2023-08-11 05:20:25 -04:00
|
|
|
public var ranking:FlxSprite;
|
|
|
|
|
|
|
|
var ranks:Array<String> = ["fail", "average", "great", "excellent", "perfect"];
|
2023-01-22 22:25:45 -05:00
|
|
|
|
|
|
|
public var targetPos:FlxPoint = new FlxPoint();
|
|
|
|
public var doLerp:Bool = false;
|
|
|
|
public var doJumpIn:Bool = false;
|
|
|
|
|
2023-03-15 21:05:15 -04:00
|
|
|
public var doJumpOut:Bool = false;
|
|
|
|
|
2023-08-06 16:24:34 -04:00
|
|
|
public var onConfirm:Void->Void;
|
2024-01-11 00:30:00 -05:00
|
|
|
public var grayscaleShader:Grayscale;
|
2023-08-06 16:24:34 -04:00
|
|
|
|
2023-08-28 14:52:03 -04:00
|
|
|
public var hsvShader(default, set):HSVShader;
|
|
|
|
|
2024-01-11 00:30:00 -05:00
|
|
|
var diffRatingSprite:FlxSprite;
|
|
|
|
|
2023-10-16 20:12:40 -04:00
|
|
|
public function new(x:Float, y:Float)
|
2023-01-22 22:25:45 -05:00
|
|
|
{
|
|
|
|
super(x, y);
|
|
|
|
|
|
|
|
capsule = new FlxSprite();
|
|
|
|
capsule.frames = Paths.getSparrowAtlas('freeplay/freeplayCapsule');
|
|
|
|
capsule.animation.addByPrefix('selected', 'mp3 capsule w backing0', 24);
|
|
|
|
capsule.animation.addByPrefix('unselected', 'mp3 capsule w backing NOT SELECTED', 24);
|
|
|
|
// capsule.animation
|
|
|
|
add(capsule);
|
|
|
|
|
2023-08-12 00:31:43 -04:00
|
|
|
// doesn't get added, simply is here to help with visibility of things for the pop in!
|
|
|
|
grpHide = new FlxGroup();
|
|
|
|
|
2023-08-11 05:20:25 -04:00
|
|
|
var rank:String = FlxG.random.getObject(ranks);
|
|
|
|
|
2023-08-13 23:22:24 -04:00
|
|
|
ranking = new FlxSprite(capsule.width * 0.84, 30);
|
2023-08-11 05:20:25 -04:00
|
|
|
ranking.loadGraphic(Paths.image("freeplay/ranks/" + rank));
|
|
|
|
ranking.scale.x = ranking.scale.y = realScaled;
|
|
|
|
ranking.alpha = 0.75;
|
2023-08-13 23:22:24 -04:00
|
|
|
ranking.origin.set(capsule.origin.x - ranking.x, capsule.origin.y - ranking.y);
|
2023-08-11 05:20:25 -04:00
|
|
|
add(ranking);
|
2023-08-12 00:31:43 -04:00
|
|
|
grpHide.add(ranking);
|
2023-08-11 05:20:25 -04:00
|
|
|
|
|
|
|
switch (rank)
|
|
|
|
{
|
|
|
|
case "perfect":
|
|
|
|
ranking.x -= 10;
|
|
|
|
}
|
|
|
|
|
2024-01-11 00:30:00 -05:00
|
|
|
grayscaleShader = new Grayscale(1);
|
|
|
|
|
|
|
|
diffRatingSprite = new FlxSprite(145, 90).loadGraphic(Paths.image("freeplay/diffRatings/diff00"));
|
|
|
|
diffRatingSprite.shader = grayscaleShader;
|
|
|
|
diffRatingSprite.visible = false;
|
|
|
|
add(diffRatingSprite);
|
|
|
|
diffRatingSprite.origin.set(capsule.origin.x - diffRatingSprite.x, capsule.origin.y - diffRatingSprite.y);
|
|
|
|
grpHide.add(diffRatingSprite);
|
|
|
|
|
2023-10-16 20:12:40 -04:00
|
|
|
songText = new CapsuleText(capsule.width * 0.26, 45, 'Random', Std.int(40 * realScaled));
|
2023-01-22 22:25:45 -05:00
|
|
|
add(songText);
|
2023-08-12 00:31:43 -04:00
|
|
|
grpHide.add(songText);
|
2023-01-22 22:25:45 -05:00
|
|
|
|
2024-01-11 00:30:00 -05:00
|
|
|
// TODO: Use value from metadata instead of random.
|
|
|
|
updateDifficultyRating(FlxG.random.int(0, 15));
|
|
|
|
|
2024-01-10 20:58:29 -05:00
|
|
|
pixelIcon = new FlxSprite(160, 35);
|
2024-01-11 00:30:00 -05:00
|
|
|
|
2023-08-08 14:35:07 -04:00
|
|
|
pixelIcon.makeGraphic(32, 32, 0x00000000);
|
|
|
|
pixelIcon.antialiasing = false;
|
2023-08-09 02:47:22 -04:00
|
|
|
pixelIcon.active = false;
|
2023-08-08 14:35:07 -04:00
|
|
|
add(pixelIcon);
|
2023-08-12 00:31:43 -04:00
|
|
|
grpHide.add(pixelIcon);
|
2023-08-08 14:35:07 -04:00
|
|
|
|
2023-08-04 18:09:40 -04:00
|
|
|
favIcon = new FlxSprite(400, 40);
|
2023-01-22 22:25:45 -05:00
|
|
|
favIcon.frames = Paths.getSparrowAtlas('freeplay/favHeart');
|
|
|
|
favIcon.animation.addByPrefix('fav', "favorite heart", 24, false);
|
|
|
|
favIcon.animation.play('fav');
|
2023-08-13 23:22:24 -04:00
|
|
|
favIcon.setGraphicSize(50, 50);
|
|
|
|
favIcon.visible = false;
|
2023-01-22 22:25:45 -05:00
|
|
|
add(favIcon);
|
2023-08-13 23:22:24 -04:00
|
|
|
// grpHide.add(favIcon);
|
2023-08-12 00:31:43 -04:00
|
|
|
|
|
|
|
setVisibleGrp(false);
|
2023-01-22 22:25:45 -05:00
|
|
|
}
|
|
|
|
|
2024-01-11 00:30:00 -05:00
|
|
|
function updateDifficultyRating(newRating:Int)
|
|
|
|
{
|
|
|
|
var ratingPadded:String = newRating < 10 ? '0$newRating' : '$newRating';
|
|
|
|
diffRatingSprite.loadGraphic(Paths.image('freeplay/diffRatings/diff${ratingPadded}'));
|
|
|
|
}
|
|
|
|
|
2023-08-28 14:52:03 -04:00
|
|
|
function set_hsvShader(value:HSVShader):HSVShader
|
|
|
|
{
|
|
|
|
this.hsvShader = value;
|
|
|
|
capsule.shader = hsvShader;
|
|
|
|
songText.shader = hsvShader;
|
|
|
|
|
|
|
|
return value;
|
|
|
|
}
|
|
|
|
|
2023-08-12 00:42:17 -04:00
|
|
|
function textAppear()
|
|
|
|
{
|
|
|
|
songText.scale.x = 1.7;
|
|
|
|
songText.scale.y = 0.2;
|
|
|
|
|
|
|
|
new FlxTimer().start(1 / 24, function(_) {
|
|
|
|
songText.scale.x = 0.4;
|
|
|
|
songText.scale.y = 1.4;
|
|
|
|
});
|
|
|
|
|
|
|
|
new FlxTimer().start(2 / 24, function(_) {
|
|
|
|
songText.scale.x = songText.scale.y = 1;
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2023-08-12 00:31:43 -04:00
|
|
|
function setVisibleGrp(value:Bool)
|
|
|
|
{
|
|
|
|
for (spr in grpHide.members)
|
|
|
|
{
|
|
|
|
spr.visible = value;
|
|
|
|
}
|
2023-08-12 00:42:17 -04:00
|
|
|
|
|
|
|
if (value) textAppear();
|
2023-09-19 19:10:30 -04:00
|
|
|
|
2023-10-16 20:12:40 -04:00
|
|
|
updateSelected();
|
2023-08-12 00:31:43 -04:00
|
|
|
}
|
|
|
|
|
2024-01-11 00:30:00 -05:00
|
|
|
public function init(?x:Float, ?y:Float, songData:Null<FreeplaySongData>)
|
2023-08-09 02:47:22 -04:00
|
|
|
{
|
2024-01-11 00:30:00 -05:00
|
|
|
if (x != null) this.x = x;
|
|
|
|
if (y != null) this.y = y;
|
2023-10-16 20:12:40 -04:00
|
|
|
this.songData = songData;
|
|
|
|
|
|
|
|
// Update capsule text.
|
|
|
|
songText.text = songData?.songName ?? 'Random';
|
|
|
|
// Update capsule character.
|
|
|
|
if (songData?.songCharacter != null) setCharacter(songData.songCharacter);
|
2024-01-11 00:30:00 -05:00
|
|
|
updateDifficultyRating(songData?.songRating ?? 0);
|
2023-10-16 20:12:40 -04:00
|
|
|
// Update opacity, offsets, etc.
|
|
|
|
updateSelected();
|
2023-08-09 02:47:22 -04:00
|
|
|
}
|
|
|
|
|
2023-08-08 14:35:07 -04:00
|
|
|
/**
|
2023-10-09 20:04:21 -04:00
|
|
|
* Set the character displayed next to this song in the freeplay menu.
|
|
|
|
* @param char The character ID used by this song.
|
|
|
|
* If the character has no freeplay icon, a warning will be thrown and nothing will display.
|
2023-08-08 14:35:07 -04:00
|
|
|
*/
|
|
|
|
public function setCharacter(char:String)
|
|
|
|
{
|
|
|
|
var charPath:String = "freeplay/icons/";
|
|
|
|
|
2023-09-28 20:29:19 -04:00
|
|
|
trace(char);
|
|
|
|
|
2023-10-16 20:12:40 -04:00
|
|
|
// TODO: Put this in the character metadata where it belongs.
|
2023-12-11 00:19:33 -05:00
|
|
|
// TODO: Also, can use CharacterDataParser.getCharPixelIconAsset()
|
2023-08-08 14:35:07 -04:00
|
|
|
switch (char)
|
|
|
|
{
|
|
|
|
case "monster-christmas":
|
|
|
|
charPath += "monsterpixel";
|
2023-09-28 20:29:19 -04:00
|
|
|
case "mom-car":
|
2023-08-08 14:35:07 -04:00
|
|
|
charPath += "mommypixel";
|
|
|
|
case "dad":
|
|
|
|
charPath += "daddypixel";
|
2023-09-28 20:29:19 -04:00
|
|
|
case "darnell-blazin":
|
|
|
|
charPath += "darnellpixel";
|
|
|
|
case "senpai-angry":
|
|
|
|
charPath += "senpaipixel";
|
2023-08-08 14:35:07 -04:00
|
|
|
default:
|
|
|
|
charPath += char + "pixel";
|
|
|
|
}
|
|
|
|
|
2023-10-09 20:04:21 -04:00
|
|
|
if (!openfl.utils.Assets.exists(Paths.image(charPath)))
|
|
|
|
{
|
|
|
|
trace('[WARN] Character ${char} has no freeplay icon.');
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2023-08-08 14:35:07 -04:00
|
|
|
pixelIcon.loadGraphic(Paths.image(charPath));
|
2023-09-19 01:31:54 -04:00
|
|
|
pixelIcon.scale.x = pixelIcon.scale.y = 2;
|
2024-01-11 00:52:42 -05:00
|
|
|
|
|
|
|
switch (char)
|
|
|
|
{
|
|
|
|
case "parents-christmas":
|
|
|
|
pixelIcon.origin.x = 140;
|
|
|
|
default:
|
|
|
|
pixelIcon.origin.x = 100;
|
|
|
|
}
|
2023-08-13 23:22:24 -04:00
|
|
|
// pixelIcon.origin.x = capsule.origin.x;
|
|
|
|
// pixelIcon.offset.x -= pixelIcon.origin.x;
|
2023-08-08 14:35:07 -04:00
|
|
|
}
|
|
|
|
|
2023-03-15 21:05:15 -04:00
|
|
|
var frameInTicker:Float = 0;
|
|
|
|
var frameInTypeBeat:Int = 0;
|
|
|
|
|
|
|
|
var frameOutTicker:Float = 0;
|
|
|
|
var frameOutTypeBeat:Int = 0;
|
2023-01-22 22:25:45 -05:00
|
|
|
|
|
|
|
var xFrames:Array<Float> = [1.7, 1.8, 0.85, 0.85, 0.97, 0.97, 1];
|
|
|
|
var xPosLerpLol:Array<Float> = [0.9, 0.4, 0.16, 0.16, 0.22, 0.22, 0.245]; // NUMBERS ARE JANK CUZ THE SCALING OR WHATEVER
|
2023-03-15 21:05:15 -04:00
|
|
|
var xPosOutLerpLol:Array<Float> = [0.245, 0.75, 0.98, 0.98, 1.2]; // NUMBERS ARE JANK CUZ THE SCALING OR WHATEVER
|
2023-01-22 22:25:45 -05:00
|
|
|
|
2023-08-13 22:12:08 -04:00
|
|
|
public var realScaled:Float = 0.8;
|
2023-08-04 16:19:48 -04:00
|
|
|
|
2023-08-06 16:24:34 -04:00
|
|
|
public function initJumpIn(maxTimer:Float, ?force:Bool):Void
|
|
|
|
{
|
2023-08-09 02:47:22 -04:00
|
|
|
frameInTypeBeat = 0;
|
|
|
|
|
2023-08-06 16:24:34 -04:00
|
|
|
new FlxTimer().start((1 / 24) * maxTimer, function(doShit) {
|
|
|
|
doJumpIn = true;
|
|
|
|
});
|
|
|
|
|
|
|
|
new FlxTimer().start((0.09 * maxTimer) + 0.85, function(lerpTmr) {
|
|
|
|
doLerp = true;
|
|
|
|
});
|
|
|
|
|
2023-08-09 02:47:22 -04:00
|
|
|
if (force)
|
2023-08-06 16:24:34 -04:00
|
|
|
{
|
2023-09-19 19:10:30 -04:00
|
|
|
visible = true;
|
|
|
|
capsule.alpha = 1;
|
2023-08-12 00:31:43 -04:00
|
|
|
setVisibleGrp(true);
|
2023-08-06 16:24:34 -04:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2023-08-09 02:47:22 -04:00
|
|
|
new FlxTimer().start((xFrames.length / 24) * 2.5, function(_) {
|
2023-09-19 19:10:30 -04:00
|
|
|
visible = true;
|
|
|
|
capsule.alpha = 1;
|
2023-08-12 00:31:43 -04:00
|
|
|
setVisibleGrp(true);
|
2023-08-09 02:47:22 -04:00
|
|
|
});
|
2023-08-06 16:24:34 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-08-12 00:31:43 -04:00
|
|
|
var grpHide:FlxGroup;
|
|
|
|
|
2023-08-09 02:47:22 -04:00
|
|
|
public function forcePosition()
|
|
|
|
{
|
2023-09-19 19:10:30 -04:00
|
|
|
visible = true;
|
|
|
|
capsule.alpha = 1;
|
2023-10-16 20:12:40 -04:00
|
|
|
updateSelected();
|
2023-08-09 02:47:22 -04:00
|
|
|
doLerp = true;
|
|
|
|
doJumpIn = false;
|
|
|
|
doJumpOut = false;
|
|
|
|
|
|
|
|
frameInTypeBeat = xFrames.length;
|
|
|
|
frameOutTypeBeat = 0;
|
|
|
|
|
|
|
|
capsule.scale.x = xFrames[frameInTypeBeat - 1];
|
|
|
|
capsule.scale.y = 1 / xFrames[frameInTypeBeat - 1];
|
|
|
|
// x = FlxG.width * xPosLerpLol[Std.int(Math.min(frameInTypeBeat - 1, xPosLerpLol.length - 1))];
|
|
|
|
|
|
|
|
x = targetPos.x;
|
|
|
|
y = targetPos.y;
|
|
|
|
|
|
|
|
capsule.scale.x *= realScaled;
|
|
|
|
capsule.scale.y *= realScaled;
|
|
|
|
|
2023-08-12 00:31:43 -04:00
|
|
|
setVisibleGrp(true);
|
2023-08-09 02:47:22 -04:00
|
|
|
}
|
|
|
|
|
2023-01-22 22:25:45 -05:00
|
|
|
override function update(elapsed:Float)
|
|
|
|
{
|
|
|
|
if (doJumpIn)
|
|
|
|
{
|
2023-03-15 21:05:15 -04:00
|
|
|
frameInTicker += elapsed;
|
|
|
|
|
|
|
|
if (frameInTicker >= 1 / 24 && frameInTypeBeat < xFrames.length)
|
|
|
|
{
|
|
|
|
frameInTicker = 0;
|
|
|
|
|
2023-08-08 14:35:07 -04:00
|
|
|
capsule.scale.x = xFrames[frameInTypeBeat];
|
|
|
|
capsule.scale.y = 1 / xFrames[frameInTypeBeat];
|
2023-03-15 21:05:15 -04:00
|
|
|
x = FlxG.width * xPosLerpLol[Std.int(Math.min(frameInTypeBeat, xPosLerpLol.length - 1))];
|
|
|
|
|
2023-08-08 14:35:07 -04:00
|
|
|
capsule.scale.x *= realScaled;
|
|
|
|
capsule.scale.y *= realScaled;
|
2023-08-04 16:19:48 -04:00
|
|
|
|
2023-03-15 21:05:15 -04:00
|
|
|
frameInTypeBeat += 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (doJumpOut)
|
|
|
|
{
|
|
|
|
frameOutTicker += elapsed;
|
2023-01-22 22:25:45 -05:00
|
|
|
|
2023-03-15 21:05:15 -04:00
|
|
|
if (frameOutTicker >= 1 / 24 && frameOutTypeBeat < xFrames.length)
|
2023-01-22 22:25:45 -05:00
|
|
|
{
|
2023-03-15 21:05:15 -04:00
|
|
|
frameOutTicker = 0;
|
2023-01-22 22:25:45 -05:00
|
|
|
|
2023-08-08 14:35:07 -04:00
|
|
|
capsule.scale.x = xFrames[frameOutTypeBeat];
|
|
|
|
capsule.scale.y = 1 / xFrames[frameOutTypeBeat];
|
2023-03-15 21:05:15 -04:00
|
|
|
x = FlxG.width * xPosOutLerpLol[Std.int(Math.min(frameOutTypeBeat, xPosOutLerpLol.length - 1))];
|
2023-01-22 22:25:45 -05:00
|
|
|
|
2023-08-08 14:35:07 -04:00
|
|
|
capsule.scale.x *= realScaled;
|
|
|
|
capsule.scale.y *= realScaled;
|
2023-08-04 16:19:48 -04:00
|
|
|
|
2023-03-15 21:05:15 -04:00
|
|
|
frameOutTypeBeat += 1;
|
2023-01-22 22:25:45 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (doLerp)
|
|
|
|
{
|
2023-11-07 04:04:22 -05:00
|
|
|
x = MathUtil.coolLerp(x, targetPos.x, 0.3);
|
|
|
|
y = MathUtil.coolLerp(y, targetPos.y, 0.4);
|
2023-01-22 22:25:45 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
super.update(elapsed);
|
|
|
|
}
|
|
|
|
|
2023-08-04 17:10:27 -04:00
|
|
|
public function intendedY(index:Int):Float
|
|
|
|
{
|
2023-08-04 18:09:40 -04:00
|
|
|
return (index * ((height * realScaled) + 10)) + 120;
|
2023-08-04 17:10:27 -04:00
|
|
|
}
|
|
|
|
|
2023-10-16 20:12:40 -04:00
|
|
|
function set_selected(value:Bool):Bool
|
2023-10-09 20:04:21 -04:00
|
|
|
{
|
2023-10-16 20:12:40 -04:00
|
|
|
// cute one liners, lol!
|
|
|
|
selected = value;
|
|
|
|
updateSelected();
|
|
|
|
return selected;
|
2023-10-09 20:04:21 -04:00
|
|
|
}
|
|
|
|
|
2023-10-16 20:12:40 -04:00
|
|
|
function updateSelected():Void
|
2023-01-22 22:25:45 -05:00
|
|
|
{
|
2024-01-11 00:30:00 -05:00
|
|
|
grayscaleShader.setAmount(this.selected ? 0 : 0.8);
|
2023-10-16 20:12:40 -04:00
|
|
|
songText.alpha = this.selected ? 1 : 0.6;
|
|
|
|
songText.blurredText.visible = this.selected ? true : false;
|
|
|
|
capsule.offset.x = this.selected ? 0 : -5;
|
|
|
|
capsule.animation.play(this.selected ? "selected" : "unselected");
|
|
|
|
ranking.alpha = this.selected ? 1 : 0.7;
|
|
|
|
ranking.color = this.selected ? 0xFFFFFFFF : 0xFFAAAAAA;
|
2023-01-22 22:25:45 -05:00
|
|
|
}
|
2021-10-21 17:40:53 -04:00
|
|
|
}
|