mirror of
https://github.com/FunkinCrew/Funkin.git
synced 2024-11-14 19:25:16 -05:00
Properly implemented Polymod for mod loading.
This commit is contained in:
parent
81f7a04916
commit
bfad5b3352
11 changed files with 271 additions and 43 deletions
31
Project.xml
31
Project.xml
|
@ -29,9 +29,6 @@
|
|||
<!--Mobile-specific-->
|
||||
<window if="mobile" orientation="landscape" fullscreen="true" width="0" height="0" resizable="false"/>
|
||||
|
||||
<!--Switch-specific-->
|
||||
<window if="switch" orientation="landscape" fullscreen="true" width="0" height="0" resizable="true" />
|
||||
|
||||
<!-- _____________________________ Path Settings ____________________________ -->
|
||||
|
||||
<set name="BUILD_DIR" value="export/debug" if="debug" />
|
||||
|
@ -40,12 +37,9 @@
|
|||
|
||||
<classpath name="source" />
|
||||
|
||||
<!-- <assets path='assets/preload/music' include="*mp4" embed='false' /> -->
|
||||
|
||||
<assets path="assets/preload" rename="assets" exclude="*.ogg" if="web"/>
|
||||
<assets path="assets/preload" rename="assets" exclude="*.mp3" unless="web"/>
|
||||
|
||||
<!-- <define name="PRELOAD_ALL" /> -->
|
||||
<define name="PRELOAD_ALL" unless="web" />
|
||||
<define name="NO_PRELOAD_ALL" unless="PRELOAD_ALL"/>
|
||||
|
||||
|
@ -102,9 +96,8 @@
|
|||
|
||||
<!-- <assets path='example_mods' rename='mods' embed='false'/> -->
|
||||
|
||||
<assets path='example_mods' rename='mods' embed='false'/>
|
||||
<assets path='example_mods' rename='mods' embed='false' exclude="*.md"/>
|
||||
<assets path='art/readme.txt' rename='do NOT readme.txt' />
|
||||
<!-- <template path='mods' /> -->
|
||||
|
||||
<assets path="CHANGELOG.md" rename='changelog.txt'/>
|
||||
|
||||
|
@ -223,6 +216,28 @@
|
|||
|
||||
<config:ios allow-provisioning-updates="true" team-id=""/>
|
||||
|
||||
<!-- Options for Polymod -->
|
||||
<section if="polymod">
|
||||
<!-- Turns on additional debug logging. -->
|
||||
<haxedef name="POLYMOD_DEBUG" value="true" if="debug" />
|
||||
<!-- The file extension to use for script files. -->
|
||||
<haxedef name="POLYMOD_SCRIPT_EXT" value=".hscript" />
|
||||
<!-- Which asset library to use for scripts. -->
|
||||
<haxedef name="POLYMOD_SCRIPT_LIBRARY" value="scripts" />
|
||||
<!-- The base path from which scripts should be accessed. -->
|
||||
<haxedef name="POLYMOD_ROOT_PATH" value="scripts/" />
|
||||
<!-- Determines the precision required for mods to be compatible. -->
|
||||
<haxedef name="POLYMOD_API_VERSION_MATCH" value="MATCH_MINOR" />
|
||||
<!-- Determines the subdirectory of the mod folder used for file appending. -->
|
||||
<haxedef name="POLYMOD_APPEND_FOLDER" value="_append" />
|
||||
<!-- Determines the subdirectory of the mod folder used for file merges. -->
|
||||
<haxedef name="POLYMOD_MERGE_FOLDER" value="_merge" />
|
||||
<!-- Determines the file in the mod folder used for metadata. -->
|
||||
<haxedef name="POLYMOD_MOD_METADATA_FILE" value="_polymod_meta.json" />
|
||||
<!-- Determines the file in the mod folder used for the icon. -->
|
||||
<haxedef name="POLYMOD_MOD_ICON_FILE" value="_polymod_icon.png" />
|
||||
</section>
|
||||
|
||||
<section if='TOOLS'>
|
||||
<!-- Compiles tool for old song conversion shit -->
|
||||
<!-- Assumes you use it on windows/desktop!!!! -->
|
||||
|
|
21
README.md
21
README.md
|
@ -2,19 +2,26 @@
|
|||
|
||||
This is the repository for Friday Night Funkin, a game originally made for Ludum Dare 47 "Stuck In a Loop".
|
||||
|
||||
Play the Ludum Dare prototype here: https://ninja-muffin24.itch.io/friday-night-funkin
|
||||
Play the Newgrounds one here: https://www.newgrounds.com/portal/view/770371
|
||||
Support the project on the itch.io page: https://ninja-muffin24.itch.io/funkin
|
||||
Play free in your browser on Newgrounds: https://www.newgrounds.com/portal/view/770371
|
||||
Download the game for desktop on Itch.io: https://ninja-muffin24.itch.io/funkin
|
||||
|
||||
# Credits
|
||||
|
||||
- [ninjamuffin99](https://twitter.com/ninja_muffin99) - Programmer
|
||||
- [PhantomArcade3K](https://twitter.com/phantomarcade3k) and [Evilsk8r](https://twitter.com/evilsk8r) - Art
|
||||
- [Kawaisprite](https://twitter.com/kawaisprite) - Musician
|
||||
|
||||
This game was made with love to Newgrounds and it's community. Extra love to Tom Fulp.
|
||||
|
||||
|
||||
|
||||
IF YOU MAKE A MOD AND DISTRIBUTE A MODIFIED / RECOMIPLED VERSION, YOU MUST OPEN SOURCE YOUR MOD AS WELL
|
||||
|
||||
## Credits / shoutouts
|
||||
|
||||
- [ninjamuffin99 (me!)](https://twitter.com/ninja_muffin99) - Programmer
|
||||
- [PhantomArcade3K](https://twitter.com/phantomarcade3k) and [Evilsk8r](https://twitter.com/evilsk8r) - Art
|
||||
- [Kawaisprite](https://twitter.com/kawaisprite) - Musician
|
||||
|
||||
This game was made with love to Newgrounds and it's community. Extra love to Tom Fulp.
|
||||
|
||||
|
||||
|
||||
## Build instructions
|
||||
|
||||
|
|
2
example_mods/.gitignore
vendored
Normal file
2
example_mods/.gitignore
vendored
Normal file
|
@ -0,0 +1,2 @@
|
|||
./tricky
|
||||
./enaSkin
|
|
@ -1 +0,0 @@
|
|||
introMod
|
|
@ -1,2 +0,0 @@
|
|||
THIS MOD FOLDER DOES NOT ENTIRELY WORK JUST YET!!!
|
||||
DONT EXPECT MUCH OUT OF IT RIGHT NOW!!!
|
BIN
example_mods/testing123/_polymod_icon.png
Normal file
BIN
example_mods/testing123/_polymod_icon.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 65 KiB |
8
example_mods/testing123/_polymod_meta.json
Normal file
8
example_mods/testing123/_polymod_meta.json
Normal file
|
@ -0,0 +1,8 @@
|
|||
{
|
||||
"title": "Testing123",
|
||||
"description": "Newgrounds? More like OLDGROUNDS lol.",
|
||||
"author": "MasterEric",
|
||||
"api_version": "0.1.0",
|
||||
"mod_version": "1.0.0",
|
||||
"license": "Apache-2.0"
|
||||
}
|
|
@ -1,5 +1,6 @@
|
|||
package;
|
||||
|
||||
import modding.PolymodHandler;
|
||||
import flixel.FlxGame;
|
||||
import flixel.FlxState;
|
||||
import flixel.util.FlxColor;
|
||||
|
@ -41,30 +42,14 @@ class Main extends Sprite
|
|||
{
|
||||
super();
|
||||
|
||||
#if polymod
|
||||
polymod.Polymod.init({
|
||||
modRoot: "mods",
|
||||
dirs: ['testing123'],
|
||||
frameworkParams: {
|
||||
assetLibraryPaths: [
|
||||
"songs" => "songs", "shared" => "shared", "tutorial" => "tutorial", "week1" => "week1", "week2" => "week2", "week3" => "week3",
|
||||
"week4" => "week4", "week5" => "week5", "week6" => "week6", "week7" => "week7", "week8" => "week8"
|
||||
]
|
||||
},
|
||||
framework: OPENFL,
|
||||
errorCallback: function(error:polymod.Polymod.PolymodError)
|
||||
{
|
||||
trace("POLYMOD ERROR! code = "
|
||||
+ error.code
|
||||
+ " severity = "
|
||||
+ error.severity
|
||||
+ " origin = "
|
||||
+ error.origin
|
||||
+ " message = "
|
||||
+ error.message);
|
||||
}
|
||||
});
|
||||
#end
|
||||
// TODO: Ideally this should change to utilize a user interface.
|
||||
// 1. Call PolymodHandler.getAllMods(). This gives you an array of ModMetadata items,
|
||||
// each of which contains information about the mod including an icon.
|
||||
// 2. Provide an interface to enable, disable, and reorder enabled mods.
|
||||
// A design similar to that of Minecraft resource packs would be intuitive.
|
||||
// 3. The interface should save (to the save file) and output an ordered array of mod IDs.
|
||||
// 4. Replace the call to PolymodHandler.loadAllMods() with a call to PolymodHandler.loadModsById(ids:Array<String>).
|
||||
PolymodHandler.loadAllMods();
|
||||
|
||||
if (stage != null)
|
||||
{
|
||||
|
|
|
@ -278,7 +278,7 @@ class TitleState extends MusicBeatState
|
|||
|
||||
override function update(elapsed:Float)
|
||||
{
|
||||
trace(FlxG.renderBlit);
|
||||
// trace(FlxG.renderBlit);
|
||||
|
||||
#if HAS_PITCH
|
||||
if (FlxG.keys.pressed.UP)
|
||||
|
|
13
source/modding/IHook.hx
Normal file
13
source/modding/IHook.hx
Normal file
|
@ -0,0 +1,13 @@
|
|||
package modding;
|
||||
|
||||
import polymod.hscript.HScriptable;
|
||||
|
||||
/**
|
||||
* Add this interface to a class to make it a scriptable object.
|
||||
* Functions annotated with @:hscript will call the relevant script.
|
||||
*/
|
||||
@:hscript({
|
||||
// ALL of these values are added to ALL scripts in the child classes.
|
||||
context: [FlxG, FlxSprite, Math, Paths, Std]
|
||||
})
|
||||
interface IHook extends HScriptable {}
|
201
source/modding/PolymodHandler.hx
Normal file
201
source/modding/PolymodHandler.hx
Normal file
|
@ -0,0 +1,201 @@
|
|||
package modding;
|
||||
|
||||
#if cpp
|
||||
import polymod.Polymod;
|
||||
import polymod.backends.OpenFLBackend;
|
||||
import polymod.backends.PolymodAssets.PolymodAssetType;
|
||||
import polymod.format.ParseRules.LinesParseFormat;
|
||||
import polymod.format.ParseRules.TextFileFormat;
|
||||
#end
|
||||
|
||||
class PolymodHandler
|
||||
{
|
||||
/**
|
||||
* The API version that mods should comply with.
|
||||
* Format this with Semantic Versioning; <MAJOR>.<MINOR>.<PATCH>.
|
||||
* Bug fixes increment the patch version, new features increment the minor version.
|
||||
* Changes that break old mods increment the major version.
|
||||
*/
|
||||
static final API_VERSION = "0.1.0";
|
||||
|
||||
/**
|
||||
* Where relative to the executable that mods are located.
|
||||
*/
|
||||
static final MOD_FOLDER = "mods";
|
||||
|
||||
/**
|
||||
* Loads the game with ALL mods enabled with Polymod.
|
||||
*/
|
||||
public static function loadAllMods()
|
||||
{
|
||||
#if cpp
|
||||
trace("Initializing Polymod (using all mods)...");
|
||||
loadModsById(getAllModIds());
|
||||
#else
|
||||
trace("Polymod not initialized; not supported on this platform.");
|
||||
#end
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the game without any mods enabled with Polymod.
|
||||
*/
|
||||
public static function loadNoMods()
|
||||
{
|
||||
// We still need to configure the debug print calls etc.
|
||||
#if cpp
|
||||
trace("Initializing Polymod (using no mods)...");
|
||||
loadModsById([]);
|
||||
#else
|
||||
trace("Polymod not initialized; not supported on this platform.");
|
||||
#end
|
||||
}
|
||||
|
||||
public static function loadModsById(ids:Array<String>)
|
||||
{
|
||||
#if cpp
|
||||
if (ids.length == 0)
|
||||
{
|
||||
trace('You attempted to load zero mods.');
|
||||
}
|
||||
else
|
||||
{
|
||||
trace('Attempting to load ${ids.length} mods...');
|
||||
}
|
||||
var loadedModList = polymod.Polymod.init({
|
||||
// Root directory for all mods.
|
||||
modRoot: MOD_FOLDER,
|
||||
// The directories for one or more mods to load.
|
||||
dirs: ids,
|
||||
// Framework being used to load assets.
|
||||
framework: OPENFL,
|
||||
// The current version of our API.
|
||||
apiVersion: API_VERSION,
|
||||
// Call this function any time an error occurs.
|
||||
errorCallback: onPolymodError,
|
||||
// Enforce semantic version patterns for each mod.
|
||||
// modVersions: null,
|
||||
// A map telling Polymod what the asset type is for unfamiliar file extensions.
|
||||
// extensionMap: [],
|
||||
|
||||
frameworkParams: buildFrameworkParams(),
|
||||
|
||||
// List of filenames to ignore in mods. Use the default list to ignore the metadata file, etc.
|
||||
ignoredFiles: Polymod.getDefaultIgnoreList(),
|
||||
|
||||
// Parsing rules for various data formats.
|
||||
parseRules: buildParseRules(),
|
||||
});
|
||||
|
||||
if (loadedModList == null)
|
||||
{
|
||||
trace('[POLYMOD] An error occurred! Failed when loading mods!');
|
||||
}
|
||||
else
|
||||
{
|
||||
if (loadedModList.length == 0)
|
||||
{
|
||||
trace('[POLYMOD] Mod loading complete. We loaded no mods / ${ids.length} mods.');
|
||||
}
|
||||
else
|
||||
{
|
||||
trace('[POLYMOD] Mod loading complete. We loaded ${loadedModList.length} / ${ids.length} mods.');
|
||||
}
|
||||
}
|
||||
|
||||
for (mod in loadedModList)
|
||||
trace(' * ${mod.title} v${mod.modVersion} [${mod.id}]');
|
||||
|
||||
#if debug
|
||||
var fileList = Polymod.listModFiles("IMAGE");
|
||||
trace('[POLYMOD] Installed mods have replaced ${fileList.length} images.');
|
||||
for (item in fileList)
|
||||
trace(' * $item');
|
||||
|
||||
fileList = Polymod.listModFiles("TEXT");
|
||||
trace('[POLYMOD] Installed mods have replaced ${fileList.length} text files.');
|
||||
for (item in fileList)
|
||||
trace(' * $item');
|
||||
|
||||
fileList = Polymod.listModFiles("MUSIC");
|
||||
trace('[POLYMOD] Installed mods have replaced ${fileList.length} music files.');
|
||||
for (item in fileList)
|
||||
trace(' * $item');
|
||||
|
||||
fileList = Polymod.listModFiles("SOUND");
|
||||
trace('[POLYMOD] Installed mods have replaced ${fileList.length} sound files.');
|
||||
for (item in fileList)
|
||||
trace(' * $item');
|
||||
#end
|
||||
#else
|
||||
trace("[POLYMOD] Mods are not supported on this platform.");
|
||||
#end
|
||||
}
|
||||
|
||||
#if cpp
|
||||
static function buildParseRules():polymod.format.ParseRules
|
||||
{
|
||||
var output = polymod.format.ParseRules.getDefault();
|
||||
// Ensure TXT files have merge support.
|
||||
output.addType("txt", TextFileFormat.LINES);
|
||||
// Ensure script files have merge support.
|
||||
output.addType("hscript", TextFileFormat.PLAINTEXT);
|
||||
|
||||
// You can specify the format of a specific file, with file extension.
|
||||
// output.addFile("data/introText.txt", TextFileFormat.LINES)
|
||||
return output;
|
||||
}
|
||||
|
||||
static inline function buildFrameworkParams():polymod.Polymod.FrameworkParams
|
||||
{
|
||||
return {
|
||||
assetLibraryPaths: [
|
||||
"songs" => "./songs", "shared" => "./", "tutorial" => "./tutorial", "scripts" => "./scripts", "week1" => "./week1", "week2" => "./week2",
|
||||
"week3" => "./week3", "week4" => "./week4", "week5" => "./week5", "week6" => "./week6", "week7" => "./week7", "week8" => "./week8",
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
static function onPolymodError(error:PolymodError):Void
|
||||
{
|
||||
// Perform an action based on the error code.
|
||||
switch (error.code)
|
||||
{
|
||||
case MOD_LOAD_PREPARE:
|
||||
trace('[POLYMOD] ${error.message}');
|
||||
case MOD_LOAD_DONE:
|
||||
trace('[POLYMOD] ${error.message}');
|
||||
case MISSING_ICON:
|
||||
trace('[POLYMOD] A mod is missing an icon. Please add one.');
|
||||
default:
|
||||
// Log the message based on its severity.
|
||||
switch (error.severity)
|
||||
{
|
||||
case NOTICE:
|
||||
trace('[POLYMOD] ${error.message}');
|
||||
case WARNING:
|
||||
trace('[POLYMOD] ${error.message}');
|
||||
case ERROR:
|
||||
trace('[POLYMOD] ${error.message}');
|
||||
}
|
||||
}
|
||||
}
|
||||
#end
|
||||
|
||||
public static function getAllMods():Array<ModMetadata>
|
||||
{
|
||||
#if cpp
|
||||
trace('Scanning the mods folder...');
|
||||
var modMetadata = Polymod.scan(MOD_FOLDER);
|
||||
trace('Found ${modMetadata.length} mods when scanning.');
|
||||
return modMetadata;
|
||||
#else
|
||||
return [];
|
||||
#end
|
||||
}
|
||||
|
||||
public static function getAllModIds():Array<String>
|
||||
{
|
||||
var modIds = [for (i in getAllMods()) i.id];
|
||||
return modIds;
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue