mirror of
https://github.com/FunkinCrew/Funkin.git
synced 2024-11-14 19:25:16 -05:00
Compare commits
24 commits
b476f9a989
...
e9a1ee42b2
Author | SHA1 | Date | |
---|---|---|---|
|
e9a1ee42b2 | ||
|
9ee4b0b94e | ||
|
907f300267 | ||
|
ba1dbb5f68 | ||
|
d27433318d | ||
|
ee63e8d8d6 | ||
|
ba72fee609 | ||
|
42d93c616a | ||
|
374f6837eb | ||
|
f3b4cac92d | ||
|
918292d5a8 | ||
|
07749285f1 | ||
|
dcc58e9e33 | ||
|
6d35d07245 | ||
|
807373c2d2 | ||
|
c75112b998 | ||
|
b26b1bf4e8 | ||
|
0e36e9261d | ||
|
c00e4e8316 | ||
|
2d68f60c9b | ||
|
a01a5907e7 | ||
|
ad23a1c6d0 | ||
|
d105686251 | ||
|
0d8e4a5330 |
4 changed files with 446 additions and 4 deletions
|
@ -588,6 +588,15 @@ class Project extends HXProject {
|
||||||
function configureCustomMacros() {
|
function configureCustomMacros() {
|
||||||
// This macro allows addition of new functionality to existing Flixel. -->
|
// This macro allows addition of new functionality to existing Flixel. -->
|
||||||
addHaxeMacro("addMetadata('@:build(funkin.util.macro.FlxMacro.buildFlxBasic())', 'flixel.FlxBasic')");
|
addHaxeMacro("addMetadata('@:build(funkin.util.macro.FlxMacro.buildFlxBasic())', 'flixel.FlxBasic')");
|
||||||
|
|
||||||
|
var abstracts:Array<String> = [
|
||||||
|
'.*',
|
||||||
|
'!thx.Set',
|
||||||
|
'!cpp.*',
|
||||||
|
'!lime.*',
|
||||||
|
'!openfl.*'
|
||||||
|
];
|
||||||
|
addHaxeMacro("funkin.util.macro.PolymodMacro.buildPolymodAbstracts(['" + abstracts.join("', '") + "'])");
|
||||||
}
|
}
|
||||||
|
|
||||||
function configureOutputDir() {
|
function configureOutputDir() {
|
||||||
|
|
|
@ -248,6 +248,11 @@ class PolymodHandler
|
||||||
Polymod.addImportAlias('lime.utils.Assets', funkin.Assets);
|
Polymod.addImportAlias('lime.utils.Assets', funkin.Assets);
|
||||||
Polymod.addImportAlias('openfl.utils.Assets', funkin.Assets);
|
Polymod.addImportAlias('openfl.utils.Assets', funkin.Assets);
|
||||||
|
|
||||||
|
for (key => value in funkin.util.macro.PolymodMacro.aliases)
|
||||||
|
{
|
||||||
|
Polymod.addImportAlias(key, Type.resolveClass(value));
|
||||||
|
}
|
||||||
|
|
||||||
// Add blacklisting for prohibited classes and packages.
|
// Add blacklisting for prohibited classes and packages.
|
||||||
|
|
||||||
// `Sys`
|
// `Sys`
|
||||||
|
@ -333,8 +338,19 @@ class PolymodHandler
|
||||||
{
|
{
|
||||||
return {
|
return {
|
||||||
assetLibraryPaths: [
|
assetLibraryPaths: [
|
||||||
'default' => 'preload', 'shared' => 'shared', 'songs' => 'songs', 'videos' => 'videos', 'tutorial' => 'tutorial', 'week1' => 'week1',
|
'default' => 'preload',
|
||||||
'week2' => 'week2', 'week3' => 'week3', 'week4' => 'week4', 'week5' => 'week5', 'week6' => 'week6', 'week7' => 'week7', 'weekend1' => 'weekend1',
|
'shared' => 'shared',
|
||||||
|
'songs' => 'songs',
|
||||||
|
'videos' => 'videos',
|
||||||
|
'tutorial' => 'tutorial',
|
||||||
|
'week1' => 'week1',
|
||||||
|
'week2' => 'week2',
|
||||||
|
'week3' => 'week3',
|
||||||
|
'week4' => 'week4',
|
||||||
|
'week5' => 'week5',
|
||||||
|
'week6' => 'week6',
|
||||||
|
'week7' => 'week7',
|
||||||
|
'weekend1' => 'weekend1',
|
||||||
],
|
],
|
||||||
coreAssetRedirect: CORE_FOLDER,
|
coreAssetRedirect: CORE_FOLDER,
|
||||||
}
|
}
|
||||||
|
|
|
@ -2729,6 +2729,7 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
|
||||||
{
|
{
|
||||||
// Update the conductor and audio tracks to match.
|
// Update the conductor and audio tracks to match.
|
||||||
currentScrollEase = d.value;
|
currentScrollEase = d.value;
|
||||||
|
easeSongToScrollPosition(currentScrollEase);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2741,7 +2742,7 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
|
||||||
playbarHeadDraggingWasPlaying = false;
|
playbarHeadDraggingWasPlaying = false;
|
||||||
|
|
||||||
// Disabled code to resume song playback on drag.
|
// Disabled code to resume song playback on drag.
|
||||||
// startAudioPlayback();
|
startAudioPlayback();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3873,7 +3874,7 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
|
||||||
}
|
}
|
||||||
|
|
||||||
// Mouse Wheel = Scroll
|
// Mouse Wheel = Scroll
|
||||||
if (FlxG.mouse.wheel != 0 && !FlxG.keys.pressed.CONTROL)
|
if (FlxG.mouse.wheel != 0)
|
||||||
{
|
{
|
||||||
scrollAmount = -50 * FlxG.mouse.wheel;
|
scrollAmount = -50 * FlxG.mouse.wheel;
|
||||||
shouldPause = true;
|
shouldPause = true;
|
||||||
|
@ -4469,6 +4470,7 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
|
||||||
0, songLengthInPixels);
|
0, songLengthInPixels);
|
||||||
|
|
||||||
currentScrollEase = clickedPosInPixels;
|
currentScrollEase = clickedPosInPixels;
|
||||||
|
easeSongToScrollPosition(currentScrollEase);
|
||||||
}
|
}
|
||||||
else if (scrollAnchorScreenPos != null)
|
else if (scrollAnchorScreenPos != null)
|
||||||
{
|
{
|
||||||
|
|
415
source/funkin/util/macro/PolymodMacro.hx
Normal file
415
source/funkin/util/macro/PolymodMacro.hx
Normal file
|
@ -0,0 +1,415 @@
|
||||||
|
package funkin.util.macro;
|
||||||
|
|
||||||
|
import haxe.macro.Context;
|
||||||
|
import haxe.macro.Expr;
|
||||||
|
import haxe.macro.Type;
|
||||||
|
|
||||||
|
using haxe.macro.ExprTools;
|
||||||
|
using StringTools;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This macro creates aliases for abstracts.
|
||||||
|
* That way we can use abstracts in hscript
|
||||||
|
*/
|
||||||
|
@SuppressWarnings(['checkstyle:CodeSimilarity', 'checkstyle:Dynamic'])
|
||||||
|
class PolymodMacro
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* The abstracts and their corresponding aliases
|
||||||
|
* `key` => original class path
|
||||||
|
* `value` => alias class path
|
||||||
|
*/
|
||||||
|
public static var aliases(get, never):Map<String, String>;
|
||||||
|
|
||||||
|
static function get_aliases():Map<String, String>
|
||||||
|
{
|
||||||
|
// truly a sight to behold
|
||||||
|
return Reflect.callMethod(null, Reflect.field(Type.resolveClass('funkin.util.macro.AbstractAliases'), 'get'), []);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Function that builds all the alias classes
|
||||||
|
* @param abstractClasses An array of packages and classes
|
||||||
|
* (`!` infront of the name will exclude classes from being built)
|
||||||
|
*/
|
||||||
|
public static macro function buildPolymodAbstracts(abstractClasses:Array<String>):Void
|
||||||
|
{
|
||||||
|
Context.onAfterTyping((types) -> {
|
||||||
|
if (alreadyCalled)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var sortedAbstractClasses:Array<String> = [];
|
||||||
|
for (abstractCls in abstractClasses)
|
||||||
|
{
|
||||||
|
if (abstractCls.startsWith('!'))
|
||||||
|
{
|
||||||
|
sortedAbstractClasses.insert(0, abstractCls);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
sortedAbstractClasses.push(abstractCls);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var abstractAliases:Map<String, String> = new Map<String, String>();
|
||||||
|
|
||||||
|
for (type in types)
|
||||||
|
{
|
||||||
|
switch (type)
|
||||||
|
{
|
||||||
|
case ModuleType.TAbstract(a):
|
||||||
|
var cls:AbstractType = a.get();
|
||||||
|
if (cls.isPrivate)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (abstractCls in sortedAbstractClasses)
|
||||||
|
{
|
||||||
|
var negate:Bool = abstractCls.startsWith('!');
|
||||||
|
var name:String = abstractCls.replace('!', '').replace('.*', '');
|
||||||
|
if (!negate && !cls.module.startsWith(name) && moduleTypePath(cls) != name && packTypePath(cls) != name)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
else if (negate)
|
||||||
|
{
|
||||||
|
if (cls.module.startsWith(name) || moduleTypePath(cls) == name || packTypePath(cls) == name)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
abstractAliases.set('${packTypePath(cls)}', 'polymod.abstracts.${packTypePath(cls)}${classSuffix}');
|
||||||
|
buildAbstract(cls);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
// do nothing
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Context.defineModule('funkin.util.macro.PolymodMacro', [
|
||||||
|
{
|
||||||
|
pack: ['funkin', 'util', 'macro'],
|
||||||
|
name: 'AbstractAliases',
|
||||||
|
kind: TypeDefKind.TDClass(null, [], false, false, false),
|
||||||
|
fields: [
|
||||||
|
{
|
||||||
|
name: 'get',
|
||||||
|
access: [Access.APublic, Access.AStatic],
|
||||||
|
kind: FieldType.FFun(
|
||||||
|
{
|
||||||
|
args: [],
|
||||||
|
ret: (macro :Map<String, String>),
|
||||||
|
expr: macro
|
||||||
|
{
|
||||||
|
return $v{abstractAliases};
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
pos: Context.currentPos()
|
||||||
|
}
|
||||||
|
],
|
||||||
|
pos: Context.currentPos()
|
||||||
|
}
|
||||||
|
]);
|
||||||
|
|
||||||
|
// the callback is called twice, which this leads to issues
|
||||||
|
alreadyCalled = true;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#if macro
|
||||||
|
static var classSuffix:String = '_';
|
||||||
|
static var alreadyCalled:Bool = false;
|
||||||
|
static var skipFields:Array<String> = [];
|
||||||
|
|
||||||
|
static function buildAbstract(abstractCls:AbstractType):Void
|
||||||
|
{
|
||||||
|
if (abstractCls.impl == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
skipFields = [];
|
||||||
|
|
||||||
|
var cls:ClassType = abstractCls.impl.get();
|
||||||
|
|
||||||
|
// we use the functions to check whether we need to skip some fields
|
||||||
|
// that is why we sort the fields, so that functions are handled first
|
||||||
|
var sortedFields:Array<ClassField> = sortFields(cls.statics.get());
|
||||||
|
|
||||||
|
var fields:Array<Field> = [];
|
||||||
|
for (field in sortedFields)
|
||||||
|
{
|
||||||
|
if (field.name == '_new')
|
||||||
|
{
|
||||||
|
fields.push(buildCreateField(abstractCls, field));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
fields = fields.concat(createFields(abstractCls, field));
|
||||||
|
};
|
||||||
|
|
||||||
|
Context.defineType(
|
||||||
|
{
|
||||||
|
pos: Context.currentPos(),
|
||||||
|
pack: ['polymod', 'abstracts'].concat(abstractCls.pack),
|
||||||
|
name: abstractCls.name + classSuffix,
|
||||||
|
kind: TypeDefKind.TDClass(null, [], false, false, false),
|
||||||
|
fields: fields,
|
||||||
|
}, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
static function sortFields(fields:Array<ClassField>):Array<ClassField>
|
||||||
|
{
|
||||||
|
var sortedFields:Array<ClassField> = [];
|
||||||
|
for (field in fields)
|
||||||
|
{
|
||||||
|
switch (field.type)
|
||||||
|
{
|
||||||
|
case Type.TLazy(f):
|
||||||
|
switch (f())
|
||||||
|
{
|
||||||
|
case Type.TFun(_, _):
|
||||||
|
sortedFields.insert(0, field);
|
||||||
|
default:
|
||||||
|
sortedFields.push(field);
|
||||||
|
}
|
||||||
|
case Type.TFun(_, _):
|
||||||
|
sortedFields.insert(0, field);
|
||||||
|
default:
|
||||||
|
sortedFields.push(field);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return sortedFields;
|
||||||
|
}
|
||||||
|
|
||||||
|
static function buildCreateField(cls:AbstractType, field:ClassField):Field
|
||||||
|
{
|
||||||
|
var funcArgs:Array<FunctionArg> = [];
|
||||||
|
var funcArgNames:Array<String> = [];
|
||||||
|
switch (field.type)
|
||||||
|
{
|
||||||
|
case Type.TFun(args, _):
|
||||||
|
for (arg in args)
|
||||||
|
{
|
||||||
|
funcArgs.push(
|
||||||
|
{
|
||||||
|
name: arg.name,
|
||||||
|
type: null,
|
||||||
|
opt: arg.opt
|
||||||
|
});
|
||||||
|
funcArgNames.push(arg.name);
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
throw 'how is this not a function';
|
||||||
|
}
|
||||||
|
|
||||||
|
var expr:String = '${newExpr(cls, field)}(${funcArgNames.join(', ')})';
|
||||||
|
|
||||||
|
return {
|
||||||
|
name: 'create',
|
||||||
|
access: [Access.APublic, Access.AStatic],
|
||||||
|
kind: FieldType.FFun(
|
||||||
|
{
|
||||||
|
args: funcArgs,
|
||||||
|
ret: null,
|
||||||
|
expr: macro
|
||||||
|
{
|
||||||
|
@:privateAccess
|
||||||
|
return ${Context.parse(expr, Context.currentPos())};
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
pos: Context.currentPos()
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
static function newExpr(cls:AbstractType, field:ClassField):Null<String>
|
||||||
|
{
|
||||||
|
if ('${moduleTypePath(cls)}' == 'flixel.util.FlxSignal.FlxTypedSignal')
|
||||||
|
{
|
||||||
|
return 'new flixel.util.FlxSignal.FlxTypedSignal<Dynamic->Void>';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cls.params.length <= 0)
|
||||||
|
{
|
||||||
|
return 'new ${moduleTypePath(cls)}';
|
||||||
|
}
|
||||||
|
|
||||||
|
return 'new ${moduleTypePath(cls)}< ${[for (_ in 0...cls.params.length) 'Dynamic'].join(', ')} >';
|
||||||
|
}
|
||||||
|
|
||||||
|
static function createFields(cls:AbstractType, field:ClassField):Array<Field>
|
||||||
|
{
|
||||||
|
if (skipFields.contains(field.name))
|
||||||
|
{
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (field.type)
|
||||||
|
{
|
||||||
|
case Type.TLazy(f):
|
||||||
|
return _createFields(cls, field, f());
|
||||||
|
default:
|
||||||
|
return _createFields(cls, field, field.type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static function _createFields(cls:AbstractType, field:ClassField, type:Type):Array<Field>
|
||||||
|
{
|
||||||
|
if (field.meta.has(':to'))
|
||||||
|
{
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
var fields:Array<Field> = [];
|
||||||
|
|
||||||
|
switch (type)
|
||||||
|
{
|
||||||
|
case Type.TFun(args, ret):
|
||||||
|
var fieldArgs:Array<FunctionArg> = [];
|
||||||
|
var exprArgs:Array<String> = [];
|
||||||
|
for (arg in args)
|
||||||
|
{
|
||||||
|
if (arg.name == 'this')
|
||||||
|
{
|
||||||
|
var memberVariable:String = field.name.replace('get_', '').replace('set_', '');
|
||||||
|
if (memberVariable != field.name)
|
||||||
|
{
|
||||||
|
skipFields.push(memberVariable);
|
||||||
|
}
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
exprArgs.push(arg.name);
|
||||||
|
fieldArgs.push(
|
||||||
|
{
|
||||||
|
name: arg.name,
|
||||||
|
type: null,
|
||||||
|
opt: arg.opt,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
var returnStr:String = switch (ret)
|
||||||
|
{
|
||||||
|
case Type.TAbstract(t, _):
|
||||||
|
if (t.get().name == 'Void')
|
||||||
|
{
|
||||||
|
'';
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
'return ';
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
'return ';
|
||||||
|
};
|
||||||
|
|
||||||
|
var expr:String = '${returnStr}${moduleTypePath(cls)}.${field.name}(${exprArgs.join(', ')})';
|
||||||
|
|
||||||
|
fields.push(
|
||||||
|
{
|
||||||
|
name: field.name,
|
||||||
|
doc: field.doc,
|
||||||
|
access: [Access.APublic, Access.AStatic],
|
||||||
|
kind: FieldType.FFun(
|
||||||
|
{
|
||||||
|
args: fieldArgs,
|
||||||
|
ret: null,
|
||||||
|
expr: macro
|
||||||
|
{
|
||||||
|
@:privateAccess
|
||||||
|
${Context.parse(expr, Context.currentPos())};
|
||||||
|
},
|
||||||
|
params: []
|
||||||
|
}),
|
||||||
|
pos: Context.currentPos()
|
||||||
|
});
|
||||||
|
case Type.TAbstract(t, params):
|
||||||
|
fields.push(
|
||||||
|
{
|
||||||
|
name: field.name,
|
||||||
|
doc: field.doc,
|
||||||
|
access: [Access.APublic, Access.AStatic],
|
||||||
|
kind: FieldType.FProp('get', 'never', (macro :Dynamic), null),
|
||||||
|
pos: Context.currentPos()
|
||||||
|
});
|
||||||
|
|
||||||
|
var expr:String = '${moduleTypePath(cls)}.${field.name}';
|
||||||
|
|
||||||
|
fields.push(
|
||||||
|
{
|
||||||
|
name: 'get_${field.name}',
|
||||||
|
doc: field.doc,
|
||||||
|
access: [Access.APublic, Access.AStatic],
|
||||||
|
kind: FieldType.FFun(
|
||||||
|
{
|
||||||
|
args: [],
|
||||||
|
ret: null,
|
||||||
|
expr: macro
|
||||||
|
{
|
||||||
|
@:privateAccess
|
||||||
|
return ${Context.parse(expr, Context.currentPos())};
|
||||||
|
},
|
||||||
|
params: []
|
||||||
|
}),
|
||||||
|
pos: Context.currentPos()
|
||||||
|
});
|
||||||
|
case TType(t, params):
|
||||||
|
fields.push(
|
||||||
|
{
|
||||||
|
name: field.name,
|
||||||
|
doc: field.doc,
|
||||||
|
access: [Access.APublic, Access.AStatic],
|
||||||
|
kind: FieldType.FProp('get', 'never', (macro :Dynamic), null),
|
||||||
|
pos: Context.currentPos()
|
||||||
|
});
|
||||||
|
|
||||||
|
var expr:String = '${moduleTypePath(cls)}.${field.name}';
|
||||||
|
|
||||||
|
fields.push(
|
||||||
|
{
|
||||||
|
name: 'get_${field.name}',
|
||||||
|
doc: field.doc,
|
||||||
|
access: [Access.APublic, Access.AStatic],
|
||||||
|
kind: FieldType.FFun(
|
||||||
|
{
|
||||||
|
args: [],
|
||||||
|
ret: null,
|
||||||
|
expr: macro
|
||||||
|
{
|
||||||
|
@:privateAccess
|
||||||
|
return ${Context.parse(expr, Context.currentPos())};
|
||||||
|
},
|
||||||
|
params: []
|
||||||
|
}),
|
||||||
|
pos: Context.currentPos()
|
||||||
|
});
|
||||||
|
default:
|
||||||
|
return [];
|
||||||
|
};
|
||||||
|
|
||||||
|
return fields;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Dynamic so any kind of type with `module` and `name` fields works
|
||||||
|
static function moduleTypePath(type:Dynamic):String
|
||||||
|
{
|
||||||
|
var dot:String = type.module.length != 0 ? '.' : '';
|
||||||
|
return '${type.module}${dot}${type.name}';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Dynamic so any kind of type with `pack` and `name` fields works
|
||||||
|
static function packTypePath(type:Dynamic):String
|
||||||
|
{
|
||||||
|
var dot:String = type.pack.length != 0 ? '.' : '';
|
||||||
|
return '${type.pack.join('.')}${dot}${type.name}';
|
||||||
|
}
|
||||||
|
#end
|
||||||
|
}
|
Loading…
Reference in a new issue