diff --git a/flixel/FlxG.hx b/flixel/FlxG.hx index a7dd006d2a..912532fdcb 100644 --- a/flixel/FlxG.hx +++ b/flixel/FlxG.hx @@ -6,6 +6,7 @@ import flixel.math.FlxRandom; import flixel.math.FlxRect; import flixel.system.FlxQuadTree; import flixel.system.FlxVersion; +import flixel.system.frontEnds.AssetFrontEnd; import flixel.system.frontEnds.BitmapFrontEnd; import flixel.system.frontEnds.BitmapLogFrontEnd; import flixel.system.frontEnds.CameraFrontEnd; @@ -335,6 +336,12 @@ class FlxG */ public static var signals(default, null):SignalFrontEnd = new SignalFrontEnd(); + /** + * Contains helper functions relating to retrieving assets + * @since 5.9.0 + */ + public static var assets(default, null):AssetFrontEnd = new AssetFrontEnd(); + /** * Resizes the game within the window by reapplying the current scale mode. */ diff --git a/flixel/graphics/FlxGraphic.hx b/flixel/graphics/FlxGraphic.hx index 287da7dbd0..35c40a3e2d 100644 --- a/flixel/graphics/FlxGraphic.hx +++ b/flixel/graphics/FlxGraphic.hx @@ -40,7 +40,7 @@ class FlxGraphic implements IFlxDestroyable if (!Cache) { - bitmap = FlxAssets.getBitmapData(Source); + bitmap = FlxG.assets.getBitmapData(Source); if (bitmap == null) return null; return createGraphic(bitmap, Key, Unique, Cache); @@ -51,7 +51,7 @@ class FlxGraphic implements IFlxDestroyable if (graphic != null) return graphic; - bitmap = FlxAssets.getBitmapData(Source); + bitmap = FlxG.assets.getBitmapData(Source); if (bitmap == null) return null; @@ -564,7 +564,7 @@ class FlxGraphic implements IFlxDestroyable if (assetsClass != null) newBitmap = FlxAssets.getBitmapFromClass(assetsClass); else if (assetsKey != null) - newBitmap = FlxAssets.getBitmapData(assetsKey); + newBitmap = FlxG.assets.getBitmapData(assetsKey); if (newBitmap != null) return FlxGraphic.getBitmap(newBitmap, unique); diff --git a/flixel/graphics/frames/FlxAtlasFrames.hx b/flixel/graphics/frames/FlxAtlasFrames.hx index b1f3245c74..161d3698d1 100644 --- a/flixel/graphics/frames/FlxAtlasFrames.hx +++ b/flixel/graphics/frames/FlxAtlasFrames.hx @@ -9,7 +9,6 @@ import flixel.math.FlxPoint; import flixel.math.FlxRect; import flixel.system.FlxAssets; import haxe.xml.Access; -import openfl.Assets; import openfl.geom.Rectangle; /** @@ -40,7 +39,7 @@ class FlxAtlasFrames extends FlxFramesCollection * * @param source The image source (can be `FlxGraphic`, `String`, or `BitmapData`). * @param description Contents of JSON file with atlas description. - * You can get it with `Assets.getText(path/to/description.json)`. + * You can get it with `FlxG.assets.getText(path/to/description.json)`. * Or you can just a pass path to the JSON file in the assets directory. * You can also directly pass in the parsed object. * @return Newly created `FlxAtlasFrames` collection. @@ -56,7 +55,7 @@ class FlxAtlasFrames extends FlxFramesCollection * * @param source The image source (can be `FlxGraphic`, `String`, or `BitmapData`). * @param description Contents of JSON file with atlas description. - * You can get it with `Assets.getText(path/to/description.json)`. + * You can get it with `FlxG.assets.getText(path/to/description.json)`. * Or you can just a pass path to the JSON file in the assets directory. * You can also directly pass in the parsed object. * @param useFrameDuration If true, any frame durations defined in the JSON will override the @@ -132,7 +131,7 @@ class FlxAtlasFrames extends FlxFramesCollection * * @param source The image source (can be `FlxGraphic`, `String` or `BitmapData`). * @param description Contents of the file with atlas description. - * You can get it with `Assets.getText(path/to/description/file)`. + * You can get it with `FlxG.assets.getText(path/to/description/file)`. * Or you can just pass path to the description file in the assets directory. * @return Newly created `FlxAtlasFrames` collection. */ @@ -152,8 +151,8 @@ class FlxAtlasFrames extends FlxFramesCollection frames = new FlxAtlasFrames(graphic); - if (Assets.exists(description)) - description = Assets.getText(description); + if (FlxG.assets.exists(description)) + description = FlxG.assets.getTextUnsafe(description); var pack:String = StringTools.trim(description); var lines:Array = pack.split("\n"); @@ -358,7 +357,7 @@ class FlxAtlasFrames extends FlxFramesCollection * * @param Source The image source (can be `FlxGraphic`, `String` or `BitmapData`). * @param Description Contents of the file with atlas description. - * You can get it with `Assets.getText(path/to/description/file)`. + * You can get it with `FlxG.assets.getText(path/to/description/file)`. * Or you can just pass a path to the description file in the assets directory. * @return Newly created `FlxAtlasFrames` collection. */ @@ -378,8 +377,8 @@ class FlxAtlasFrames extends FlxFramesCollection frames = new FlxAtlasFrames(graphic); - if (Assets.exists(Description)) - Description = Assets.getText(Description); + if (FlxG.assets.exists(Description)) + Description = FlxG.assets.getTextUnsafe(Description); var pack = StringTools.trim(Description); var lines:Array = pack.split("\n"); diff --git a/flixel/input/mouse/FlxMouse.hx b/flixel/input/mouse/FlxMouse.hx index 65f1c984b4..9b191291fb 100644 --- a/flixel/input/mouse/FlxMouse.hx +++ b/flixel/input/mouse/FlxMouse.hx @@ -285,7 +285,7 @@ class FlxMouse extends FlxPointer implements IFlxInputManager } else if ((Graphic is String)) { - cursor = new Bitmap(FlxAssets.getBitmapData(Graphic)); + cursor = new Bitmap(FlxG.assets.getBitmapData(Graphic, false)); } else { diff --git a/flixel/sound/FlxSound.hx b/flixel/sound/FlxSound.hx index 950c389e88..e087968790 100644 --- a/flixel/sound/FlxSound.hx +++ b/flixel/sound/FlxSound.hx @@ -7,7 +7,6 @@ import flixel.math.FlxPoint; import flixel.system.FlxAssets.FlxSoundAsset; import flixel.tweens.FlxTween; import flixel.util.FlxStringUtil; -import openfl.Assets; import openfl.events.Event; import openfl.events.IEventDispatcher; import openfl.media.Sound; @@ -17,9 +16,6 @@ import openfl.net.URLRequest; #if flash11 import openfl.utils.ByteArray; #end -#if (openfl >= "8.0.0") -import openfl.utils.AssetType; -#end /** * This is the universal flixel sound object, used for streaming, music, and sound effects. @@ -365,8 +361,8 @@ class FlxSound extends FlxBasic } else if ((EmbeddedSound is String)) { - if (Assets.exists(EmbeddedSound, AssetType.SOUND) || Assets.exists(EmbeddedSound, AssetType.MUSIC)) - _sound = Assets.getSound(EmbeddedSound); + if (FlxG.assets.exists(EmbeddedSound, SOUND)) + _sound = FlxG.assets.getSoundUnsafe(EmbeddedSound); else FlxG.log.error('Could not find a Sound asset with an ID of \'$EmbeddedSound\'.'); } diff --git a/flixel/system/FlxAssets.hx b/flixel/system/FlxAssets.hx index 503dee6459..1ab20ca686 100644 --- a/flixel/system/FlxAssets.hx +++ b/flixel/system/FlxAssets.hx @@ -2,9 +2,6 @@ package flixel.system; import haxe.macro.Expr; #if !macro -import openfl.display.BitmapData; -import openfl.display.Graphics; -import openfl.media.Sound; import flixel.FlxG; import flixel.graphics.FlxGraphic; import flixel.graphics.atlas.AseAtlas; @@ -12,6 +9,7 @@ import flixel.graphics.atlas.TexturePackerAtlas; import flixel.graphics.frames.FlxAtlasFrames; import flixel.graphics.frames.FlxFrame; import flixel.graphics.frames.FlxFramesCollection; +import flixel.system.frontEnds.AssetFrontEnd; import flixel.graphics.frames.bmfont.BMFont; import flixel.util.typeLimit.OneOfFour; import flixel.util.typeLimit.OneOfThree; @@ -19,7 +17,9 @@ import flixel.util.typeLimit.OneOfTwo; import haxe.io.Bytes; import haxe.Json; import haxe.xml.Access; -import openfl.Assets; +import openfl.display.BitmapData; +import openfl.display.Graphics; +import openfl.media.Sound; import openfl.utils.ByteArray; using StringTools; @@ -66,7 +66,7 @@ abstract FlxXmlAsset(OneOfTwo) from Xml from String if ((this is String)) { final str:String = cast this; - if (Assets.exists(str)) + if (FlxG.assets.exists(str)) return fromPath(str); return fromXmlString(str); @@ -77,12 +77,12 @@ abstract FlxXmlAsset(OneOfTwo) from Xml from String static inline function fromPath(path:String):Xml { - return fromXmlString(Assets.getText(path)); + return FlxG.assets.getXmlUnsafe(path); } static inline function fromXmlString(data:String):Xml { - return Xml.parse(data); + return FlxG.assets.parseXml(data); } } @@ -93,7 +93,7 @@ abstract FlxJsonAsset(OneOfTwo) from T from String if ((this is String)) { final str:String = cast this; - if (Assets.exists(str)) + if (FlxG.assets.exists(str)) return fromPath(str); return fromDataString(str); @@ -104,12 +104,12 @@ abstract FlxJsonAsset(OneOfTwo) from T from String static inline function fromPath(path:String):T { - return fromDataString(Assets.getText(path)); + return cast FlxG.assets.getJsonUnsafe(path); } static inline function fromDataString(data:String):T { - return cast Json.parse(data); + return cast FlxG.assets.parseJson(data); } } @@ -272,20 +272,22 @@ class FlxAssets graph.lineTo(100, 100); graph.endFill(); } - + + /** + * Gets an instance of a bitmap, logs when the asset is not found. + * @param id The ID or asset path for the bitmap + * @return A new BitmapData object + **/ public static inline function getBitmapData(id:String):BitmapData { - if (Assets.exists(id)) - return Assets.getBitmapData(id, true); - FlxG.log.error('Could not find a BitmapData asset with ID \'$id\'.'); - return null; + return FlxG.assets.getBitmapData(id); } /** * Generates BitmapData from specified class. Less typing. * - * @param source BitmapData class to generate BitmapData object from. - * @return Newly instantiated BitmapData object. + * @param source BitmapData class to generate BitmapData object from. + * @return Newly instantiated BitmapData object. */ public static inline function getBitmapFromClass(source:Class):BitmapData { @@ -299,22 +301,22 @@ class FlxAssets * 3) if the input is String, then it will get BitmapData from openfl.Assets; * 4) it will return null in any other case. * - * @param Graphic input data to get BitmapData object for. - * @return BitmapData for specified Dynamic object. + * @param graphic input data to get BitmapData object for. + * @return BitmapData for specified Dynamic object. */ - public static function resolveBitmapData(Graphic:FlxGraphicSource):BitmapData + public static function resolveBitmapData(graphic:FlxGraphicSource):BitmapData { - if ((Graphic is BitmapData)) + if ((graphic is BitmapData)) { - return cast Graphic; + return cast graphic; } - else if ((Graphic is Class)) + else if ((graphic is Class)) { - return FlxAssets.getBitmapFromClass(cast Graphic); + return getBitmapFromClass(cast graphic); } - else if ((Graphic is String)) + else if ((graphic is String)) { - return FlxAssets.getBitmapData(Graphic); + return FlxG.assets.getBitmapData(graphic); } return null; @@ -327,30 +329,28 @@ class FlxAssets * 3) if the input is String, then it will return it; * 4) it will return null in any other case. * - * @param Graphic input data to get string key for. - * @param Key optional key string. - * @return Key String for specified Graphic object. + * @param graphic input data to get string key for. + * @param key optional key string. + * @return Key String for specified Graphic object. */ - public static function resolveKey(Graphic:FlxGraphicSource, ?Key:String):String + public static function resolveKey(graphic:FlxGraphicSource, ?key:String):String { - if (Key != null) - { - return Key; - } - - if ((Graphic is BitmapData)) + if (key != null) + return key; + + if ((graphic is BitmapData)) { - return Key; + return key; } - else if ((Graphic is Class)) + else if ((graphic is Class)) { - return FlxG.bitmap.getKeyForClass(cast Graphic); + return FlxG.bitmap.getKeyForClass(cast graphic); } - else if ((Graphic is String)) + else if ((graphic is String)) { - return Graphic; + return graphic; } - + return null; } @@ -361,12 +361,27 @@ class FlxAssets * @param id The asset id of the local sound file. * @return The sound file. */ - public static function getSound(id:String):Sound + @:deprecated("FlxAssets.getSound is deprecated, use getSoundAddExtension, instead") + public static inline function getSound(id:String):Sound + { + return getSoundAddExtension(id); + } + + /** + * Loads an OpenFL sound asset from the given asset id. If an extension not provided the + * `defaultSoundExtension` is used (defaults to "ogg" on non-flash targets). + * + * @param id The asset id of the local sound file. + * @return The sound file. + * + * @since 5.9.0 + */ + public static function getSoundAddExtension(id:String, useCache = true):Sound { if (!id.endsWith(".mp3") && !id.endsWith(".ogg") && !id.endsWith(".wav")) id += "." + defaultSoundExtension; - return Assets.getSound(id); + return FlxG.assets.getSoundUnsafe(id, useCache); } public static function getVirtualInputFrames():FlxAtlasFrames diff --git a/flixel/system/FlxSplash.hx b/flixel/system/FlxSplash.hx index 3becdcc3fb..10092194a4 100644 --- a/flixel/system/FlxSplash.hx +++ b/flixel/system/FlxSplash.hx @@ -87,7 +87,7 @@ class FlxSplash extends FlxState #if FLX_SOUND_SYSTEM if (!muted) { - FlxG.sound.load(FlxAssets.getSound("flixel/sounds/flixel")).play(); + FlxG.sound.load(FlxAssets.getSoundAddExtension("flixel/sounds/flixel")).play(); } #end } diff --git a/flixel/system/frontEnds/AssetFrontEnd.hx b/flixel/system/frontEnds/AssetFrontEnd.hx new file mode 100644 index 0000000000..00f1f68e5e --- /dev/null +++ b/flixel/system/frontEnds/AssetFrontEnd.hx @@ -0,0 +1,640 @@ +package flixel.system.frontEnds; + +import flixel.FlxG; +import flixel.system.FlxAssets; +import flixel.system.debug.log.LogStyle; +import haxe.io.Bytes; +import haxe.io.Path; +import haxe.Json; +import haxe.xml.Access; +import openfl.display.BitmapData; +import openfl.media.Sound; +import openfl.utils.Assets; +import openfl.utils.AssetType; +import openfl.utils.AssetCache; +import openfl.utils.Future; +import openfl.text.Font; + +using StringTools; + +/** + * Accessed via `FlxG.assets`. The main interface for the asset system. By default, OpenFl's + * Asset system is used, which uses relative path strings to retrive assets, though you can completely + * avoid Openfl's asset system by setting custom methods to the following dynamic fields: `getAssetUnsafe`, + * `loadAsset`, `exists`, `isLocal` and `list`. + * + * ## Common Uses + * The initial reason for making customizable asset system + * was to allow for "hot-reloading", or testing new assets in your game without recompiling, with + * each change. Say, if you would like a debug feature where you load assets from source assets, + * rather than the assets copied over to your export folder, you could overwrite this system to do + * just that. + * + * Other potential uses for this are modding, bypassing the manifest and loading resources from + * a remote location. + * + * ### Quick Setup for "Hot-Reloading" + * To simplify the process mentioned above, the `FLX_CUSTOM_ASSETS_DIRECTORY` flag was created. + * By adding `-DFLX_CUSTOM_ASSETS_DIRECTORY="../../../assets"` to your lime build command + * it will automatically grab assets from your project root's assets folder rather than, the + * default "export/hl/bin/assets". This will only work with a single asset root folder with one + * asset library and will use the openfl asset system if the asset id starts with "flixel/" or + * tries to references a specific library using the format: "libName:asset/path/file.ext". + * + * @since 5.9.0 + */ +class AssetFrontEnd +{ + #if FLX_CUSTOM_ASSETS_DIRECTORY + /** + * The target directory + */ + final directory:String; + + /** + * The parent of the target directory, is prepended to any `id` passed in + */ + final parentDirectory:String; + + public function new () + { + final rawPath = '${haxe.macro.Compiler.getDefine("FLX_CUSTOM_ASSETS_DIRECTORY")}'; + // Remove final slash and accepts backslashes and removes redundancies + directory = Path.normalize(rawPath); + // Verify valid directory + if (sys.FileSystem.exists(directory) == false) + throw 'Invalid value:"$directory" of FLX_CUSTOM_ASSETS_DIRECTORY, expecting relative or absolute path'; + // remove final "/assets" since the id typically contains it + final split = sys.FileSystem.absolutePath(directory).split("/"); + split.pop(); + parentDirectory = split.join("/"); + } + + function getPath(id:String) + { + return Path.normalize('$parentDirectory/$id'); + } + + /** + * True for assets packaged with all HaxeFlixel build, and any non-default libraries + */ + function useOpenflAssets(id:String) + { + return id.startsWith("flixel/") || id.contains(':'); + } + #else + public function new () {} + #end + + /** + * Used by methods like `getAsset`, `getBitmapData`, `getText`, their "unsafe" counterparts and + * the like to get assets synchronously. Can be set to a custom function to avoid the existing + * asset system. Unlike its "safe" counterpart, there is no log on missing assets + * + * @param id The id of the asset, usually a path + * @param type The type of asset to look for, determines the type + * @param useCache Whether to allow use of the asset cache (if one exists) + * @return The asset, if found, otherwise `null` is returned + */ + public dynamic function getAssetUnsafe(id:String, type:FlxAssetType, useCache = true):Null + { + #if FLX_STANDARD_ASSETS_DIRECTORY + return getOpenflAssetUnsafe(id, type, useCache); + #else + + if (useOpenflAssets(id)) + return getOpenflAssetUnsafe(id, type, useCache); + // load from custom assets directory + final canUseCache = useCache && Assets.cache.enabled; + + final asset:Any = switch type + { + // No caching + case TEXT: + sys.io.File.getContent(getPath(id)); + case BINARY: + sys.io.File.getBytes(getPath(id)); + + // Check cache + case IMAGE if (canUseCache && Assets.cache.hasBitmapData(id)): + Assets.cache.getBitmapData(id); + case SOUND if (canUseCache && Assets.cache.hasSound(id)): + Assets.cache.getSound(id); + case FONT if (canUseCache && Assets.cache.hasFont(id)): + Assets.cache.getFont(id); + + // Get asset and set cache + case IMAGE: + final bitmap = BitmapData.fromFile(getPath(id)); + if (canUseCache) + Assets.cache.setBitmapData(id, bitmap); + bitmap; + case SOUND: + final sound = Sound.fromFile(getPath(id)); + if (canUseCache) + Assets.cache.setSound(id, sound); + sound; + case FONT: + final font = Font.fromFile(getPath(id)); + if (canUseCache) + Assets.cache.setFont(id, font); + font; + } + + return asset; + #end + } + + function getOpenflAssetUnsafe(id:String, type:FlxAssetType, useCache = true):Null + { + // Use openfl assets + return switch(type) + { + case TEXT: Assets.getText(id); + case BINARY: Assets.getBytes(id); + case IMAGE: Assets.getBitmapData(id, useCache); + case SOUND: Assets.getSound(id, useCache); + case FONT: Assets.getFont(id, useCache); + } + } + + /** + * Calls `getAssetUnsafe` if the asset exists, otherwise logs that the asset is missing, via `FlxG.log` + * + * @param id The id of the asset, usually a path + * @param type The type of asset to look for, determines the type + * @param useCache Whether to allow use of the asset cache (if one exists) + * @param logStyle How to log, if the asset is not found. Uses `LogStyle.ERROR` by default + */ + public function getAsset(id:String, type:FlxAssetType, useCache = true, ?logStyle:LogStyle):Null + { + inline function log(message:String) + { + if (logStyle == null) + logStyle = LogStyle.ERROR; + FlxG.log.advanced(message, logStyle); + } + + if (exists(id, type)) + { + if (isLocal(id, type)) + return getAssetUnsafe(id, type, useCache); + + log('$type asset "$id" exists, but only asynchronously'); + return null; + } + + log('Could not find a $type asset with ID \'$id\'.'); + return null; + } + + /** + * Used by methods like `loadBitmapData`, `loadText` and the like to get assets asynchronously. + * Can be set to a custom function to avoid the existing asset system. + * + * @param id The id of the asset, usually a path + * @param type The type of asset to look for, determines the type + * @param useCache Whether to allow use of the asset cache (if one exists) + */ + public dynamic function loadAsset(id:String, type:FlxAssetType, useCache = true):Future + { + #if FLX_STANDARD_ASSETS_DIRECTORY + return loadOpenflAsset(id, type, useCache); + #else + + if (useOpenflAssets(id)) + return loadOpenflAsset(id, type, useCache); + + // get the asset synchronously and wrap it in a future + return Future.withValue(getAsset(id, type, useCache)); + // TODO: html? + #end + } + + function loadOpenflAsset(id:String, type:FlxAssetType, useCache = true):Future + { + return switch(type) + { + case TEXT: Assets.loadText(id); + case BINARY: Assets.loadBytes(id); + case IMAGE: Assets.loadBitmapData(id, useCache); + case SOUND: Assets.loadSound(id, useCache); + case FONT: Assets.loadFont(id, useCache); + } + } + + /** + * Whether a specific asset ID and type exists. + * Can be set to a custom function to avoid the existing asset system. + * + * @param id The ID or asset path for the asset + * @param type The asset type to match, or null to match any type + */ + public dynamic function exists(id:String, ?type:FlxAssetType) + { + #if FLX_STANDARD_ASSETS_DIRECTORY + return Assets.exists(id, type.toOpenFlType()); + #else + if (useOpenflAssets(id)) + return Assets.exists(id, type.toOpenFlType()); + // Can't verify contents match expected type without + return sys.FileSystem.exists(getPath(id)); + #end + } + + /** + * Returns whether an asset is "local", and therefore can be loaded synchronously, or with the + * `getAsset` method, otherwise the `loadAsset` method should be used. + * Can be set to a custom function to avoid the existing asset system. + * + * @param id The ID or asset path for the asset + * @param type The asset type to match, or null to match any type + * @param useCache Whether to allow use of the asset cache (if one exists) + * @return Whether the asset is local + */ + public dynamic function isLocal(id:String, ?type:FlxAssetType, useCache = true) + { + #if FLX_STANDARD_ASSETS_DIRECTORY + return Assets.isLocal(id, type.toOpenFlType(), useCache); + #else + + if (useOpenflAssets(id)) + Assets.isLocal(id, type.toOpenFlType(), useCache); + + return true; + #end + } + + /** + * Returns a list of all assets (by type). + * Can be set to a custom function to avoid the existing asset system. + * + * @param type The asset type to match, or null to match any type + * @return An array of asset ID values + */ + public dynamic function list(?type:FlxAssetType) + { + #if FLX_STANDARD_ASSETS_DIRECTORY + return Assets.list(type.toOpenFlType()); + #else + // list all files in the directory, recursively + final list = []; + function addFiles(directory:String, prefix = "") + { + for (path in sys.FileSystem.readDirectory(directory)) + { + if (sys.FileSystem.isDirectory('$directory/$path')) + addFiles('$directory/$path',path + '/'); + else + list.push(prefix + path); + } + } + final prefix = Path.withoutDirectory(directory) + "/"; + addFiles(directory, prefix); + return list; + #end + } + + /** + * Gets an instance of a bitmap. Unlike its "safe" counterpart, there is no log on missing assets + * + * @param id The ID or asset path for the bitmap + * @param useCache Whether to allow use of the asset cache (if one exists) + * @return A new BitmapData object + */ + public inline function getBitmapDataUnsafe(id:String, useCache = false):BitmapData + { + return cast getAssetUnsafe(id, IMAGE, useCache); + } + + /** + * Gets an instance of a bitmap, logs when the asset is not found + * + * @param id The ID or asset path for the bitmap + * @param useCache Whether to allow use of the asset cache (if one exists) + * @return A new BitmapData object + */ + public inline function getBitmapData(id:String, useCache = false, ?logStyle:LogStyle):BitmapData + { + return cast getAsset(id, IMAGE, useCache, logStyle); + } + + /** + * Gets an instance of a sound. Unlike its "safe" counterpart, there is no log on missing assets + * + * @param id The ID or asset path for the sound + * @param useCache Whether to allow use of the asset cache (if one exists) + * @return A new `Sound` object Note: Dos not return a `FlxSound` + */ + public inline function getSoundUnsafe(id:String, useCache = true):Sound + { + return cast getAssetUnsafe(id, SOUND, useCache); + } + + /** + * Gets an instance of a sound, logs when the asset is not found + * + * @param id The ID or asset path for the sound + * @param useCache Whether to allow use of the asset cache (if one exists) + * @param logStyle How to log, if the asset is not found. Uses `LogStyle.ERROR` by default + * @return A new `Sound` object Note: Dos not return a `FlxSound` + */ + public inline function getSound(id:String, useCache = true, ?logStyle:LogStyle):Sound + { + return cast getAsset(id, SOUND, useCache, logStyle); + } + + /** + * Gets the contents of a text-based asset. Unlike its "safe" counterpart, there is no log + * on missing assets + * + * **Note:** The default asset system does not cache text assets + * + * @param id The ID or asset path for the asset + * @param useCache Whether to allow use of the asset cache (if one exists) + */ + public inline function getTextUnsafe(id:String, useCache = true):String + { + return cast getAssetUnsafe(id, TEXT, useCache); + } + + /** + * Gets the contents of a text-based asset, logs when the asset is not found + * + * **Note:** The default asset system does not cache text assets + * + * @param id The ID or asset path for the asset + * @param useCache Whether to allow use of the asset cache (if one exists) + * @param logStyle How to log, if the asset is not found. Uses `LogStyle.ERROR` by default + */ + public inline function getText(id:String, useCache = true, ?logStyle:LogStyle):String + { + return cast getAsset(id, TEXT, useCache, logStyle); + } + + /** + * Parses the contents of a xml-based asset into an `Xml` object. + * Unlike its "safe" counterpart, there is no log on missing assets + * + * **Note:** The default asset system does not cache xml assets + * + * @param id The ID or asset path for the asset + * @param useCache Whether to allow use of the asset cache (if one exists) + */ + public inline function getXmlUnsafe(id:String, useCache = true) + { + final text = getTextUnsafe(id, useCache); + return text != null ? parseXml(text) : null; + } + + /** + * Parses the contents of a xml-based asset into an `Xml` object, logs when the asset is not found + * + * **Note:** The default asset system does not cache xml assets + * + * @param id The ID or asset path for the asset + * @param useCache Whether to allow use of the asset cache (if one exists) + * @param logStyle How to log, if the asset is not found. Uses `LogStyle.ERROR` by default + */ + public inline function getXml(id:String, useCache = true, ?logStyle:LogStyle) + { + final text = getText(id, useCache, logStyle); + return text != null ? parseXml(text) : null; + } + + /** + * Gets the contents of a xml-based asset. + * Unlike its "safe" counterpart, there is no log on missing assets + * + * **Note:** The default asset system does not cache json assets + * + * @param id The ID or asset path for the asset + * @param useCache Whether to allow use of the asset cache (if one exists) + */ + public inline function getJsonUnsafe(id:String, useCache = true) + { + final text = getTextUnsafe(id, useCache); + return text != null ? parseJson(text) : null; + } + + /** + * Gets the contents of a xml-based asset, logs when the asset is not found + * + * **Note:** The default asset system does not cache json assets + * + * @param id The ID or asset path for the asset + * @param useCache Whether to allow use of the asset cache (if one exists) + * @param logStyle How to log, if the asset is not found. Uses `LogStyle.ERROR` by default + */ + public inline function getJson(id:String, useCache = true, ?logStyle:LogStyle) + { + final text = getText(id, useCache, logStyle); + return text != null ? parseJson(text) : null; + } + + /** + * Gets the contents of a binary asset. + * Unlike its "safe" counterpart, there is no log on missing assets + * + * **Note:** The default asset system does not cache binary assets + * + * @param id The ID or asset path for the asset + * @param useCache Whether to allow use of the asset cache (if one exists) + */ + public inline function getBytesUnsafe(id:String, useCache = true):Bytes + { + return cast getAssetUnsafe(id, BINARY, useCache); + } + + /** + * Gets the contents of a binary asset, logs when the asset is not found + * + * **Note:** The default asset system does not cache binary assets + * + * @param id The ID or asset path for the asset + * @param useCache Whether to allow use of the asset cache (if one exists) + * @param logStyle How to log, if the asset is not found. Uses `LogStyle.ERROR` by default + */ + public inline function getBytes(id:String, useCache = true, ?logStyle:LogStyle):Bytes + { + return cast getAsset(id, BINARY, useCache); + } + + /** + * Gets the contents of a font asset. + * Unlike its "safe" counterpart, there is no log on missing assets + * + * @param id The ID or asset path for the asset + * @param useCache Whether to allow use of the asset cache + */ + public inline function getFontUnsafe(id:String, useCache = true):Font + { + return cast getAssetUnsafe(id, FONT, useCache); + } + + /** + * Gets the contents of a font asset, logs when the asset is not found + * + * @param id The ID or asset path for the asset + * @param useCache Whether to allow use of the asset cache (if one exists) + * @param logStyle How to log, if the asset is not found. Uses `LogStyle.ERROR` by default + */ + public inline function getFont(id:String, useCache = true, ?logStyle:LogStyle):Font + { + return cast getAsset(id, FONT, useCache, logStyle); + } + + /** + * Loads an bitmap asset asynchronously + * + * @param id The ID or asset path for the asset + * @param useCache Whether to allow use of the asset cache (if one exists) + * @return Returns a `Future` which allows listeners to be added via methods like `onComplete` + */ + public inline function loadBitmapData(id:String, useCache = false):Future + { + return cast loadAsset(id, IMAGE, useCache); + } + + /** + * Loads a sound asset asynchronously + * + * @param id The ID or asset path for the asset + * @param useCache Whether to allow use of the asset cache (if one exists) + * @return Returns a `Future` which allows listeners to be added via methods like `onComplete` + */ + public inline function loadSound(id:String, useCache = true):Future + { + return cast loadAsset(id, SOUND, useCache); + } + + /** + * Loads a text asset asynchronously + * + * **Note:** The default asset system does not cache text assets + * + * @param id The ID or asset path for the asset + * @param useCache Whether to allow use of the asset cache (if one exists) + * @return Returns a `Future` which allows listeners to be added via methods like `onComplete` + */ + public inline function loadText(id:String, useCache = true):Future + { + return cast loadAsset(id, TEXT, useCache); + } + + /** + * Loads a text asset asynchronously + * + * **Note:** The default asset system does not cache xml assets + * + * @param id The ID or asset path for the asset + * @param useCache Whether to allow use of the asset cache (if one exists) + * @return Returns a `Future` which allows listeners to be added via methods like `onComplete` + */ + public inline function loadXml(id:String, useCache = true):Future + { + return wrapFuture(loadText(id, useCache), parseXml); + } + + /** + * Loads a text asset asynchronously + * + * **Note:** The default asset system does not cache json assets + * + * @param id The ID or asset path for the asset + * @param useCache Whether to allow use of the asset cache (if one exists) + * @return Returns a `Future` which allows listeners to be added via methods like `onComplete` + */ + public inline function loadJson(id:String, useCache = true):Future + { + return wrapFuture(loadText(id, useCache), parseJson); + } + + /** + * Loads a binary asset asynchronously + * + * **Note:** The default asset system does not cache binary assets + * + * @param id The ID or asset path for the asset + * @param useCache Whether to allow use of the asset cache (if one exists) + * @return Returns a `Future` which allows listeners to be added via methods like `onComplete` + */ + public inline function loadBytes(id:String, useCache = true):Future + { + return cast loadAsset(id, BINARY, useCache); + } + + /** + * Loads a font asset asynchronously + * + * @param id The ID or asset path for the asset + * @param useCache Whether to allow use of the asset cache (if one exists) + * @return Returns a `Future` which allows listeners to be added via methods like `onComplete` + */ + public inline function loadFont(id:String, useCache = true):Future + { + return cast loadAsset(id, FONT, useCache); + } + + /** + * Parses a json string, creates and returns a struct + */ + public inline function parseJson(jsonText:String) + { + return Json.parse(jsonText); + } + + /** + * Parses an xml string, creates and returns an Xml object + */ + public inline function parseXml(xmlText:String) + { + return Xml.parse(xmlText); + } + + inline function wrapFuture(future:Future, converter:(T1)->T2):Future + { + final promise = new lime.app.Promise(); + + future.onComplete((data)->promise.complete(converter(data))); + future.onError((error)->promise.error(error)); + future.onProgress((progress, total)->promise.progress(progress, total)); + + return promise.future; + } +} + +/** + * The AssetType enum lists the core set of available + * asset types from the OpenFL command-line tools. + * @since 5.9.0 + */ +enum abstract FlxAssetType(String) +{ + /** Binary assets (data that is not readable as text) */ + var BINARY = "binary"; + + /** Font assets, such as *.ttf or *.otf files */ + var FONT = "font"; + + /** Image assets, such as *.png or *.jpg files */ + var IMAGE ="image"; + + /** Audio assets, such as *.ogg or *.wav files */ + var SOUND = "sound"; + + /** Text assets */ + var TEXT = "text"; + + public function toOpenFlType() + { + return switch((cast this:FlxAssetType)) + { + case BINARY: AssetType.BINARY; + case FONT: AssetType.FONT; + case IMAGE: AssetType.IMAGE; + case SOUND: AssetType.SOUND; + case TEXT: AssetType.TEXT; + } + } +} diff --git a/flixel/system/frontEnds/BitmapFrontEnd.hx b/flixel/system/frontEnds/BitmapFrontEnd.hx index fb66af7025..4076756083 100644 --- a/flixel/system/frontEnds/BitmapFrontEnd.hx +++ b/flixel/system/frontEnds/BitmapFrontEnd.hx @@ -5,7 +5,7 @@ import flixel.graphics.FlxGraphic; import flixel.graphics.frames.FlxFrame; import flixel.math.FlxPoint; import flixel.math.FlxRect; -import flixel.system.FlxAssets.FlxGraphicAsset; +import flixel.system.FlxAssets; import flixel.util.FlxColor; import openfl.Assets; #if FLX_OPENGL_AVAILABLE @@ -216,7 +216,7 @@ class BitmapFrontEnd /** * Creates string key for further caching. * - * @param systemKey The first string key to use as a base for a new key. It's usually a key from openfl.Assets ("assets/image.png"). + * @param systemKey The first string key to use as a base for a new key. It's usually an asset key ("assets/image.png"). * @param userKey The second string key to use as a base for a new key. It's usually a key provided by the user * @param unique Whether generated key should be unique or not. * @return Created key. diff --git a/flixel/system/frontEnds/LogFrontEnd.hx b/flixel/system/frontEnds/LogFrontEnd.hx index b355c7ceb3..a93f27df74 100644 --- a/flixel/system/frontEnds/LogFrontEnd.hx +++ b/flixel/system/frontEnds/LogFrontEnd.hx @@ -64,7 +64,7 @@ class LogFrontEnd #if (FLX_SOUND_SYSTEM && !FLX_UNIT_TEST) if (style.errorSound != null) { - final sound = FlxAssets.getSound(style.errorSound); + final sound = FlxAssets.getSoundAddExtension(style.errorSound); if (sound != null) FlxG.sound.load(sound).play(); } diff --git a/flixel/system/frontEnds/SoundFrontEnd.hx b/flixel/system/frontEnds/SoundFrontEnd.hx index 9784d759bb..3a40e9ddc5 100644 --- a/flixel/system/frontEnds/SoundFrontEnd.hx +++ b/flixel/system/frontEnds/SoundFrontEnd.hx @@ -11,11 +11,7 @@ import flixel.system.FlxAssets; import flixel.system.ui.FlxSoundTray; import flixel.text.FlxInputText; import flixel.util.FlxSignal; -import openfl.Assets; import openfl.media.Sound; -#if (openfl >= "8.0.0") -import openfl.utils.AssetType; -#end /** * Accessed via `FlxG.sound`. @@ -213,8 +209,8 @@ class SoundFrontEnd public inline function cache(embeddedSound:String):Sound { // load the sound into the OpenFL assets cache - if (Assets.exists(embeddedSound, AssetType.SOUND) || Assets.exists(embeddedSound, AssetType.MUSIC)) - return Assets.getSound(embeddedSound, true); + if (FlxG.assets.exists(embeddedSound, SOUND)) + return FlxG.assets.getSoundUnsafe(embeddedSound, true); FlxG.log.error('Could not find a Sound asset with an ID of \'$embeddedSound\'.'); return null; } @@ -225,7 +221,7 @@ class SoundFrontEnd */ public function cacheAll():Void { - for (id in Assets.list(AssetType.SOUND)) + for (id in FlxG.assets.list(SOUND)) { cache(id); } diff --git a/flixel/system/macros/FlxDefines.hx b/flixel/system/macros/FlxDefines.hx index 1ef3234f44..882bb6aaf5 100644 --- a/flixel/system/macros/FlxDefines.hx +++ b/flixel/system/macros/FlxDefines.hx @@ -3,6 +3,7 @@ package flixel.system.macros; import haxe.macro.Compiler; import haxe.macro.Context; import haxe.macro.Expr.Position; +import haxe.io.Path; #if (flixel_addons >= "3.2.2") import flixel.addons.system.macros.FlxAddonDefines; #end @@ -43,6 +44,14 @@ private enum UserDefines FLX_TRACK_POOLS; /** Adds `creationInfo` to FlxGraphic instances, automatically defined with FLX_DEBUG */ FLX_TRACK_GRAPHICS; + /** + * Loads from the specified relative or absolute directory. Unlike other boolean flags, + * this flag should contain a string value. + * + * **Note:** When using assets entirely from outside the build directory, it is wise to disable + * any `` tags in your project.xml, to reduce your total memory + */ + FLX_CUSTOM_ASSETS_DIRECTORY; } /** @@ -87,6 +96,8 @@ private enum HelperDefines FLX_NO_TRACK_POOLS; FLX_NO_TRACK_GRAPHICS; FLX_OPENGL_AVAILABLE; + /** Defined to `1`(or `true`) if `FLX_CUSTOM_ASSETS_DIRECTORY` is not defined */ + FLX_STANDARD_ASSETS_DIRECTORY; } class FlxDefines @@ -260,6 +271,28 @@ class FlxDefines #end defineInversion(FLX_TRACK_GRAPHICS, FLX_NO_TRACK_GRAPHICS); + + if (defined(FLX_CUSTOM_ASSETS_DIRECTORY)) + { + if (!defined("sys")) + { + abort('FLX_CUSTOM_ASSETS_DIRECTORY is only available on sys targets', (macro null).pos); + } + else + { + // Todo: check sys targets + final rawDirectory = Path.normalize(definedValue(FLX_CUSTOM_ASSETS_DIRECTORY)); + final directory = Path.normalize(rawDirectory); + if (!sys.FileSystem.isDirectory(directory) || directory == "1") + { + final absPath = sys.FileSystem.absolutePath(directory); + abort('FLX_CUSTOM_ASSETS_DIRECTORY must be a path to a directory, got "$rawDirectory"' + + '\nabsolute path: $absPath', (macro null).pos); + } + } + } + else // define boolean inversion + define(FLX_STANDARD_ASSETS_DIRECTORY); } static function defineInversion(userDefine:UserDefines, invertedDefine:HelperDefines) @@ -287,6 +320,11 @@ class FlxDefines abort(errorMessage, (macro null).pos); } + static inline function definedValue(define:Dynamic):String + { + return Context.definedValue(Std.string(define)); + } + static inline function defined(define:Dynamic) { return Context.defined(Std.string(define)); diff --git a/flixel/system/ui/FlxSoundTray.hx b/flixel/system/ui/FlxSoundTray.hx index a5492bca4d..e04212ad84 100644 --- a/flixel/system/ui/FlxSoundTray.hx +++ b/flixel/system/ui/FlxSoundTray.hx @@ -148,7 +148,7 @@ class FlxSoundTray extends Sprite { if (!silent) { - var sound = FlxAssets.getSound(up ? volumeUpSound : volumeDownSound); + var sound = FlxAssets.getSoundAddExtension(up ? volumeUpSound : volumeDownSound); if (sound != null) FlxG.sound.load(sound).play(); } diff --git a/flixel/text/FlxText.hx b/flixel/text/FlxText.hx index 3378887100..f7228b9ce1 100644 --- a/flixel/text/FlxText.hx +++ b/flixel/text/FlxText.hx @@ -26,9 +26,6 @@ using flixel.util.FlxStringUtil; #if flash import openfl.geom.Rectangle; #end -#if (openfl >= "8.0.0") -import openfl.utils.AssetType; -#end // TODO: think about filters and text @@ -693,9 +690,9 @@ class FlxText extends FlxSprite if (Font != null) { var newFontName:String = Font; - if (Assets.exists(Font, AssetType.FONT)) + if (FlxG.assets.exists(Font, FONT)) { - newFontName = Assets.getFont(Font).fontName; + newFontName = FlxG.assets.getFontUnsafe(Font).fontName; } _defaultFormat.font = newFontName; diff --git a/flixel/tile/FlxBaseTilemap.hx b/flixel/tile/FlxBaseTilemap.hx index 590095422c..f60d6eeb08 100644 --- a/flixel/tile/FlxBaseTilemap.hx +++ b/flixel/tile/FlxBaseTilemap.hx @@ -11,7 +11,6 @@ import flixel.util.FlxCollision; import flixel.util.FlxColor; import flixel.util.FlxDirectionFlags; import flixel.util.FlxStringUtil; -import openfl.Assets; import openfl.display.BitmapData; using StringTools; @@ -390,9 +389,9 @@ class FlxBaseTilemap extends FlxObject startingIndex = 0, drawIndex = 1, collideIndex = 1) { // path to map data file? - if (Assets.exists(mapData)) + if (FlxG.assets.exists(mapData)) { - mapData = Assets.getText(mapData); + mapData = FlxG.assets.getTextUnsafe(mapData); } // Figure out the map dimensions based on the data string diff --git a/flixel/util/FlxStringUtil.hx b/flixel/util/FlxStringUtil.hx index 13ef02ecf7..92bef9c222 100644 --- a/flixel/util/FlxStringUtil.hx +++ b/flixel/util/FlxStringUtil.hx @@ -568,7 +568,7 @@ class FlxStringUtil if ((ImageFile is String)) { - tempBitmapData = FlxAssets.getBitmapData(ImageFile); + tempBitmapData = FlxG.assets.getBitmapData(ImageFile); } else {