mirror of
https://github.com/FunkinCrew/Funkin.git
synced 2024-11-30 03:25:47 -05:00
Merge pull request #347 from FunkinCrew/feature/blazin-cutscene
Blazin' Cutscene
This commit is contained in:
commit
b88d0b063b
45 changed files with 747 additions and 251 deletions
8
.prettierignore
Normal file
8
.prettierignore
Normal file
|
@ -0,0 +1,8 @@
|
|||
# Ignore artifacts
|
||||
export
|
||||
|
||||
# Ignore all asset files (including FlxAnimate JSONs)
|
||||
assets
|
||||
|
||||
# Don't ignore data files
|
||||
!assets/preload/data
|
26
.vscode/settings.json
vendored
26
.vscode/settings.json
vendored
|
@ -93,7 +93,7 @@
|
|||
{
|
||||
"label": "Windows / Debug",
|
||||
"target": "windows",
|
||||
"args": ["-debug"]
|
||||
"args": ["-debug", "-DFORCE_DEBUG_VERSION"]
|
||||
},
|
||||
{
|
||||
"label": "HashLink / Debug",
|
||||
|
@ -103,7 +103,7 @@
|
|||
{
|
||||
"label": "Windows / Debug (FlxAnimate Test)",
|
||||
"target": "windows",
|
||||
"args": ["-debug", "-DANIMATE"]
|
||||
"args": ["-debug", "-DANIMATE", "-DFORCE_DEBUG_VERSION"]
|
||||
},
|
||||
{
|
||||
"label": "HashLink / Debug (FlxAnimate Test)",
|
||||
|
@ -113,7 +113,7 @@
|
|||
{
|
||||
"label": "Windows / Debug (Straight to Freeplay)",
|
||||
"target": "windows",
|
||||
"args": ["-debug", "-DFREEPLAY"]
|
||||
"args": ["-debug", "-DFREEPLAY", "-DFORCE_DEBUG_VERSION"]
|
||||
},
|
||||
{
|
||||
"label": "HashLink / Debug (Straight to Freeplay)",
|
||||
|
@ -123,7 +123,11 @@
|
|||
{
|
||||
"label": "Windows / Debug (Straight to Play - Bopeebo Normal)",
|
||||
"target": "windows",
|
||||
"args": ["-debug", "-DSONG=bopeebo -DDIFFICULTY=normal"]
|
||||
"args": [
|
||||
"-debug",
|
||||
"-DSONG=bopeebo -DDIFFICULTY=normal",
|
||||
"-DFORCE_DEBUG_VERSION"
|
||||
]
|
||||
},
|
||||
{
|
||||
"label": "HashLink / Debug (Straight to Play - Bopeebo Normal)",
|
||||
|
@ -133,7 +137,7 @@
|
|||
{
|
||||
"label": "Windows / Debug (Conversation Test)",
|
||||
"target": "windows",
|
||||
"args": ["-debug", "-DDIALOGUE"]
|
||||
"args": ["-debug", "-DDIALOGUE", "-DFORCE_DEBUG_VERSION"]
|
||||
},
|
||||
{
|
||||
"label": "HashLink / Debug (Conversation Test)",
|
||||
|
@ -143,7 +147,7 @@
|
|||
{
|
||||
"label": "Windows / Debug (Straight to Chart Editor)",
|
||||
"target": "windows",
|
||||
"args": ["-debug", "-DCHARTING"]
|
||||
"args": ["-debug", "-DCHARTING", "-DFORCE_DEBUG_VERSION"]
|
||||
},
|
||||
{
|
||||
"label": "HashLink / Debug (Straight to Chart Editor)",
|
||||
|
@ -153,7 +157,7 @@
|
|||
{
|
||||
"label": "Windows / Debug (Straight to Animation Editor)",
|
||||
"target": "windows",
|
||||
"args": ["-debug", "-DANIMDEBUG"]
|
||||
"args": ["-debug", "-DANIMDEBUG", "-DFORCE_DEBUG_VERSION"]
|
||||
},
|
||||
{
|
||||
"label": "HashLink / Debug (Straight to Animation Editor)",
|
||||
|
@ -163,7 +167,7 @@
|
|||
{
|
||||
"label": "Windows / Debug (Latency Test)",
|
||||
"target": "windows",
|
||||
"args": ["-debug", "-DLATENCY"]
|
||||
"args": ["-debug", "-DLATENCY", "-DFORCE_DEBUG_VERSION"]
|
||||
},
|
||||
{
|
||||
"label": "HashLink / Debug (Latency Test)",
|
||||
|
@ -173,7 +177,7 @@
|
|||
{
|
||||
"label": "Windows / Debug (Waveform Test)",
|
||||
"target": "windows",
|
||||
"args": ["-debug", "-DWAVEFORM"]
|
||||
"args": ["-debug", "-DWAVEFORM", "-DFORCE_DEBUG_VERSION"]
|
||||
},
|
||||
{
|
||||
"label": "HashLink / Debug (Waveform Test)",
|
||||
|
@ -183,12 +187,12 @@
|
|||
{
|
||||
"label": "HTML5 / Debug",
|
||||
"target": "html5",
|
||||
"args": ["-debug"]
|
||||
"args": ["-debug", "-DFORCE_DEBUG_VERSION"]
|
||||
},
|
||||
{
|
||||
"label": "HTML5 / Debug (Watch)",
|
||||
"target": "html5",
|
||||
"args": ["-debug", "-watch"]
|
||||
"args": ["-debug", "-watch", "-DFORCE_DEBUG_VERSION"]
|
||||
}
|
||||
],
|
||||
"cmake.configureOnOpen": false,
|
||||
|
|
|
@ -108,7 +108,7 @@
|
|||
<haxelib name="flixel-text-input" /> <!-- Improved text field rendering for HaxeUI -->
|
||||
<haxelib name="polymod" /> <!-- Modding framework -->
|
||||
<haxelib name="flxanimate" /> <!-- Texture atlas rendering -->
|
||||
<haxelib name="hxCodec" if="desktop release" /> <!-- Video playback -->
|
||||
<haxelib name="hxCodec" if="desktop" /> <!-- Video playback -->
|
||||
|
||||
<haxelib name="json2object" /> <!-- JSON parsing -->
|
||||
<haxelib name="thx.semver" /> <!-- Version string handling -->
|
||||
|
|
2
assets
2
assets
|
@ -1 +1 @@
|
|||
Subproject commit cb0fbb56b9667f68a9776a216c16a4e2b29f7096
|
||||
Subproject commit f8c2595844eff9375b522f117bfdadbdc6728c49
|
4
hmm.json
4
hmm.json
|
@ -54,14 +54,14 @@
|
|||
"name": "haxeui-core",
|
||||
"type": "git",
|
||||
"dir": null,
|
||||
"ref": "8a7846b",
|
||||
"ref": "0212d8fdfcafeb5f0d5a41e1ddba8ff21d0e183b",
|
||||
"url": "https://github.com/haxeui/haxeui-core"
|
||||
},
|
||||
{
|
||||
"name": "haxeui-flixel",
|
||||
"type": "git",
|
||||
"dir": null,
|
||||
"ref": "e9f880522e27134b29df4067f82df7d7e5237b70",
|
||||
"ref": "63a906a6148958dbfde8c7b48d90b0693767fd95",
|
||||
"url": "https://github.com/haxeui/haxeui-flixel"
|
||||
},
|
||||
{
|
||||
|
|
|
@ -146,12 +146,11 @@ class InitState extends FlxState
|
|||
#end
|
||||
|
||||
// Make errors and warnings less annoying.
|
||||
#if FORCE_DEBUG_VERSION
|
||||
// Forcing this always since I have never been happy to have the debugger to pop up
|
||||
LogStyle.ERROR.openConsole = false;
|
||||
LogStyle.ERROR.errorSound = null;
|
||||
LogStyle.WARNING.openConsole = false;
|
||||
LogStyle.WARNING.errorSound = null;
|
||||
#end
|
||||
|
||||
//
|
||||
// FLIXEL TRANSITIONS
|
||||
|
|
|
@ -16,6 +16,20 @@ class Paths
|
|||
currentLevel = name.toLowerCase();
|
||||
}
|
||||
|
||||
public static function stripLibrary(path:String):String
|
||||
{
|
||||
var parts = path.split(':');
|
||||
if (parts.length < 2) return path;
|
||||
return parts[1];
|
||||
}
|
||||
|
||||
public static function getLibrary(path:String):String
|
||||
{
|
||||
var parts = path.split(':');
|
||||
if (parts.length < 2) return "preload";
|
||||
return parts[0];
|
||||
}
|
||||
|
||||
static function getPath(file:String, type:AssetType, library:Null<String>)
|
||||
{
|
||||
if (library != null) return getLibraryPath(file, library);
|
||||
|
|
|
@ -23,7 +23,7 @@ import openfl.utils.AssetType;
|
|||
@:nullSafety
|
||||
class FunkinSound extends FlxSound implements ICloneable<FunkinSound>
|
||||
{
|
||||
static final MAX_VOLUME:Float = 2.0;
|
||||
static final MAX_VOLUME:Float = 1.0;
|
||||
|
||||
static var cache(default, null):FlxTypedGroup<FunkinSound> = new FlxTypedGroup<FunkinSound>();
|
||||
|
||||
|
@ -40,7 +40,6 @@ class FunkinSound extends FlxSound implements ICloneable<FunkinSound>
|
|||
override function set_volume(value:Float):Float
|
||||
{
|
||||
// Uncap the volume.
|
||||
fixMaxVolume();
|
||||
_volume = FlxMath.bound(value, 0.0, MAX_VOLUME);
|
||||
updateTransform();
|
||||
return _volume;
|
||||
|
@ -126,17 +125,6 @@ class FunkinSound extends FlxSound implements ICloneable<FunkinSound>
|
|||
return this;
|
||||
}
|
||||
|
||||
function fixMaxVolume():Void
|
||||
{
|
||||
#if lime_openal
|
||||
// This code is pretty fragile, it reaches through 5 layers of private access.
|
||||
@:privateAccess
|
||||
var handle = this?._channel?.__source?.__backend?.handle;
|
||||
if (handle == null) return;
|
||||
lime.media.openal.AL.sourcef(handle, lime.media.openal.AL.MAX_GAIN, MAX_VOLUME);
|
||||
#end
|
||||
}
|
||||
|
||||
public override function play(forceRestart:Bool = false, startTime:Float = 0, ?endTime:Float):FunkinSound
|
||||
{
|
||||
if (!exists) return this;
|
||||
|
|
|
@ -187,6 +187,8 @@ class WaveformData
|
|||
*/
|
||||
public function merge(that:WaveformData):WaveformData
|
||||
{
|
||||
if (that == null) return this.clone();
|
||||
|
||||
var result = this.clone([]);
|
||||
|
||||
for (channelIndex in 0...this.channels)
|
||||
|
|
|
@ -61,7 +61,16 @@ abstract class BaseRegistry<T:(IRegistryEntry<J> & Constructible<EntryConstructo
|
|||
|
||||
for (entryCls in scriptedEntryClassNames)
|
||||
{
|
||||
var entry:T = createScriptedEntry(entryCls);
|
||||
var entry:Null<T> = null;
|
||||
try
|
||||
{
|
||||
entry = createScriptedEntry(entryCls);
|
||||
}
|
||||
catch (e:Dynamic)
|
||||
{
|
||||
log('Failed to create scripted entry (${entryCls})');
|
||||
continue;
|
||||
}
|
||||
|
||||
if (entry != null)
|
||||
{
|
||||
|
@ -196,6 +205,11 @@ abstract class BaseRegistry<T:(IRegistryEntry<J> & Constructible<EntryConstructo
|
|||
*/
|
||||
public function parseEntryDataWithMigration(id:String, version:thx.semver.Version):Null<J>
|
||||
{
|
||||
if (version == null)
|
||||
{
|
||||
throw '[${registryId}] Entry ${id} could not be JSON-parsed or does not have a parseable version.';
|
||||
}
|
||||
|
||||
// If a version rule is not specified, do not check against it.
|
||||
if (versionRule == null || VersionUtil.validateVersion(version, versionRule))
|
||||
{
|
||||
|
|
|
@ -108,8 +108,8 @@ class SongEventRegistry
|
|||
|
||||
public static function handleEvent(data:SongEventData):Void
|
||||
{
|
||||
var eventType:String = data.event;
|
||||
var eventHandler:SongEvent = eventCache.get(eventType);
|
||||
var eventKind:String = data.eventKind;
|
||||
var eventHandler:SongEvent = eventCache.get(eventKind);
|
||||
|
||||
if (eventHandler != null)
|
||||
{
|
||||
|
@ -117,7 +117,7 @@ class SongEventRegistry
|
|||
}
|
||||
else
|
||||
{
|
||||
trace('WARNING: No event handler for event with id: ${eventType}');
|
||||
trace('WARNING: No event handler for event with kind: ${eventKind}');
|
||||
}
|
||||
|
||||
data.activated = true;
|
||||
|
@ -148,6 +148,29 @@ class SongEventRegistry
|
|||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* The currentTime has jumped far ahead or back.
|
||||
* If we moved back in time, we need to reset all the events in that space.
|
||||
* If we moved forward in time, we need to skip all the events in that space.
|
||||
*/
|
||||
public static function handleSkippedEvents(events:Array<SongEventData>, currentTime:Float):Void
|
||||
{
|
||||
for (event in events)
|
||||
{
|
||||
// Deactivate future events.
|
||||
if (event.time > currentTime)
|
||||
{
|
||||
event.activated = false;
|
||||
}
|
||||
|
||||
// Skip past events.
|
||||
if (event.time < currentTime)
|
||||
{
|
||||
event.activated = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset activation of all the provided events.
|
||||
*/
|
||||
|
|
|
@ -110,7 +110,8 @@ class SongMetadata implements ICloneable<SongMetadata>
|
|||
*/
|
||||
public function serialize(pretty:Bool = true):String
|
||||
{
|
||||
var writer = new json2object.JsonWriter<SongMetadata>();
|
||||
var ignoreNullOptionals = true;
|
||||
var writer = new json2object.JsonWriter<SongMetadata>(ignoreNullOptionals);
|
||||
// I believe @:jignored should be iggnored by the writer?
|
||||
// var output = this.clone();
|
||||
// output.variation = null; // Not sure how to make a field optional on the reader and ignored on the writer.
|
||||
|
@ -597,7 +598,8 @@ class SongChartData implements ICloneable<SongChartData>
|
|||
*/
|
||||
public function serialize(pretty:Bool = true):String
|
||||
{
|
||||
var writer = new json2object.JsonWriter<SongChartData>();
|
||||
var ignoreNullOptionals = true;
|
||||
var writer = new json2object.JsonWriter<SongChartData>(ignoreNullOptionals);
|
||||
return writer.write(this, pretty ? ' ' : null);
|
||||
}
|
||||
|
||||
|
@ -648,7 +650,7 @@ class SongEventDataRaw implements ICloneable<SongEventDataRaw>
|
|||
* Custom events can be added by scripts with the `ScriptedSongEvent` class.
|
||||
*/
|
||||
@:alias("e")
|
||||
public var event:String;
|
||||
public var eventKind:String;
|
||||
|
||||
/**
|
||||
* The data for the event.
|
||||
|
@ -668,10 +670,10 @@ class SongEventDataRaw implements ICloneable<SongEventDataRaw>
|
|||
@:jignored
|
||||
public var activated:Bool = false;
|
||||
|
||||
public function new(time:Float, event:String, value:Dynamic = null)
|
||||
public function new(time:Float, eventKind:String, value:Dynamic = null)
|
||||
{
|
||||
this.time = time;
|
||||
this.event = event;
|
||||
this.eventKind = eventKind;
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
|
@ -687,19 +689,19 @@ class SongEventDataRaw implements ICloneable<SongEventDataRaw>
|
|||
|
||||
public function clone():SongEventDataRaw
|
||||
{
|
||||
return new SongEventDataRaw(this.time, this.event, this.value);
|
||||
return new SongEventDataRaw(this.time, this.eventKind, this.value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrap SongEventData in an abstract so we can overload operators.
|
||||
*/
|
||||
@:forward(time, event, value, activated, getStepTime, clone)
|
||||
@:forward(time, eventKind, value, activated, getStepTime, clone)
|
||||
abstract SongEventData(SongEventDataRaw) from SongEventDataRaw to SongEventDataRaw
|
||||
{
|
||||
public function new(time:Float, event:String, value:Dynamic = null)
|
||||
public function new(time:Float, eventKind:String, value:Dynamic = null)
|
||||
{
|
||||
this = new SongEventDataRaw(time, event, value);
|
||||
this = new SongEventDataRaw(time, eventKind, value);
|
||||
}
|
||||
|
||||
public inline function valueAsStruct(?defaultKey:String = "key"):Dynamic
|
||||
|
@ -726,12 +728,12 @@ abstract SongEventData(SongEventDataRaw) from SongEventDataRaw to SongEventDataR
|
|||
|
||||
public inline function getHandler():Null<SongEvent>
|
||||
{
|
||||
return SongEventRegistry.getEvent(this.event);
|
||||
return SongEventRegistry.getEvent(this.eventKind);
|
||||
}
|
||||
|
||||
public inline function getSchema():Null<SongEventSchema>
|
||||
{
|
||||
return SongEventRegistry.getEventSchema(this.event);
|
||||
return SongEventRegistry.getEventSchema(this.eventKind);
|
||||
}
|
||||
|
||||
public inline function getDynamic(key:String):Null<Dynamic>
|
||||
|
@ -784,7 +786,7 @@ abstract SongEventData(SongEventDataRaw) from SongEventDataRaw to SongEventDataR
|
|||
var eventHandler = getHandler();
|
||||
var eventSchema = getSchema();
|
||||
|
||||
if (eventSchema == null) return 'Unknown Event: ${this.event}';
|
||||
if (eventSchema == null) return 'Unknown Event: ${this.eventKind}';
|
||||
|
||||
var result = '${eventHandler.getTitle()}';
|
||||
|
||||
|
@ -809,19 +811,19 @@ abstract SongEventData(SongEventDataRaw) from SongEventDataRaw to SongEventDataR
|
|||
|
||||
public function clone():SongEventData
|
||||
{
|
||||
return new SongEventData(this.time, this.event, this.value);
|
||||
return new SongEventData(this.time, this.eventKind, this.value);
|
||||
}
|
||||
|
||||
@:op(A == B)
|
||||
public function op_equals(other:SongEventData):Bool
|
||||
{
|
||||
return this.time == other.time && this.event == other.event && this.value == other.value;
|
||||
return this.time == other.time && this.eventKind == other.eventKind && this.value == other.value;
|
||||
}
|
||||
|
||||
@:op(A != B)
|
||||
public function op_notEquals(other:SongEventData):Bool
|
||||
{
|
||||
return this.time != other.time || this.event != other.event || this.value != other.value;
|
||||
return this.time != other.time || this.eventKind != other.eventKind || this.value != other.value;
|
||||
}
|
||||
|
||||
@:op(A > B)
|
||||
|
@ -853,7 +855,7 @@ abstract SongEventData(SongEventDataRaw) from SongEventDataRaw to SongEventDataR
|
|||
*/
|
||||
public function toString():String
|
||||
{
|
||||
return 'SongEventData(${this.time}ms, ${this.event}: ${this.value})';
|
||||
return 'SongEventData(${this.time}ms, ${this.eventKind}: ${this.value})';
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1022,6 +1024,12 @@ class SongNoteDataRaw implements ICloneable<SongNoteDataRaw>
|
|||
{
|
||||
return new SongNoteDataRaw(this.time, this.data, this.length, this.kind);
|
||||
}
|
||||
|
||||
public function toString():String
|
||||
{
|
||||
return 'SongNoteData(${this.time}ms, ' + (this.length > 0 ? '[${this.length}ms hold]' : '') + ' ${this.data}'
|
||||
+ (this.kind != '' ? ' [kind: ${this.kind}])' : ')');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -47,7 +47,7 @@ class SongDataUtils
|
|||
public static function offsetSongEventData(events:Array<SongEventData>, offset:Float):Array<SongEventData>
|
||||
{
|
||||
return events.map(function(event:SongEventData):SongEventData {
|
||||
return new SongEventData(event.time + offset, event.event, event.value);
|
||||
return new SongEventData(event.time + offset, event.eventKind, event.value);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -6,9 +6,23 @@ import flixel.graphics.FlxGraphic;
|
|||
|
||||
/**
|
||||
* An FlxSprite with additional functionality.
|
||||
* - A more efficient method for creating solid color sprites.
|
||||
* - TODO: Better cache handling for textures.
|
||||
*/
|
||||
class FunkinSprite extends FlxSprite
|
||||
{
|
||||
/**
|
||||
* An internal list of all the textures cached with `cacheTexture`.
|
||||
* This excludes any temporary textures like those from `FlxText` or `makeSolidColor`.
|
||||
*/
|
||||
static var currentCachedTextures:Map<String, FlxGraphic> = [];
|
||||
|
||||
/**
|
||||
* An internal list of textures that were cached in the previous state.
|
||||
* We don't know whether we want to keep them cached or not.
|
||||
*/
|
||||
static var previousCachedTextures:Map<String, FlxGraphic> = [];
|
||||
|
||||
/**
|
||||
* @param x Starting X position
|
||||
* @param y Starting Y position
|
||||
|
@ -18,19 +32,184 @@ class FunkinSprite extends FlxSprite
|
|||
super(x, y);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new FunkinSprite with a static texture.
|
||||
* @param x The starting X position.
|
||||
* @param y The starting Y position.
|
||||
* @param key The key of the texture to load.
|
||||
* @return The new FunkinSprite.
|
||||
*/
|
||||
public static function create(x:Float = 0.0, y:Float = 0.0, key:String):FunkinSprite
|
||||
{
|
||||
var sprite = new FunkinSprite(x, y);
|
||||
sprite.loadTexture(key);
|
||||
return sprite;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new FunkinSprite with a Sparrow atlas animated texture.
|
||||
* @param x The starting X position.
|
||||
* @param y The starting Y position.
|
||||
* @param key The key of the texture to load.
|
||||
* @return The new FunkinSprite.
|
||||
*/
|
||||
public static function createSparrow(x:Float = 0.0, y:Float = 0.0, key:String):FunkinSprite
|
||||
{
|
||||
var sprite = new FunkinSprite(x, y);
|
||||
sprite.loadSparrow(key);
|
||||
return sprite;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new FunkinSprite with a Packer atlas animated texture.
|
||||
* @param x The starting X position.
|
||||
* @param y The starting Y position.
|
||||
* @param key The key of the texture to load.
|
||||
* @return The new FunkinSprite.
|
||||
*/
|
||||
public static function createPacker(x:Float = 0.0, y:Float = 0.0, key:String):FunkinSprite
|
||||
{
|
||||
var sprite = new FunkinSprite(x, y);
|
||||
sprite.loadPacker(key);
|
||||
return sprite;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load a static image as the sprite's texture.
|
||||
* @param key The key of the texture to load.
|
||||
* @return This sprite, for chaining.
|
||||
*/
|
||||
public function loadTexture(key:String):FunkinSprite
|
||||
{
|
||||
if (!isTextureCached(key)) FlxG.log.warn('Texture not cached, may experience stuttering! $key');
|
||||
|
||||
loadGraphic(key);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load an animated texture (Sparrow atlas spritesheet) as the sprite's texture.
|
||||
* @param key The key of the texture to load.
|
||||
* @return This sprite, for chaining.
|
||||
*/
|
||||
public function loadSparrow(key:String):FunkinSprite
|
||||
{
|
||||
var graphicKey = Paths.image(key);
|
||||
if (!isTextureCached(graphicKey)) FlxG.log.warn('Texture not cached, may experience stuttering! $graphicKey');
|
||||
|
||||
this.frames = Paths.getSparrowAtlas(key);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load an animated texture (Packer atlas spritesheet) as the sprite's texture.
|
||||
* @param key The key of the texture to load.
|
||||
* @return This sprite, for chaining.
|
||||
*/
|
||||
public function loadPacker(key:String):FunkinSprite
|
||||
{
|
||||
var graphicKey = Paths.image(key);
|
||||
if (!isTextureCached(graphicKey)) FlxG.log.warn('Texture not cached, may experience stuttering! $graphicKey');
|
||||
|
||||
this.frames = Paths.getPackerAtlas(key);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public static function isTextureCached(key:String):Bool
|
||||
{
|
||||
return FlxG.bitmap.get(key) != null;
|
||||
}
|
||||
|
||||
public static function cacheTexture(key:String):Void
|
||||
{
|
||||
// We don't want to cache the same texture twice.
|
||||
if (currentCachedTextures.exists(key)) return;
|
||||
|
||||
if (previousCachedTextures.exists(key))
|
||||
{
|
||||
// Move the graphic from the previous cache to the current cache.
|
||||
var graphic = previousCachedTextures.get(key);
|
||||
previousCachedTextures.remove(key);
|
||||
currentCachedTextures.set(key, graphic);
|
||||
return;
|
||||
}
|
||||
|
||||
// Else, texture is currently uncached.
|
||||
var graphic = flixel.graphics.FlxGraphic.fromAssetKey(key, false, null, true);
|
||||
if (graphic == null)
|
||||
{
|
||||
FlxG.log.warn('Failed to cache graphic: $key');
|
||||
}
|
||||
else
|
||||
{
|
||||
trace('Successfully cached graphic: $key');
|
||||
graphic.persist = true;
|
||||
currentCachedTextures.set(key, graphic);
|
||||
}
|
||||
}
|
||||
|
||||
public static function cacheSparrow(key:String):Void
|
||||
{
|
||||
cacheTexture(Paths.image(key));
|
||||
}
|
||||
|
||||
public static function cachePacker(key:String):Void
|
||||
{
|
||||
cacheTexture(Paths.image(key));
|
||||
}
|
||||
|
||||
/**
|
||||
* Call this, then `cacheTexture` to keep the textures we still need, then `purgeCache` to remove the textures that we won't be using anymore.
|
||||
*/
|
||||
public static function preparePurgeCache():Void
|
||||
{
|
||||
previousCachedTextures = currentCachedTextures;
|
||||
currentCachedTextures = [];
|
||||
}
|
||||
|
||||
public static function purgeCache():Void
|
||||
{
|
||||
// Everything that is in previousCachedTextures but not in currentCachedTextures should be destroyed.
|
||||
for (graphicKey in previousCachedTextures.keys())
|
||||
{
|
||||
var graphic = previousCachedTextures.get(graphicKey);
|
||||
FlxG.bitmap.remove(graphic);
|
||||
graphic.destroy();
|
||||
previousCachedTextures.remove(graphicKey);
|
||||
}
|
||||
}
|
||||
|
||||
static function isGraphicCached(graphic:FlxGraphic):Bool
|
||||
{
|
||||
if (graphic == null) return false;
|
||||
var result = FlxG.bitmap.get(graphic.key);
|
||||
if (result == null) return false;
|
||||
if (result != graphic)
|
||||
{
|
||||
FlxG.log.warn('Cached graphic does not match original: ${graphic.key}');
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Acts similarly to `makeGraphic`, but with improved memory usage,
|
||||
* at the expense of not being able to paint onto the sprite.
|
||||
* at the expense of not being able to paint onto the resulting sprite.
|
||||
*
|
||||
* @param width The target width of the sprite.
|
||||
* @param height The target height of the sprite.
|
||||
* @param color The color to fill the sprite with.
|
||||
* @return This sprite, for chaining.
|
||||
*/
|
||||
public function makeSolidColor(width:Int, height:Int, color:FlxColor = FlxColor.WHITE):FunkinSprite
|
||||
{
|
||||
// Create a tiny solid color graphic and scale it up to the desired size.
|
||||
var graphic:FlxGraphic = FlxG.bitmap.create(2, 2, color, false, 'solid#${color.toHexString(true, false)}');
|
||||
frames = graphic.imageFrame;
|
||||
scale.set(width / 2, height / 2);
|
||||
scale.set(width / 2.0, height / 2.0);
|
||||
updateHitbox();
|
||||
|
||||
return this;
|
||||
|
|
|
@ -18,7 +18,7 @@ class FlxAtlasSprite extends FlxAnimate
|
|||
// ?OnComplete:Void -> Void,
|
||||
ShowPivot: #if debug false #else false #end,
|
||||
Antialiasing: true,
|
||||
ScrollFactor: new FlxPoint(1, 1),
|
||||
ScrollFactor: null,
|
||||
// Offset: new FlxPoint(0, 0), // This is just FlxSprite.offset
|
||||
};
|
||||
|
||||
|
@ -55,8 +55,9 @@ class FlxAtlasSprite extends FlxAnimate
|
|||
*/
|
||||
public function listAnimations():Array<String>
|
||||
{
|
||||
// return this.anim.getFrameLabels();
|
||||
return [""];
|
||||
if (this.anim == null) return [];
|
||||
return this.anim.getFrameLabels();
|
||||
// return [""];
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -82,8 +83,10 @@ class FlxAtlasSprite extends FlxAnimate
|
|||
* @param restart Whether to restart the animation if it is already playing.
|
||||
* @param ignoreOther Whether to ignore all other animation inputs, until this one is done playing
|
||||
*/
|
||||
public function playAnimation(id:String, restart:Bool = false, ignoreOther:Bool = false):Void
|
||||
public function playAnimation(id:String, restart:Bool = false, ignoreOther:Bool = false, ?loop:Bool = false):Void
|
||||
{
|
||||
if (loop == null) loop = false;
|
||||
|
||||
// Skip if not allowed to play animations.
|
||||
if ((!canPlayOtherAnims && !ignoreOther)) return;
|
||||
|
||||
|
@ -110,15 +113,14 @@ class FlxAtlasSprite extends FlxAnimate
|
|||
return;
|
||||
}
|
||||
|
||||
// Stop the current animation if it is playing.
|
||||
// This includes removing existing frame callbacks.
|
||||
if (this.currentAnimation != null) this.stopAnimation();
|
||||
|
||||
// Add a callback to ensure `onAnimationFinish` is dispatched.
|
||||
addFrameCallback(getNextFrameLabel(id), function() {
|
||||
trace('Animation finished: ' + id);
|
||||
anim.callback = function(_, frame:Int) {
|
||||
if (frame == (anim.getFrameLabel(id).duration - 1) + anim.getFrameLabel(id).index)
|
||||
{
|
||||
if (loop) playAnimation(id, true, false, true);
|
||||
else
|
||||
onAnimationFinish.dispatch(id);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// Prevent other animations from playing if `ignoreOther` is true.
|
||||
if (ignoreOther) canPlayOtherAnims = false;
|
||||
|
@ -128,6 +130,11 @@ class FlxAtlasSprite extends FlxAnimate
|
|||
this.currentAnimation = id;
|
||||
}
|
||||
|
||||
override public function update(elapsed:Float)
|
||||
{
|
||||
super.update(elapsed);
|
||||
}
|
||||
|
||||
/**
|
||||
* Stops the current animation.
|
||||
*/
|
||||
|
@ -146,22 +153,22 @@ class FlxAtlasSprite extends FlxAnimate
|
|||
frameLabel.add(callback);
|
||||
}
|
||||
|
||||
inline function goToFrameLabel(label:String):Void
|
||||
function goToFrameLabel(label:String):Void
|
||||
{
|
||||
this.anim.goToFrameLabel(label);
|
||||
}
|
||||
|
||||
inline function getNextFrameLabel(label:String):String
|
||||
function getNextFrameLabel(label:String):String
|
||||
{
|
||||
return listAnimations()[(getLabelIndex(label) + 1) % listAnimations().length];
|
||||
}
|
||||
|
||||
inline function getLabelIndex(label:String):Int
|
||||
function getLabelIndex(label:String):Int
|
||||
{
|
||||
return listAnimations().indexOf(label);
|
||||
}
|
||||
|
||||
inline function goToFrameIndex(index:Int):Void
|
||||
function goToFrameIndex(index:Int):Void
|
||||
{
|
||||
this.anim.curFrame = index;
|
||||
}
|
||||
|
|
|
@ -106,12 +106,19 @@ class NoteScriptEvent extends ScriptEvent
|
|||
*/
|
||||
public var playSound(default, default):Bool;
|
||||
|
||||
/**
|
||||
* A multiplier to the health gained or lost from this note.
|
||||
* This affects both hits and misses. Remember that max health is 2.00.
|
||||
*/
|
||||
public var healthMulti:Float;
|
||||
|
||||
public function new(type:ScriptEventType, note:NoteSprite, comboCount:Int = 0, cancelable:Bool = false):Void
|
||||
{
|
||||
super(type, cancelable);
|
||||
this.note = note;
|
||||
this.comboCount = comboCount;
|
||||
this.playSound = true;
|
||||
this.healthMulti = 1.0;
|
||||
}
|
||||
|
||||
public override function toString():String
|
||||
|
@ -182,17 +189,17 @@ class SongEventScriptEvent extends ScriptEvent
|
|||
* The note associated with this event.
|
||||
* You cannot replace it, but you can edit it.
|
||||
*/
|
||||
public var event(default, null):funkin.data.song.SongData.SongEventData;
|
||||
public var eventData(default, null):funkin.data.song.SongData.SongEventData;
|
||||
|
||||
public function new(event:funkin.data.song.SongData.SongEventData):Void
|
||||
public function new(eventData:funkin.data.song.SongData.SongEventData):Void
|
||||
{
|
||||
super(SONG_EVENT, true);
|
||||
this.event = event;
|
||||
this.eventData = eventData;
|
||||
}
|
||||
|
||||
public override function toString():String
|
||||
{
|
||||
return 'SongEventScriptEvent(event=' + event + ')';
|
||||
return 'SongEventScriptEvent(event=' + eventData + ')';
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -3,6 +3,7 @@ package funkin.play;
|
|||
import flixel.tweens.FlxEase;
|
||||
import flixel.tweens.FlxTween;
|
||||
import flixel.FlxSprite;
|
||||
import funkin.graphics.FunkinSprite;
|
||||
import funkin.modding.events.ScriptEventDispatcher;
|
||||
import funkin.modding.module.ModuleHandler;
|
||||
import funkin.modding.events.ScriptEvent;
|
||||
|
@ -214,7 +215,7 @@ class Countdown
|
|||
|
||||
if (spritePath == null) return;
|
||||
|
||||
var countdownSprite:FlxSprite = new FlxSprite(0, 0).loadGraphic(Paths.image(spritePath));
|
||||
var countdownSprite:FunkinSprite = FunkinSprite.create(Paths.image(spritePath));
|
||||
countdownSprite.scrollFactor.set(0, 0);
|
||||
|
||||
if (isPixelStyle) countdownSprite.setGraphicSize(Std.int(countdownSprite.width * Constants.PIXEL_ART_SCALE));
|
||||
|
|
|
@ -4,16 +4,18 @@ import flixel.FlxG;
|
|||
import flixel.FlxObject;
|
||||
import flixel.FlxSprite;
|
||||
import flixel.sound.FlxSound;
|
||||
import funkin.ui.story.StoryMenuState;
|
||||
import funkin.audio.FunkinSound;
|
||||
import flixel.util.FlxColor;
|
||||
import flixel.util.FlxTimer;
|
||||
import funkin.graphics.FunkinSprite;
|
||||
import funkin.ui.MusicBeatSubState;
|
||||
import funkin.modding.events.ScriptEvent;
|
||||
import funkin.modding.events.ScriptEventDispatcher;
|
||||
import funkin.play.character.BaseCharacter;
|
||||
import funkin.play.PlayState;
|
||||
import funkin.ui.freeplay.FreeplayState;
|
||||
import funkin.play.character.BaseCharacter;
|
||||
import funkin.ui.MusicBeatSubState;
|
||||
import funkin.ui.story.StoryMenuState;
|
||||
import openfl.utils.Assets;
|
||||
|
||||
/**
|
||||
* A substate which renders over the PlayState when the player dies.
|
||||
|
@ -63,7 +65,7 @@ class GameOverSubState extends MusicBeatSubState
|
|||
/**
|
||||
* The music playing in the background of the state.
|
||||
*/
|
||||
var gameOverMusic:FlxSound = new FlxSound();
|
||||
var gameOverMusic:Null<FunkinSound> = null;
|
||||
|
||||
/**
|
||||
* Whether the player has confirmed and prepared to restart the level.
|
||||
|
@ -71,6 +73,11 @@ class GameOverSubState extends MusicBeatSubState
|
|||
*/
|
||||
var isEnding:Bool = false;
|
||||
|
||||
/**
|
||||
* Whether the death music is on its first loop.
|
||||
*/
|
||||
var isStarting:Bool = true;
|
||||
|
||||
var isChartingMode:Bool = false;
|
||||
|
||||
var transparent:Bool;
|
||||
|
@ -140,14 +147,16 @@ class GameOverSubState extends MusicBeatSubState
|
|||
// Set up the audio
|
||||
//
|
||||
|
||||
// Prepare the game over music.
|
||||
FlxG.sound.list.add(gameOverMusic);
|
||||
gameOverMusic.stop();
|
||||
|
||||
// The conductor now represents the BPM of the game over music.
|
||||
Conductor.instance.update(0);
|
||||
}
|
||||
|
||||
public function resetCameraZoom():Void
|
||||
{
|
||||
// Apply camera zoom level from stage data.
|
||||
FlxG.camera.zoom = PlayState?.instance?.currentStage?.camZoom ?? 1.0;
|
||||
}
|
||||
|
||||
var hasStartedAnimation:Bool = false;
|
||||
|
||||
override function update(elapsed:Float)
|
||||
|
@ -216,7 +225,7 @@ class GameOverSubState extends MusicBeatSubState
|
|||
}
|
||||
}
|
||||
|
||||
if (gameOverMusic.playing)
|
||||
if (gameOverMusic != null && gameOverMusic.playing)
|
||||
{
|
||||
// Match the conductor to the music.
|
||||
// This enables the stepHit and beatHit events.
|
||||
|
@ -291,24 +300,71 @@ class GameOverSubState extends MusicBeatSubState
|
|||
ScriptEventDispatcher.callEvent(boyfriend, event);
|
||||
}
|
||||
|
||||
/**
|
||||
* Rather than hardcoding stuff, we look for the presence of a music file
|
||||
* with the given suffix, and strip it down until we find one that's valid.
|
||||
*/
|
||||
function resolveMusicPath(suffix:String, starting:Bool = false, ending:Bool = false):Null<String>
|
||||
{
|
||||
var basePath = 'gameplay/gameover/gameOver';
|
||||
if (starting) basePath += 'Start';
|
||||
else if (ending) basePath += 'End';
|
||||
|
||||
var musicPath = Paths.music(basePath + suffix);
|
||||
while (!Assets.exists(musicPath) && suffix.length > 0)
|
||||
{
|
||||
suffix = suffix.split('-').slice(0, -1).join('-');
|
||||
musicPath = Paths.music(basePath + suffix);
|
||||
}
|
||||
if (!Assets.exists(musicPath)) return null;
|
||||
trace('Resolved music path: ' + musicPath);
|
||||
return musicPath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts the death music at the appropriate volume.
|
||||
* @param startingVolume
|
||||
*/
|
||||
function startDeathMusic(?startingVolume:Float = 1, force:Bool = false):Void
|
||||
public function startDeathMusic(startingVolume:Float = 1, force:Bool = false):Void
|
||||
{
|
||||
var musicPath = Paths.music('gameplay/gameover/gameOver' + musicSuffix);
|
||||
if (isEnding)
|
||||
var musicPath = resolveMusicPath(musicSuffix, isStarting, isEnding);
|
||||
var onComplete = null;
|
||||
if (isStarting)
|
||||
{
|
||||
musicPath = Paths.music('gameplay/gameover/gameOverEnd' + musicSuffix);
|
||||
if (musicPath == null)
|
||||
{
|
||||
isStarting = false;
|
||||
musicPath = resolveMusicPath(musicSuffix, isStarting, isEnding);
|
||||
}
|
||||
if (!gameOverMusic.playing || force)
|
||||
else
|
||||
{
|
||||
gameOverMusic.loadEmbedded(musicPath);
|
||||
isStarting = false;
|
||||
onComplete = function() {
|
||||
// We need to force to ensure that the non-starting music plays.
|
||||
startDeathMusic(1.0, true);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
if (musicPath == null)
|
||||
{
|
||||
trace('Could not find game over music!');
|
||||
return;
|
||||
}
|
||||
else if (gameOverMusic == null || !gameOverMusic.playing || force)
|
||||
{
|
||||
if (gameOverMusic != null) gameOverMusic.stop();
|
||||
gameOverMusic = FunkinSound.load(musicPath);
|
||||
gameOverMusic.volume = startingVolume;
|
||||
gameOverMusic.looped = !isEnding;
|
||||
gameOverMusic.looped = !(isEnding || isStarting);
|
||||
gameOverMusic.onComplete = onComplete;
|
||||
gameOverMusic.play();
|
||||
}
|
||||
else
|
||||
{
|
||||
@:privateAccess
|
||||
trace('Music already playing! ${gameOverMusic?._label}');
|
||||
}
|
||||
}
|
||||
|
||||
static var blueballed:Bool = false;
|
||||
|
@ -320,8 +376,15 @@ class GameOverSubState extends MusicBeatSubState
|
|||
public static function playBlueBalledSFX()
|
||||
{
|
||||
blueballed = true;
|
||||
if (Assets.exists(Paths.sound('gameplay/gameover/fnf_loss_sfx' + blueBallSuffix)))
|
||||
{
|
||||
FlxG.sound.play(Paths.sound('gameplay/gameover/fnf_loss_sfx' + blueBallSuffix));
|
||||
}
|
||||
else
|
||||
{
|
||||
FlxG.log.error('Missing blue ball sound effect: ' + Paths.sound('gameplay/gameover/fnf_loss_sfx' + blueBallSuffix));
|
||||
}
|
||||
}
|
||||
|
||||
var playingJeffQuote:Bool = false;
|
||||
|
||||
|
@ -344,6 +407,14 @@ class GameOverSubState extends MusicBeatSubState
|
|||
});
|
||||
}
|
||||
|
||||
public override function destroy()
|
||||
{
|
||||
super.destroy();
|
||||
if (gameOverMusic != null) gameOverMusic.stop();
|
||||
gameOverMusic = null;
|
||||
instance = null;
|
||||
}
|
||||
|
||||
public override function toString():String
|
||||
{
|
||||
return "GameOverSubState";
|
||||
|
|
|
@ -3,6 +3,7 @@ package funkin.play;
|
|||
import flixel.FlxSprite;
|
||||
import flixel.graphics.frames.FlxAtlasFrames;
|
||||
import funkin.play.PlayState;
|
||||
import funkin.graphics.FunkinSprite;
|
||||
import funkin.ui.MusicBeatState;
|
||||
import flixel.addons.transition.FlxTransitionableState;
|
||||
import funkin.ui.mainmenu.MainMenuState;
|
||||
|
@ -27,25 +28,22 @@ class GitarooPause extends MusicBeatState
|
|||
{
|
||||
if (FlxG.sound.music != null) FlxG.sound.music.stop();
|
||||
|
||||
var bg:FlxSprite = new FlxSprite().loadGraphic(Paths.image('pauseAlt/pauseBG'));
|
||||
var bg:FunkinSprite = FunkinSprite.create(Paths.image('pauseAlt/pauseBG'));
|
||||
add(bg);
|
||||
|
||||
var bf:FlxSprite = new FlxSprite(0, 30);
|
||||
bf.frames = Paths.getSparrowAtlas('pauseAlt/bfLol');
|
||||
var bf:FunkinSprite = FunkinSprite.createSparrow(0, 30, 'pauseAlt/bfLol');
|
||||
bf.animation.addByPrefix('lol', "funnyThing", 13);
|
||||
bf.animation.play('lol');
|
||||
add(bf);
|
||||
bf.screenCenter(X);
|
||||
|
||||
replayButton = new FlxSprite(FlxG.width * 0.28, FlxG.height * 0.7);
|
||||
replayButton.frames = Paths.getSparrowAtlas('pauseAlt/pauseUI');
|
||||
replayButton = FunkinSprite.createSparrow(FlxG.width * 0.28, FlxG.height * 0.7, 'pauseAlt/pauseUI');
|
||||
replayButton.animation.addByPrefix('selected', 'bluereplay', 0, false);
|
||||
replayButton.animation.appendByPrefix('selected', 'yellowreplay');
|
||||
replayButton.animation.play('selected');
|
||||
add(replayButton);
|
||||
|
||||
cancelButton = new FlxSprite(FlxG.width * 0.58, replayButton.y);
|
||||
cancelButton.frames = Paths.getSparrowAtlas('pauseAlt/pauseUI');
|
||||
cancelButton = FunkinSprite.createSparrow(FlxG.width * 0.58, replayButton.y, 'pauseAlt/pauseUI');
|
||||
cancelButton.animation.addByPrefix('selected', 'bluecancel', 0, false);
|
||||
cancelButton.animation.appendByPrefix('selected', 'cancelyellow');
|
||||
cancelButton.animation.play('selected');
|
||||
|
|
|
@ -13,6 +13,7 @@ import flixel.util.FlxColor;
|
|||
import funkin.play.PlayState;
|
||||
import funkin.data.song.SongRegistry;
|
||||
import funkin.ui.Alphabet;
|
||||
import funkin.graphics.FunkinSprite;
|
||||
|
||||
class PauseSubState extends MusicBeatSubState
|
||||
{
|
||||
|
@ -72,7 +73,7 @@ class PauseSubState extends MusicBeatSubState
|
|||
|
||||
FlxG.sound.list.add(pauseMusic);
|
||||
|
||||
bg = new FlxSprite().makeGraphic(FlxG.width, FlxG.height, FlxColor.BLACK);
|
||||
bg = new FunkinSprite().makeSolidColor(FlxG.width, FlxG.height, FlxColor.BLACK);
|
||||
bg.alpha = 0;
|
||||
bg.scrollFactor.set();
|
||||
add(bg);
|
||||
|
|
|
@ -10,12 +10,14 @@ import flixel.addons.transition.Transition;
|
|||
import flixel.addons.transition.Transition;
|
||||
import flixel.FlxCamera;
|
||||
import flixel.FlxObject;
|
||||
import flixel.FlxSprite;
|
||||
import flixel.FlxState;
|
||||
import funkin.graphics.FunkinSprite;
|
||||
import flixel.FlxSubState;
|
||||
import funkin.graphics.FunkinSprite;
|
||||
import flixel.math.FlxMath;
|
||||
import flixel.math.FlxPoint;
|
||||
import flixel.math.FlxRect;
|
||||
import funkin.graphics.FunkinSprite;
|
||||
import flixel.text.FlxText;
|
||||
import flixel.tweens.FlxEase;
|
||||
import flixel.tweens.FlxTween;
|
||||
|
@ -213,7 +215,7 @@ class PlayState extends MusicBeatSubState
|
|||
* The current gameplay camera will always follow this object. Tween its position to move the camera smoothly.
|
||||
*
|
||||
* It needs to be an object in the scene for the camera to be configured to follow it.
|
||||
* We optionally make this an FlxSprite so we can draw a debug graphic with it.
|
||||
* We optionally make this a sprite so we can draw a debug graphic with it.
|
||||
*/
|
||||
public var cameraFollowPoint:FlxObject;
|
||||
|
||||
|
@ -400,7 +402,7 @@ class PlayState extends MusicBeatSubState
|
|||
* The background image used for the health bar.
|
||||
* Emma says the image is slightly skewed so I'm leaving it as an image instead of a `createGraphic`.
|
||||
*/
|
||||
public var healthBarBG:FlxSprite;
|
||||
public var healthBarBG:FunkinSprite;
|
||||
|
||||
/**
|
||||
* The health icon representing the player.
|
||||
|
@ -568,12 +570,15 @@ class PlayState extends MusicBeatSubState
|
|||
|
||||
if (!assertChartExists()) return;
|
||||
|
||||
// TODO: Add something to toggle this on!
|
||||
if (false)
|
||||
{
|
||||
// Displays the camera follow point as a sprite for debug purposes.
|
||||
cameraFollowPoint = new FlxSprite(0, 0).makeGraphic(8, 8, 0xFF00FF00);
|
||||
var cameraFollowPoint = new FunkinSprite(0, 0);
|
||||
cameraFollowPoint.makeSolidColor(8, 8, 0xFF00FF00);
|
||||
cameraFollowPoint.visible = false;
|
||||
cameraFollowPoint.zIndex = 1000000;
|
||||
this.cameraFollowPoint = cameraFollowPoint;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -918,6 +923,7 @@ class PlayState extends MusicBeatSubState
|
|||
{
|
||||
FlxG.watch.addQuick('bfAnim', currentStage.getBoyfriend().getCurrentAnimation());
|
||||
}
|
||||
FlxG.watch.addQuick('health', health);
|
||||
|
||||
// TODO: Add a song event for Handle GF dance speed.
|
||||
|
||||
|
@ -981,8 +987,21 @@ class PlayState extends MusicBeatSubState
|
|||
}
|
||||
}
|
||||
|
||||
processSongEvents();
|
||||
|
||||
// Handle keybinds.
|
||||
processInputQueue();
|
||||
if (!isInCutscene && !disableKeys) debugKeyShit();
|
||||
if (isInCutscene && !disableKeys) handleCutsceneKeys(elapsed);
|
||||
|
||||
// Moving notes into position is now done by Strumline.update().
|
||||
processNotes(elapsed);
|
||||
}
|
||||
|
||||
function processSongEvents():Void
|
||||
{
|
||||
// Query and activate song events.
|
||||
// TODO: Check that these work even when songPosition is less than 0.
|
||||
// TODO: Check that these work appropriately even when songPosition is less than 0, to play events during countdown.
|
||||
if (songEvents != null && songEvents.length > 0)
|
||||
{
|
||||
var songEventsToActivate:Array<SongEventData> = SongEventRegistry.queryEvents(songEvents, Conductor.instance.songPosition);
|
||||
|
@ -992,8 +1011,9 @@ class PlayState extends MusicBeatSubState
|
|||
trace('Found ${songEventsToActivate.length} event(s) to activate.');
|
||||
for (event in songEventsToActivate)
|
||||
{
|
||||
// If an event is trying to play, but it's over 5 seconds old, skip it.
|
||||
if (event.time - Conductor.instance.songPosition < -5000)
|
||||
// If an event is trying to play, but it's over 1 second old, skip it.
|
||||
var eventAge:Float = Conductor.instance.songPosition - event.time;
|
||||
if (eventAge > 1000)
|
||||
{
|
||||
event.activated = true;
|
||||
continue;
|
||||
|
@ -1009,14 +1029,6 @@ class PlayState extends MusicBeatSubState
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Handle keybinds.
|
||||
processInputQueue();
|
||||
if (!isInCutscene && !disableKeys) debugKeyShit();
|
||||
if (isInCutscene && !disableKeys) handleCutsceneKeys(elapsed);
|
||||
|
||||
// Moving notes into position is now done by Strumline.update().
|
||||
processNotes(elapsed);
|
||||
}
|
||||
|
||||
public override function dispatchEvent(event:ScriptEvent):Void
|
||||
|
@ -1348,7 +1360,7 @@ class PlayState extends MusicBeatSubState
|
|||
function initHealthBar():Void
|
||||
{
|
||||
var healthBarYPos:Float = Preferences.downscroll ? FlxG.height * 0.1 : FlxG.height * 0.9;
|
||||
healthBarBG = new FlxSprite(0, healthBarYPos).loadGraphic(Paths.image('healthBar'));
|
||||
healthBarBG = FunkinSprite.create(0, healthBarYPos, Paths.image('healthBar'));
|
||||
healthBarBG.screenCenter(X);
|
||||
healthBarBG.scrollFactor.set(0, 0);
|
||||
add(healthBarBG);
|
||||
|
@ -1382,7 +1394,7 @@ class PlayState extends MusicBeatSubState
|
|||
function initMinimalMode():Void
|
||||
{
|
||||
// Create the green background.
|
||||
var menuBG = new FlxSprite().loadGraphic(Paths.image('menuDesat'));
|
||||
var menuBG = FunkinSprite.create(Paths.image('menuDesat'));
|
||||
menuBG.color = 0xFF4CAF50;
|
||||
menuBG.setGraphicSize(Std.int(menuBG.width * 1.1));
|
||||
menuBG.updateHitbox();
|
||||
|
@ -1408,8 +1420,7 @@ class PlayState extends MusicBeatSubState
|
|||
var event:ScriptEvent = new ScriptEvent(CREATE, false);
|
||||
ScriptEventDispatcher.callEvent(currentStage, event);
|
||||
|
||||
// Apply camera zoom level from stage data.
|
||||
defaultCameraZoom = currentStage.camZoom;
|
||||
resetCameraZoom();
|
||||
|
||||
// Add the stage to the scene.
|
||||
this.add(currentStage);
|
||||
|
@ -1425,6 +1436,12 @@ class PlayState extends MusicBeatSubState
|
|||
}
|
||||
}
|
||||
|
||||
public function resetCameraZoom():Void
|
||||
{
|
||||
// Apply camera zoom level from stage data.
|
||||
defaultCameraZoom = currentStage.camZoom;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates the character sprites and adds them to the stage.
|
||||
*/
|
||||
|
@ -1750,7 +1767,7 @@ class PlayState extends MusicBeatSubState
|
|||
currentChart.playInst(1.0, false);
|
||||
}
|
||||
|
||||
FlxG.sound.music.onComplete = endSong;
|
||||
FlxG.sound.music.onComplete = endSong.bind(false);
|
||||
// A negative instrumental offset means the song skips the first few milliseconds of the track.
|
||||
// This just gets added into the startTimestamp behavior so we don't need to do anything extra.
|
||||
FlxG.sound.music.time = startTimestamp - Conductor.instance.instrumentalOffset;
|
||||
|
@ -1978,7 +1995,7 @@ class PlayState extends MusicBeatSubState
|
|||
// Judge the miss.
|
||||
// NOTE: This is what handles the scoring.
|
||||
trace('Missed note! ${note.noteData}');
|
||||
onNoteMiss(note);
|
||||
onNoteMiss(note, event.playSound, event.healthMulti);
|
||||
|
||||
note.handledMiss = true;
|
||||
}
|
||||
|
@ -2030,6 +2047,7 @@ class PlayState extends MusicBeatSubState
|
|||
}
|
||||
}
|
||||
|
||||
// Respawns notes that were b
|
||||
playerStrumline.handleSkippedNotes();
|
||||
opponentStrumline.handleSkippedNotes();
|
||||
}
|
||||
|
@ -2129,7 +2147,7 @@ class PlayState extends MusicBeatSubState
|
|||
// Calling event.cancelEvent() skips all the other logic! Neat!
|
||||
if (event.eventCanceled) return;
|
||||
|
||||
popUpScore(note, input);
|
||||
popUpScore(note, input, event.healthMulti);
|
||||
|
||||
if (note.isHoldNote && note.holdNoteSprite != null)
|
||||
{
|
||||
|
@ -2143,15 +2161,11 @@ class PlayState extends MusicBeatSubState
|
|||
* Called when a note leaves the screen and is considered missed by the player.
|
||||
* @param note
|
||||
*/
|
||||
function onNoteMiss(note:NoteSprite):Void
|
||||
function onNoteMiss(note:NoteSprite, playSound:Bool = false, healthLossMulti:Float = 1.0):Void
|
||||
{
|
||||
// a MISS is when you let a note scroll past you!!
|
||||
var event:NoteScriptEvent = new NoteScriptEvent(NOTE_MISS, note, Highscore.tallies.combo, true);
|
||||
dispatchEvent(event);
|
||||
// Calling event.cancelEvent() skips all the other logic! Neat!
|
||||
if (event.eventCanceled) return;
|
||||
// If we are here, we already CALLED the onNoteMiss script hook!
|
||||
|
||||
health -= Constants.HEALTH_MISS_PENALTY;
|
||||
health -= Constants.HEALTH_MISS_PENALTY * healthLossMulti;
|
||||
songScore -= 10;
|
||||
|
||||
if (!isPracticeMode)
|
||||
|
@ -2201,7 +2215,7 @@ class PlayState extends MusicBeatSubState
|
|||
Highscore.tallies.combo = comboPopUps.displayCombo(0);
|
||||
}
|
||||
|
||||
if (event.playSound)
|
||||
if (playSound)
|
||||
{
|
||||
vocals.playerVolume = 0;
|
||||
FlxG.sound.play(Paths.soundRandom('missnote', 1, 3), FlxG.random.float(0.1, 0.2));
|
||||
|
@ -2274,11 +2288,6 @@ class PlayState extends MusicBeatSubState
|
|||
if (FlxG.keys.justPressed.H) camHUD.visible = !camHUD.visible;
|
||||
#end
|
||||
|
||||
// Eject button
|
||||
if (FlxG.keys.justPressed.F4) FlxG.switchState(() -> new MainMenuState());
|
||||
|
||||
if (FlxG.keys.justPressed.F5) debug_refreshModules();
|
||||
|
||||
// Open the stage editor overlaying the current state.
|
||||
if (controls.DEBUG_STAGE)
|
||||
{
|
||||
|
@ -2301,7 +2310,7 @@ class PlayState extends MusicBeatSubState
|
|||
|
||||
#if (debug || FORCE_DEBUG_VERSION)
|
||||
// 1: End the song immediately.
|
||||
if (FlxG.keys.justPressed.ONE) endSong();
|
||||
if (FlxG.keys.justPressed.ONE) endSong(true);
|
||||
|
||||
// 2: Gain 10% health.
|
||||
if (FlxG.keys.justPressed.TWO) health += 0.1 * Constants.HEALTH_MAX;
|
||||
|
@ -2328,7 +2337,7 @@ class PlayState extends MusicBeatSubState
|
|||
/**
|
||||
* Handles health, score, and rating popups when a note is hit.
|
||||
*/
|
||||
function popUpScore(daNote:NoteSprite, input:PreciseInputEvent):Void
|
||||
function popUpScore(daNote:NoteSprite, input:PreciseInputEvent, healthGainMulti:Float = 1.0):Void
|
||||
{
|
||||
vocals.playerVolume = 1;
|
||||
|
||||
|
@ -2359,19 +2368,19 @@ class PlayState extends MusicBeatSubState
|
|||
{
|
||||
case 'sick':
|
||||
Highscore.tallies.sick += 1;
|
||||
health += Constants.HEALTH_SICK_BONUS;
|
||||
health += Constants.HEALTH_SICK_BONUS * healthGainMulti;
|
||||
isComboBreak = Constants.JUDGEMENT_SICK_COMBO_BREAK;
|
||||
case 'good':
|
||||
Highscore.tallies.good += 1;
|
||||
health += Constants.HEALTH_GOOD_BONUS;
|
||||
health += Constants.HEALTH_GOOD_BONUS * healthGainMulti;
|
||||
isComboBreak = Constants.JUDGEMENT_GOOD_COMBO_BREAK;
|
||||
case 'bad':
|
||||
Highscore.tallies.bad += 1;
|
||||
health += Constants.HEALTH_BAD_BONUS;
|
||||
health += Constants.HEALTH_BAD_BONUS * healthGainMulti;
|
||||
isComboBreak = Constants.JUDGEMENT_BAD_COMBO_BREAK;
|
||||
case 'shit':
|
||||
Highscore.tallies.shit += 1;
|
||||
health += Constants.HEALTH_SHIT_BONUS;
|
||||
health += Constants.HEALTH_SHIT_BONUS * healthGainMulti;
|
||||
isComboBreak = Constants.JUDGEMENT_SHIT_COMBO_BREAK;
|
||||
}
|
||||
|
||||
|
@ -2495,16 +2504,35 @@ class PlayState extends MusicBeatSubState
|
|||
|
||||
if (skipHeldTimer >= 1.5)
|
||||
{
|
||||
VideoCutscene.finishVideo();
|
||||
skipVideoCutscene();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* End the song. Handle saving high scores and transitioning to the results screen.
|
||||
* Handle logic for actually skipping a video cutscene after it has been held.
|
||||
*/
|
||||
function endSong():Void
|
||||
function skipVideoCutscene():Void
|
||||
{
|
||||
dispatchEvent(new ScriptEvent(SONG_END));
|
||||
VideoCutscene.finishVideo();
|
||||
}
|
||||
|
||||
/**
|
||||
* End the song. Handle saving high scores and transitioning to the results screen.
|
||||
*
|
||||
* Broadcasts an `onSongEnd` event, which can be cancelled to prevent the song from ending (for a cutscene or something).
|
||||
* Remember to call `endSong` again when the song should actually end!
|
||||
* @param rightGoddamnNow If true, don't play the fancy animation where you zoom onto Girlfriend. Used after a cutscene.
|
||||
*/
|
||||
public function endSong(rightGoddamnNow:Bool = false):Void
|
||||
{
|
||||
FlxG.sound.music.volume = 0;
|
||||
vocals.volume = 0;
|
||||
mayPauseGame = false;
|
||||
|
||||
// Check if any events want to prevent the song from ending.
|
||||
var event = new ScriptEvent(SONG_END, true);
|
||||
dispatchEvent(event);
|
||||
if (event.eventCanceled) return;
|
||||
|
||||
#if sys
|
||||
// spitter for ravy, teehee!!
|
||||
|
@ -2514,9 +2542,7 @@ class PlayState extends MusicBeatSubState
|
|||
#end
|
||||
|
||||
deathCounter = 0;
|
||||
mayPauseGame = false;
|
||||
FlxG.sound.music.volume = 0;
|
||||
vocals.volume = 0;
|
||||
|
||||
if (currentSong != null && currentSong.validScore)
|
||||
{
|
||||
// crackhead double thingie, sets whether was new highscore, AND saves the song!
|
||||
|
@ -2602,9 +2628,16 @@ class PlayState extends MusicBeatSubState
|
|||
this.close();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (rightGoddamnNow)
|
||||
{
|
||||
moveToResultsScreen();
|
||||
}
|
||||
else
|
||||
{
|
||||
zoomIntoResultsScreen();
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -2621,10 +2654,10 @@ class PlayState extends MusicBeatSubState
|
|||
// TODO: Softcode this cutscene.
|
||||
if (currentSong.id == 'eggnog')
|
||||
{
|
||||
var blackShit:FlxSprite = new FlxSprite(-FlxG.width * FlxG.camera.zoom,
|
||||
-FlxG.height * FlxG.camera.zoom).makeGraphic(FlxG.width * 3, FlxG.height * 3, FlxColor.BLACK);
|
||||
blackShit.scrollFactor.set();
|
||||
add(blackShit);
|
||||
var blackBG:FunkinSprite = new FunkinSprite(-FlxG.width * FlxG.camera.zoom, -FlxG.height * FlxG.camera.zoom);
|
||||
blackBG.makeSolidColor(FlxG.width * 3, FlxG.height * 3, FlxColor.BLACK);
|
||||
blackBG.scrollFactor.set();
|
||||
add(blackBG);
|
||||
camHUD.visible = false;
|
||||
isInCutscene = true;
|
||||
|
||||
|
@ -2660,9 +2693,16 @@ class PlayState extends MusicBeatSubState
|
|||
this.close();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (rightGoddamnNow)
|
||||
{
|
||||
moveToResultsScreen();
|
||||
}
|
||||
else
|
||||
{
|
||||
zoomIntoResultsScreen();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2715,9 +2755,9 @@ class PlayState extends MusicBeatSubState
|
|||
}
|
||||
|
||||
/**
|
||||
* Play the camera zoom animation and move to the results screen.
|
||||
* Play the camera zoom animation and then move to the results screen once it's done.
|
||||
*/
|
||||
function moveToResultsScreen():Void
|
||||
function zoomIntoResultsScreen():Void
|
||||
{
|
||||
trace('WENT TO RESULTS SCREEN!');
|
||||
|
||||
|
@ -2771,6 +2811,17 @@ class PlayState extends MusicBeatSubState
|
|||
{
|
||||
ease: FlxEase.expoIn,
|
||||
onComplete: function(_) {
|
||||
moveToResultsScreen();
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Move to the results screen right goddamn now.
|
||||
*/
|
||||
function moveToResultsScreen():Void
|
||||
{
|
||||
persistentUpdate = false;
|
||||
vocals.stop();
|
||||
camHUD.alpha = 1;
|
||||
|
@ -2783,9 +2834,6 @@ class PlayState extends MusicBeatSubState
|
|||
res.camera = camHUD;
|
||||
openSubState(res);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Pauses music and vocals easily.
|
||||
|
@ -2816,14 +2864,18 @@ class PlayState extends MusicBeatSubState
|
|||
*/
|
||||
function changeSection(sections:Int):Void
|
||||
{
|
||||
FlxG.sound.music.pause();
|
||||
// FlxG.sound.music.pause();
|
||||
|
||||
var targetTimeSteps:Float = Conductor.instance.currentStepTime + (Conductor.instance.timeSignatureNumerator * Constants.STEPS_PER_BEAT * sections);
|
||||
var targetTimeSteps:Float = Conductor.instance.currentStepTime + (Conductor.instance.stepsPerMeasure * sections);
|
||||
var targetTimeMs:Float = Conductor.instance.getStepTimeInMs(targetTimeSteps);
|
||||
|
||||
// Don't go back in time to before the song started.
|
||||
targetTimeMs = Math.max(0, targetTimeMs);
|
||||
|
||||
FlxG.sound.music.time = targetTimeMs;
|
||||
|
||||
handleSkippedNotes();
|
||||
SongEventRegistry.handleSkippedEvents(songEvents, Conductor.instance.songPosition);
|
||||
// regenNoteData(FlxG.sound.music.time);
|
||||
|
||||
Conductor.instance.update(FlxG.sound.music.time);
|
||||
|
|
|
@ -4,6 +4,7 @@ import funkin.ui.story.StoryMenuState;
|
|||
import funkin.graphics.adobeanimate.FlxAtlasSprite;
|
||||
import flixel.FlxBasic;
|
||||
import flixel.FlxSprite;
|
||||
import funkin.graphics.FunkinSprite;
|
||||
import flixel.graphics.frames.FlxAtlasFrames;
|
||||
import flixel.graphics.frames.FlxBitmapFont;
|
||||
import flixel.group.FlxGroup.FlxTypedGroup;
|
||||
|
@ -96,8 +97,7 @@ class ResultState extends MusicBeatSubState
|
|||
bfSHIT.anim.play(); // unpauses this anim, since it's on PlayOnce!
|
||||
};
|
||||
|
||||
var gf:FlxSprite = new FlxSprite(500, 300);
|
||||
gf.frames = Paths.getSparrowAtlas('resultScreen/resultGirlfriendGOOD');
|
||||
var gf:FlxSprite = FunkinSprite.createSparrow(500, 300, 'resultScreen/resultGirlfriendGOOD');
|
||||
gf.animation.addByPrefix("clap", "Girlfriend Good Anim", 24, false);
|
||||
gf.visible = false;
|
||||
gf.animation.finishCallback = _ -> {
|
||||
|
@ -105,8 +105,7 @@ class ResultState extends MusicBeatSubState
|
|||
};
|
||||
add(gf);
|
||||
|
||||
var boyfriend:FlxSprite = new FlxSprite(640, -200);
|
||||
boyfriend.frames = Paths.getSparrowAtlas('resultScreen/resultBoyfriendGOOD');
|
||||
var boyfriend:FlxSprite = FunkinSprite.createSparrow(640, -200, 'resultScreen/resultBoyfriendGOOD');
|
||||
boyfriend.animation.addByPrefix("fall", "Boyfriend Good", 24, false);
|
||||
boyfriend.visible = false;
|
||||
boyfriend.animation.finishCallback = function(_) {
|
||||
|
@ -115,8 +114,7 @@ class ResultState extends MusicBeatSubState
|
|||
|
||||
add(boyfriend);
|
||||
|
||||
var soundSystem:FlxSprite = new FlxSprite(-15, -180);
|
||||
soundSystem.frames = Paths.getSparrowAtlas("resultScreen/soundSystem");
|
||||
var soundSystem:FlxSprite = FunkinSprite.createSparrow(-15, -180, 'resultScreen/soundSystem');
|
||||
soundSystem.animation.addByPrefix("idle", "sound system", 24, false);
|
||||
soundSystem.visible = false;
|
||||
new FlxTimer().start(0.4, _ -> {
|
||||
|
@ -162,20 +160,17 @@ class ResultState extends MusicBeatSubState
|
|||
FlxTween.tween(blackTopBar, {y: 0}, 0.4, {ease: FlxEase.quartOut, startDelay: 0.5});
|
||||
add(blackTopBar);
|
||||
|
||||
var resultsAnim:FlxSprite = new FlxSprite(-200, -10);
|
||||
resultsAnim.frames = Paths.getSparrowAtlas("resultScreen/results");
|
||||
var resultsAnim:FunkinSprite = FunkinSprite.createSparrow(-200, -10, "resultScreen/results");
|
||||
resultsAnim.animation.addByPrefix("result", "results", 24, false);
|
||||
resultsAnim.animation.play("result");
|
||||
add(resultsAnim);
|
||||
|
||||
var ratingsPopin:FlxSprite = new FlxSprite(-150, 120);
|
||||
ratingsPopin.frames = Paths.getSparrowAtlas("resultScreen/ratingsPopin");
|
||||
var ratingsPopin:FunkinSprite = FunkinSprite.createSparrow(-150, 120, "resultScreen/ratingsPopin");
|
||||
ratingsPopin.animation.addByPrefix("idle", "Categories", 24, false);
|
||||
ratingsPopin.visible = false;
|
||||
add(ratingsPopin);
|
||||
|
||||
var scorePopin:FlxSprite = new FlxSprite(-180, 520);
|
||||
scorePopin.frames = Paths.getSparrowAtlas("resultScreen/scorePopin");
|
||||
var scorePopin:FunkinSprite = FunkinSprite.createSparrow(-180, 520, "resultScreen/scorePopin");
|
||||
scorePopin.animation.addByPrefix("score", "tally score", 24, false);
|
||||
scorePopin.visible = false;
|
||||
add(scorePopin);
|
||||
|
|
|
@ -305,9 +305,15 @@ class CharacterDataParser
|
|||
icon = "darnell";
|
||||
case "senpai-angry":
|
||||
icon = "senpai";
|
||||
case "tankman" | "tankman-atlas":
|
||||
icon = "tankmen";
|
||||
}
|
||||
|
||||
return Paths.image("freeplay/icons/" + icon + "pixel");
|
||||
var path = Paths.image("freeplay/icons/" + icon + "pixel");
|
||||
if (Assets.exists(path)) return path;
|
||||
|
||||
// TODO: Hardcode some additional behavior or a fallback.
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -6,6 +6,7 @@ import flixel.math.FlxMath;
|
|||
import flixel.math.FlxPoint;
|
||||
import funkin.play.character.CharacterData.CharacterDataParser;
|
||||
import openfl.utils.Assets;
|
||||
import funkin.graphics.FunkinSprite;
|
||||
import funkin.util.MathUtil;
|
||||
|
||||
/**
|
||||
|
@ -26,7 +27,7 @@ import funkin.util.MathUtil;
|
|||
* @author MasterEric
|
||||
*/
|
||||
@:nullSafety
|
||||
class HealthIcon extends FlxSprite
|
||||
class HealthIcon extends FunkinSprite
|
||||
{
|
||||
/**
|
||||
* The character this icon is representing.
|
||||
|
@ -408,7 +409,7 @@ class HealthIcon extends FlxSprite
|
|||
|
||||
if (!isLegacyStyle)
|
||||
{
|
||||
frames = Paths.getSparrowAtlas('icons/icon-$charId');
|
||||
loadSparrow('icons/icon-$charId');
|
||||
|
||||
loadAnimationNew();
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@ package funkin.play.components;
|
|||
import flixel.FlxSprite;
|
||||
import flixel.group.FlxGroup.FlxTypedGroup;
|
||||
import flixel.tweens.FlxTween;
|
||||
import funkin.graphics.FunkinSprite;
|
||||
import funkin.play.PlayState;
|
||||
|
||||
class PopUpStuff extends FlxTypedGroup<FlxSprite>
|
||||
|
@ -14,17 +15,20 @@ class PopUpStuff extends FlxTypedGroup<FlxSprite>
|
|||
|
||||
public function displayRating(daRating:String)
|
||||
{
|
||||
#if sys
|
||||
var perfStart:Float = Sys.time();
|
||||
#end
|
||||
|
||||
if (daRating == null) daRating = "good";
|
||||
|
||||
var rating:FlxSprite = new FlxSprite(0, 0);
|
||||
rating.scrollFactor.set(0.2, 0.2);
|
||||
|
||||
rating.zIndex = 1000;
|
||||
var ratingPath:String = daRating;
|
||||
|
||||
if (PlayState.instance.currentStageId.startsWith('school')) ratingPath = "weeb/pixelUI/" + ratingPath + "-pixel";
|
||||
|
||||
rating.loadGraphic(Paths.image(ratingPath));
|
||||
var rating:FunkinSprite = FunkinSprite.create(0, 0, Paths.image(ratingPath));
|
||||
rating.scrollFactor.set(0.2, 0.2);
|
||||
|
||||
rating.zIndex = 1000;
|
||||
rating.x = FlxG.width * 0.50;
|
||||
rating.x -= FlxG.camera.scroll.x * 0.2;
|
||||
// make sure rating is visible lol!
|
||||
|
@ -61,10 +65,19 @@ class PopUpStuff extends FlxTypedGroup<FlxSprite>
|
|||
},
|
||||
startDelay: Conductor.instance.beatLengthMs * 0.001
|
||||
});
|
||||
|
||||
#if sys
|
||||
var perfEnd:Float = Sys.time();
|
||||
trace("displayRating took: " + (perfEnd - perfStart));
|
||||
#end
|
||||
}
|
||||
|
||||
public function displayCombo(?combo:Int = 0):Int
|
||||
{
|
||||
#if sys
|
||||
var perfStart:Float = Sys.time();
|
||||
#end
|
||||
|
||||
if (combo == null) combo = 0;
|
||||
|
||||
var pixelShitPart1:String = "";
|
||||
|
@ -75,7 +88,7 @@ class PopUpStuff extends FlxTypedGroup<FlxSprite>
|
|||
pixelShitPart1 = 'weeb/pixelUI/';
|
||||
pixelShitPart2 = '-pixel';
|
||||
}
|
||||
var comboSpr:FlxSprite = new FlxSprite().loadGraphic(Paths.image(pixelShitPart1 + 'combo' + pixelShitPart2));
|
||||
var comboSpr:FunkinSprite = FunkinSprite.create(Paths.image(pixelShitPart1 + 'combo' + pixelShitPart2));
|
||||
comboSpr.y = FlxG.camera.height * 0.4 + 80;
|
||||
comboSpr.x = FlxG.width * 0.50;
|
||||
comboSpr.x -= FlxG.camera.scroll.x * 0.2;
|
||||
|
@ -129,8 +142,7 @@ class PopUpStuff extends FlxTypedGroup<FlxSprite>
|
|||
var daLoop:Int = 1;
|
||||
for (i in seperatedScore)
|
||||
{
|
||||
var numScore:FlxSprite = new FlxSprite().loadGraphic(Paths.image(pixelShitPart1 + 'num' + Std.int(i) + pixelShitPart2));
|
||||
numScore.y = comboSpr.y;
|
||||
var numScore:FunkinSprite = FunkinSprite.create(0, comboSpr.y, Paths.image(pixelShitPart1 + 'num' + Std.int(i) + pixelShitPart2));
|
||||
|
||||
if (PlayState.instance.currentStageId.startsWith('school'))
|
||||
{
|
||||
|
@ -163,6 +175,11 @@ class PopUpStuff extends FlxTypedGroup<FlxSprite>
|
|||
daLoop++;
|
||||
}
|
||||
|
||||
#if sys
|
||||
var perfEnd:Float = Sys.time();
|
||||
trace("displayCombo took: " + (perfEnd - perfStart));
|
||||
#end
|
||||
|
||||
return combo;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,13 +19,22 @@ import hxcodec.flixel.FlxVideoSprite;
|
|||
class VideoCutscene
|
||||
{
|
||||
static var blackScreen:FlxSprite;
|
||||
static var cutsceneType:CutsceneType;
|
||||
|
||||
#if html5
|
||||
static var vid:FlxVideo;
|
||||
#end
|
||||
#if hxCodec
|
||||
static var vid:FlxVideoSprite;
|
||||
#end
|
||||
|
||||
/**
|
||||
* Play a video cutscene.
|
||||
* TODO: Currently this is hardcoded to start the countdown after the video is done.
|
||||
* @param path The path to the video file. Use Paths.file(path) to get the correct path.
|
||||
* @param cutseneType The type of cutscene to play, determines what the game does after. Defaults to `CutsceneType.STARTING`.
|
||||
*/
|
||||
public static function play(filePath:String):Void
|
||||
public static function play(filePath:String, ?cutsceneType:CutsceneType = STARTING):Void
|
||||
{
|
||||
if (PlayState.instance == null) return;
|
||||
|
||||
|
@ -36,6 +45,8 @@ class VideoCutscene
|
|||
return;
|
||||
}
|
||||
|
||||
var rawFilePath = Paths.stripLibrary(filePath);
|
||||
|
||||
// Trigger the cutscene. Don't play the song in the background.
|
||||
PlayState.instance.isInCutscene = true;
|
||||
PlayState.instance.camHUD.visible = false;
|
||||
|
@ -47,12 +58,14 @@ class VideoCutscene
|
|||
blackScreen.cameras = [PlayState.instance.camCutscene];
|
||||
PlayState.instance.add(blackScreen);
|
||||
|
||||
VideoCutscene.cutsceneType = cutsceneType;
|
||||
|
||||
#if html5
|
||||
playVideoHTML5(filePath);
|
||||
#end
|
||||
|
||||
#if hxCodec
|
||||
playVideoNative(filePath);
|
||||
#elseif hxCodec
|
||||
playVideoNative(rawFilePath);
|
||||
#else
|
||||
throw "No video support for this platform!";
|
||||
#end
|
||||
}
|
||||
|
||||
|
@ -66,8 +79,6 @@ class VideoCutscene
|
|||
}
|
||||
|
||||
#if html5
|
||||
static var vid:FlxVideo;
|
||||
|
||||
static function playVideoHTML5(filePath:String):Void
|
||||
{
|
||||
// Video displays OVER the FlxState.
|
||||
|
@ -92,8 +103,6 @@ class VideoCutscene
|
|||
#end
|
||||
|
||||
#if hxCodec
|
||||
static var vid:FlxVideoSprite;
|
||||
|
||||
static function playVideoNative(filePath:String):Void
|
||||
{
|
||||
// Video displays OVER the FlxState.
|
||||
|
@ -110,6 +119,15 @@ class VideoCutscene
|
|||
|
||||
PlayState.instance.refresh();
|
||||
vid.play(filePath, false);
|
||||
|
||||
// Resize videos bigger or smaller than the screen.
|
||||
vid.bitmap.onTextureSetup.add(() -> {
|
||||
vid.setGraphicSize(FlxG.width, FlxG.height);
|
||||
vid.updateHitbox();
|
||||
vid.x = 0;
|
||||
vid.y = 0;
|
||||
// vid.scale.set(0.5, 0.5);
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -118,10 +136,17 @@ class VideoCutscene
|
|||
}
|
||||
#end
|
||||
|
||||
/**
|
||||
* Finish the active video cutscene. Done when the video is finished or when the player skips the cutscene.
|
||||
* @param transitionTime The duration of the transition to the next state. Defaults to 0.5 seconds (this time is always used when cancelling the video).
|
||||
* @param finishCutscene The callback to call when the transition is finished.
|
||||
*/
|
||||
public static function finishVideo(?transitionTime:Float = 0.5):Void
|
||||
{
|
||||
trace('ALERT: Finish video cutscene called!');
|
||||
|
||||
var cutsceneType:CutsceneType = VideoCutscene.cutsceneType;
|
||||
|
||||
#if html5
|
||||
if (vid != null)
|
||||
{
|
||||
|
@ -157,8 +182,32 @@ class VideoCutscene
|
|||
{
|
||||
ease: FlxEase.quadInOut,
|
||||
onComplete: function(twn:FlxTween) {
|
||||
PlayState.instance.startCountdown();
|
||||
onCutsceneFinish(cutsceneType);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* The default callback used when a cutscene is finished.
|
||||
* You can specify your own callback when calling `VideoCutscene#play()`.
|
||||
*/
|
||||
static function onCutsceneFinish(cutsceneType:CutsceneType):Void
|
||||
{
|
||||
switch (cutsceneType)
|
||||
{
|
||||
case CutsceneType.STARTING:
|
||||
PlayState.instance.startCountdown();
|
||||
case CutsceneType.ENDING:
|
||||
PlayState.instance.endSong(true); // true = right goddamn now
|
||||
case CutsceneType.MIDSONG:
|
||||
throw "Not implemented!";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum CutsceneType
|
||||
{
|
||||
STARTING; // The default cutscene type. Starts the countdown after the video is done.
|
||||
MIDSONG; // TODO: Implement this!
|
||||
ENDING; // Ends the song after the video is done.
|
||||
}
|
||||
|
|
|
@ -35,6 +35,7 @@ class NoteSplash extends FlxSprite
|
|||
*/
|
||||
function setup():Void
|
||||
{
|
||||
if (frameCollection?.parent?.isDestroyed ?? false) frameCollection = null;
|
||||
if (frameCollection == null) preloadFrames();
|
||||
|
||||
this.frames = frameCollection;
|
||||
|
@ -75,6 +76,8 @@ class NoteSplash extends FlxSprite
|
|||
this.playAnimation('splash${variant}Right');
|
||||
}
|
||||
|
||||
if (animation.curAnim == null) return;
|
||||
|
||||
// Vary the speed of the animation a bit.
|
||||
animation.curAnim.frameRate = FRAMERATE_DEFAULT + FlxG.random.int(-FRAMERATE_VARIANCE, FRAMERATE_VARIANCE);
|
||||
|
||||
|
|
|
@ -4,9 +4,10 @@ import funkin.data.song.SongData.SongNoteData;
|
|||
import funkin.play.notes.notestyle.NoteStyle;
|
||||
import flixel.graphics.frames.FlxAtlasFrames;
|
||||
import flixel.FlxSprite;
|
||||
import funkin.graphics.FunkinSprite;
|
||||
import funkin.graphics.shaders.HSVShader;
|
||||
|
||||
class NoteSprite extends FlxSprite
|
||||
class NoteSprite extends FunkinSprite
|
||||
{
|
||||
static final DIRECTION_COLORS:Array<String> = ['purple', 'blue', 'green', 'red'];
|
||||
|
||||
|
|
|
@ -4,6 +4,7 @@ import flixel.graphics.frames.FlxAtlasFrames;
|
|||
import flixel.graphics.frames.FlxFramesCollection;
|
||||
import funkin.data.animation.AnimationData;
|
||||
import funkin.data.IRegistryEntry;
|
||||
import funkin.graphics.FunkinSprite;
|
||||
import funkin.data.notestyle.NoteStyleData;
|
||||
import funkin.data.notestyle.NoteStyleRegistry;
|
||||
import funkin.data.notestyle.NoteStyleRegistry;
|
||||
|
@ -100,6 +101,14 @@ class NoteStyle implements IRegistryEntry<NoteStyleData>
|
|||
|
||||
function buildNoteFrames(force:Bool = false):FlxAtlasFrames
|
||||
{
|
||||
if (!FunkinSprite.isTextureCached(Paths.image(getNoteAssetPath())))
|
||||
{
|
||||
FlxG.log.warn('Note texture is not cached: ${getNoteAssetPath()}');
|
||||
}
|
||||
|
||||
// Purge the note frames if the cached atlas is invalid.
|
||||
if (noteFrames?.parent?.isDestroyed ?? false) noteFrames = null;
|
||||
|
||||
if (noteFrames != null && !force) return noteFrames;
|
||||
|
||||
noteFrames = Paths.getSparrowAtlas(getNoteAssetPath(), getNoteAssetLibrary());
|
||||
|
@ -109,8 +118,6 @@ class NoteStyle implements IRegistryEntry<NoteStyleData>
|
|||
throw 'Could not load note frames for note style: $id';
|
||||
}
|
||||
|
||||
noteFrames.parent.persist = true;
|
||||
|
||||
return noteFrames;
|
||||
}
|
||||
|
||||
|
|
|
@ -185,9 +185,9 @@ class Stage extends FlxSpriteGroup implements IPlayStateScriptedClass implements
|
|||
switch (dataProp.animType)
|
||||
{
|
||||
case 'packer':
|
||||
propSprite.frames = Paths.getPackerAtlas(dataProp.assetPath);
|
||||
propSprite.loadPacker(dataProp.assetPath);
|
||||
default: // 'sparrow'
|
||||
propSprite.frames = Paths.getSparrowAtlas(dataProp.assetPath);
|
||||
propSprite.loadSparrow(dataProp.assetPath);
|
||||
}
|
||||
}
|
||||
else if (isSolidColor)
|
||||
|
@ -209,7 +209,7 @@ class Stage extends FlxSpriteGroup implements IPlayStateScriptedClass implements
|
|||
else
|
||||
{
|
||||
// Initalize static sprite.
|
||||
propSprite.loadGraphic(Paths.image(dataProp.assetPath));
|
||||
propSprite.loadTexture(Paths.image(dataProp.assetPath));
|
||||
|
||||
// Disables calls to update() for a performance boost.
|
||||
propSprite.active = false;
|
||||
|
@ -397,15 +397,18 @@ class Stage extends FlxSpriteGroup implements IPlayStateScriptedClass implements
|
|||
this.characters.set('bf', character);
|
||||
charData = _data.characters.bf;
|
||||
character.flipX = !character.getDataFlipX();
|
||||
character.name = 'bf';
|
||||
character.initHealthIcon(false);
|
||||
case GF:
|
||||
this.characters.set('gf', character);
|
||||
charData = _data.characters.gf;
|
||||
character.flipX = character.getDataFlipX();
|
||||
character.name = 'gf';
|
||||
case DAD:
|
||||
this.characters.set('dad', character);
|
||||
charData = _data.characters.dad;
|
||||
character.flipX = character.getDataFlipX();
|
||||
character.name = 'dad';
|
||||
character.initHealthIcon(true);
|
||||
default:
|
||||
this.characters.set(character.characterId, character);
|
||||
|
|
|
@ -3408,7 +3408,7 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
|
|||
// Update the event sprite's position.
|
||||
eventSprite.updateEventPosition(renderedEvents);
|
||||
// Update the sprite's graphic. TODO: Is this inefficient?
|
||||
eventSprite.playAnimation(eventSprite.eventData.event);
|
||||
eventSprite.playAnimation(eventSprite.eventData.eventKind);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -4669,9 +4669,9 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
|
|||
|
||||
var eventData:SongEventData = gridGhostEvent.eventData != null ? gridGhostEvent.eventData : new SongEventData(cursorMs, eventKindToPlace, null);
|
||||
|
||||
if (eventKindToPlace != eventData.event)
|
||||
if (eventKindToPlace != eventData.eventKind)
|
||||
{
|
||||
eventData.event = eventKindToPlace;
|
||||
eventData.eventKind = eventKindToPlace;
|
||||
}
|
||||
eventData.time = cursorSnappedMs;
|
||||
|
||||
|
|
|
@ -34,11 +34,11 @@ class SelectItemsCommand implements ChartEditorCommand
|
|||
}
|
||||
|
||||
// If we just selected one or more events (and no notes), then we should make the event data toolbox display the event data for the selected event.
|
||||
if (this.notes.length == 0 && this.events.length >= 1)
|
||||
if (this.notes.length == 0 && this.events.length == 1)
|
||||
{
|
||||
var eventSelected = this.events[0];
|
||||
|
||||
state.eventKindToPlace = eventSelected.event;
|
||||
state.eventKindToPlace = eventSelected.eventKind;
|
||||
|
||||
// This code is here to parse event data that's not built as a struct for some reason.
|
||||
// TODO: Clean this up or get rid of it.
|
||||
|
@ -46,7 +46,7 @@ class SelectItemsCommand implements ChartEditorCommand
|
|||
var defaultKey = null;
|
||||
if (eventSchema == null)
|
||||
{
|
||||
trace('[WARNING] Event schema not found for event ${eventSelected.event}.');
|
||||
trace('[WARNING] Event schema not found for event ${eventSelected.eventKind}.');
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -60,7 +60,7 @@ class SelectItemsCommand implements ChartEditorCommand
|
|||
}
|
||||
|
||||
// If we just selected one or more notes (and no events), then we should make the note data toolbox display the note data for the selected note.
|
||||
if (this.events.length == 0 && this.notes.length >= 1)
|
||||
if (this.events.length == 0 && this.notes.length == 1)
|
||||
{
|
||||
var noteSelected = this.notes[0];
|
||||
|
||||
|
|
|
@ -31,11 +31,11 @@ class SetItemSelectionCommand implements ChartEditorCommand
|
|||
state.currentEventSelection = events;
|
||||
|
||||
// If we just selected one or more events (and no notes), then we should make the event data toolbox display the event data for the selected event.
|
||||
if (this.notes.length == 0 && this.events.length >= 1)
|
||||
if (this.notes.length == 0 && this.events.length == 1)
|
||||
{
|
||||
var eventSelected = this.events[0];
|
||||
|
||||
state.eventKindToPlace = eventSelected.event;
|
||||
state.eventKindToPlace = eventSelected.eventKind;
|
||||
|
||||
// This code is here to parse event data that's not built as a struct for some reason.
|
||||
// TODO: Clean this up or get rid of it.
|
||||
|
@ -43,7 +43,7 @@ class SetItemSelectionCommand implements ChartEditorCommand
|
|||
var defaultKey = null;
|
||||
if (eventSchema == null)
|
||||
{
|
||||
trace('[WARNING] Event schema not found for event ${eventSelected.event}.');
|
||||
trace('[WARNING] Event schema not found for event ${eventSelected.eventKind}.');
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -57,7 +57,7 @@ class SetItemSelectionCommand implements ChartEditorCommand
|
|||
}
|
||||
|
||||
// IF we just selected one or more notes (and no events), then we should make the note data toolbox display the note data for the selected note.
|
||||
if (this.events.length == 0 && this.notes.length >= 1)
|
||||
if (this.events.length == 0 && this.notes.length == 1)
|
||||
{
|
||||
var noteSelected = this.notes[0];
|
||||
|
||||
|
|
|
@ -133,7 +133,7 @@ class ChartEditorEventSprite extends FlxSprite
|
|||
|
||||
public function playAnimation(?name:String):Void
|
||||
{
|
||||
if (name == null) name = eventData?.event ?? DEFAULT_EVENT;
|
||||
if (name == null) name = eventData?.eventKind ?? DEFAULT_EVENT;
|
||||
|
||||
var correctedName = correctAnimationName(name);
|
||||
this.animation.play(correctedName);
|
||||
|
@ -160,7 +160,7 @@ class ChartEditorEventSprite extends FlxSprite
|
|||
else
|
||||
{
|
||||
this.visible = true;
|
||||
playAnimation(value.event);
|
||||
playAnimation(value.eventKind);
|
||||
this.eventData = value;
|
||||
// Update the position to match the note data.
|
||||
updateEventPosition();
|
||||
|
|
|
@ -299,16 +299,14 @@ class ChartEditorAudioHandler
|
|||
*/
|
||||
public static function playSound(_state:ChartEditorState, path:String, volume:Float = 1.0):Void
|
||||
{
|
||||
var snd:FlxSound = FlxG.sound.list.recycle(FlxSound) ?? new FlxSound();
|
||||
var asset:Null<FlxSoundAsset> = FlxG.sound.cache(path);
|
||||
if (asset == null)
|
||||
{
|
||||
trace('WARN: Failed to play sound $path, asset not found.');
|
||||
return;
|
||||
}
|
||||
snd.loadEmbedded(asset);
|
||||
var snd:FunkinSound = FunkinSound.load(asset);
|
||||
snd.autoDestroy = true;
|
||||
FlxG.sound.list.add(snd);
|
||||
snd.play(true);
|
||||
snd.volume = volume;
|
||||
}
|
||||
|
|
|
@ -90,7 +90,7 @@ class ChartEditorEventDataToolbox extends ChartEditorBaseToolbox
|
|||
// Edit the event data of any selected events.
|
||||
for (event in chartEditorState.currentEventSelection)
|
||||
{
|
||||
event.event = chartEditorState.eventKindToPlace;
|
||||
event.eventKind = chartEditorState.eventKindToPlace;
|
||||
event.value = chartEditorState.eventDataToPlace;
|
||||
}
|
||||
chartEditorState.saveDataDirty = true;
|
||||
|
@ -255,7 +255,7 @@ class ChartEditorEventDataToolbox extends ChartEditorBaseToolbox
|
|||
{
|
||||
for (event in chartEditorState.currentEventSelection)
|
||||
{
|
||||
event.event = chartEditorState.eventKindToPlace;
|
||||
event.eventKind = chartEditorState.eventKindToPlace;
|
||||
event.value = chartEditorState.eventDataToPlace;
|
||||
}
|
||||
chartEditorState.saveDataDirty = true;
|
||||
|
|
|
@ -289,10 +289,10 @@ class ChartEditorFreeplayToolbox extends ChartEditorBaseToolbox
|
|||
// Build player waveform.
|
||||
// waveformMusic.waveform.forceUpdate = true;
|
||||
var perfStart = haxe.Timer.stamp();
|
||||
var waveformData1 = playerVoice.waveformData;
|
||||
var waveformData2 = opponentVoice?.waveformData ?? playerVoice.waveformData; // this null check is for songs that only have 1 vocals file!
|
||||
var waveformData1 = playerVoice?.waveformData;
|
||||
var waveformData2 = opponentVoice?.waveformData ?? playerVoice?.waveformData; // this null check is for songs that only have 1 vocals file!
|
||||
var waveformData3 = chartEditorState.audioInstTrack.waveformData;
|
||||
var waveformData = waveformData1.merge(waveformData2).merge(waveformData3);
|
||||
var waveformData = waveformData3.merge(waveformData1).merge(waveformData2);
|
||||
trace('Waveform data merging took: ${haxe.Timer.stamp() - perfStart} seconds');
|
||||
|
||||
waveformMusic.waveform.waveformData = waveformData;
|
||||
|
|
|
@ -270,24 +270,21 @@ class ChartEditorOffsetsToolbox extends ChartEditorBaseToolbox
|
|||
|
||||
// Build player waveform.
|
||||
// waveformPlayer.waveform.forceUpdate = true;
|
||||
waveformPlayer.waveform.waveformData = playerVoice.waveformData;
|
||||
waveformPlayer.waveform.waveformData = playerVoice?.waveformData;
|
||||
// Set the width and duration to render the full waveform, with the clipRect applied we only render a segment of it.
|
||||
waveformPlayer.waveform.duration = playerVoice.length / Constants.MS_PER_SEC;
|
||||
waveformPlayer.waveform.duration = (playerVoice?.length ?? 1000) / Constants.MS_PER_SEC;
|
||||
|
||||
// Build opponent waveform.
|
||||
// waveformOpponent.waveform.forceUpdate = true;
|
||||
// note: if song only has one set of vocals (Vocals.ogg/mp3) then this is null and crashes charting editor
|
||||
// so we null check
|
||||
if (opponentVoice != null)
|
||||
{
|
||||
waveformOpponent.waveform.waveformData = opponentVoice.waveformData;
|
||||
waveformOpponent.waveform.duration = opponentVoice.length / Constants.MS_PER_SEC;
|
||||
}
|
||||
waveformOpponent.waveform.waveformData = opponentVoice?.waveformData;
|
||||
waveformOpponent.waveform.duration = (opponentVoice?.length ?? 1000) / Constants.MS_PER_SEC;
|
||||
|
||||
// Build instrumental waveform.
|
||||
// waveformInstrumental.waveform.forceUpdate = true;
|
||||
waveformInstrumental.waveform.waveformData = chartEditorState.audioInstTrack.waveformData;
|
||||
waveformInstrumental.waveform.duration = instTrack.length / Constants.MS_PER_SEC;
|
||||
waveformInstrumental.waveform.duration = (instTrack?.length ?? 1000) / Constants.MS_PER_SEC;
|
||||
|
||||
addOffsetsToAudioPreview();
|
||||
}
|
||||
|
|
|
@ -147,6 +147,8 @@ class ChartEditorDropdowns
|
|||
dropDown.dataSource.add(value);
|
||||
}
|
||||
|
||||
dropDown.dataSource.sort('id', ASCENDING);
|
||||
|
||||
return returnValue;
|
||||
}
|
||||
|
||||
|
|
|
@ -156,8 +156,6 @@ class DJBoyfriend extends FlxAtlasSprite
|
|||
|
||||
function setupAnimations():Void
|
||||
{
|
||||
// frames = FlxAnimationUtil.combineFramesCollections(Paths.getSparrowAtlas('freeplay/bfFreeplay'), Paths.getSparrowAtlas('freeplay/bf-freeplay-afk'));
|
||||
|
||||
// animation.addByPrefix('intro', "boyfriend dj intro", 24, false);
|
||||
addOffset('boyfriend dj intro', 8, 3);
|
||||
|
||||
|
|
|
@ -23,7 +23,7 @@ class FreeplayFlames extends FlxSpriteGroup
|
|||
{
|
||||
var flame:FlxSprite = new FlxSprite(flameX + (flameSpreadX * i), flameY + (flameSpreadY * i));
|
||||
flame.frames = Paths.getSparrowAtlas("freeplay/freeplayFlame");
|
||||
flame.animation.addByPrefix("flame", "fire loop", FlxG.random.int(23, 25), false);
|
||||
flame.animation.addByPrefix("flame", "fire loop full instance 1", FlxG.random.int(23, 25), false);
|
||||
flame.animation.play("flame");
|
||||
flame.visible = false;
|
||||
flameCount = 0;
|
||||
|
|
|
@ -111,7 +111,7 @@ class ScoreNum extends FlxSprite
|
|||
for (i in 0...10)
|
||||
{
|
||||
var stringNum:String = numToString[i];
|
||||
animation.addByPrefix(stringNum, stringNum, 24, false);
|
||||
animation.addByPrefix(stringNum, '$stringNum DIGITAL', 24, false);
|
||||
}
|
||||
|
||||
this.digit = initDigit;
|
||||
|
|
|
@ -7,6 +7,7 @@ import flixel.addons.ui.FlxInputText;
|
|||
import flixel.FlxCamera;
|
||||
import flixel.FlxGame;
|
||||
import flixel.FlxSprite;
|
||||
import funkin.graphics.FunkinSprite;
|
||||
import flixel.FlxState;
|
||||
import flixel.group.FlxGroup;
|
||||
import flixel.group.FlxGroup.FlxTypedGroup;
|
||||
|
@ -226,17 +227,17 @@ class FreeplayState extends MusicBeatSubState
|
|||
trace(FlxG.camera.initialZoom);
|
||||
trace(FlxCamera.defaultZoom);
|
||||
|
||||
var pinkBack:FlxSprite = new FlxSprite().loadGraphic(Paths.image('freeplay/pinkBack'));
|
||||
var pinkBack:FunkinSprite = FunkinSprite.create(Paths.image('freeplay/pinkBack'));
|
||||
pinkBack.color = 0xFFffd4e9; // sets it to pink!
|
||||
pinkBack.x -= pinkBack.width;
|
||||
|
||||
FlxTween.tween(pinkBack, {x: 0}, 0.6, {ease: FlxEase.quartOut});
|
||||
add(pinkBack);
|
||||
|
||||
var orangeBackShit:FlxSprite = new FlxSprite(84, 440).makeGraphic(Std.int(pinkBack.width), 75, 0xFFfeda00);
|
||||
var orangeBackShit:FunkinSprite = new FunkinSprite(84, 440).makeSolidColor(Std.int(pinkBack.width), 75, 0xFFfeda00);
|
||||
add(orangeBackShit);
|
||||
|
||||
var alsoOrangeLOL:FlxSprite = new FlxSprite(0, orangeBackShit.y).makeGraphic(100, Std.int(orangeBackShit.height), 0xFFffd400);
|
||||
var alsoOrangeLOL:FunkinSprite = new FunkinSprite(0, orangeBackShit.y).makeSolidColor(100, Std.int(orangeBackShit.height), 0xFFffd400);
|
||||
add(alsoOrangeLOL);
|
||||
|
||||
exitMovers.set([pinkBack, orangeBackShit, alsoOrangeLOL],
|
||||
|
@ -462,7 +463,7 @@ class FreeplayState extends MusicBeatSubState
|
|||
|
||||
var fnfHighscoreSpr:FlxSprite = new FlxSprite(860, 70);
|
||||
fnfHighscoreSpr.frames = Paths.getSparrowAtlas('freeplay/highscore');
|
||||
fnfHighscoreSpr.animation.addByPrefix("highscore", "highscore", 24, false);
|
||||
fnfHighscoreSpr.animation.addByPrefix("highscore", "highscore small instance 1", 24, false);
|
||||
fnfHighscoreSpr.visible = false;
|
||||
fnfHighscoreSpr.setGraphicSize(0, Std.int(fnfHighscoreSpr.height * 1));
|
||||
fnfHighscoreSpr.updateHitbox();
|
||||
|
|
|
@ -3,6 +3,7 @@ package funkin.ui.transition;
|
|||
import flixel.FlxSprite;
|
||||
import flixel.math.FlxMath;
|
||||
import flixel.tweens.FlxEase;
|
||||
import funkin.graphics.FunkinSprite;
|
||||
import flixel.tweens.FlxTween;
|
||||
import flixel.util.FlxTimer;
|
||||
import funkin.graphics.shaders.ScreenWipeShader;
|
||||
|
@ -44,11 +45,10 @@ class LoadingState extends MusicBeatState
|
|||
|
||||
override function create():Void
|
||||
{
|
||||
var bg:FlxSprite = new FunkinSprite().makeSolidColor(FlxG.width, FlxG.height, 0xFFcaff4d);
|
||||
var bg:FunkinSprite = new FunkinSprite().makeSolidColor(FlxG.width, FlxG.height, 0xFFcaff4d);
|
||||
add(bg);
|
||||
|
||||
funkay = new FlxSprite();
|
||||
funkay.loadGraphic(Paths.image('funkay'));
|
||||
funkay = FunkinSprite.create(Paths.image('funkay'));
|
||||
funkay.setGraphicSize(0, FlxG.height);
|
||||
funkay.updateHitbox();
|
||||
add(funkay);
|
||||
|
@ -209,6 +209,43 @@ class LoadingState extends MusicBeatState
|
|||
params.targetSong.cacheCharts(true);
|
||||
}
|
||||
|
||||
// TODO: This section is a hack! Redo this later when we have a proper asset caching system.
|
||||
FunkinSprite.preparePurgeCache();
|
||||
FunkinSprite.cacheTexture(Paths.image('combo'));
|
||||
FunkinSprite.cacheTexture(Paths.image('healthBar'));
|
||||
FunkinSprite.cacheTexture(Paths.image('menuDesat'));
|
||||
FunkinSprite.cacheTexture(Paths.image('combo'));
|
||||
FunkinSprite.cacheTexture(Paths.image('num0'));
|
||||
FunkinSprite.cacheTexture(Paths.image('num1'));
|
||||
FunkinSprite.cacheTexture(Paths.image('num2'));
|
||||
FunkinSprite.cacheTexture(Paths.image('num3'));
|
||||
FunkinSprite.cacheTexture(Paths.image('num4'));
|
||||
FunkinSprite.cacheTexture(Paths.image('num5'));
|
||||
FunkinSprite.cacheTexture(Paths.image('num6'));
|
||||
FunkinSprite.cacheTexture(Paths.image('num7'));
|
||||
FunkinSprite.cacheTexture(Paths.image('num8'));
|
||||
FunkinSprite.cacheTexture(Paths.image('num9'));
|
||||
FunkinSprite.cacheTexture(Paths.image('notes', 'shared'));
|
||||
FunkinSprite.cacheTexture(Paths.image('noteSplashes', 'shared'));
|
||||
FunkinSprite.cacheTexture(Paths.image('noteStrumline', 'shared'));
|
||||
FunkinSprite.cacheTexture(Paths.image('NOTE_hold_assets'));
|
||||
FunkinSprite.cacheTexture(Paths.image('ready', 'shared'));
|
||||
FunkinSprite.cacheTexture(Paths.image('set', 'shared'));
|
||||
FunkinSprite.cacheTexture(Paths.image('go', 'shared'));
|
||||
FunkinSprite.cacheTexture(Paths.image('sick', 'shared'));
|
||||
FunkinSprite.cacheTexture(Paths.image('good', 'shared'));
|
||||
FunkinSprite.cacheTexture(Paths.image('bad', 'shared'));
|
||||
FunkinSprite.cacheTexture(Paths.image('shit', 'shared'));
|
||||
FunkinSprite.cacheTexture(Paths.image('miss', 'shared')); // TODO: remove this
|
||||
|
||||
// FunkinSprite.cacheAllNoteStyleTextures(noteStyle) // This will replace the stuff above!
|
||||
// FunkinSprite.cacheAllCharacterTextures(player)
|
||||
// FunkinSprite.cacheAllCharacterTextures(girlfriend)
|
||||
// FunkinSprite.cacheAllCharacterTextures(opponent)
|
||||
// FunkinSprite.cacheAllStageTextures(stage)
|
||||
|
||||
FunkinSprite.purgeCache();
|
||||
|
||||
FlxG.switchState(playStateCtor);
|
||||
#end
|
||||
}
|
||||
|
@ -354,7 +391,7 @@ class MultiCallback
|
|||
|
||||
public static function coolSwitchState(state:NextState, transitionTex:String = "shaderTransitionStuff/coolDots", time:Float = 2)
|
||||
{
|
||||
var screenShit:FlxSprite = new FlxSprite().loadGraphic(Paths.image("shaderTransitionStuff/coolDots"));
|
||||
var screenShit:FunkinSprite = FunkinSprite.create(Paths.image("shaderTransitionStuff/coolDots"));
|
||||
var screenWipeShit:ScreenWipeShader = new ScreenWipeShader();
|
||||
|
||||
screenWipeShit.funnyShit.input = screenShit.pixels;
|
||||
|
|
|
@ -3,6 +3,7 @@ package funkin.ui.transition;
|
|||
import flixel.FlxSprite;
|
||||
import haxe.Json;
|
||||
import lime.utils.Assets;
|
||||
import funkin.graphics.FunkinSprite;
|
||||
// import flxtyped group
|
||||
import funkin.ui.MusicBeatSubState;
|
||||
import funkin.ui.story.StoryMenuState;
|
||||
|
@ -245,6 +246,10 @@ class StickerSubState extends MusicBeatSubState
|
|||
FlxTransitionableState.skipNextTransIn = true;
|
||||
FlxTransitionableState.skipNextTransOut = true;
|
||||
|
||||
// TODO: Rework this asset caching stuff
|
||||
FunkinSprite.preparePurgeCache();
|
||||
FunkinSprite.purgeCache();
|
||||
|
||||
// I think this grabs the screen and puts it under the stickers?
|
||||
// Leaving this commented out rather than stripping it out because it's cool...
|
||||
/*
|
||||
|
@ -301,14 +306,14 @@ class StickerSubState extends MusicBeatSubState
|
|||
}
|
||||
}
|
||||
|
||||
class StickerSprite extends FlxSprite
|
||||
class StickerSprite extends FunkinSprite
|
||||
{
|
||||
public var timing:Float = 0;
|
||||
|
||||
public function new(x:Float, y:Float, stickerSet:String, stickerName:String):Void
|
||||
{
|
||||
super(x, y);
|
||||
loadGraphic(Paths.image('transitionSwag/' + stickerSet + '/' + stickerName));
|
||||
loadTexture(Paths.image('transitionSwag/' + stickerSet + '/' + stickerName));
|
||||
updateHitbox();
|
||||
scrollFactor.set();
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue