mirror of
https://github.com/ninjamuffin99/Funkin.git
synced 2025-03-21 09:29:41 +00:00
assets submod
This commit is contained in:
commit
5c44e61ce1
18
CHANGELOG.md
18
CHANGELOG.md
|
@ -4,14 +4,28 @@ All notable changes 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).
|
||||
|
||||
## [0.5.0] - 2024-08-??
|
||||
## [0.5.0] - 2024-09-12
|
||||
### Added
|
||||
- Added a new Character Select screen to switch between playable characters in Freeplay
|
||||
- Modding isn't 100% there but we're working on it!
|
||||
- Added Pico as a playable character! Unlock him by completing Weekend 1 (if you haven't already done that)
|
||||
- The songs from Weekend 1 have moved; you must now switch to Pico in Freeplay to access them
|
||||
- Added ## new Pico remixes! Access them by selecting Pico from in the Character Select screen
|
||||
- Added 10 new Pico remixes! Access them by selecting Pico from in the Character Select screen
|
||||
- Bopeebo (Pico Mix)
|
||||
- Fresh (Pico Mix)
|
||||
- DadBattle (Pico Mix)
|
||||
- Spookeez (Pico Mix)
|
||||
- South (Pico Mix)
|
||||
- Philly Nice (Pico Mix)
|
||||
- Blammed (Pico Mix)
|
||||
- Eggnog (Pico Mix)
|
||||
- Ugh (Pico Mix)
|
||||
- Guns (Pico Mix)
|
||||
- Added 1 new Boyfriend remix! Access it by selecting Pico from in the Character Select screen
|
||||
- Darnell (BF Mix)
|
||||
- Added 2 new Erect remixes! Access them by switching difficulty on the song
|
||||
- Cocoa Erect
|
||||
- Ugh Erect
|
||||
- Implemented support for a new Instrumental Selector in Freeplay
|
||||
- Beating a Pico remix lets you use that instrumental when playing as Boyfriend
|
||||
- Added the first batch of Erect Stages! These graphical overhauls of the original stages will be used when playing Erect remixes and Pico remixes
|
||||
|
|
2
assets
2
assets
|
@ -1 +1 @@
|
|||
Subproject commit e80d92ac484fe432ab34d45b22158f5d565afb0f
|
||||
Subproject commit 5e5e01fbed1e2d5979f983e195b9c0dfbf852b91
|
38
source/funkin/Assets.hx
Normal file
38
source/funkin/Assets.hx
Normal file
|
@ -0,0 +1,38 @@
|
|||
package funkin;
|
||||
|
||||
/**
|
||||
* A wrapper around `openfl.utils.Assets` which disallows access to the harmful functions.
|
||||
* Later we'll add Funkin-specific caching to this.
|
||||
*/
|
||||
class Assets
|
||||
{
|
||||
public static function getText(path:String):String
|
||||
{
|
||||
return openfl.utils.Assets.getText(path);
|
||||
}
|
||||
|
||||
public static function getMusic(path:String):openfl.media.Sound
|
||||
{
|
||||
return openfl.utils.Assets.getMusic(path);
|
||||
}
|
||||
|
||||
public static function getBitmapData(path:String):openfl.display.BitmapData
|
||||
{
|
||||
return openfl.utils.Assets.getBitmapData(path);
|
||||
}
|
||||
|
||||
public static function getBytes(path:String):haxe.io.Bytes
|
||||
{
|
||||
return openfl.utils.Assets.getBytes(path);
|
||||
}
|
||||
|
||||
public static function exists(path:String, ?type:openfl.utils.AssetType):Bool
|
||||
{
|
||||
return openfl.utils.Assets.exists(path, type);
|
||||
}
|
||||
|
||||
public static function list(type:openfl.utils.AssetType):Array<String>
|
||||
{
|
||||
return openfl.utils.Assets.list(type);
|
||||
}
|
||||
}
|
|
@ -19,6 +19,7 @@ import funkin.play.PlayStatePlaylist;
|
|||
import openfl.display.BitmapData;
|
||||
import funkin.data.story.level.LevelRegistry;
|
||||
import funkin.data.notestyle.NoteStyleRegistry;
|
||||
import funkin.data.freeplay.style.FreeplayStyleRegistry;
|
||||
import funkin.data.event.SongEventRegistry;
|
||||
import funkin.data.stage.StageRegistry;
|
||||
import funkin.data.dialogue.conversation.ConversationRegistry;
|
||||
|
@ -170,6 +171,7 @@ class InitState extends FlxState
|
|||
ConversationRegistry.instance.loadEntries();
|
||||
DialogueBoxRegistry.instance.loadEntries();
|
||||
SpeakerRegistry.instance.loadEntries();
|
||||
FreeplayStyleRegistry.instance.loadEntries();
|
||||
AlbumRegistry.instance.loadEntries();
|
||||
StageRegistry.instance.loadEntries();
|
||||
|
||||
|
|
|
@ -31,6 +31,13 @@ class PlayerData
|
|||
@:default(false)
|
||||
public var showUnownedChars:Bool = false;
|
||||
|
||||
/**
|
||||
* Which freeplay style to use for this character.
|
||||
*/
|
||||
@:optional
|
||||
@:default("bf")
|
||||
public var freeplayStyle:String = Constants.DEFAULT_FREEPLAY_STYLE;
|
||||
|
||||
/**
|
||||
* Data for displaying this character in the Freeplay menu.
|
||||
* If null, display no DJ.
|
||||
|
@ -105,6 +112,9 @@ class PlayerFreeplayDJData
|
|||
@:jignored
|
||||
var prefixToOffsetsMap:Map<String, Array<Float>>;
|
||||
|
||||
@:optional
|
||||
var charSelect:Null<PlayerFreeplayDJCharSelectData>;
|
||||
|
||||
@:optional
|
||||
var cartoon:Null<PlayerFreeplayDJCartoonData>;
|
||||
|
||||
|
@ -237,6 +247,11 @@ class PlayerFreeplayDJData
|
|||
{
|
||||
return fistPump?.loopBadEndFrame ?? 0;
|
||||
}
|
||||
|
||||
public function getCharSelectTransitionDelay():Float
|
||||
{
|
||||
return charSelect?.transitionDelay ?? 0.25;
|
||||
}
|
||||
}
|
||||
|
||||
class PlayerCharSelectData
|
||||
|
@ -253,6 +268,8 @@ class PlayerCharSelectData
|
|||
|
||||
typedef PlayerResultsData =
|
||||
{
|
||||
var music:PlayerResultsMusicData;
|
||||
|
||||
var perfect:Array<PlayerResultsAnimationData>;
|
||||
var excellent:Array<PlayerResultsAnimationData>;
|
||||
var great:Array<PlayerResultsAnimationData>;
|
||||
|
@ -260,6 +277,27 @@ typedef PlayerResultsData =
|
|||
var loss:Array<PlayerResultsAnimationData>;
|
||||
};
|
||||
|
||||
typedef PlayerResultsMusicData =
|
||||
{
|
||||
@:optional
|
||||
var PERFECT_GOLD:String;
|
||||
|
||||
@:optional
|
||||
var PERFECT:String;
|
||||
|
||||
@:optional
|
||||
var EXCELLENT:String;
|
||||
|
||||
@:optional
|
||||
var GREAT:String;
|
||||
|
||||
@:optional
|
||||
var GOOD:String;
|
||||
|
||||
@:optional
|
||||
var SHIT:String;
|
||||
}
|
||||
|
||||
typedef PlayerResultsAnimationData =
|
||||
{
|
||||
/**
|
||||
|
@ -300,6 +338,11 @@ typedef PlayerResultsAnimationData =
|
|||
var loopFrameLabel:Null<String>;
|
||||
};
|
||||
|
||||
typedef PlayerFreeplayDJCharSelectData =
|
||||
{
|
||||
var transitionDelay:Float;
|
||||
}
|
||||
|
||||
typedef PlayerFreeplayDJCartoonData =
|
||||
{
|
||||
var soundClickFrame:Int;
|
||||
|
|
9
source/funkin/data/freeplay/style/CHANGELOG.md
Normal file
9
source/funkin/data/freeplay/style/CHANGELOG.md
Normal file
|
@ -0,0 +1,9 @@
|
|||
# Freeplay Style Data Schema Changelog
|
||||
|
||||
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).
|
||||
|
||||
## [1.0.0]
|
||||
Initial release.
|
48
source/funkin/data/freeplay/style/FreeplayStyleData.hx
Normal file
48
source/funkin/data/freeplay/style/FreeplayStyleData.hx
Normal file
|
@ -0,0 +1,48 @@
|
|||
package funkin.data.freeplay.style;
|
||||
|
||||
import funkin.data.animation.AnimationData;
|
||||
|
||||
/**
|
||||
* A type definition for the data for an album of songs.
|
||||
* It includes things like what graphics to display in Freeplay.
|
||||
* @see https://lib.haxe.org/p/json2object/
|
||||
*/
|
||||
typedef FreeplayStyleData =
|
||||
{
|
||||
/**
|
||||
* Semantic version for style data.
|
||||
*/
|
||||
public var version:String;
|
||||
|
||||
/**
|
||||
* Asset key for the background image.
|
||||
*/
|
||||
public var bgAsset:String;
|
||||
|
||||
/**
|
||||
* Asset key for the difficulty selector image.
|
||||
*/
|
||||
public var selectorAsset:String;
|
||||
|
||||
/**
|
||||
* Asset key for the numbers shown at the top right of the screen.
|
||||
*/
|
||||
public var numbersAsset:String;
|
||||
|
||||
/**
|
||||
* Asset key for the freeplay capsules.
|
||||
*/
|
||||
public var capsuleAsset:String;
|
||||
|
||||
/**
|
||||
* Color data for the capsule text outline.
|
||||
* the order of this array goes as follows: [DESELECTED, SELECTED]
|
||||
*/
|
||||
public var capsuleTextColors:Array<String>;
|
||||
|
||||
/**
|
||||
* Delay time after confirming a song selection, before entering PlayState.
|
||||
* Useful for letting longer animations play out.
|
||||
*/
|
||||
public var startDelay:Float;
|
||||
}
|
84
source/funkin/data/freeplay/style/FreeplayStyleRegistry.hx
Normal file
84
source/funkin/data/freeplay/style/FreeplayStyleRegistry.hx
Normal file
|
@ -0,0 +1,84 @@
|
|||
package funkin.data.freeplay.style;
|
||||
|
||||
import funkin.ui.freeplay.FreeplayStyle;
|
||||
import funkin.data.freeplay.style.FreeplayStyleData;
|
||||
import funkin.ui.freeplay.ScriptedFreeplayStyle;
|
||||
|
||||
class FreeplayStyleRegistry extends BaseRegistry<FreeplayStyle, FreeplayStyleData>
|
||||
{
|
||||
/**
|
||||
* The current version string for the style data format.
|
||||
* Handle breaking changes by incrementing this value
|
||||
* and adding migration to the `migrateStyleData()` function.
|
||||
*/
|
||||
public static final FREEPLAYSTYLE_DATA_VERSION:thx.semver.Version = '1.0.0';
|
||||
|
||||
public static final FREEPLAYSTYLE_DATA_VERSION_RULE:thx.semver.VersionRule = '1.0.x';
|
||||
|
||||
public static final instance:FreeplayStyleRegistry = new FreeplayStyleRegistry();
|
||||
|
||||
public function new()
|
||||
{
|
||||
super('FREEPLAYSTYLE', 'ui/freeplay/styles', FREEPLAYSTYLE_DATA_VERSION_RULE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Read, parse, and validate the JSON data and produce the corresponding data object.
|
||||
* @param id The ID of the entry to load.
|
||||
* @return The parsed data object.
|
||||
*/
|
||||
public function parseEntryData(id:String):Null<FreeplayStyleData>
|
||||
{
|
||||
// JsonParser does not take type parameters,
|
||||
// otherwise this function would be in BaseRegistry.
|
||||
var parser:json2object.JsonParser<FreeplayStyleData> = new json2object.JsonParser<FreeplayStyleData>();
|
||||
parser.ignoreUnknownVariables = false;
|
||||
|
||||
switch (loadEntryFile(id))
|
||||
{
|
||||
case {fileName: fileName, contents: contents}:
|
||||
parser.fromJson(contents, fileName);
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
|
||||
if (parser.errors.length > 0)
|
||||
{
|
||||
printErrors(parser.errors, id);
|
||||
return null;
|
||||
}
|
||||
return parser.value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse and validate the JSON data and produce the corresponding data object.
|
||||
*
|
||||
* NOTE: Must be implemented on the implementation class.
|
||||
* @param contents The JSON as a string.
|
||||
* @param fileName An optional file name for error reporting.
|
||||
* @return The parsed data object.
|
||||
*/
|
||||
public function parseEntryDataRaw(contents:String, ?fileName:String):Null<FreeplayStyleData>
|
||||
{
|
||||
var parser:json2object.JsonParser<FreeplayStyleData> = new json2object.JsonParser<FreeplayStyleData>();
|
||||
parser.ignoreUnknownVariables = false;
|
||||
parser.fromJson(contents, fileName);
|
||||
|
||||
if (parser.errors.length > 0)
|
||||
{
|
||||
printErrors(parser.errors, fileName);
|
||||
return null;
|
||||
}
|
||||
return parser.value;
|
||||
}
|
||||
|
||||
function createScriptedEntry(clsName:String):FreeplayStyle
|
||||
{
|
||||
return ScriptedFreeplayStyle.init(clsName, 'unknown');
|
||||
}
|
||||
|
||||
function getScriptedClassNames():Array<String>
|
||||
{
|
||||
return ScriptedFreeplayStyle.listScriptClasses();
|
||||
}
|
||||
}
|
|
@ -11,6 +11,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||
- If the value isn't present, it will use the `playData.characters.opponent`, but if it is present, it will be used (even if it's empty, in which case no vocals will be used for the opponent)
|
||||
- Added `playData.characters.playerVocals` to specify which vocal track(s) to play for the player.
|
||||
- If the value isn't present, it will use the `playData.characters.player`, but if it is present, it will be used (even if it's empty, in which case no vocals will be used for the player)
|
||||
- Added `offsets.altVocals` field to apply vocal offsets when alternate instrumentals are used.
|
||||
|
||||
|
||||
## [2.2.3]
|
||||
### Added
|
||||
|
|
|
@ -257,18 +257,27 @@ class SongOffsets implements ICloneable<SongOffsets>
|
|||
public var altInstrumentals:Map<String, Float>;
|
||||
|
||||
/**
|
||||
* The offset, in milliseconds, to apply to the song's vocals, relative to the chart.
|
||||
* The offset, in milliseconds, to apply to the song's vocals, relative to the song's base instrumental.
|
||||
* These are applied ON TOP OF the instrumental offset.
|
||||
*/
|
||||
@:optional
|
||||
@:default([])
|
||||
public var vocals:Map<String, Float>;
|
||||
|
||||
public function new(instrumental:Float = 0.0, ?altInstrumentals:Map<String, Float>, ?vocals:Map<String, Float>)
|
||||
/**
|
||||
* The offset, in milliseconds, to apply to the songs vocals, relative to each alternate instrumental.
|
||||
* This is useful for the circumstance where, for example, an alt instrumental has a few seconds of lead in before the song starts.
|
||||
*/
|
||||
@:optional
|
||||
@:default([])
|
||||
public var altVocals:Map<String, Map<String, Float>>;
|
||||
|
||||
public function new(instrumental:Float = 0.0, ?altInstrumentals:Map<String, Float>, ?vocals:Map<String, Float>, ?altVocals:Map<String, Map<String, Float>>)
|
||||
{
|
||||
this.instrumental = instrumental;
|
||||
this.altInstrumentals = altInstrumentals == null ? new Map<String, Float>() : altInstrumentals;
|
||||
this.vocals = vocals == null ? new Map<String, Float>() : vocals;
|
||||
this.altVocals = altVocals == null ? new Map<String, Map<String, Float>>() : altVocals;
|
||||
}
|
||||
|
||||
public function getInstrumentalOffset(?instrumental:String):Float
|
||||
|
@ -293,11 +302,19 @@ class SongOffsets implements ICloneable<SongOffsets>
|
|||
return value;
|
||||
}
|
||||
|
||||
public function getVocalOffset(charId:String):Float
|
||||
public function getVocalOffset(charId:String, ?instrumental:String):Float
|
||||
{
|
||||
if (!this.vocals.exists(charId)) return 0.0;
|
||||
|
||||
return this.vocals.get(charId);
|
||||
if (instrumental == null)
|
||||
{
|
||||
if (!this.vocals.exists(charId)) return 0.0;
|
||||
return this.vocals.get(charId);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!this.altVocals.exists(instrumental)) return 0.0;
|
||||
if (!this.altVocals.get(instrumental).exists(charId)) return 0.0;
|
||||
return this.altVocals.get(instrumental).get(charId);
|
||||
}
|
||||
}
|
||||
|
||||
public function setVocalOffset(charId:String, value:Float):Float
|
||||
|
@ -320,7 +337,7 @@ class SongOffsets implements ICloneable<SongOffsets>
|
|||
*/
|
||||
public function toString():String
|
||||
{
|
||||
return 'SongOffsets(${this.instrumental}ms, ${this.altInstrumentals}, ${this.vocals})';
|
||||
return 'SongOffsets(${this.instrumental}ms, ${this.altInstrumentals}, ${this.vocals}, ${this.altVocals})';
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -20,7 +20,7 @@ class SongRegistry extends BaseRegistry<Song, SongMetadata>
|
|||
* Handle breaking changes by incrementing this value
|
||||
* and adding migration to the `migrateStageData()` function.
|
||||
*/
|
||||
public static final SONG_METADATA_VERSION:thx.semver.Version = "2.2.3";
|
||||
public static final SONG_METADATA_VERSION:thx.semver.Version = "2.2.4";
|
||||
|
||||
public static final SONG_METADATA_VERSION_RULE:thx.semver.VersionRule = "2.2.x";
|
||||
|
||||
|
|
|
@ -105,23 +105,6 @@ class FlxAtlasSprite extends FlxAnimate
|
|||
return this.currentAnimation;
|
||||
}
|
||||
|
||||
/**
|
||||
* `anim.finished` always returns false on looping animations,
|
||||
* but this function will return true if we are on the last frame of the looping animation.
|
||||
*/
|
||||
public function isLoopFinished():Bool
|
||||
{
|
||||
if (this.anim == null) return false;
|
||||
if (!this.anim.isPlaying) return false;
|
||||
|
||||
// Reverse animation finished.
|
||||
if (this.anim.reversed && this.anim.curFrame == 0) return true;
|
||||
// Forward animation finished.
|
||||
if (!this.anim.reversed && this.anim.curFrame >= (this.anim.length - 1)) return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
var _completeAnim:Bool = false;
|
||||
|
||||
var fr:FlxKeyFrame = null;
|
||||
|
@ -142,6 +125,8 @@ class FlxAtlasSprite extends FlxAnimate
|
|||
// Skip if not allowed to play animations.
|
||||
if ((!canPlayOtherAnims && !ignoreOther)) return;
|
||||
|
||||
if (anim == null) return;
|
||||
|
||||
if (id == null || id == '') id = this.currentAnimation;
|
||||
|
||||
if (this.currentAnimation == id && !restart)
|
||||
|
@ -189,10 +174,16 @@ class FlxAtlasSprite extends FlxAnimate
|
|||
// Move to the first frame of the animation.
|
||||
// goToFrameLabel(id);
|
||||
trace('Playing animation $id');
|
||||
this.anim.play(id, restart, false, startFrame);
|
||||
goToFrameLabel(id);
|
||||
|
||||
fr = anim.getFrameLabel(id);
|
||||
if (this.anim.symbolDictionary.exists(id) || (this.anim.getByName(id) != null))
|
||||
{
|
||||
this.anim.play(id, restart, false, startFrame);
|
||||
}
|
||||
// Only call goToFrameLabel if there is a frame label with that name. This prevents annoying warnings!
|
||||
if (getFrameLabelNames().indexOf(id) != -1)
|
||||
{
|
||||
goToFrameLabel(id);
|
||||
fr = anim.getFrameLabel(id);
|
||||
}
|
||||
|
||||
anim.curFrame += startFrame;
|
||||
this.currentAnimation = id;
|
||||
|
@ -218,6 +209,8 @@ class FlxAtlasSprite extends FlxAnimate
|
|||
*/
|
||||
public function isLoopComplete():Bool
|
||||
{
|
||||
if (this.anim == null) return false;
|
||||
if (!this.anim.isPlaying) return false;
|
||||
return (anim.reversed && anim.curFrame == 0 || !(anim.reversed) && (anim.curFrame) >= (anim.length - 1));
|
||||
}
|
||||
|
||||
|
@ -244,6 +237,18 @@ class FlxAtlasSprite extends FlxAnimate
|
|||
this.anim.goToFrameLabel(label);
|
||||
}
|
||||
|
||||
function getFrameLabelNames(?layer:haxe.extern.EitherType<Int, String> = null)
|
||||
{
|
||||
var labels = this.anim.getFrameLabels(layer);
|
||||
var array = [];
|
||||
for (label in labels)
|
||||
{
|
||||
array.push(label.name);
|
||||
}
|
||||
|
||||
return array;
|
||||
}
|
||||
|
||||
function getNextFrameLabel(label:String):String
|
||||
{
|
||||
return listAnimations()[(getLabelIndex(label) + 1) % listAnimations().length];
|
||||
|
@ -272,7 +277,7 @@ class FlxAtlasSprite extends FlxAnimate
|
|||
{
|
||||
onAnimationFrame.dispatch(currentAnimation, frame);
|
||||
|
||||
if (fr != null && frame > (fr.index + fr.duration - 1) || isLoopFinished())
|
||||
if (fr != null && frame > (fr.index + fr.duration - 1) || isLoopComplete())
|
||||
{
|
||||
anim.pause();
|
||||
_onAnimationComplete();
|
||||
|
|
|
@ -1,12 +1,25 @@
|
|||
package funkin.graphics.shaders;
|
||||
|
||||
import flixel.system.FlxAssets.FlxShader;
|
||||
import flixel.util.FlxColor;
|
||||
|
||||
class AngleMask extends FlxShader
|
||||
{
|
||||
public var extraColor(default, set):FlxColor = 0xFFFFFFFF;
|
||||
|
||||
function set_extraColor(value:FlxColor):FlxColor
|
||||
{
|
||||
extraTint.value = [value.redFloat, value.greenFloat, value.blueFloat];
|
||||
this.extraColor = value;
|
||||
|
||||
return this.extraColor;
|
||||
}
|
||||
|
||||
@:glFragmentSource('
|
||||
#pragma header
|
||||
|
||||
uniform vec3 extraTint;
|
||||
|
||||
uniform vec2 endPosition;
|
||||
vec2 hash22(vec2 p) {
|
||||
vec3 p3 = fract(vec3(p.xyx) * vec3(.1031, .1030, .0973));
|
||||
|
@ -69,6 +82,7 @@ class AngleMask extends FlxShader
|
|||
|
||||
void main() {
|
||||
vec4 col = antialias(openfl_TextureCoordv);
|
||||
col.xyz = col.xyz * extraTint.xyz;
|
||||
// col.xyz = gamma(col.xyz);
|
||||
gl_FragColor = col;
|
||||
}')
|
||||
|
@ -77,5 +91,6 @@ class AngleMask extends FlxShader
|
|||
super();
|
||||
|
||||
endPosition.value = [90, 100]; // 100 AS DEFAULT WORKS NICELY FOR FREEPLAY?
|
||||
extraTint.value = [1, 1, 1];
|
||||
}
|
||||
}
|
||||
|
|
51
source/funkin/graphics/shaders/BlueFade.hx
Normal file
51
source/funkin/graphics/shaders/BlueFade.hx
Normal file
|
@ -0,0 +1,51 @@
|
|||
package funkin.graphics.shaders;
|
||||
|
||||
import flixel.system.FlxAssets.FlxShader;
|
||||
import flixel.tweens.FlxEase;
|
||||
import flixel.tweens.FlxTween;
|
||||
|
||||
class BlueFade extends FlxShader
|
||||
{
|
||||
public var fadeVal(default, set):Float;
|
||||
|
||||
function set_fadeVal(val:Float):Float
|
||||
{
|
||||
fadeAmt.value = [val];
|
||||
fadeVal = val;
|
||||
// trace(fadeVal);
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
public function fade(startAmt:Float = 0, targetAmt:Float = 1, duration:Float, _options:TweenOptions):Void
|
||||
{
|
||||
fadeVal = startAmt;
|
||||
FlxTween.tween(this, {fadeVal: targetAmt}, duration, _options);
|
||||
}
|
||||
|
||||
@:glFragmentSource('
|
||||
#pragma header
|
||||
|
||||
// Value from (0, 1)
|
||||
uniform float fadeAmt;
|
||||
|
||||
// fade the image to blue as it fades to black
|
||||
|
||||
void main()
|
||||
{
|
||||
vec4 tex = flixel_texture2D(bitmap, openfl_TextureCoordv);
|
||||
|
||||
vec4 finalColor = mix(vec4(vec4(0.0, 0.0, tex.b, tex.a) * fadeAmt), vec4(tex * fadeAmt), fadeAmt);
|
||||
|
||||
// Output to screen
|
||||
gl_FragColor = finalColor;
|
||||
}
|
||||
|
||||
')
|
||||
public function new()
|
||||
{
|
||||
super();
|
||||
|
||||
this.fadeVal = 1;
|
||||
}
|
||||
}
|
|
@ -73,6 +73,22 @@ interface INoteScriptedClass extends IScriptedClass
|
|||
public function onNoteMiss(event:NoteScriptEvent):Void;
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines a set of callbacks available to scripted classes which represent sprites synced with the BPM.
|
||||
*/
|
||||
interface IBPMSyncedScriptedClass extends IScriptedClass
|
||||
{
|
||||
/**
|
||||
* Called once every step of the song.
|
||||
*/
|
||||
public function onStepHit(event:SongTimeScriptEvent):Void;
|
||||
|
||||
/**
|
||||
* Called once every beat of the song.
|
||||
*/
|
||||
public function onBeatHit(event:SongTimeScriptEvent):Void;
|
||||
}
|
||||
|
||||
/**
|
||||
* Developer note:
|
||||
*
|
||||
|
@ -86,7 +102,7 @@ interface INoteScriptedClass extends IScriptedClass
|
|||
/**
|
||||
* Defines a set of callbacks available to scripted classes that involve the lifecycle of the Play State.
|
||||
*/
|
||||
interface IPlayStateScriptedClass extends INoteScriptedClass
|
||||
interface IPlayStateScriptedClass extends INoteScriptedClass extends IBPMSyncedScriptedClass
|
||||
{
|
||||
/**
|
||||
* Called when the game is paused.
|
||||
|
@ -136,16 +152,6 @@ interface IPlayStateScriptedClass extends INoteScriptedClass
|
|||
*/
|
||||
public function onSongEvent(event:SongEventScriptEvent):Void;
|
||||
|
||||
/**
|
||||
* Called once every step of the song.
|
||||
*/
|
||||
public function onStepHit(event:SongTimeScriptEvent):Void;
|
||||
|
||||
/**
|
||||
* Called once every beat of the song.
|
||||
*/
|
||||
public function onBeatHit(event:SongTimeScriptEvent):Void;
|
||||
|
||||
/**
|
||||
* Called when the countdown of the song starts.
|
||||
*/
|
||||
|
|
|
@ -235,6 +235,10 @@ class PolymodHandler
|
|||
|
||||
Polymod.addImportAlias('funkin.data.event.SongEventSchema', funkin.data.event.SongEventSchema.SongEventSchemaRaw);
|
||||
|
||||
// `lime.utils.Assets` literally just has a private `resolveClass` function for some reason? so we replace it with our own.
|
||||
Polymod.addImportAlias('lime.utils.Assets', funkin.Assets);
|
||||
Polymod.addImportAlias('openfl.utils.Assets', funkin.Assets);
|
||||
|
||||
// Add blacklisting for prohibited classes and packages.
|
||||
|
||||
// `Sys`
|
||||
|
@ -269,11 +273,6 @@ class PolymodHandler
|
|||
// System.load() can load malicious DLLs
|
||||
Polymod.blacklistImport('lime.system.System');
|
||||
|
||||
// `lime.utils.Assets`
|
||||
// Literally just has a private `resolveClass` function for some reason?
|
||||
Polymod.blacklistImport('lime.utils.Assets');
|
||||
Polymod.blacklistImport('openfl.utils.Assets');
|
||||
|
||||
// `openfl.desktop.NativeProcess`
|
||||
// Can load native processes on the host operating system.
|
||||
Polymod.blacklistImport('openfl.desktop.NativeProcess');
|
||||
|
|
|
@ -94,6 +94,21 @@ class ScriptEventDispatcher
|
|||
}
|
||||
}
|
||||
|
||||
if (Std.isOfType(target, IBPMSyncedScriptedClass))
|
||||
{
|
||||
var t:IBPMSyncedScriptedClass = cast(target, IBPMSyncedScriptedClass);
|
||||
switch (event.type)
|
||||
{
|
||||
case SONG_BEAT_HIT:
|
||||
t.onBeatHit(cast event);
|
||||
return;
|
||||
case SONG_STEP_HIT:
|
||||
t.onStepHit(cast event);
|
||||
return;
|
||||
default: // Continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (Std.isOfType(target, IPlayStateScriptedClass))
|
||||
{
|
||||
var t:IPlayStateScriptedClass = cast(target, IPlayStateScriptedClass);
|
||||
|
@ -102,12 +117,6 @@ class ScriptEventDispatcher
|
|||
case NOTE_GHOST_MISS:
|
||||
t.onNoteGhostMiss(cast event);
|
||||
return;
|
||||
case SONG_BEAT_HIT:
|
||||
t.onBeatHit(cast event);
|
||||
return;
|
||||
case SONG_STEP_HIT:
|
||||
t.onStepHit(cast event);
|
||||
return;
|
||||
case SONG_START:
|
||||
t.onSongStart(event);
|
||||
return;
|
||||
|
|
|
@ -668,7 +668,7 @@ class PlayState extends MusicBeatSubState
|
|||
// Prepare the current song's instrumental and vocals to be played.
|
||||
if (!overrideMusic && currentChart != null)
|
||||
{
|
||||
currentChart.cacheInst();
|
||||
currentChart.cacheInst(currentInstrumental);
|
||||
currentChart.cacheVocals();
|
||||
}
|
||||
|
||||
|
@ -677,7 +677,7 @@ class PlayState extends MusicBeatSubState
|
|||
|
||||
if (currentChart.offsets != null)
|
||||
{
|
||||
Conductor.instance.instrumentalOffset = currentChart.offsets.getInstrumentalOffset();
|
||||
Conductor.instance.instrumentalOffset = currentChart.offsets.getInstrumentalOffset(currentInstrumental);
|
||||
}
|
||||
|
||||
Conductor.instance.mapTimeChanges(currentChart.timeChanges);
|
||||
|
@ -860,7 +860,7 @@ class PlayState extends MusicBeatSubState
|
|||
{
|
||||
// Stop the vocals if they already exist.
|
||||
if (vocals != null) vocals.stop();
|
||||
vocals = currentChart.buildVocals();
|
||||
vocals = currentChart.buildVocals(currentInstrumental);
|
||||
|
||||
if (vocals.members.length == 0)
|
||||
{
|
||||
|
@ -1791,7 +1791,7 @@ class PlayState extends MusicBeatSubState
|
|||
{
|
||||
// Stop the vocals if they already exist.
|
||||
if (vocals != null) vocals.stop();
|
||||
vocals = currentChart.buildVocals();
|
||||
vocals = currentChart.buildVocals(currentInstrumental);
|
||||
|
||||
if (vocals.members.length == 0)
|
||||
{
|
||||
|
|
|
@ -404,12 +404,12 @@ class ResultState extends MusicBeatSubState
|
|||
// }
|
||||
|
||||
new FlxTimer().start(rank.getMusicDelay(), _ -> {
|
||||
if (rank.hasMusicIntro())
|
||||
var introMusic:String = Paths.music(getMusicPath(playerCharacter, rank) + '/' + getMusicPath(playerCharacter, rank) + '-intro');
|
||||
if (Assets.exists(introMusic))
|
||||
{
|
||||
// Play the intro music.
|
||||
var introMusic:String = Paths.music(rank.getMusicPath() + '/' + rank.getMusicPath() + '-intro');
|
||||
FunkinSound.load(introMusic, 1.0, false, true, true, () -> {
|
||||
FunkinSound.playMusic(rank.getMusicPath(),
|
||||
FunkinSound.playMusic(getMusicPath(playerCharacter, rank),
|
||||
{
|
||||
startingVolume: 1.0,
|
||||
overrideExisting: true,
|
||||
|
@ -420,7 +420,7 @@ class ResultState extends MusicBeatSubState
|
|||
}
|
||||
else
|
||||
{
|
||||
FunkinSound.playMusic(rank.getMusicPath(),
|
||||
FunkinSound.playMusic(getMusicPath(playerCharacter, rank),
|
||||
{
|
||||
startingVolume: 1.0,
|
||||
overrideExisting: true,
|
||||
|
@ -441,6 +441,11 @@ class ResultState extends MusicBeatSubState
|
|||
super.create();
|
||||
}
|
||||
|
||||
function getMusicPath(playerCharacter:Null<PlayableCharacter>, rank:ScoringRank):String
|
||||
{
|
||||
return playerCharacter?.getResultsMusicPath(rank) ?? 'resultsNORMAL';
|
||||
}
|
||||
|
||||
var rankTallyTimer:Null<FlxTimer> = null;
|
||||
var clearPercentTarget:Int = 100;
|
||||
var clearPercentLerp:Int = 0;
|
||||
|
|
|
@ -556,40 +556,6 @@ enum abstract ScoringRank(String)
|
|||
}
|
||||
}
|
||||
|
||||
public function getMusicPath():String
|
||||
{
|
||||
switch (abstract)
|
||||
{
|
||||
case PERFECT_GOLD:
|
||||
return 'resultsPERFECT';
|
||||
case PERFECT:
|
||||
return 'resultsPERFECT';
|
||||
case EXCELLENT:
|
||||
return 'resultsEXCELLENT';
|
||||
case GREAT:
|
||||
return 'resultsNORMAL';
|
||||
case GOOD:
|
||||
return 'resultsNORMAL';
|
||||
case SHIT:
|
||||
return 'resultsSHIT';
|
||||
default:
|
||||
return 'resultsNORMAL';
|
||||
}
|
||||
}
|
||||
|
||||
public function hasMusicIntro():Bool
|
||||
{
|
||||
switch (abstract)
|
||||
{
|
||||
case EXCELLENT:
|
||||
return true;
|
||||
case SHIT:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public function getFreeplayRankIconAsset():String
|
||||
{
|
||||
switch (abstract)
|
||||
|
|
|
@ -533,6 +533,28 @@ class Song implements IPlayStateScriptedClass implements IRegistryEntry<SongMeta
|
|||
return variation.playData.difficulties.contains(diffId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the list of available alternate instrumentals.
|
||||
* Scripts can override this, fun.
|
||||
* @param variationId
|
||||
* @param difficultyId
|
||||
*/
|
||||
public function listAltInstrumentalIds(difficultyId:String, variationId:String):Array<String>
|
||||
{
|
||||
var targetDifficulty:Null<SongDifficulty> = getDifficulty(difficultyId, variationId);
|
||||
if (targetDifficulty == null) return [];
|
||||
|
||||
return targetDifficulty?.characters?.altInstrumentals ?? [];
|
||||
}
|
||||
|
||||
public function getBaseInstrumentalId(difficultyId:String, variationId:String):String
|
||||
{
|
||||
var targetDifficulty:Null<SongDifficulty> = getDifficulty(difficultyId, variationId);
|
||||
if (targetDifficulty == null) return '';
|
||||
|
||||
return targetDifficulty?.characters?.instrumental ?? '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Purge the cached chart data for each difficulty of this song.
|
||||
*/
|
||||
|
@ -851,7 +873,7 @@ class SongDifficulty
|
|||
* @param charId The player ID.
|
||||
* @return The generated vocal group.
|
||||
*/
|
||||
public function buildVocals():VoicesGroup
|
||||
public function buildVocals(?instId:String = ''):VoicesGroup
|
||||
{
|
||||
var result:VoicesGroup = new VoicesGroup();
|
||||
|
||||
|
@ -870,8 +892,8 @@ class SongDifficulty
|
|||
result.addOpponentVoice(FunkinSound.load(opponentVoice));
|
||||
}
|
||||
|
||||
result.playerVoicesOffset = offsets.getVocalOffset(characters.player);
|
||||
result.opponentVoicesOffset = offsets.getVocalOffset(characters.opponent);
|
||||
result.playerVoicesOffset = offsets.getVocalOffset(characters.player, instId);
|
||||
result.opponentVoicesOffset = offsets.getVocalOffset(characters.opponent, instId);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
|
|
@ -7,10 +7,12 @@ import flixel.math.FlxMath;
|
|||
import funkin.util.FramesJSFLParser;
|
||||
import funkin.util.FramesJSFLParser.FramesJSFLInfo;
|
||||
import funkin.util.FramesJSFLParser.FramesJSFLFrame;
|
||||
import funkin.modding.IScriptedClass.IBPMSyncedScriptedClass;
|
||||
import flixel.math.FlxMath;
|
||||
import funkin.modding.events.ScriptEvent;
|
||||
import funkin.vis.dsp.SpectralAnalyzer;
|
||||
|
||||
class CharSelectGF extends FlxAtlasSprite
|
||||
class CharSelectGF extends FlxAtlasSprite implements IBPMSyncedScriptedClass
|
||||
{
|
||||
var fadeTimer:Float = 0;
|
||||
var fadingStatus:FadeStatus = OFF;
|
||||
|
@ -54,6 +56,7 @@ class CharSelectGF extends FlxAtlasSprite
|
|||
default:
|
||||
}
|
||||
|
||||
#if FEATURE_DEBUG_FUNCTIONS
|
||||
if (FlxG.keys.justPressed.J)
|
||||
{
|
||||
alpha = 1;
|
||||
|
@ -65,8 +68,27 @@ class CharSelectGF extends FlxAtlasSprite
|
|||
alpha = 0;
|
||||
fadingStatus = FADE_IN;
|
||||
}
|
||||
#end
|
||||
}
|
||||
|
||||
public function onStepHit(event:SongTimeScriptEvent):Void {}
|
||||
|
||||
var danceEvery:Int = 2;
|
||||
|
||||
public function onBeatHit(event:SongTimeScriptEvent):Void
|
||||
{
|
||||
// TODO: There's a minor visual bug where there's a little stutter.
|
||||
// This happens because the animation is getting restarted while it's already playing.
|
||||
// I tried make this not interrupt an existing idle,
|
||||
// but isAnimationFinished() and isLoopComplete() both don't work! What the hell?
|
||||
// danceEvery isn't necessary if that gets fixed.
|
||||
if (getCurrentAnimation() == "idle" && (event.beat % danceEvery == 0))
|
||||
{
|
||||
trace('GF beat hit');
|
||||
playAnimation("idle", true, false, false);
|
||||
}
|
||||
};
|
||||
|
||||
override public function draw()
|
||||
{
|
||||
if (analyzer != null) drawFFT();
|
||||
|
@ -160,18 +182,27 @@ class CharSelectGF extends FlxAtlasSprite
|
|||
}
|
||||
|
||||
// We don't need to update any anims if we didn't change GF
|
||||
if (prevGF == curGF) return;
|
||||
if (prevGF != curGF)
|
||||
{
|
||||
loadAtlas(Paths.animateAtlas("charSelect/" + curGF + "Chill"));
|
||||
|
||||
loadAtlas(Paths.animateAtlas("charSelect/" + curGF + "Chill"));
|
||||
animInInfo = FramesJSFLParser.parse(Paths.file("images/charSelect/" + curGF + "AnimInfo/" + curGF + "In.txt"));
|
||||
animOutInfo = FramesJSFLParser.parse(Paths.file("images/charSelect/" + curGF + "AnimInfo/" + curGF + "Out.txt"));
|
||||
}
|
||||
|
||||
animInInfo = FramesJSFLParser.parse(Paths.file("images/charSelect/" + curGF + "AnimInfo/" + curGF + "In.txt"));
|
||||
animOutInfo = FramesJSFLParser.parse(Paths.file("images/charSelect/" + curGF + "AnimInfo/" + curGF + "Out.txt"));
|
||||
|
||||
playAnimation("idle", true, false, true);
|
||||
playAnimation("idle", true, false, false);
|
||||
// addFrameCallback(getNextFrameLabel("idle"), () -> playAnimation("idle", true, false, false));
|
||||
|
||||
updateHitbox();
|
||||
}
|
||||
|
||||
public function onScriptEvent(event:ScriptEvent):Void {};
|
||||
|
||||
public function onCreate(event:ScriptEvent):Void {};
|
||||
|
||||
public function onDestroy(event:ScriptEvent):Void {};
|
||||
|
||||
public function onUpdate(event:UpdateScriptEvent):Void {};
|
||||
}
|
||||
|
||||
enum FadeStatus
|
||||
|
|
|
@ -3,8 +3,10 @@ package funkin.ui.charSelect;
|
|||
import flixel.FlxSprite;
|
||||
import funkin.graphics.adobeanimate.FlxAtlasSprite;
|
||||
import flxanimate.animate.FlxKeyFrame;
|
||||
import funkin.modding.IScriptedClass.IBPMSyncedScriptedClass;
|
||||
import funkin.modding.events.ScriptEvent;
|
||||
|
||||
class CharSelectPlayer extends FlxAtlasSprite
|
||||
class CharSelectPlayer extends FlxAtlasSprite implements IBPMSyncedScriptedClass
|
||||
{
|
||||
var desLp:FlxKeyFrame = null;
|
||||
|
||||
|
@ -18,11 +20,20 @@ class CharSelectPlayer extends FlxAtlasSprite
|
|||
switch (animLabel)
|
||||
{
|
||||
case "slidein":
|
||||
if (hasAnimation("slidein idle point")) playAnimation("slidein idle point", true, false, false);
|
||||
if (hasAnimation("slidein idle point"))
|
||||
{
|
||||
playAnimation("slidein idle point", true, false, false);
|
||||
}
|
||||
else
|
||||
playAnimation("idle", true, false, true);
|
||||
{
|
||||
// Handled by onBeatHit now
|
||||
playAnimation("idle", true, false, false);
|
||||
}
|
||||
case "slidein idle point":
|
||||
playAnimation("idle", true, false, true);
|
||||
// Handled by onBeatHit now
|
||||
playAnimation("idle", true, false, false);
|
||||
case "idle":
|
||||
trace('Waiting for onBeatHit');
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -31,6 +42,22 @@ class CharSelectPlayer extends FlxAtlasSprite
|
|||
});
|
||||
}
|
||||
|
||||
public function onStepHit(event:SongTimeScriptEvent):Void {}
|
||||
|
||||
public function onBeatHit(event:SongTimeScriptEvent):Void
|
||||
{
|
||||
// TODO: There's a minor visual bug where there's a little stutter.
|
||||
// This happens because the animation is getting restarted while it's already playing.
|
||||
// I tried make this not interrupt an existing idle,
|
||||
// but isAnimationFinished() and isLoopComplete() both don't work! What the hell?
|
||||
// danceEvery isn't necessary if that gets fixed.
|
||||
if (getCurrentAnimation() == "idle")
|
||||
{
|
||||
trace('Player beat hit');
|
||||
playAnimation("idle", true, false, false);
|
||||
}
|
||||
};
|
||||
|
||||
public function updatePosition(str:String)
|
||||
{
|
||||
switch (str)
|
||||
|
@ -61,4 +88,12 @@ class CharSelectPlayer extends FlxAtlasSprite
|
|||
|
||||
updatePosition(str);
|
||||
}
|
||||
|
||||
public function onScriptEvent(event:ScriptEvent):Void {};
|
||||
|
||||
public function onCreate(event:ScriptEvent):Void {};
|
||||
|
||||
public function onDestroy(event:ScriptEvent):Void {};
|
||||
|
||||
public function onUpdate(event:UpdateScriptEvent):Void {};
|
||||
}
|
||||
|
|
|
@ -26,6 +26,12 @@ import funkin.ui.PixelatedIcon;
|
|||
import funkin.util.MathUtil;
|
||||
import funkin.vis.dsp.SpectralAnalyzer;
|
||||
import openfl.display.BlendMode;
|
||||
import flixel.util.FlxTimer;
|
||||
import flixel.tweens.FlxEase;
|
||||
import flixel.sound.FlxSound;
|
||||
import funkin.audio.FunkinSound;
|
||||
import funkin.graphics.shaders.BlueFade;
|
||||
import openfl.filters.ShaderFilter;
|
||||
|
||||
class CharSelectSubState extends MusicBeatSubState
|
||||
{
|
||||
|
@ -57,9 +63,16 @@ class CharSelectSubState extends MusicBeatSubState
|
|||
var gfChill:CharSelectGF;
|
||||
var gfChillOut:CharSelectGF;
|
||||
|
||||
var barthing:FlxAtlasSprite;
|
||||
var dipshitBacking:FlxSprite;
|
||||
var chooseDipshit:FlxSprite;
|
||||
var dipshitBlur:FlxSprite;
|
||||
var transitionGradient:FlxSprite;
|
||||
|
||||
var curChar(default, set):String = "pico";
|
||||
var nametag:Nametag;
|
||||
var camFollow:FlxObject;
|
||||
var autoFollow:Bool = false;
|
||||
|
||||
var availableChars:Map<Int, String> = new Map<Int, String>();
|
||||
var pressedSelect:Bool = false;
|
||||
|
@ -100,10 +113,17 @@ class CharSelectSubState extends MusicBeatSubState
|
|||
}
|
||||
}
|
||||
|
||||
var fadeShader:BlueFade = new BlueFade();
|
||||
|
||||
override public function create():Void
|
||||
{
|
||||
super.create();
|
||||
|
||||
autoFollow = false;
|
||||
|
||||
var fadeShaderFilter:ShaderFilter = new ShaderFilter(fadeShader);
|
||||
FlxG.camera.filters = [fadeShaderFilter];
|
||||
|
||||
selectSound = new FunkinSound();
|
||||
selectSound.loadEmbedded(Paths.sound('CS_select'));
|
||||
selectSound.pitch = 1;
|
||||
|
@ -148,7 +168,7 @@ class CharSelectSubState extends MusicBeatSubState
|
|||
|
||||
var stageSpr:FlxSprite = new FlxSprite(-40, 391);
|
||||
stageSpr.frames = Paths.getSparrowAtlas("charSelect/charSelectStage");
|
||||
stageSpr.animation.addByPrefix("idle", "stage", 24, true);
|
||||
stageSpr.animation.addByPrefix("idle", "stage full instance 1", 24, true);
|
||||
stageSpr.animation.play("idle");
|
||||
add(stageSpr);
|
||||
|
||||
|
@ -157,12 +177,15 @@ class CharSelectSubState extends MusicBeatSubState
|
|||
curtains.scrollFactor.set(1.4, 1.4);
|
||||
add(curtains);
|
||||
|
||||
var barthing:FlxAtlasSprite = new FlxAtlasSprite(0, 0, Paths.animateAtlas("charSelect/barThing"));
|
||||
barthing = new FlxAtlasSprite(0, 0, Paths.animateAtlas("charSelect/barThing"));
|
||||
barthing.anim.play("");
|
||||
barthing.blend = BlendMode.MULTIPLY;
|
||||
barthing.scrollFactor.set(0, 0);
|
||||
add(barthing);
|
||||
|
||||
barthing.y += 80;
|
||||
FlxTween.tween(barthing, {y: barthing.y - 80}, 1.3, {ease: FlxEase.expoOut});
|
||||
|
||||
var charLight:FlxSprite = new FlxSprite(800, 250);
|
||||
charLight.loadGraphic(Paths.image('charSelect/charLight'));
|
||||
add(charLight);
|
||||
|
@ -193,24 +216,33 @@ class CharSelectSubState extends MusicBeatSubState
|
|||
fgBlur.blend = openfl.display.BlendMode.MULTIPLY;
|
||||
add(fgBlur);
|
||||
|
||||
var dipshitBlur:FlxSprite = new FlxSprite(419, -65);
|
||||
dipshitBlur = new FlxSprite(419, -65);
|
||||
dipshitBlur.frames = Paths.getSparrowAtlas("charSelect/dipshitBlur");
|
||||
dipshitBlur.animation.addByPrefix('idle', "CHOOSE vertical", 24, true);
|
||||
dipshitBlur.animation.addByPrefix('idle', "CHOOSE vertical offset instance 1", 24, true);
|
||||
dipshitBlur.blend = BlendMode.ADD;
|
||||
dipshitBlur.animation.play("idle");
|
||||
add(dipshitBlur);
|
||||
|
||||
var dipshitBacking:FlxSprite = new FlxSprite(423, -17);
|
||||
dipshitBacking = new FlxSprite(423, -17);
|
||||
dipshitBacking.frames = Paths.getSparrowAtlas("charSelect/dipshitBacking");
|
||||
dipshitBacking.animation.addByPrefix('idle', "CHOOSE horizontal", 24, true);
|
||||
dipshitBacking.animation.addByPrefix('idle', "CHOOSE horizontal offset instance 1", 24, true);
|
||||
dipshitBacking.blend = BlendMode.ADD;
|
||||
dipshitBacking.animation.play("idle");
|
||||
add(dipshitBacking);
|
||||
|
||||
var chooseDipshit:FlxSprite = new FlxSprite(426, -13);
|
||||
dipshitBacking.y += 210;
|
||||
FlxTween.tween(dipshitBacking, {y: dipshitBacking.y - 210}, 1.1, {ease: FlxEase.expoOut});
|
||||
|
||||
chooseDipshit = new FlxSprite(426, -13);
|
||||
chooseDipshit.loadGraphic(Paths.image('charSelect/chooseDipshit'));
|
||||
add(chooseDipshit);
|
||||
|
||||
chooseDipshit.y += 200;
|
||||
FlxTween.tween(chooseDipshit, {y: chooseDipshit.y - 200}, 1, {ease: FlxEase.expoOut});
|
||||
|
||||
dipshitBlur.y += 220;
|
||||
FlxTween.tween(dipshitBlur, {y: dipshitBlur.y - 220}, 1.2, {ease: FlxEase.expoOut});
|
||||
|
||||
chooseDipshit.scrollFactor.set();
|
||||
dipshitBacking.scrollFactor.set();
|
||||
dipshitBlur.scrollFactor.set();
|
||||
|
@ -261,14 +293,14 @@ class CharSelectSubState extends MusicBeatSubState
|
|||
cursorConfirmed = new FlxSprite(0, 0);
|
||||
cursorConfirmed.scrollFactor.set();
|
||||
cursorConfirmed.frames = Paths.getSparrowAtlas("charSelect/charSelectorConfirm");
|
||||
cursorConfirmed.animation.addByPrefix("idle", "cursor ACCEPTED", 24, true);
|
||||
cursorConfirmed.animation.addByPrefix("idle", "cursor ACCEPTED instance 1", 24, true);
|
||||
cursorConfirmed.visible = false;
|
||||
add(cursorConfirmed);
|
||||
|
||||
cursorDenied = new FlxSprite(0, 0);
|
||||
cursorDenied.scrollFactor.set();
|
||||
cursorDenied.frames = Paths.getSparrowAtlas("charSelect/charSelectorDenied");
|
||||
cursorDenied.animation.addByPrefix("idle", "cursor DENIED", 24, false);
|
||||
cursorDenied.animation.addByPrefix("idle", "cursor DENIED instance 1", 24, false);
|
||||
cursorDenied.visible = false;
|
||||
add(cursorDenied);
|
||||
|
||||
|
@ -278,6 +310,12 @@ class CharSelectSubState extends MusicBeatSubState
|
|||
|
||||
initLocks();
|
||||
|
||||
for (index => member in grpIcons.members)
|
||||
{
|
||||
member.y += 300;
|
||||
FlxTween.tween(member, {y: member.y - 300}, 1, {ease: FlxEase.expoOut});
|
||||
}
|
||||
|
||||
cursor.scrollFactor.set();
|
||||
cursorBlue.scrollFactor.set();
|
||||
cursorDarkBlue.scrollFactor.set();
|
||||
|
@ -289,13 +327,12 @@ class CharSelectSubState extends MusicBeatSubState
|
|||
FlxG.debugger.addTrackerProfile(new TrackerProfile(CharSelectSubState, ["curChar", "grpXSpread", "grpYSpread"]));
|
||||
FlxG.debugger.track(this);
|
||||
|
||||
FlxG.sound.playMusic(Paths.music('charSelect/charSelectMusic'));
|
||||
|
||||
camFollow = new FlxObject(0, 0, 1, 1);
|
||||
add(camFollow);
|
||||
camFollow.screenCenter();
|
||||
|
||||
FlxG.camera.follow(camFollow, LOCKON, 0.01);
|
||||
// FlxG.camera.follow(camFollow, LOCKON, 0.01);
|
||||
FlxG.camera.follow(camFollow, LOCKON);
|
||||
|
||||
var temp:FlxSprite = new FlxSprite();
|
||||
temp.loadGraphic(Paths.image('charSelect/placement'));
|
||||
|
@ -303,6 +340,25 @@ class CharSelectSubState extends MusicBeatSubState
|
|||
temp.alpha = 0.0;
|
||||
Conductor.stepHit.add(spamOnStep);
|
||||
// FlxG.debugger.track(temp, "tempBG");
|
||||
|
||||
transitionGradient = new FlxSprite(0, 0).loadGraphic(Paths.image('freeplay/transitionGradient'));
|
||||
transitionGradient.scale.set(1280, 1);
|
||||
transitionGradient.flipY = true;
|
||||
transitionGradient.updateHitbox();
|
||||
FlxTween.tween(transitionGradient, {y: -720}, 1, {ease: FlxEase.expoOut});
|
||||
add(transitionGradient);
|
||||
|
||||
camFollow.screenCenter();
|
||||
camFollow.y -= 150;
|
||||
fadeShader.fade(0.0, 1.0, 0.8, {ease: FlxEase.quadOut});
|
||||
FlxTween.tween(camFollow, {y: camFollow.y + 150}, 1.5,
|
||||
{
|
||||
ease: FlxEase.expoOut,
|
||||
onComplete: function(_) {
|
||||
autoFollow = true;
|
||||
FlxG.camera.follow(camFollow, LOCKON, 0.01);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
var grpIcons:FlxSpriteGroup;
|
||||
|
@ -373,6 +429,42 @@ class CharSelectSubState extends MusicBeatSubState
|
|||
}
|
||||
}
|
||||
|
||||
function goToFreeplay():Void
|
||||
{
|
||||
autoFollow = false;
|
||||
|
||||
FlxTween.tween(cursor, {alpha: 0}, 0.8, {ease: FlxEase.expoOut});
|
||||
FlxTween.tween(cursorBlue, {alpha: 0}, 0.8, {ease: FlxEase.expoOut});
|
||||
FlxTween.tween(cursorDarkBlue, {alpha: 0}, 0.8, {ease: FlxEase.expoOut});
|
||||
FlxTween.tween(cursorConfirmed, {alpha: 0}, 0.8, {ease: FlxEase.expoOut});
|
||||
|
||||
FlxTween.tween(barthing, {y: barthing.y + 80}, 0.8, {ease: FlxEase.backIn});
|
||||
FlxTween.tween(dipshitBacking, {y: dipshitBacking.y + 210}, 0.8, {ease: FlxEase.backIn});
|
||||
FlxTween.tween(chooseDipshit, {y: chooseDipshit.y + 200}, 0.8, {ease: FlxEase.backIn});
|
||||
FlxTween.tween(dipshitBlur, {y: dipshitBlur.y + 220}, 0.8, {ease: FlxEase.backIn});
|
||||
for (index => member in grpIcons.members)
|
||||
{
|
||||
// member.y += 300;
|
||||
FlxTween.tween(member, {y: member.y + 300}, 0.8, {ease: FlxEase.backIn});
|
||||
}
|
||||
FlxG.camera.follow(camFollow, LOCKON);
|
||||
FlxTween.tween(transitionGradient, {y: -150}, 0.8, {ease: FlxEase.backIn});
|
||||
fadeShader.fade(1.0, 0, 0.8, {ease: FlxEase.quadIn});
|
||||
FlxTween.tween(camFollow, {y: camFollow.y - 150}, 0.8,
|
||||
{
|
||||
ease: FlxEase.backIn,
|
||||
onComplete: function(_) {
|
||||
FlxG.switchState(FreeplayState.build(
|
||||
{
|
||||
{
|
||||
character: curChar,
|
||||
fromCharSelect: true
|
||||
}
|
||||
}));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
var holdTmrUp:Float = 0;
|
||||
var holdTmrDown:Float = 0;
|
||||
var holdTmrLeft:Float = 0;
|
||||
|
@ -494,18 +586,20 @@ class CharSelectSubState extends MusicBeatSubState
|
|||
|
||||
FlxG.sound.play(Paths.sound('CS_confirm'));
|
||||
|
||||
FlxTween.tween(FlxG.sound.music, {pitch: 0.1}, 1.5, {ease: FlxEase.quadInOut});
|
||||
FlxTween.tween(FlxG.sound.music, {pitch: 0.1}, 1, {ease: FlxEase.quadInOut});
|
||||
FlxTween.tween(FlxG.sound.music, {volume: 0.0}, 1.5, {ease: FlxEase.quadInOut});
|
||||
playerChill.playAnimation("select");
|
||||
gfChill.playAnimation("confirm");
|
||||
pressedSelect = true;
|
||||
selectTimer.start(1.5, (_) -> {
|
||||
pressedSelect = false;
|
||||
FlxG.switchState(FreeplayState.build(
|
||||
{
|
||||
{
|
||||
character: curChar
|
||||
}
|
||||
}));
|
||||
// FlxG.switchState(FreeplayState.build(
|
||||
// {
|
||||
// {
|
||||
// character: curChar
|
||||
// }
|
||||
// }));
|
||||
goToFreeplay();
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -515,6 +609,7 @@ class CharSelectSubState extends MusicBeatSubState
|
|||
grpCursors.visible = true;
|
||||
|
||||
FlxTween.globalManager.cancelTweensOf(FlxG.sound.music);
|
||||
FlxTween.tween(FlxG.sound.music, {pitch: 1.0, volume: 1.0}, 1, {ease: FlxEase.quartInOut});
|
||||
playerChill.playAnimation("deselect");
|
||||
gfChill.playAnimation("deselect");
|
||||
FlxTween.tween(FlxG.sound.music, {pitch: 1.0}, 1,
|
||||
|
@ -547,9 +642,12 @@ class CharSelectSubState extends MusicBeatSubState
|
|||
|
||||
updateLockAnims();
|
||||
|
||||
camFollow.screenCenter();
|
||||
camFollow.x += cursorX * 10;
|
||||
camFollow.y += cursorY * 10;
|
||||
if (autoFollow == true)
|
||||
{
|
||||
camFollow.screenCenter();
|
||||
camFollow.x += cursorX * 10;
|
||||
camFollow.y += cursorY * 10;
|
||||
}
|
||||
|
||||
cursorLocIntended.x = (cursorFactor * cursorX) + (FlxG.width / 2) - cursor.width / 2;
|
||||
cursorLocIntended.y = (cursorFactor * cursorY) + (FlxG.height / 2) - cursor.height / 2;
|
||||
|
@ -567,6 +665,16 @@ class CharSelectSubState extends MusicBeatSubState
|
|||
cursorDarkBlue.y = MathUtil.coolLerp(cursorDarkBlue.y, cursorLocIntended.y, lerpAmnt * 0.2);
|
||||
}
|
||||
|
||||
public override function dispatchEvent(event:ScriptEvent):Void
|
||||
{
|
||||
// super.dispatchEvent(event) dispatches event to module scripts.
|
||||
super.dispatchEvent(event);
|
||||
|
||||
// Dispatch events (like onBeatHit) to props
|
||||
ScriptEventDispatcher.callEvent(playerChill, event);
|
||||
ScriptEventDispatcher.callEvent(gfChill, event);
|
||||
}
|
||||
|
||||
function spamOnStep():Void
|
||||
{
|
||||
if (spamUp || spamDown || spamLeft || spamRight)
|
||||
|
|
|
@ -41,6 +41,7 @@ class AlbumRoll extends FlxSpriteGroup
|
|||
|
||||
var difficultyStars:DifficultyStars;
|
||||
var _exitMovers:Null<FreeplayState.ExitMoverData>;
|
||||
var _exitMoversCharSel:Null<FreeplayState.ExitMoverData>;
|
||||
|
||||
var albumData:Album;
|
||||
|
||||
|
@ -128,7 +129,7 @@ class AlbumRoll extends FlxSpriteGroup
|
|||
* Apply exit movers for the album roll.
|
||||
* @param exitMovers The exit movers to apply.
|
||||
*/
|
||||
public function applyExitMovers(?exitMovers:FreeplayState.ExitMoverData):Void
|
||||
public function applyExitMovers(?exitMovers:FreeplayState.ExitMoverData, ?exitMoversCharSel:FreeplayState.ExitMoverData):Void
|
||||
{
|
||||
if (exitMovers == null)
|
||||
{
|
||||
|
@ -141,12 +142,30 @@ class AlbumRoll extends FlxSpriteGroup
|
|||
|
||||
if (exitMovers == null) return;
|
||||
|
||||
if (exitMoversCharSel == null)
|
||||
{
|
||||
exitMoversCharSel = _exitMoversCharSel;
|
||||
}
|
||||
else
|
||||
{
|
||||
_exitMoversCharSel = exitMoversCharSel;
|
||||
}
|
||||
|
||||
if (exitMoversCharSel == null) return;
|
||||
|
||||
exitMovers.set([newAlbumArt, difficultyStars],
|
||||
{
|
||||
x: FlxG.width,
|
||||
speed: 0.4,
|
||||
wait: 0
|
||||
});
|
||||
|
||||
exitMoversCharSel.set([newAlbumArt, difficultyStars],
|
||||
{
|
||||
y: -175,
|
||||
speed: 0.8,
|
||||
wait: 0.1
|
||||
});
|
||||
}
|
||||
|
||||
var titleTimer:Null<FlxTimer> = null;
|
||||
|
@ -207,6 +226,13 @@ class AlbumRoll extends FlxSpriteGroup
|
|||
speed: 0.4,
|
||||
wait: 0
|
||||
});
|
||||
|
||||
if (_exitMoversCharSel != null) _exitMoversCharSel.set([albumTitle],
|
||||
{
|
||||
y: -190,
|
||||
speed: 0.8,
|
||||
wait: 0.1
|
||||
});
|
||||
}
|
||||
|
||||
public function setDifficultyStars(?difficulty:Int):Void
|
||||
|
|
176
source/funkin/ui/freeplay/CapsuleOptionsMenu.hx
Normal file
176
source/funkin/ui/freeplay/CapsuleOptionsMenu.hx
Normal file
|
@ -0,0 +1,176 @@
|
|||
package funkin.ui.freeplay;
|
||||
|
||||
import funkin.graphics.shaders.PureColor;
|
||||
import funkin.input.Controls;
|
||||
import flixel.group.FlxSpriteGroup;
|
||||
import funkin.graphics.FunkinSprite;
|
||||
import flixel.util.FlxColor;
|
||||
import flixel.util.FlxTimer;
|
||||
import flixel.text.FlxText;
|
||||
import flixel.text.FlxText.FlxTextAlign;
|
||||
|
||||
@:nullSafety
|
||||
class CapsuleOptionsMenu extends FlxSpriteGroup
|
||||
{
|
||||
var capsuleMenuBG:FunkinSprite;
|
||||
var parent:FreeplayState;
|
||||
|
||||
var queueDestroy:Bool = false;
|
||||
|
||||
var instrumentalIds:Array<String> = [''];
|
||||
var currentInstrumentalIndex:Int = 0;
|
||||
|
||||
var currentInstrumental:FlxText;
|
||||
|
||||
public function new(parent:FreeplayState, x:Float = 0, y:Float = 0, instIds:Array<String>):Void
|
||||
{
|
||||
super(x, y);
|
||||
|
||||
this.parent = parent;
|
||||
this.instrumentalIds = instIds;
|
||||
|
||||
capsuleMenuBG = FunkinSprite.createSparrow(0, 0, 'freeplay/instBox/instBox');
|
||||
|
||||
capsuleMenuBG.animation.addByPrefix('open', 'open0', 24, false);
|
||||
capsuleMenuBG.animation.addByPrefix('idle', 'idle0', 24, true);
|
||||
capsuleMenuBG.animation.addByPrefix('open', 'open0', 24, false);
|
||||
|
||||
currentInstrumental = new FlxText(0, 36, capsuleMenuBG.width, '');
|
||||
currentInstrumental.setFormat('VCR OSD Mono', 40, FlxTextAlign.CENTER, true);
|
||||
|
||||
final PAD = 4;
|
||||
var leftArrow = new InstrumentalSelector(parent, PAD, 30, false, parent.getControls());
|
||||
var rightArrow = new InstrumentalSelector(parent, capsuleMenuBG.width - leftArrow.width - PAD, 30, true, parent.getControls());
|
||||
|
||||
var label:FlxText = new FlxText(0, 5, capsuleMenuBG.width, 'INSTRUMENTAL');
|
||||
label.setFormat('VCR OSD Mono', 24, FlxTextAlign.CENTER, true);
|
||||
|
||||
add(capsuleMenuBG);
|
||||
add(leftArrow);
|
||||
add(rightArrow);
|
||||
add(label);
|
||||
add(currentInstrumental);
|
||||
|
||||
capsuleMenuBG.animation.finishCallback = function(_) {
|
||||
capsuleMenuBG.animation.play('idle', true);
|
||||
};
|
||||
capsuleMenuBG.animation.play('open', true);
|
||||
}
|
||||
|
||||
public override function update(elapsed:Float):Void
|
||||
{
|
||||
super.update(elapsed);
|
||||
|
||||
if (queueDestroy)
|
||||
{
|
||||
destroy();
|
||||
return;
|
||||
}
|
||||
@:privateAccess
|
||||
if (parent.controls.BACK)
|
||||
{
|
||||
close();
|
||||
return;
|
||||
}
|
||||
|
||||
var changedInst = false;
|
||||
if (parent.getControls().UI_LEFT_P)
|
||||
{
|
||||
currentInstrumentalIndex = (currentInstrumentalIndex + 1) % instrumentalIds.length;
|
||||
changedInst = true;
|
||||
}
|
||||
if (parent.getControls().UI_RIGHT_P)
|
||||
{
|
||||
currentInstrumentalIndex = (currentInstrumentalIndex - 1 + instrumentalIds.length) % instrumentalIds.length;
|
||||
changedInst = true;
|
||||
}
|
||||
if (!changedInst && currentInstrumental.text == '') changedInst = true;
|
||||
|
||||
if (changedInst)
|
||||
{
|
||||
currentInstrumental.text = instrumentalIds[currentInstrumentalIndex].toTitleCase() ?? '';
|
||||
if (currentInstrumental.text == '') currentInstrumental.text = 'Default';
|
||||
}
|
||||
|
||||
if (parent.getControls().ACCEPT)
|
||||
{
|
||||
onConfirm(instrumentalIds[currentInstrumentalIndex] ?? '');
|
||||
}
|
||||
}
|
||||
|
||||
public function close():Void
|
||||
{
|
||||
// Play in reverse.
|
||||
capsuleMenuBG.animation.play('open', true, true);
|
||||
capsuleMenuBG.animation.finishCallback = function(_) {
|
||||
parent.cleanupCapsuleOptionsMenu();
|
||||
queueDestroy = true;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Override this with `capsuleOptionsMenu.onConfirm = myFunction;`
|
||||
*/
|
||||
public dynamic function onConfirm(targetInstId:String):Void
|
||||
{
|
||||
throw 'onConfirm not implemented!';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The difficulty selector arrows to the left and right of the difficulty.
|
||||
*/
|
||||
class InstrumentalSelector extends FunkinSprite
|
||||
{
|
||||
var controls:Controls;
|
||||
var whiteShader:PureColor;
|
||||
|
||||
var parent:FreeplayState;
|
||||
|
||||
var baseScale:Float = 0.6;
|
||||
|
||||
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');
|
||||
animation.addByPrefix('shine', 'arrow pointer loop', 24);
|
||||
animation.play('shine');
|
||||
|
||||
whiteShader = new PureColor(FlxColor.WHITE);
|
||||
|
||||
shader = whiteShader;
|
||||
|
||||
flipX = flipped;
|
||||
|
||||
scale.x = scale.y = 1 * baseScale;
|
||||
updateHitbox();
|
||||
}
|
||||
|
||||
override function update(elapsed:Float):Void
|
||||
{
|
||||
if (flipX && controls.UI_RIGHT_P) moveShitDown();
|
||||
if (!flipX && controls.UI_LEFT_P) moveShitDown();
|
||||
|
||||
super.update(elapsed);
|
||||
}
|
||||
|
||||
function moveShitDown():Void
|
||||
{
|
||||
offset.y -= 5;
|
||||
|
||||
whiteShader.colorSet = true;
|
||||
|
||||
scale.x = scale.y = 0.5 * baseScale;
|
||||
|
||||
new FlxTimer().start(2 / 24, function(tmr) {
|
||||
scale.x = scale.y = 1 * baseScale;
|
||||
whiteShader.colorSet = false;
|
||||
updateHitbox();
|
||||
});
|
||||
}
|
||||
}
|
|
@ -10,6 +10,7 @@ import flixel.tweens.FlxEase;
|
|||
import flixel.util.FlxTimer;
|
||||
import flixel.tweens.FlxTween;
|
||||
import openfl.display.BlendMode;
|
||||
import flixel.util.FlxColor;
|
||||
|
||||
class CapsuleText extends FlxSpriteGroup
|
||||
{
|
||||
|
@ -25,6 +26,8 @@ class CapsuleText extends FlxSpriteGroup
|
|||
|
||||
public var tooLong:Bool = false;
|
||||
|
||||
var glowColor:FlxColor = 0xFF00ccff;
|
||||
|
||||
// 255, 27 normal
|
||||
// 220, 27 favourited
|
||||
|
||||
|
@ -38,7 +41,7 @@ class CapsuleText extends FlxSpriteGroup
|
|||
// whiteText.shader = new GaussianBlurShader(0.3);
|
||||
text = songTitle;
|
||||
|
||||
blurredText.color = 0xFF00ccff;
|
||||
blurredText.color = glowColor;
|
||||
whiteText.color = 0xFFFFFFFF;
|
||||
add(blurredText);
|
||||
add(whiteText);
|
||||
|
@ -51,6 +54,16 @@ class CapsuleText extends FlxSpriteGroup
|
|||
return text;
|
||||
}
|
||||
|
||||
public function applyStyle(styleData:FreeplayStyle):Void
|
||||
{
|
||||
glowColor = styleData.getCapsuleSelCol();
|
||||
blurredText.color = glowColor;
|
||||
whiteText.textField.filters = [
|
||||
new openfl.filters.GlowFilter(glowColor, 1, 5, 5, 210, BitmapFilterQuality.MEDIUM),
|
||||
// new openfl.filters.BlurFilter(5, 5, BitmapFilterQuality.LOW)
|
||||
];
|
||||
}
|
||||
|
||||
// ???? none
|
||||
// 255, 27 normal
|
||||
// 220, 27 favourited
|
||||
|
@ -99,7 +112,7 @@ class CapsuleText extends FlxSpriteGroup
|
|||
whiteText.text = value;
|
||||
checkClipWidth();
|
||||
whiteText.textField.filters = [
|
||||
new openfl.filters.GlowFilter(0x00ccff, 1, 5, 5, 210, BitmapFilterQuality.MEDIUM),
|
||||
new openfl.filters.GlowFilter(glowColor, 1, 5, 5, 210, BitmapFilterQuality.MEDIUM),
|
||||
// new openfl.filters.BlurFilter(5, 5, BitmapFilterQuality.LOW)
|
||||
];
|
||||
|
||||
|
@ -186,7 +199,7 @@ class CapsuleText extends FlxSpriteGroup
|
|||
}
|
||||
else
|
||||
{
|
||||
blurredText.color = 0xFF00aadd;
|
||||
blurredText.color = glowColor;
|
||||
whiteText.color = 0xFFDDDDDD;
|
||||
whiteText.textField.filters = [
|
||||
new openfl.filters.GlowFilter(0xDDDDDD, 1, 5, 5, 210, BitmapFilterQuality.MEDIUM),
|
||||
|
|
|
@ -15,7 +15,9 @@ class FreeplayDJ extends FlxAtlasSprite
|
|||
{
|
||||
// Represents the sprite's current status.
|
||||
// Without state machines I would have driven myself crazy years ago.
|
||||
public var currentState:FreeplayDJState = Intro;
|
||||
// Made this PRIVATE so we can keep track of everything that can alter the state!
|
||||
// Add a function to this class if you want to edit this value from outside.
|
||||
private var currentState:FreeplayDJState = Intro;
|
||||
|
||||
// A callback activated when the intro animation finishes.
|
||||
public var onIntroDone:FlxSignal = new FlxSignal();
|
||||
|
@ -378,7 +380,7 @@ class FreeplayDJ extends FlxAtlasSprite
|
|||
|
||||
public function toCharSelect():Void
|
||||
{
|
||||
if (hasAnimation('charSelect'))
|
||||
if (hasAnimation(playableCharData.getAnimationPrefix('charSelect')))
|
||||
{
|
||||
currentState = CharSelect;
|
||||
var animPrefix = playableCharData.getAnimationPrefix('charSelect');
|
||||
|
@ -386,6 +388,7 @@ class FreeplayDJ extends FlxAtlasSprite
|
|||
}
|
||||
else
|
||||
{
|
||||
FlxG.log.warn("Freeplay character does not have 'charSelect' animation!");
|
||||
currentState = Confirm;
|
||||
// Call this immediately; otherwise, we get locked out of Character Select.
|
||||
onCharSelectComplete();
|
||||
|
|
|
@ -42,13 +42,20 @@ class FreeplayScore extends FlxTypedSpriteGroup<ScoreNum>
|
|||
return val;
|
||||
}
|
||||
|
||||
public function new(x:Float, y:Float, digitCount:Int, scoreShit:Int = 100)
|
||||
public function new(x:Float, y:Float, digitCount:Int, scoreShit:Int = 100, ?styleData:FreeplayStyle)
|
||||
{
|
||||
super(x, y);
|
||||
|
||||
for (i in 0...digitCount)
|
||||
{
|
||||
add(new ScoreNum(x + (45 * i), y, 0));
|
||||
if (styleData == null)
|
||||
{
|
||||
add(new ScoreNum(x + (45 * i), y, 0));
|
||||
}
|
||||
else
|
||||
{
|
||||
add(new ScoreNum(x + (45 * i), y, 0, styleData));
|
||||
}
|
||||
}
|
||||
|
||||
this.scoreShit = scoreShit;
|
||||
|
@ -76,16 +83,16 @@ class ScoreNum extends FlxSprite
|
|||
case 1:
|
||||
offset.x -= 15;
|
||||
case 5:
|
||||
// set offsets
|
||||
// offset.x += 0;
|
||||
// offset.y += 10;
|
||||
// set offsets
|
||||
// offset.x += 0;
|
||||
// offset.y += 10;
|
||||
|
||||
case 7:
|
||||
// offset.y += 6;
|
||||
// offset.y += 6;
|
||||
case 4:
|
||||
// offset.y += 5;
|
||||
// offset.y += 5;
|
||||
case 9:
|
||||
// offset.y += 5;
|
||||
// offset.y += 5;
|
||||
default:
|
||||
centerOffsets(false);
|
||||
}
|
||||
|
@ -99,14 +106,21 @@ class ScoreNum extends FlxSprite
|
|||
|
||||
var numToString:Array<String> = ["ZERO", "ONE", "TWO", "THREE", "FOUR", "FIVE", "SIX", "SEVEN", "EIGHT", "NINE"];
|
||||
|
||||
public function new(x:Float, y:Float, ?initDigit:Int = 0)
|
||||
public function new(x:Float, y:Float, ?initDigit:Int = 0, ?styleData:FreeplayStyle)
|
||||
{
|
||||
super(x, y);
|
||||
|
||||
baseY = y;
|
||||
baseX = x;
|
||||
|
||||
frames = Paths.getSparrowAtlas('digital_numbers');
|
||||
if (styleData == null)
|
||||
{
|
||||
frames = Paths.getSparrowAtlas('digital_numbers');
|
||||
}
|
||||
else
|
||||
{
|
||||
frames = Paths.getSparrowAtlas(styleData.getNumbersAssetKey());
|
||||
}
|
||||
|
||||
for (i in 0...10)
|
||||
{
|
||||
|
|
File diff suppressed because it is too large
Load diff
121
source/funkin/ui/freeplay/FreeplayStyle.hx
Normal file
121
source/funkin/ui/freeplay/FreeplayStyle.hx
Normal file
|
@ -0,0 +1,121 @@
|
|||
package funkin.ui.freeplay;
|
||||
|
||||
import funkin.data.freeplay.style.FreeplayStyleData;
|
||||
import funkin.data.freeplay.style.FreeplayStyleRegistry;
|
||||
import funkin.data.animation.AnimationData;
|
||||
import funkin.data.IRegistryEntry;
|
||||
import flixel.graphics.FlxGraphic;
|
||||
import flixel.util.FlxColor;
|
||||
|
||||
/**
|
||||
* A class representing the data for a style of the Freeplay menu.
|
||||
*/
|
||||
class FreeplayStyle implements IRegistryEntry<FreeplayStyleData>
|
||||
{
|
||||
/**
|
||||
* The internal ID for this freeplay style.
|
||||
*/
|
||||
public final id:String;
|
||||
|
||||
/**
|
||||
* The full data for a freeplay style.
|
||||
*/
|
||||
public final _data:FreeplayStyleData;
|
||||
|
||||
public function new(id:String)
|
||||
{
|
||||
this.id = id;
|
||||
this._data = _fetchData(id);
|
||||
|
||||
if (_data == null)
|
||||
{
|
||||
throw 'Could not parse album data for id: $id';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the background art as a graphic, ready to apply to a sprite.
|
||||
* @return The built graphic
|
||||
*/
|
||||
public function getBgAssetGraphic():FlxGraphic
|
||||
{
|
||||
return FlxG.bitmap.add(Paths.image(getBgAssetKey()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the asset key for the background.
|
||||
* @return The asset key
|
||||
*/
|
||||
public function getBgAssetKey():String
|
||||
{
|
||||
return _data.bgAsset;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the asset key for the background.
|
||||
* @return The asset key
|
||||
*/
|
||||
public function getSelectorAssetKey():String
|
||||
{
|
||||
return _data.selectorAsset;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the asset key for the number assets.
|
||||
* @return The asset key
|
||||
*/
|
||||
public function getCapsuleAssetKey():String
|
||||
{
|
||||
return _data.capsuleAsset;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the asset key for the capsule art.
|
||||
* @return The asset key
|
||||
*/
|
||||
public function getNumbersAssetKey():String
|
||||
{
|
||||
return _data.numbersAsset;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the deselected color of the text outline
|
||||
* for freeplay capsules.
|
||||
* @return The deselected color
|
||||
*/
|
||||
public function getCapsuleDeselCol():FlxColor
|
||||
{
|
||||
return FlxColor.fromString(_data.capsuleTextColors[0]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the song selection transition delay.
|
||||
* @return The start delay
|
||||
*/
|
||||
public function getStartDelay():Float
|
||||
{
|
||||
return _data.startDelay;
|
||||
}
|
||||
|
||||
public function toString():String
|
||||
{
|
||||
return 'Style($id)';
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the selected color of the text outline
|
||||
* for freeplay capsules.
|
||||
* @return The selected color
|
||||
*/
|
||||
public function getCapsuleSelCol():FlxColor
|
||||
{
|
||||
return FlxColor.fromString(_data.capsuleTextColors[1]);
|
||||
}
|
||||
|
||||
public function destroy():Void {}
|
||||
|
||||
static function _fetchData(id:String):Null<FreeplayStyleData>
|
||||
{
|
||||
return FreeplayStyleRegistry.instance.parseEntryDataWithMigration(id, FreeplayStyleRegistry.instance.fetchEntryVersion(id));
|
||||
}
|
||||
}
|
9
source/funkin/ui/freeplay/ScriptedFreeplayStyle.hx
Normal file
9
source/funkin/ui/freeplay/ScriptedFreeplayStyle.hx
Normal file
|
@ -0,0 +1,9 @@
|
|||
package funkin.ui.freeplay;
|
||||
|
||||
/**
|
||||
* A script that can be tied to a Freeplay style.
|
||||
* Create a scripted class that extends FreeplayStyle to use this.
|
||||
* This allows you to customize how a specific style works.
|
||||
*/
|
||||
@:hscriptClass
|
||||
class ScriptedFreeplayStyle extends funkin.ui.freeplay.FreeplayStyle implements polymod.hscript.HScriptedClass {}
|
|
@ -88,7 +88,7 @@ class SongMenuItem extends FlxSpriteGroup
|
|||
super(x, y);
|
||||
|
||||
capsule = new FlxSprite();
|
||||
capsule.frames = Paths.getSparrowAtlas('freeplay/freeplayCapsule');
|
||||
capsule.frames = Paths.getSparrowAtlas('freeplay/freeplayCapsule/capsule/freeplayCapsule');
|
||||
capsule.animation.addByPrefix('selected', 'mp3 capsule w backing0', 24);
|
||||
capsule.animation.addByPrefix('unselected', 'mp3 capsule w backing NOT SELECTED', 24);
|
||||
// capsule.animation
|
||||
|
@ -500,12 +500,23 @@ class SongMenuItem extends FlxSpriteGroup
|
|||
updateSelected();
|
||||
}
|
||||
|
||||
public function init(?x:Float, ?y:Float, songData:Null<FreeplaySongData>):Void
|
||||
public function init(?x:Float, ?y:Float, songData:Null<FreeplaySongData>, ?styleData:FreeplayStyle = null):Void
|
||||
{
|
||||
if (x != null) this.x = x;
|
||||
if (y != null) this.y = y;
|
||||
this.songData = songData;
|
||||
|
||||
// im so mad i have to do this but im pretty sure with the capsules recycling i cant call the new function properly :/
|
||||
// if thats possible someone Please change the new function to be something like
|
||||
// capsule.frames = Paths.getSparrowAtlas(styleData == null ? 'freeplay/freeplayCapsule/capsule/freeplayCapsule' : styleData.getCapsuleAssetKey()); thank u luv u
|
||||
if (styleData != null)
|
||||
{
|
||||
capsule.frames = Paths.getSparrowAtlas(styleData.getCapsuleAssetKey());
|
||||
capsule.animation.addByPrefix('selected', 'mp3 capsule w backing0', 24);
|
||||
capsule.animation.addByPrefix('unselected', 'mp3 capsule w backing NOT SELECTED', 24);
|
||||
songText.applyStyle(styleData);
|
||||
}
|
||||
|
||||
// Update capsule text.
|
||||
songText.text = songData?.songName ?? 'Random';
|
||||
// Update capsule character.
|
||||
|
@ -727,7 +738,7 @@ class FreeplayRank extends FlxSprite
|
|||
switch (val)
|
||||
{
|
||||
case SHIT:
|
||||
// offset.x -= 1;
|
||||
// offset.x -= 1;
|
||||
case GOOD:
|
||||
// offset.x -= 1;
|
||||
offset.y -= 8;
|
||||
|
@ -735,11 +746,11 @@ class FreeplayRank extends FlxSprite
|
|||
// offset.x -= 1;
|
||||
offset.y -= 8;
|
||||
case EXCELLENT:
|
||||
// offset.y += 5;
|
||||
// offset.y += 5;
|
||||
case PERFECT:
|
||||
// offset.y += 5;
|
||||
// offset.y += 5;
|
||||
case PERFECT_GOLD:
|
||||
// offset.y += 5;
|
||||
// offset.y += 5;
|
||||
default:
|
||||
centerOffsets(false);
|
||||
this.visible = false;
|
||||
|
@ -796,9 +807,9 @@ class CapsuleNumber extends FlxSprite
|
|||
case 6:
|
||||
|
||||
case 4:
|
||||
// offset.y += 5;
|
||||
// offset.y += 5;
|
||||
case 9:
|
||||
// offset.y += 5;
|
||||
// offset.y += 5;
|
||||
default:
|
||||
centerOffsets(false);
|
||||
}
|
||||
|
|
249
source/funkin/ui/freeplay/backcards/BackingCard.hx
Normal file
249
source/funkin/ui/freeplay/backcards/BackingCard.hx
Normal file
|
@ -0,0 +1,249 @@
|
|||
package funkin.ui.freeplay.backcards;
|
||||
|
||||
import funkin.ui.freeplay.FreeplayState;
|
||||
import flixel.FlxCamera;
|
||||
import flixel.FlxSprite;
|
||||
import flixel.group.FlxGroup;
|
||||
import flixel.group.FlxGroup.FlxTypedGroup;
|
||||
import flixel.group.FlxSpriteGroup.FlxTypedSpriteGroup;
|
||||
import flixel.math.FlxAngle;
|
||||
import flixel.math.FlxPoint;
|
||||
import flixel.text.FlxText;
|
||||
import flixel.tweens.FlxEase;
|
||||
import flixel.tweens.FlxTween;
|
||||
import flixel.util.FlxColor;
|
||||
import flixel.util.FlxSpriteUtil;
|
||||
import flixel.util.FlxTimer;
|
||||
import funkin.graphics.adobeanimate.FlxAtlasSprite;
|
||||
import funkin.graphics.FunkinSprite;
|
||||
import funkin.ui.freeplay.charselect.PlayableCharacter;
|
||||
import funkin.ui.MusicBeatSubState;
|
||||
import lime.utils.Assets;
|
||||
import openfl.display.BlendMode;
|
||||
import flixel.group.FlxSpriteGroup;
|
||||
|
||||
/**
|
||||
* A class for the backing cards so they dont have to be part of freeplayState......
|
||||
*/
|
||||
class BackingCard extends FlxSpriteGroup
|
||||
{
|
||||
public var backingTextYeah:FlxAtlasSprite;
|
||||
public var orangeBackShit:FunkinSprite;
|
||||
public var alsoOrangeLOL:FunkinSprite;
|
||||
public var pinkBack:FunkinSprite;
|
||||
public var confirmGlow:FlxSprite;
|
||||
public var confirmGlow2:FlxSprite;
|
||||
public var confirmTextGlow:FlxSprite;
|
||||
public var cardGlow:FlxSprite;
|
||||
|
||||
var _exitMovers:Null<FreeplayState.ExitMoverData>;
|
||||
var _exitMoversCharSel:Null<FreeplayState.ExitMoverData>;
|
||||
|
||||
public var instance:FreeplayState;
|
||||
|
||||
public function new(currentCharacter:PlayableCharacter, ?_instance:FreeplayState)
|
||||
{
|
||||
super();
|
||||
|
||||
if (_instance != null) instance = _instance;
|
||||
|
||||
cardGlow = new FlxSprite(-30, -30).loadGraphic(Paths.image('freeplay/cardGlow'));
|
||||
confirmGlow = new FlxSprite(-30, 240).loadGraphic(Paths.image('freeplay/confirmGlow'));
|
||||
confirmTextGlow = new FlxSprite(-8, 115).loadGraphic(Paths.image('freeplay/glowingText'));
|
||||
pinkBack = FunkinSprite.create('freeplay/pinkBack');
|
||||
orangeBackShit = new FunkinSprite(84, 440).makeSolidColor(Std.int(pinkBack.width), 75, 0xFFFEDA00);
|
||||
alsoOrangeLOL = new FunkinSprite(0, orangeBackShit.y).makeSolidColor(100, Std.int(orangeBackShit.height), 0xFFFFD400);
|
||||
confirmGlow2 = new FlxSprite(confirmGlow.x, confirmGlow.y).loadGraphic(Paths.image('freeplay/confirmGlow2'));
|
||||
backingTextYeah = new FlxAtlasSprite(640, 370, Paths.animateAtlas("freeplay/backing-text-yeah"),
|
||||
{
|
||||
FrameRate: 24.0,
|
||||
Reversed: false,
|
||||
// ?OnComplete:Void -> Void,
|
||||
ShowPivot: false,
|
||||
Antialiasing: true,
|
||||
ScrollFactor: new FlxPoint(1, 1),
|
||||
});
|
||||
|
||||
pinkBack.color = 0xFFFFD4E9; // sets it to pink!
|
||||
pinkBack.x -= pinkBack.width;
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply exit movers for the pieces of the backing card.
|
||||
* @param exitMovers The exit movers to apply.
|
||||
*/
|
||||
public function applyExitMovers(?exitMovers:FreeplayState.ExitMoverData, ?exitMoversCharSel:FreeplayState.ExitMoverData):Void
|
||||
{
|
||||
if (exitMovers == null)
|
||||
{
|
||||
exitMovers = _exitMovers;
|
||||
}
|
||||
else
|
||||
{
|
||||
_exitMovers = exitMovers;
|
||||
}
|
||||
|
||||
if (exitMovers == null) return;
|
||||
|
||||
if (exitMoversCharSel == null)
|
||||
{
|
||||
exitMoversCharSel = _exitMoversCharSel;
|
||||
}
|
||||
else
|
||||
{
|
||||
_exitMoversCharSel = exitMoversCharSel;
|
||||
}
|
||||
|
||||
if (exitMoversCharSel == null) return;
|
||||
|
||||
exitMovers.set([pinkBack, orangeBackShit, alsoOrangeLOL],
|
||||
{
|
||||
x: -pinkBack.width,
|
||||
y: pinkBack.y,
|
||||
speed: 0.4,
|
||||
wait: 0
|
||||
});
|
||||
|
||||
exitMoversCharSel.set([pinkBack],
|
||||
{
|
||||
y: -100,
|
||||
speed: 0.8,
|
||||
wait: 0.1
|
||||
});
|
||||
|
||||
exitMoversCharSel.set([orangeBackShit, alsoOrangeLOL],
|
||||
{
|
||||
y: -40,
|
||||
speed: 0.8,
|
||||
wait: 0.1
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to snap the back of the card to its final position.
|
||||
* Used when returning from character select, as we dont want to play the full animation of everything sliding in.
|
||||
*/
|
||||
public function skipIntroTween():Void
|
||||
{
|
||||
FlxTween.cancelTweensOf(pinkBack);
|
||||
pinkBack.x = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called in create. Adds sprites and tweens.
|
||||
*/
|
||||
public function init():Void
|
||||
{
|
||||
FlxTween.tween(pinkBack, {x: 0}, 0.6, {ease: FlxEase.quartOut});
|
||||
add(pinkBack);
|
||||
|
||||
add(orangeBackShit);
|
||||
|
||||
add(alsoOrangeLOL);
|
||||
|
||||
FlxSpriteUtil.alphaMaskFlxSprite(orangeBackShit, pinkBack, orangeBackShit);
|
||||
orangeBackShit.visible = false;
|
||||
alsoOrangeLOL.visible = false;
|
||||
|
||||
confirmTextGlow.blend = BlendMode.ADD;
|
||||
confirmTextGlow.visible = false;
|
||||
|
||||
confirmGlow.blend = BlendMode.ADD;
|
||||
|
||||
confirmGlow.visible = false;
|
||||
confirmGlow2.visible = false;
|
||||
|
||||
add(confirmGlow2);
|
||||
add(confirmGlow);
|
||||
|
||||
add(confirmTextGlow);
|
||||
|
||||
add(backingTextYeah);
|
||||
|
||||
cardGlow.blend = BlendMode.ADD;
|
||||
cardGlow.visible = false;
|
||||
|
||||
add(cardGlow);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called after the dj finishes their start animation.
|
||||
*/
|
||||
public function introDone():Void
|
||||
{
|
||||
pinkBack.color = 0xFFFFD863;
|
||||
orangeBackShit.visible = true;
|
||||
alsoOrangeLOL.visible = true;
|
||||
cardGlow.visible = true;
|
||||
FlxTween.tween(cardGlow, {alpha: 0, "scale.x": 1.2, "scale.y": 1.2}, 0.45, {ease: FlxEase.sineOut});
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when selecting a song.
|
||||
*/
|
||||
public function confirm():Void
|
||||
{
|
||||
FlxTween.color(pinkBack, 0.33, 0xFFFFD0D5, 0xFF171831, {ease: FlxEase.quadOut});
|
||||
orangeBackShit.visible = false;
|
||||
alsoOrangeLOL.visible = false;
|
||||
|
||||
confirmGlow.visible = true;
|
||||
confirmGlow2.visible = true;
|
||||
|
||||
backingTextYeah.anim.play("");
|
||||
confirmGlow2.alpha = 0;
|
||||
confirmGlow.alpha = 0;
|
||||
|
||||
FlxTween.color(instance.bgDad, 0.5, 0xFFA8A8A8, 0xFF646464,
|
||||
{
|
||||
onUpdate: function(_) {
|
||||
instance.angleMaskShader.extraColor = instance.bgDad.color;
|
||||
}
|
||||
});
|
||||
FlxTween.tween(confirmGlow2, {alpha: 0.5}, 0.33,
|
||||
{
|
||||
ease: FlxEase.quadOut,
|
||||
onComplete: function(_) {
|
||||
confirmGlow2.alpha = 0.6;
|
||||
confirmGlow.alpha = 1;
|
||||
confirmTextGlow.visible = true;
|
||||
confirmTextGlow.alpha = 1;
|
||||
FlxTween.tween(confirmTextGlow, {alpha: 0.4}, 0.5);
|
||||
FlxTween.tween(confirmGlow, {alpha: 0}, 0.5);
|
||||
FlxTween.color(instance.bgDad, 2, 0xFFCDCDCD, 0xFF555555,
|
||||
{
|
||||
ease: FlxEase.expoOut,
|
||||
onUpdate: function(_) {
|
||||
instance.angleMaskShader.extraColor = instance.bgDad.color;
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when entering character select, does nothing by default.
|
||||
*/
|
||||
public function enterCharSel():Void {}
|
||||
|
||||
/**
|
||||
* Called on each beat in freeplay state.
|
||||
*/
|
||||
public function beatHit():Void {}
|
||||
|
||||
/**
|
||||
* Called when exiting the freeplay menu.
|
||||
*/
|
||||
public function disappear():Void
|
||||
{
|
||||
FlxTween.color(pinkBack, 0.25, 0xFFFFD863, 0xFFFFD0D5, {ease: FlxEase.quadOut});
|
||||
|
||||
cardGlow.visible = true;
|
||||
cardGlow.alpha = 1;
|
||||
cardGlow.scale.set(1, 1);
|
||||
FlxTween.tween(cardGlow, {alpha: 0, "scale.x": 1.2, "scale.y": 1.2}, 0.25, {ease: FlxEase.sineOut});
|
||||
|
||||
orangeBackShit.visible = false;
|
||||
alsoOrangeLOL.visible = false;
|
||||
}
|
||||
}
|
238
source/funkin/ui/freeplay/backcards/BoyfriendCard.hx
Normal file
238
source/funkin/ui/freeplay/backcards/BoyfriendCard.hx
Normal file
|
@ -0,0 +1,238 @@
|
|||
package funkin.ui.freeplay.backcards;
|
||||
|
||||
import funkin.ui.freeplay.FreeplayState;
|
||||
import flixel.FlxCamera;
|
||||
import flixel.FlxSprite;
|
||||
import flixel.group.FlxGroup;
|
||||
import flixel.group.FlxGroup.FlxTypedGroup;
|
||||
import flixel.group.FlxSpriteGroup.FlxTypedSpriteGroup;
|
||||
import flixel.math.FlxAngle;
|
||||
import flixel.math.FlxPoint;
|
||||
import flixel.text.FlxText;
|
||||
import flixel.tweens.FlxEase;
|
||||
import flixel.tweens.FlxTween;
|
||||
import flixel.util.FlxColor;
|
||||
import flixel.util.FlxSpriteUtil;
|
||||
import flixel.util.FlxTimer;
|
||||
import funkin.graphics.adobeanimate.FlxAtlasSprite;
|
||||
import funkin.graphics.FunkinSprite;
|
||||
import funkin.ui.freeplay.charselect.PlayableCharacter;
|
||||
import funkin.ui.MusicBeatSubState;
|
||||
import lime.utils.Assets;
|
||||
import openfl.display.BlendMode;
|
||||
import flixel.group.FlxSpriteGroup;
|
||||
|
||||
class BoyfriendCard extends BackingCard
|
||||
{
|
||||
public var moreWays:BGScrollingText;
|
||||
public var funnyScroll:BGScrollingText;
|
||||
public var txtNuts:BGScrollingText;
|
||||
public var funnyScroll2:BGScrollingText;
|
||||
public var moreWays2:BGScrollingText;
|
||||
public var funnyScroll3:BGScrollingText;
|
||||
|
||||
var glow:FlxSprite;
|
||||
var glowDark:FlxSprite;
|
||||
|
||||
public override function applyExitMovers(?exitMovers:FreeplayState.ExitMoverData, ?exitMoversCharSel:FreeplayState.ExitMoverData):Void
|
||||
{
|
||||
super.applyExitMovers(exitMovers, exitMoversCharSel);
|
||||
if (exitMovers == null || exitMoversCharSel == null) return;
|
||||
exitMovers.set([moreWays],
|
||||
{
|
||||
x: FlxG.width * 2,
|
||||
speed: 0.4,
|
||||
});
|
||||
exitMovers.set([funnyScroll],
|
||||
{
|
||||
x: -funnyScroll.width * 2,
|
||||
y: funnyScroll.y,
|
||||
speed: 0.4,
|
||||
wait: 0
|
||||
});
|
||||
exitMovers.set([txtNuts],
|
||||
{
|
||||
x: FlxG.width * 2,
|
||||
speed: 0.4,
|
||||
});
|
||||
exitMovers.set([funnyScroll2],
|
||||
{
|
||||
x: -funnyScroll2.width * 2,
|
||||
speed: 0.5,
|
||||
});
|
||||
exitMovers.set([moreWays2],
|
||||
{
|
||||
x: FlxG.width * 2,
|
||||
speed: 0.4
|
||||
});
|
||||
exitMovers.set([funnyScroll3],
|
||||
{
|
||||
x: -funnyScroll3.width * 2,
|
||||
speed: 0.3
|
||||
});
|
||||
|
||||
exitMoversCharSel.set([moreWays, funnyScroll, txtNuts, funnyScroll2, moreWays2, funnyScroll3],
|
||||
{
|
||||
y: -60,
|
||||
speed: 0.8,
|
||||
wait: 0.1
|
||||
});
|
||||
}
|
||||
|
||||
public override function enterCharSel():Void
|
||||
{
|
||||
FlxTween.tween(funnyScroll, {speed: 0}, 0.8, {ease: FlxEase.sineIn});
|
||||
FlxTween.tween(funnyScroll2, {speed: 0}, 0.8, {ease: FlxEase.sineIn});
|
||||
FlxTween.tween(moreWays, {speed: 0}, 0.8, {ease: FlxEase.sineIn});
|
||||
FlxTween.tween(moreWays2, {speed: 0}, 0.8, {ease: FlxEase.sineIn});
|
||||
FlxTween.tween(txtNuts, {speed: 0}, 0.8, {ease: FlxEase.sineIn});
|
||||
FlxTween.tween(funnyScroll3, {speed: 0}, 0.8, {ease: FlxEase.sineIn});
|
||||
}
|
||||
|
||||
public override function new(currentCharacter:PlayableCharacter)
|
||||
{
|
||||
super(currentCharacter);
|
||||
|
||||
funnyScroll = new BGScrollingText(0, 220, currentCharacter.getFreeplayDJText(1), FlxG.width / 2, false, 60);
|
||||
funnyScroll2 = new BGScrollingText(0, 335, currentCharacter.getFreeplayDJText(1), FlxG.width / 2, false, 60);
|
||||
moreWays = new BGScrollingText(0, 160, currentCharacter.getFreeplayDJText(2), FlxG.width, true, 43);
|
||||
moreWays2 = new BGScrollingText(0, 397, currentCharacter.getFreeplayDJText(2), FlxG.width, true, 43);
|
||||
txtNuts = new BGScrollingText(0, 285, currentCharacter.getFreeplayDJText(3), FlxG.width / 2, true, 43);
|
||||
funnyScroll3 = new BGScrollingText(0, orangeBackShit.y + 10, currentCharacter.getFreeplayDJText(1), FlxG.width / 2, 60);
|
||||
}
|
||||
|
||||
public override function init():Void
|
||||
{
|
||||
FlxTween.tween(pinkBack, {x: 0}, 0.6, {ease: FlxEase.quartOut});
|
||||
add(pinkBack);
|
||||
|
||||
add(orangeBackShit);
|
||||
|
||||
add(alsoOrangeLOL);
|
||||
|
||||
FlxSpriteUtil.alphaMaskFlxSprite(orangeBackShit, pinkBack, orangeBackShit);
|
||||
orangeBackShit.visible = false;
|
||||
alsoOrangeLOL.visible = false;
|
||||
|
||||
confirmTextGlow.blend = BlendMode.ADD;
|
||||
confirmTextGlow.visible = false;
|
||||
|
||||
confirmGlow.blend = BlendMode.ADD;
|
||||
|
||||
confirmGlow.visible = false;
|
||||
confirmGlow2.visible = false;
|
||||
|
||||
add(confirmGlow2);
|
||||
add(confirmGlow);
|
||||
|
||||
add(confirmTextGlow);
|
||||
|
||||
add(backingTextYeah);
|
||||
|
||||
cardGlow.blend = BlendMode.ADD;
|
||||
cardGlow.visible = false;
|
||||
|
||||
moreWays.visible = false;
|
||||
funnyScroll.visible = false;
|
||||
txtNuts.visible = false;
|
||||
funnyScroll2.visible = false;
|
||||
moreWays2.visible = false;
|
||||
funnyScroll3.visible = false;
|
||||
|
||||
moreWays.funnyColor = 0xFFFFF383;
|
||||
moreWays.speed = 6.8;
|
||||
add(moreWays);
|
||||
|
||||
funnyScroll.funnyColor = 0xFFFF9963;
|
||||
funnyScroll.speed = -3.8;
|
||||
add(funnyScroll);
|
||||
|
||||
txtNuts.speed = 3.5;
|
||||
add(txtNuts);
|
||||
|
||||
funnyScroll2.funnyColor = 0xFFFF9963;
|
||||
funnyScroll2.speed = -3.8;
|
||||
add(funnyScroll2);
|
||||
|
||||
moreWays2.funnyColor = 0xFFFFF383;
|
||||
moreWays2.speed = 6.8;
|
||||
add(moreWays2);
|
||||
|
||||
funnyScroll3.funnyColor = 0xFFFEA400;
|
||||
funnyScroll3.speed = -3.8;
|
||||
add(funnyScroll3);
|
||||
|
||||
glowDark = new FlxSprite(-300, 330).loadGraphic(Paths.image('freeplay/beatglow'));
|
||||
glowDark.blend = BlendMode.MULTIPLY;
|
||||
add(glowDark);
|
||||
|
||||
glow = new FlxSprite(-300, 330).loadGraphic(Paths.image('freeplay/beatglow'));
|
||||
glow.blend = BlendMode.ADD;
|
||||
add(glow);
|
||||
|
||||
glowDark.visible = false;
|
||||
glow.visible = false;
|
||||
|
||||
add(cardGlow);
|
||||
}
|
||||
|
||||
var beatFreq:Int = 1;
|
||||
var beatFreqList:Array<Int> = [1,2,4,8];
|
||||
|
||||
public override function beatHit():Void {
|
||||
// increases the amount of beats that need to go by to pulse the glow because itd flash like craazy at high bpms.....
|
||||
beatFreq = beatFreqList[Math.floor(Conductor.instance.bpm/140)];
|
||||
|
||||
if(Conductor.instance.currentBeat % beatFreq != 0) return;
|
||||
FlxTween.cancelTweensOf(glow);
|
||||
FlxTween.cancelTweensOf(glowDark);
|
||||
|
||||
glow.alpha = 0.8;
|
||||
FlxTween.tween(glow, {alpha: 0}, 16/24, {ease: FlxEase.quartOut});
|
||||
glowDark.alpha = 0;
|
||||
FlxTween.tween(glowDark, {alpha: 0.6}, 18/24, {ease: FlxEase.quartOut});
|
||||
}
|
||||
|
||||
public override function introDone():Void
|
||||
{
|
||||
super.introDone();
|
||||
moreWays.visible = true;
|
||||
funnyScroll.visible = true;
|
||||
txtNuts.visible = true;
|
||||
funnyScroll2.visible = true;
|
||||
moreWays2.visible = true;
|
||||
funnyScroll3.visible = true;
|
||||
// grpTxtScrolls.visible = true;
|
||||
glowDark.visible = true;
|
||||
glow.visible = true;
|
||||
}
|
||||
|
||||
public override function confirm():Void
|
||||
{
|
||||
super.confirm();
|
||||
// FlxTween.color(bgDad, 0.33, 0xFFFFFFFF, 0xFF555555, {ease: FlxEase.quadOut});
|
||||
|
||||
moreWays.visible = false;
|
||||
funnyScroll.visible = false;
|
||||
txtNuts.visible = false;
|
||||
funnyScroll2.visible = false;
|
||||
moreWays2.visible = false;
|
||||
funnyScroll3.visible = false;
|
||||
glowDark.visible = false;
|
||||
glow.visible = false;
|
||||
}
|
||||
|
||||
public override function disappear():Void
|
||||
{
|
||||
super.disappear();
|
||||
|
||||
moreWays.visible = false;
|
||||
funnyScroll.visible = false;
|
||||
txtNuts.visible = false;
|
||||
funnyScroll2.visible = false;
|
||||
moreWays2.visible = false;
|
||||
funnyScroll3.visible = false;
|
||||
glowDark.visible = false;
|
||||
glow.visible = false;
|
||||
}
|
||||
}
|
278
source/funkin/ui/freeplay/backcards/NewCharacterCard.hx
Normal file
278
source/funkin/ui/freeplay/backcards/NewCharacterCard.hx
Normal file
|
@ -0,0 +1,278 @@
|
|||
package funkin.ui.freeplay.backcards;
|
||||
|
||||
import funkin.ui.freeplay.FreeplayState;
|
||||
import flash.display.BitmapData;
|
||||
import flixel.FlxCamera;
|
||||
import flixel.math.FlxMath;
|
||||
import flixel.FlxSprite;
|
||||
import flixel.group.FlxGroup;
|
||||
import flixel.group.FlxGroup.FlxTypedGroup;
|
||||
import flixel.group.FlxSpriteGroup.FlxTypedSpriteGroup;
|
||||
import flixel.math.FlxAngle;
|
||||
import flixel.math.FlxPoint;
|
||||
import flixel.text.FlxText;
|
||||
import flixel.tweens.FlxEase;
|
||||
import flixel.tweens.FlxTween;
|
||||
import flixel.util.FlxColor;
|
||||
import flixel.util.FlxSpriteUtil;
|
||||
import flixel.util.FlxTimer;
|
||||
import funkin.graphics.adobeanimate.FlxAtlasSprite;
|
||||
import funkin.graphics.FunkinSprite;
|
||||
import funkin.ui.freeplay.charselect.PlayableCharacter;
|
||||
import funkin.ui.MusicBeatSubState;
|
||||
import lime.utils.Assets;
|
||||
import openfl.display.BlendMode;
|
||||
import flixel.group.FlxSpriteGroup;
|
||||
import funkin.graphics.shaders.AdjustColorShader;
|
||||
import flixel.addons.display.FlxTiledSprite;
|
||||
import flixel.addons.display.FlxBackdrop;
|
||||
|
||||
class NewCharacterCard extends BackingCard
|
||||
{
|
||||
var confirmAtlas:FlxAtlasSprite;
|
||||
|
||||
var darkBg:FlxSprite;
|
||||
var lightLayer:FlxSprite;
|
||||
var multiply1:FlxSprite;
|
||||
var multiply2:FlxSprite;
|
||||
var lightLayer2:FlxSprite;
|
||||
var lightLayer3:FlxSprite;
|
||||
var yellow:FlxSprite;
|
||||
var multiplyBar:FlxSprite;
|
||||
|
||||
var bruh:FlxSprite;
|
||||
|
||||
public var friendFoe:BGScrollingText;
|
||||
public var newUnlock1:BGScrollingText;
|
||||
public var waiting:BGScrollingText;
|
||||
public var newUnlock2:BGScrollingText;
|
||||
public var friendFoe2:BGScrollingText;
|
||||
public var newUnlock3:BGScrollingText;
|
||||
|
||||
public override function applyExitMovers(?exitMovers:FreeplayState.ExitMoverData, ?exitMoversCharSel:FreeplayState.ExitMoverData):Void
|
||||
{
|
||||
super.applyExitMovers(exitMovers, exitMoversCharSel);
|
||||
if (exitMovers == null || exitMoversCharSel == null) return;
|
||||
exitMovers.set([friendFoe],
|
||||
{
|
||||
x: FlxG.width * 2,
|
||||
speed: 0.4,
|
||||
});
|
||||
exitMovers.set([newUnlock1],
|
||||
{
|
||||
x: -newUnlock1.width * 2,
|
||||
y: newUnlock1.y,
|
||||
speed: 0.4,
|
||||
wait: 0
|
||||
});
|
||||
exitMovers.set([waiting],
|
||||
{
|
||||
x: FlxG.width * 2,
|
||||
speed: 0.4,
|
||||
});
|
||||
exitMovers.set([newUnlock2],
|
||||
{
|
||||
x: -newUnlock2.width * 2,
|
||||
speed: 0.5,
|
||||
});
|
||||
exitMovers.set([friendFoe2],
|
||||
{
|
||||
x: FlxG.width * 2,
|
||||
speed: 0.4
|
||||
});
|
||||
exitMovers.set([newUnlock3],
|
||||
{
|
||||
x: -newUnlock3.width * 2,
|
||||
speed: 0.3
|
||||
});
|
||||
|
||||
exitMoversCharSel.set([friendFoe, newUnlock1, waiting, newUnlock2, friendFoe2, newUnlock3, multiplyBar], {
|
||||
y: -60,
|
||||
speed: 0.8,
|
||||
wait: 0.1
|
||||
});
|
||||
}
|
||||
|
||||
public override function introDone():Void
|
||||
{
|
||||
// pinkBack.color = 0xFFFFD863;
|
||||
|
||||
darkBg.visible = true;
|
||||
friendFoe.visible = true;
|
||||
newUnlock1.visible = true;
|
||||
waiting.visible = true;
|
||||
newUnlock2.visible = true;
|
||||
friendFoe2.visible = true;
|
||||
newUnlock3.visible = true;
|
||||
multiplyBar.visible = true;
|
||||
lightLayer.visible = true;
|
||||
multiply1.visible = true;
|
||||
multiply2.visible = true;
|
||||
lightLayer2.visible = true;
|
||||
yellow.visible = true;
|
||||
lightLayer3.visible = true;
|
||||
|
||||
cardGlow.visible = true;
|
||||
FlxTween.tween(cardGlow, {alpha: 0, "scale.x": 1.2, "scale.y": 1.2}, 0.45, {ease: FlxEase.sineOut});
|
||||
}
|
||||
|
||||
public override function enterCharSel():Void
|
||||
{
|
||||
FlxTween.tween(friendFoe, {speed: 0}, 0.8, {ease: FlxEase.sineIn});
|
||||
FlxTween.tween(newUnlock1, {speed: 0}, 0.8, {ease: FlxEase.sineIn});
|
||||
FlxTween.tween(waiting, {speed: 0}, 0.8, {ease: FlxEase.sineIn});
|
||||
FlxTween.tween(newUnlock2, {speed: 0}, 0.8, {ease: FlxEase.sineIn});
|
||||
FlxTween.tween(friendFoe2, {speed: 0}, 0.8, {ease: FlxEase.sineIn});
|
||||
FlxTween.tween(newUnlock3, {speed: 0}, 0.8, {ease: FlxEase.sineIn});
|
||||
}
|
||||
|
||||
public override function init():Void
|
||||
{
|
||||
FlxTween.tween(pinkBack, {x: 0}, 0.6, {ease: FlxEase.quartOut});
|
||||
add(pinkBack);
|
||||
|
||||
confirmTextGlow.blend = BlendMode.ADD;
|
||||
confirmTextGlow.visible = false;
|
||||
|
||||
confirmGlow.blend = BlendMode.ADD;
|
||||
|
||||
confirmGlow.visible = false;
|
||||
confirmGlow2.visible = false;
|
||||
|
||||
friendFoe = new BGScrollingText(0, 163, "COULD IT BE A NEW FRIEND? OR FOE??", FlxG.width, true, 43);
|
||||
newUnlock1 = new BGScrollingText(-440, 215, 'NEW UNLOCK!', FlxG.width / 2, true, 80);
|
||||
waiting = new BGScrollingText(0, 286, "SOMEONE'S WAITING!", FlxG.width / 2, true, 43);
|
||||
newUnlock2 = new BGScrollingText(-220, 331, 'NEW UNLOCK!', FlxG.width / 2, true, 80);
|
||||
friendFoe2 = new BGScrollingText(0, 402, 'COULD IT BE A NEW FRIEND? OR FOE??', FlxG.width, true, 43);
|
||||
newUnlock3 = new BGScrollingText(0, 458, 'NEW UNLOCK!', FlxG.width / 2, true, 80);
|
||||
|
||||
darkBg = new FlxSprite(0, 0).loadGraphic(Paths.image('freeplay/backingCards/newCharacter/darkback'));
|
||||
add(darkBg);
|
||||
|
||||
friendFoe.funnyColor = 0xFF139376;
|
||||
friendFoe.speed = -4;
|
||||
add(friendFoe);
|
||||
|
||||
newUnlock1.funnyColor = 0xFF99BDF2;
|
||||
newUnlock1.speed = 2;
|
||||
add(newUnlock1);
|
||||
|
||||
waiting.funnyColor = 0xFF40EA84;
|
||||
waiting.speed = -2;
|
||||
add(waiting);
|
||||
|
||||
newUnlock2.funnyColor = 0xFF99BDF2;
|
||||
newUnlock2.speed = 2;
|
||||
add(newUnlock2);
|
||||
|
||||
friendFoe2.funnyColor = 0xFF139376;
|
||||
friendFoe2.speed = -4;
|
||||
add(friendFoe2);
|
||||
|
||||
newUnlock3.funnyColor = 0xFF99BDF2;
|
||||
newUnlock3.speed = 2;
|
||||
add(newUnlock3);
|
||||
|
||||
multiplyBar = new FlxSprite(-10, 440).loadGraphic(Paths.image('freeplay/backingCards/newCharacter/multiplyBar'));
|
||||
multiplyBar.blend = BlendMode.MULTIPLY;
|
||||
add(multiplyBar);
|
||||
|
||||
lightLayer = new FlxSprite(-360, 230).loadGraphic(Paths.image('freeplay/backingCards/newCharacter/orange gradient'));
|
||||
lightLayer.blend = BlendMode.ADD;
|
||||
add(lightLayer);
|
||||
|
||||
multiply1 = new FlxSprite(-15, -125).loadGraphic(Paths.image('freeplay/backingCards/newCharacter/red'));
|
||||
multiply1.blend = BlendMode.MULTIPLY;
|
||||
add(multiply1);
|
||||
|
||||
multiply2 = new FlxSprite(-15, -125).loadGraphic(Paths.image('freeplay/backingCards/newCharacter/red'));
|
||||
multiply2.blend = BlendMode.MULTIPLY;
|
||||
add(multiply2);
|
||||
|
||||
lightLayer2 = new FlxSprite(-360, 230).loadGraphic(Paths.image('freeplay/backingCards/newCharacter/orange gradient'));
|
||||
lightLayer2.blend = BlendMode.ADD;
|
||||
add(lightLayer2);
|
||||
|
||||
yellow = new FlxSprite(0, 0).loadGraphic(Paths.image('freeplay/backingCards/newCharacter/yellow bg piece'));
|
||||
yellow.blend = BlendMode.MULTIPLY;
|
||||
add(yellow);
|
||||
|
||||
lightLayer3 = new FlxSprite(-360, 290).loadGraphic(Paths.image('freeplay/backingCards/newCharacter/red gradient'));
|
||||
lightLayer3.blend = BlendMode.ADD;
|
||||
add(lightLayer3);
|
||||
|
||||
cardGlow.blend = BlendMode.ADD;
|
||||
cardGlow.visible = false;
|
||||
|
||||
add(cardGlow);
|
||||
|
||||
darkBg.visible = false;
|
||||
friendFoe.visible = false;
|
||||
newUnlock1.visible = false;
|
||||
waiting.visible = false;
|
||||
newUnlock2.visible = false;
|
||||
friendFoe2.visible = false;
|
||||
newUnlock3.visible = false;
|
||||
multiplyBar.visible = false;
|
||||
lightLayer.visible = false;
|
||||
multiply1.visible = false;
|
||||
multiply2.visible = false;
|
||||
lightLayer2.visible = false;
|
||||
yellow.visible = false;
|
||||
lightLayer3.visible = false;
|
||||
}
|
||||
|
||||
var _timer:Float = 0;
|
||||
|
||||
override public function update(elapsed:Float):Void
|
||||
{
|
||||
super.update(elapsed);
|
||||
|
||||
_timer += elapsed * 2;
|
||||
var sinTest:Float = (Math.sin(_timer) + 1) / 2;
|
||||
lightLayer.alpha = FlxMath.lerp(0.4, 1, sinTest);
|
||||
lightLayer2.alpha = FlxMath.lerp(0.2, 0.5, sinTest);
|
||||
lightLayer3.alpha = FlxMath.lerp(0.1, 0.7, sinTest);
|
||||
|
||||
multiply1.alpha = FlxMath.lerp(1, 0.21, sinTest);
|
||||
multiply2.alpha = FlxMath.lerp(1, 0.21, sinTest);
|
||||
|
||||
yellow.alpha = FlxMath.lerp(0.2, 0.72, sinTest);
|
||||
|
||||
if (instance != null)
|
||||
{
|
||||
instance.angleMaskShader.extraColor = FlxColor.interpolate(0xFF2E2E46, 0xFF60607B, sinTest);
|
||||
}
|
||||
}
|
||||
|
||||
public override function disappear():Void
|
||||
{
|
||||
FlxTween.color(pinkBack, 0.25, 0xFF05020E, 0xFFFFD0D5, {ease: FlxEase.quadOut});
|
||||
|
||||
darkBg.visible = false;
|
||||
friendFoe.visible = false;
|
||||
newUnlock1.visible = false;
|
||||
waiting.visible = false;
|
||||
newUnlock2.visible = false;
|
||||
friendFoe2.visible = false;
|
||||
newUnlock3.visible = false;
|
||||
multiplyBar.visible = false;
|
||||
lightLayer.visible = false;
|
||||
multiply1.visible = false;
|
||||
multiply2.visible = false;
|
||||
lightLayer2.visible = false;
|
||||
yellow.visible = false;
|
||||
lightLayer3.visible = false;
|
||||
|
||||
cardGlow.visible = true;
|
||||
cardGlow.alpha = 1;
|
||||
cardGlow.scale.set(1, 1);
|
||||
FlxTween.tween(cardGlow, {alpha: 0, "scale.x": 1.2, "scale.y": 1.2}, 0.25, {ease: FlxEase.sineOut});
|
||||
}
|
||||
|
||||
override public function confirm():Void
|
||||
{
|
||||
// confirmAtlas.visible = true;
|
||||
// confirmAtlas.anim.play("");
|
||||
}
|
||||
}
|
314
source/funkin/ui/freeplay/backcards/PicoCard.hx
Normal file
314
source/funkin/ui/freeplay/backcards/PicoCard.hx
Normal file
|
@ -0,0 +1,314 @@
|
|||
package funkin.ui.freeplay.backcards;
|
||||
|
||||
import funkin.ui.freeplay.FreeplayState;
|
||||
import flash.display.BitmapData;
|
||||
import flixel.FlxCamera;
|
||||
import flixel.math.FlxMath;
|
||||
import flixel.FlxSprite;
|
||||
import flixel.group.FlxGroup;
|
||||
import flixel.group.FlxGroup.FlxTypedGroup;
|
||||
import flixel.group.FlxSpriteGroup.FlxTypedSpriteGroup;
|
||||
import flixel.math.FlxAngle;
|
||||
import flixel.math.FlxPoint;
|
||||
import flixel.text.FlxText;
|
||||
import flixel.tweens.FlxEase;
|
||||
import flixel.tweens.FlxTween;
|
||||
import flixel.util.FlxColor;
|
||||
import flixel.util.FlxSpriteUtil;
|
||||
import flixel.util.FlxTimer;
|
||||
import funkin.graphics.adobeanimate.FlxAtlasSprite;
|
||||
import funkin.graphics.FunkinSprite;
|
||||
import funkin.ui.freeplay.charselect.PlayableCharacter;
|
||||
import funkin.ui.MusicBeatSubState;
|
||||
import lime.utils.Assets;
|
||||
import openfl.display.BlendMode;
|
||||
import flixel.group.FlxSpriteGroup;
|
||||
import funkin.graphics.shaders.AdjustColorShader;
|
||||
import flixel.addons.display.FlxTiledSprite;
|
||||
import flixel.addons.display.FlxBackdrop;
|
||||
|
||||
class PicoCard extends BackingCard
|
||||
{
|
||||
var scrollBack:FlxBackdrop;
|
||||
var scrollLower:FlxBackdrop;
|
||||
var scrollTop:FlxBackdrop;
|
||||
var scrollMiddle:FlxBackdrop;
|
||||
|
||||
var glow:FlxSprite;
|
||||
var glowDark:FlxSprite;
|
||||
var blueBar:FlxSprite;
|
||||
|
||||
var confirmAtlas:FlxAtlasSprite;
|
||||
|
||||
public override function enterCharSel():Void
|
||||
{
|
||||
FlxTween.tween(scrollBack.velocity, {x: 0}, 0.8, {ease: FlxEase.sineIn});
|
||||
FlxTween.tween(scrollLower.velocity, {x: 0}, 0.8, {ease: FlxEase.sineIn});
|
||||
FlxTween.tween(scrollTop.velocity, {x: 0}, 0.8, {ease: FlxEase.sineIn});
|
||||
FlxTween.tween(scrollMiddle.velocity, {x: 0}, 0.8, {ease: FlxEase.sineIn});
|
||||
}
|
||||
|
||||
public override function applyExitMovers(?exitMovers:FreeplayState.ExitMoverData, ?exitMoversCharSel:FreeplayState.ExitMoverData):Void
|
||||
{
|
||||
super.applyExitMovers(exitMovers, exitMoversCharSel);
|
||||
if (exitMovers == null || exitMoversCharSel == null) return;
|
||||
|
||||
exitMoversCharSel.set([scrollTop],
|
||||
{
|
||||
y: -90,
|
||||
speed: 0.8,
|
||||
wait: 0.1
|
||||
});
|
||||
|
||||
exitMoversCharSel.set([scrollMiddle],
|
||||
{
|
||||
y: -80,
|
||||
speed: 0.8,
|
||||
wait: 0.1
|
||||
});
|
||||
|
||||
exitMoversCharSel.set([blueBar],
|
||||
{
|
||||
y: -70,
|
||||
speed: 0.8,
|
||||
wait: 0.1
|
||||
});
|
||||
|
||||
exitMoversCharSel.set([scrollLower],
|
||||
{
|
||||
y: -60,
|
||||
speed: 0.8,
|
||||
wait: 0.1
|
||||
});
|
||||
|
||||
exitMoversCharSel.set([scrollBack],
|
||||
{
|
||||
y: -50,
|
||||
speed: 0.8,
|
||||
wait: 0.1
|
||||
});
|
||||
}
|
||||
|
||||
public override function init():Void
|
||||
{
|
||||
FlxTween.tween(pinkBack, {x: 0}, 0.6, {ease: FlxEase.quartOut});
|
||||
add(pinkBack);
|
||||
|
||||
confirmTextGlow.blend = BlendMode.ADD;
|
||||
confirmTextGlow.visible = false;
|
||||
|
||||
confirmGlow.blend = BlendMode.ADD;
|
||||
|
||||
confirmGlow.visible = false;
|
||||
confirmGlow2.visible = false;
|
||||
|
||||
scrollBack = new FlxBackdrop(Paths.image('freeplay/backingCards/pico/lowerLoop'), X, 20);
|
||||
scrollBack.setPosition(0, 200);
|
||||
scrollBack.flipX = true;
|
||||
scrollBack.alpha = 0.39;
|
||||
scrollBack.velocity.x = 110;
|
||||
add(scrollBack);
|
||||
|
||||
scrollLower = new FlxBackdrop(Paths.image('freeplay/backingCards/pico/lowerLoop'), X, 20);
|
||||
scrollLower.setPosition(0, 406);
|
||||
scrollLower.velocity.x = -110;
|
||||
add(scrollLower);
|
||||
|
||||
blueBar = new FlxSprite(0, 239).loadGraphic(Paths.image('freeplay/backingCards/pico/blueBar'));
|
||||
blueBar.blend = BlendMode.MULTIPLY;
|
||||
blueBar.alpha = 0.4;
|
||||
add(blueBar);
|
||||
|
||||
scrollTop = new FlxBackdrop(null, X, 20);
|
||||
scrollTop.setPosition(0, 80);
|
||||
scrollTop.velocity.x = -220;
|
||||
|
||||
scrollTop.frames = Paths.getSparrowAtlas('freeplay/backingCards/pico/topLoop');
|
||||
scrollTop.animation.addByPrefix('uzi', 'uzi info', 24, false);
|
||||
scrollTop.animation.addByPrefix('sniper', 'sniper info', 24, false);
|
||||
scrollTop.animation.addByPrefix('rocket launcher', 'rocket launcher info', 24, false);
|
||||
scrollTop.animation.addByPrefix('rifle', 'rifle info', 24, false);
|
||||
scrollTop.animation.addByPrefix('base', 'base', 24, false);
|
||||
scrollTop.animation.play('base');
|
||||
|
||||
add(scrollTop);
|
||||
|
||||
scrollMiddle = new FlxBackdrop(Paths.image('freeplay/backingCards/pico/middleLoop'), X, 15);
|
||||
scrollMiddle.setPosition(0, 346);
|
||||
add(scrollMiddle);
|
||||
scrollMiddle.velocity.x = 220;
|
||||
|
||||
glowDark = new FlxSprite(-300, 330).loadGraphic(Paths.image('freeplay/backingCards/pico/glow'));
|
||||
glowDark.blend = BlendMode.MULTIPLY;
|
||||
add(glowDark);
|
||||
|
||||
glow = new FlxSprite(-300, 330).loadGraphic(Paths.image('freeplay/backingCards/pico/glow'));
|
||||
glow.blend = BlendMode.ADD;
|
||||
add(glow);
|
||||
|
||||
blueBar.visible = false;
|
||||
scrollBack.visible = false;
|
||||
scrollLower.visible = false;
|
||||
scrollTop.visible = false;
|
||||
scrollMiddle.visible = false;
|
||||
glow.visible = false;
|
||||
glowDark.visible = false;
|
||||
|
||||
confirmAtlas = new FlxAtlasSprite(5, 55, Paths.animateAtlas("freeplay/backingCards/pico/pico-confirm"));
|
||||
confirmAtlas.visible = false;
|
||||
add(confirmAtlas);
|
||||
|
||||
cardGlow.blend = BlendMode.ADD;
|
||||
cardGlow.visible = false;
|
||||
add(cardGlow);
|
||||
}
|
||||
|
||||
override public function confirm():Void
|
||||
{
|
||||
confirmAtlas.visible = true;
|
||||
confirmAtlas.anim.play("");
|
||||
|
||||
FlxTween.color(instance.bgDad, 10 / 24, 0xFFFFFFFF, 0xFF8A8A8A,
|
||||
{
|
||||
ease: FlxEase.expoOut,
|
||||
onUpdate: function(_) {
|
||||
instance.angleMaskShader.extraColor = instance.bgDad.color;
|
||||
}
|
||||
});
|
||||
|
||||
new FlxTimer().start(10 / 24, function(_) {
|
||||
// shoot
|
||||
FlxTween.color(instance.bgDad, 3 / 24, 0xFF343036, 0xFF696366,
|
||||
{
|
||||
ease: FlxEase.expoOut,
|
||||
onUpdate: function(_) {
|
||||
instance.angleMaskShader.extraColor = instance.bgDad.color;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
new FlxTimer().start(14 / 24, function(_) {
|
||||
// shoot
|
||||
FlxTween.color(instance.bgDad, 3 / 24, 0xFF27292D, 0xFF686A6F,
|
||||
{
|
||||
ease: FlxEase.expoOut,
|
||||
onUpdate: function(_) {
|
||||
instance.angleMaskShader.extraColor = instance.bgDad.color;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
new FlxTimer().start(18 / 24, function(_) {
|
||||
// shoot
|
||||
FlxTween.color(instance.bgDad, 3 / 24, 0xFF2D282D, 0xFF676164,
|
||||
{
|
||||
ease: FlxEase.expoOut,
|
||||
onUpdate: function(_) {
|
||||
instance.angleMaskShader.extraColor = instance.bgDad.color;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
new FlxTimer().start(21 / 24, function(_) {
|
||||
// shoot
|
||||
FlxTween.color(instance.bgDad, 3 / 24, 0xFF29292F, 0xFF62626B,
|
||||
{
|
||||
ease: FlxEase.expoOut,
|
||||
onUpdate: function(_) {
|
||||
instance.angleMaskShader.extraColor = instance.bgDad.color;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
new FlxTimer().start(24 / 24, function(_) {
|
||||
// shoot
|
||||
FlxTween.color(instance.bgDad, 3 / 24, 0xFF29232C, 0xFF808080,
|
||||
{
|
||||
ease: FlxEase.expoOut,
|
||||
onUpdate: function(_) {
|
||||
instance.angleMaskShader.extraColor = instance.bgDad.color;
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
var beatFreq:Int = 1;
|
||||
var beatFreqList:Array<Int> = [1,2,4,8];
|
||||
|
||||
public override function beatHit():Void {
|
||||
// increases the amount of beats that need to go by to pulse the glow because itd flash like craazy at high bpms.....
|
||||
beatFreq = beatFreqList[Math.floor(Conductor.instance.bpm/140)];
|
||||
|
||||
if(Conductor.instance.currentBeat % beatFreq != 0) return;
|
||||
FlxTween.cancelTweensOf(glow);
|
||||
FlxTween.cancelTweensOf(glowDark);
|
||||
|
||||
glow.alpha = 1;
|
||||
FlxTween.tween(glow, {alpha: 0}, 16/24, {ease: FlxEase.quartOut});
|
||||
glowDark.alpha = 0;
|
||||
FlxTween.tween(glowDark, {alpha: 1}, 18/24, {ease: FlxEase.quartOut});
|
||||
}
|
||||
|
||||
public override function introDone():Void
|
||||
{
|
||||
pinkBack.color = 0xFF98A2F3;
|
||||
|
||||
blueBar.visible = true;
|
||||
scrollBack.visible = true;
|
||||
scrollLower.visible = true;
|
||||
scrollTop.visible = true;
|
||||
scrollMiddle.visible = true;
|
||||
glowDark.visible = true;
|
||||
glow.visible = true;
|
||||
|
||||
cardGlow.visible = true;
|
||||
FlxTween.tween(cardGlow, {alpha: 0, "scale.x": 1.2, "scale.y": 1.2}, 0.45, {ease: FlxEase.sineOut});
|
||||
}
|
||||
|
||||
public override function disappear():Void
|
||||
{
|
||||
FlxTween.color(pinkBack, 0.25, 0xFF98A2F3, 0xFFFFD0D5, {ease: FlxEase.quadOut});
|
||||
|
||||
blueBar.visible = false;
|
||||
scrollBack.visible = false;
|
||||
scrollLower.visible = false;
|
||||
scrollTop.visible = false;
|
||||
scrollMiddle.visible = false;
|
||||
glowDark.visible = false;
|
||||
glow.visible = false;
|
||||
|
||||
cardGlow.visible = true;
|
||||
cardGlow.alpha = 1;
|
||||
cardGlow.scale.set(1, 1);
|
||||
FlxTween.tween(cardGlow, {alpha: 0, "scale.x": 1.2, "scale.y": 1.2}, 0.25, {ease: FlxEase.sineOut});
|
||||
}
|
||||
|
||||
override public function update(elapsed:Float):Void
|
||||
{
|
||||
super.update(elapsed);
|
||||
var scrollProgress:Float = Math.abs(scrollTop.x % (scrollTop.frameWidth + 20));
|
||||
|
||||
if (scrollTop.animation.curAnim.finished == true)
|
||||
{
|
||||
if (FlxMath.inBounds(scrollProgress, 500, 700) && scrollTop.animation.curAnim.name != 'sniper')
|
||||
{
|
||||
scrollTop.animation.play('sniper', true, false);
|
||||
}
|
||||
|
||||
if (FlxMath.inBounds(scrollProgress, 700, 1300) && scrollTop.animation.curAnim.name != 'rifle')
|
||||
{
|
||||
scrollTop.animation.play('rifle', true, false);
|
||||
}
|
||||
|
||||
if (FlxMath.inBounds(scrollProgress, 1450, 2000) && scrollTop.animation.curAnim.name != 'rocket launcher')
|
||||
{
|
||||
scrollTop.animation.play('rocket launcher', true, false);
|
||||
}
|
||||
|
||||
if (FlxMath.inBounds(scrollProgress, 0, 300) && scrollTop.animation.curAnim.name != 'uzi')
|
||||
{
|
||||
scrollTop.animation.play('uzi', true, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -9,6 +9,7 @@ import funkin.play.scoring.Scoring.ScoringRank;
|
|||
* An object used to retrieve data about a playable character (also known as "weeks").
|
||||
* Can be scripted to override each function, for custom behavior.
|
||||
*/
|
||||
@:nullSafety
|
||||
class PlayableCharacter implements IRegistryEntry<PlayerData>
|
||||
{
|
||||
/**
|
||||
|
@ -19,7 +20,7 @@ class PlayableCharacter implements IRegistryEntry<PlayerData>
|
|||
/**
|
||||
* Playable character data as parsed from the JSON file.
|
||||
*/
|
||||
public final _data:PlayerData;
|
||||
public final _data:Null<PlayerData>;
|
||||
|
||||
/**
|
||||
* @param id The ID of the JSON file to parse.
|
||||
|
@ -41,7 +42,7 @@ class PlayableCharacter implements IRegistryEntry<PlayerData>
|
|||
public function getName():String
|
||||
{
|
||||
// TODO: Maybe add localization support?
|
||||
return _data.name;
|
||||
return _data?.name ?? "Unknown";
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -50,7 +51,7 @@ class PlayableCharacter implements IRegistryEntry<PlayerData>
|
|||
*/
|
||||
public function getOwnedCharacterIds():Array<String>
|
||||
{
|
||||
return _data.ownedChars;
|
||||
return _data?.ownedChars ?? [];
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -59,17 +60,17 @@ class PlayableCharacter implements IRegistryEntry<PlayerData>
|
|||
*/
|
||||
public function shouldShowUnownedChars():Bool
|
||||
{
|
||||
return _data.showUnownedChars;
|
||||
return _data?.showUnownedChars ?? false;
|
||||
}
|
||||
|
||||
public function shouldShowCharacter(id:String):Bool
|
||||
{
|
||||
if (_data.ownedChars.contains(id))
|
||||
if (getOwnedCharacterIds().contains(id))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (_data.showUnownedChars)
|
||||
if (shouldShowUnownedChars())
|
||||
{
|
||||
var result = !PlayerRegistry.instance.isCharacterOwned(id);
|
||||
return result;
|
||||
|
@ -78,19 +79,25 @@ class PlayableCharacter implements IRegistryEntry<PlayerData>
|
|||
return false;
|
||||
}
|
||||
|
||||
public function getFreeplayDJData():PlayerFreeplayDJData
|
||||
public function getFreeplayStyleID():String
|
||||
{
|
||||
return _data.freeplayDJ;
|
||||
return _data?.freeplayStyle ?? Constants.DEFAULT_FREEPLAY_STYLE;
|
||||
}
|
||||
|
||||
public function getFreeplayDJData():Null<PlayerFreeplayDJData>
|
||||
{
|
||||
return _data?.freeplayDJ;
|
||||
}
|
||||
|
||||
public function getFreeplayDJText(index:Int):String
|
||||
{
|
||||
return _data.freeplayDJ.getFreeplayDJText(index);
|
||||
// Silly little placeholder
|
||||
return _data?.freeplayDJ?.getFreeplayDJText(index) ?? 'GET FREAKY ON A FRIDAY';
|
||||
}
|
||||
|
||||
public function getCharSelectData():PlayerCharSelectData
|
||||
public function getCharSelectData():Null<PlayerCharSelectData>
|
||||
{
|
||||
return _data.charSelect;
|
||||
return _data?.charSelect;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -99,7 +106,7 @@ class PlayableCharacter implements IRegistryEntry<PlayerData>
|
|||
*/
|
||||
public function getResultsAnimationDatas(rank:ScoringRank):Array<PlayerResultsAnimationData>
|
||||
{
|
||||
if (_data.results == null)
|
||||
if (_data == null || _data.results == null)
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
@ -119,12 +126,33 @@ class PlayableCharacter implements IRegistryEntry<PlayerData>
|
|||
}
|
||||
}
|
||||
|
||||
public function getResultsMusicPath(rank:ScoringRank):String
|
||||
{
|
||||
switch (rank)
|
||||
{
|
||||
case PERFECT_GOLD:
|
||||
return _data?.results?.music?.PERFECT_GOLD ?? "resultsPERFECT";
|
||||
case PERFECT:
|
||||
return _data?.results?.music?.PERFECT ?? "resultsPERFECT";
|
||||
case EXCELLENT:
|
||||
return _data?.results?.music?.EXCELLENT ?? "resultsEXCELLENT";
|
||||
case GREAT:
|
||||
return _data?.results?.music?.GREAT ?? "resultsNORMAL";
|
||||
case GOOD:
|
||||
return _data?.results?.music?.GOOD ?? "resultsNORMAL";
|
||||
case SHIT:
|
||||
return _data?.results?.music?.SHIT ?? "resultsSHIT";
|
||||
default:
|
||||
return _data?.results?.music?.GOOD ?? "resultsNORMAL";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether this character is unlocked.
|
||||
*/
|
||||
public function isUnlocked():Bool
|
||||
{
|
||||
return _data.unlocked;
|
||||
return _data?.unlocked ?? true;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -110,7 +110,17 @@ class MainMenuState extends MusicBeatState
|
|||
FlxTransitionableState.skipNextTransIn = true;
|
||||
FlxTransitionableState.skipNextTransOut = true;
|
||||
|
||||
openSubState(new FreeplayState());
|
||||
#if FEATURE_DEBUG_FUNCTIONS
|
||||
// Debug function: Hold SHIFT when selecting Freeplay to swap character without the char select menu
|
||||
var targetCharacter:Null<String> = (FlxG.keys.pressed.SHIFT) ? (FreeplayState.rememberedCharacterId == "pico" ? "bf" : "pico") : null;
|
||||
#else
|
||||
var targetCharacter:Null<String> = null;
|
||||
#end
|
||||
|
||||
openSubState(new FreeplayState(
|
||||
{
|
||||
character: targetCharacter
|
||||
}));
|
||||
});
|
||||
|
||||
#if CAN_OPEN_LINKS
|
||||
|
|
|
@ -258,6 +258,11 @@ class Constants
|
|||
*/
|
||||
public static final DEFAULT_NOTE_STYLE:String = 'funkin';
|
||||
|
||||
/**
|
||||
* The default freeplay style for characters.
|
||||
*/
|
||||
public static final DEFAULT_FREEPLAY_STYLE:String = 'bf';
|
||||
|
||||
/**
|
||||
* The default pixel note style for songs.
|
||||
*/
|
||||
|
|
|
@ -33,4 +33,19 @@ class ReflectUtil
|
|||
{
|
||||
return Type.getClassName(Type.getClass(obj));
|
||||
}
|
||||
|
||||
public static function getAnonymousFieldsOf(obj:Dynamic):Array<String>
|
||||
{
|
||||
return Reflect.fields(obj);
|
||||
}
|
||||
|
||||
public static function getAnonymousField(obj:Dynamic, name:String):Dynamic
|
||||
{
|
||||
return Reflect.field(obj, name);
|
||||
}
|
||||
|
||||
public static function hasAnonymousField(obj:Dynamic, name:String):Bool
|
||||
{
|
||||
return Reflect.hasField(obj, name);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue