Merge branch 'new-editors' into zoro-charterstuffs
This commit is contained in:
@@ -0,0 +1,632 @@
|
||||
/*
|
||||
* Copyright (C)2005-2019 Haxe Foundation
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
* DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
import haxe.iterators.StringIterator;
|
||||
import haxe.iterators.StringKeyValueIterator;
|
||||
|
||||
#if cpp
|
||||
using cpp.NativeString;
|
||||
#end
|
||||
|
||||
/**
|
||||
This class provides advanced methods on Strings. It is ideally used with
|
||||
`using StringTools` and then acts as an [extension](https://haxe.org/manual/lf-static-extension.html)
|
||||
to the `String` class.
|
||||
|
||||
If the first argument to any of the methods is null, the result is
|
||||
unspecified.
|
||||
**/
|
||||
class StringTools {
|
||||
/**
|
||||
Encode an URL by using the standard format.
|
||||
**/
|
||||
#if (!java && !cpp && !lua && !eval) inline #end public static function urlEncode(s:String):String {
|
||||
#if flash
|
||||
return untyped __global__["encodeURIComponent"](s);
|
||||
#elseif neko
|
||||
return untyped new String(_urlEncode(s.__s));
|
||||
#elseif js
|
||||
return untyped encodeURIComponent(s);
|
||||
#elseif cpp
|
||||
return untyped s.__URLEncode();
|
||||
#elseif java
|
||||
return postProcessUrlEncode(java.net.URLEncoder.encode(s, "UTF-8"));
|
||||
#elseif python
|
||||
return python.lib.urllib.Parse.quote(s, "");
|
||||
#elseif hl
|
||||
var len = 0;
|
||||
var b = @:privateAccess s.bytes.urlEncode(len);
|
||||
return @:privateAccess String.__alloc__(b, len);
|
||||
#elseif lua
|
||||
s = lua.NativeStringTools.gsub(s, "\n", "\r\n");
|
||||
s = lua.NativeStringTools.gsub(s, "([^%w %-%_%.%~])", function(c) {
|
||||
return lua.NativeStringTools.format("%%%02X", lua.NativeStringTools.byte(c) + '');
|
||||
});
|
||||
s = lua.NativeStringTools.gsub(s, " ", "+");
|
||||
return s;
|
||||
#else
|
||||
return null;
|
||||
#end
|
||||
}
|
||||
|
||||
#if java
|
||||
private static function postProcessUrlEncode(s:String):String {
|
||||
var ret = new StringBuf();
|
||||
var i = 0, len = s.length;
|
||||
while (i < len) {
|
||||
switch (_charAt(s, i++)) {
|
||||
case '+'.code:
|
||||
ret.add('%20');
|
||||
case '%'.code if (i <= len - 2):
|
||||
var c1 = _charAt(s, i++), c2 = _charAt(s, i++);
|
||||
switch [c1, c2] {
|
||||
case ['2'.code, '1'.code]:
|
||||
ret.addChar('!'.code);
|
||||
case ['2'.code, '7'.code]:
|
||||
ret.addChar('\''.code);
|
||||
case ['2'.code, '8'.code]:
|
||||
ret.addChar('('.code);
|
||||
case ['2'.code, '9'.code]:
|
||||
ret.addChar(')'.code);
|
||||
case ['7'.code, 'E'.code] | ['7'.code, 'e'.code]:
|
||||
ret.addChar('~'.code);
|
||||
case _:
|
||||
ret.addChar('%'.code);
|
||||
ret.addChar(cast c1);
|
||||
ret.addChar(cast c2);
|
||||
}
|
||||
case var chr:
|
||||
ret.addChar(cast chr);
|
||||
}
|
||||
}
|
||||
return ret.toString();
|
||||
}
|
||||
#end
|
||||
|
||||
/**
|
||||
Decode an URL using the standard format.
|
||||
**/
|
||||
#if (!java && !cpp && !lua && !eval) inline #end public static function urlDecode(s:String):String {
|
||||
#if flash
|
||||
return untyped __global__["decodeURIComponent"](s.split("+").join(" "));
|
||||
#elseif neko
|
||||
return untyped new String(_urlDecode(s.__s));
|
||||
#elseif js
|
||||
return untyped decodeURIComponent(s.split("+").join(" "));
|
||||
#elseif cpp
|
||||
return untyped s.__URLDecode();
|
||||
#elseif java
|
||||
try
|
||||
return java.net.URLDecoder.decode(s, "UTF-8")
|
||||
catch (e:Dynamic)
|
||||
throw e;
|
||||
#elseif python
|
||||
return python.lib.urllib.Parse.unquote(s);
|
||||
#elseif hl
|
||||
var len = 0;
|
||||
var b = @:privateAccess s.bytes.urlDecode(len);
|
||||
return @:privateAccess String.__alloc__(b, len);
|
||||
#elseif lua
|
||||
s = lua.NativeStringTools.gsub(s, "+", " ");
|
||||
s = lua.NativeStringTools.gsub(s, "%%(%x%x)", function(h) {
|
||||
return lua.NativeStringTools.char(lua.Lua.tonumber(h, 16));
|
||||
});
|
||||
s = lua.NativeStringTools.gsub(s, "\r\n", "\n");
|
||||
return s;
|
||||
#else
|
||||
return null;
|
||||
#end
|
||||
}
|
||||
|
||||
/**
|
||||
Escapes HTML special characters of the string `s`.
|
||||
|
||||
The following replacements are made:
|
||||
|
||||
- `&` becomes `&`;
|
||||
- `<` becomes `<`;
|
||||
- `>` becomes `>`;
|
||||
|
||||
If `quotes` is true, the following characters are also replaced:
|
||||
|
||||
- `"` becomes `"`;
|
||||
- `'` becomes `'`;
|
||||
**/
|
||||
public static function htmlEscape(s:String, ?quotes:Bool):String {
|
||||
var buf = new StringBuf();
|
||||
for (code in #if neko iterator(s) #else new haxe.iterators.StringIteratorUnicode(s) #end) {
|
||||
switch (code) {
|
||||
case '&'.code:
|
||||
buf.add("&");
|
||||
case '<'.code:
|
||||
buf.add("<");
|
||||
case '>'.code:
|
||||
buf.add(">");
|
||||
case '"'.code if (quotes):
|
||||
buf.add(""");
|
||||
case '\''.code if (quotes):
|
||||
buf.add("'");
|
||||
case _:
|
||||
buf.addChar(code);
|
||||
}
|
||||
}
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
Unescapes HTML special characters of the string `s`.
|
||||
|
||||
This is the inverse operation to htmlEscape, i.e. the following always
|
||||
holds: `htmlUnescape(htmlEscape(s)) == s`
|
||||
|
||||
The replacements follow:
|
||||
|
||||
- `&` becomes `&`
|
||||
- `<` becomes `<`
|
||||
- `>` becomes `>`
|
||||
- `"` becomes `"`
|
||||
- `'` becomes `'`
|
||||
**/
|
||||
public static function htmlUnescape(s:String):String {
|
||||
return s.split(">")
|
||||
.join(">")
|
||||
.split("<")
|
||||
.join("<")
|
||||
.split(""")
|
||||
.join('"')
|
||||
.split("'")
|
||||
.join("'")
|
||||
.split("&")
|
||||
.join("&");
|
||||
}
|
||||
|
||||
/**
|
||||
Returns `true` if `s` contains `value` and `false` otherwise.
|
||||
|
||||
When `value` is `null`, the result is unspecified.
|
||||
**/
|
||||
public static inline function contains(s:String, value:String):Bool {
|
||||
#if (js && js_es >= 6)
|
||||
return (cast s).includes(value);
|
||||
#else
|
||||
return s.indexOf(value) != -1;
|
||||
#end
|
||||
}
|
||||
|
||||
/**
|
||||
Tells if the string `s` starts with the string `start`.
|
||||
|
||||
If `start` is `null`, the result is unspecified.
|
||||
|
||||
If `start` is the empty String `""`, the result is true.
|
||||
**/
|
||||
public static #if (java || python || (js && js_es >= 6)) inline #end function startsWith(s:String, start:String):Bool {
|
||||
#if java
|
||||
return (cast s : java.NativeString).startsWith(start);
|
||||
#elseif hl
|
||||
return @:privateAccess (s.length >= start.length && s.bytes.compare(0, start.bytes, 0, start.length << 1) == 0);
|
||||
#elseif python
|
||||
return python.NativeStringTools.startswith(s, start);
|
||||
#elseif (js && js_es >= 6)
|
||||
return (cast s).startsWith(start);
|
||||
#elseif lua
|
||||
return untyped __lua__("{0}:sub(1, #{1}) == {1}", s, start);
|
||||
#else
|
||||
return (s.length >= start.length && s.indexOf(start, 0) == 0);
|
||||
#end
|
||||
}
|
||||
|
||||
/**
|
||||
Tells if the string `s` ends with the string `end`.
|
||||
|
||||
If `end` is `null`, the result is unspecified.
|
||||
|
||||
If `end` is the empty String `""`, the result is true.
|
||||
**/
|
||||
public static #if (java || python || (js && js_es >= 6)) inline #end function endsWith(s:String, end:String):Bool {
|
||||
#if java
|
||||
return (cast s : java.NativeString).endsWith(end);
|
||||
#elseif hl
|
||||
var elen = end.length;
|
||||
var slen = s.length;
|
||||
return @:privateAccess (slen >= elen && s.bytes.compare((slen - elen) << 1, end.bytes, 0, elen << 1) == 0);
|
||||
#elseif python
|
||||
return python.NativeStringTools.endswith(s, end);
|
||||
#elseif (js && js_es >= 6)
|
||||
return (cast s).endsWith(end);
|
||||
#elseif lua
|
||||
return end == "" || untyped __lua__("{0}:sub(-#{1}) == {1}", s, end);
|
||||
#else
|
||||
var elen = end.length;
|
||||
var slen = s.length;
|
||||
return (slen >= elen && s.lastIndexOf(end, (slen - elen)) == (slen - elen));
|
||||
#end
|
||||
}
|
||||
|
||||
/**
|
||||
Tells if the character in the string `s` at position `pos` is a space.
|
||||
|
||||
A character is considered to be a space character if its character code
|
||||
is 9,10,11,12,13 or 32.
|
||||
|
||||
If `s` is the empty String `""`, or if pos is not a valid position within
|
||||
`s`, the result is false.
|
||||
**/
|
||||
public static function isSpace(s:String, pos:Int):Bool {
|
||||
#if (python || lua)
|
||||
if (s.length == 0 || pos < 0 || pos >= s.length)
|
||||
return false;
|
||||
#end
|
||||
var c = StringTools.fastCodeAt(s, pos);
|
||||
return (c > 8 && c < 14) || c == 32;
|
||||
}
|
||||
|
||||
/**
|
||||
Removes leading space characters of `s`.
|
||||
|
||||
This function internally calls `isSpace()` to decide which characters to
|
||||
remove.
|
||||
|
||||
If `s` is the empty String `""` or consists only of space characters, the
|
||||
result is the empty String `""`.
|
||||
**/
|
||||
public inline static function ltrim(s:String):String {
|
||||
var l = s.length;
|
||||
var r = 0;
|
||||
while (r < l && isSpace(s, r)) {
|
||||
r++;
|
||||
}
|
||||
if (r > 0)
|
||||
return s.substr(r, l - r);
|
||||
else
|
||||
return s;
|
||||
}
|
||||
|
||||
/**
|
||||
Removes trailing space characters of `s`.
|
||||
|
||||
This function internally calls `isSpace()` to decide which characters to
|
||||
remove.
|
||||
|
||||
If `s` is the empty String `""` or consists only of space characters, the
|
||||
result is the empty String `""`.
|
||||
**/
|
||||
public inline static function rtrim(s:String):String {
|
||||
var l = s.length;
|
||||
var r = 0;
|
||||
while (r < l && isSpace(s, l - r - 1)) {
|
||||
r++;
|
||||
}
|
||||
if (r > 0) {
|
||||
return s.substr(0, l - r);
|
||||
} else {
|
||||
return s;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
Removes leading and trailing space characters of `s`.
|
||||
|
||||
This is a convenience function for `ltrim(rtrim(s))`.
|
||||
**/
|
||||
public #if java inline #end static function trim(s:String):String {
|
||||
#if java
|
||||
return (cast s : java.NativeString).trim();
|
||||
#else
|
||||
//return ltrim(rtrim(s));
|
||||
var l = s.length;
|
||||
var start = 0;
|
||||
var end = l;
|
||||
|
||||
// Trim leading spaces
|
||||
while (start < l && isSpace(s, start)) {
|
||||
start++;
|
||||
}
|
||||
|
||||
// Trim trailing spaces
|
||||
while (end > start && isSpace(s, end - 1)) {
|
||||
end--;
|
||||
}
|
||||
|
||||
return (start > 0 || end < l) ? s.substr(start, end - start) : s;
|
||||
#end
|
||||
}
|
||||
|
||||
/**
|
||||
Concatenates `c` to `s` until `s.length` is at least `l`.
|
||||
|
||||
If `c` is the empty String `""` or if `l` does not exceed `s.length`,
|
||||
`s` is returned unchanged.
|
||||
|
||||
If `c.length` is 1, the resulting String length is exactly `l`.
|
||||
|
||||
Otherwise the length may exceed `l`.
|
||||
|
||||
If `c` is null, the result is unspecified.
|
||||
**/
|
||||
public static function lpad(s:String, c:String, l:Int):String {
|
||||
if (c.length <= 0)
|
||||
return s;
|
||||
|
||||
var buf = new StringBuf();
|
||||
l -= s.length;
|
||||
while (buf.length < l) {
|
||||
buf.add(c);
|
||||
}
|
||||
buf.add(s);
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
Appends `c` to `s` until `s.length` is at least `l`.
|
||||
|
||||
If `c` is the empty String `""` or if `l` does not exceed `s.length`,
|
||||
`s` is returned unchanged.
|
||||
|
||||
If `c.length` is 1, the resulting String length is exactly `l`.
|
||||
|
||||
Otherwise the length may exceed `l`.
|
||||
|
||||
If `c` is null, the result is unspecified.
|
||||
**/
|
||||
public static function rpad(s:String, c:String, l:Int):String {
|
||||
if (c.length <= 0)
|
||||
return s;
|
||||
|
||||
var buf = new StringBuf();
|
||||
buf.add(s);
|
||||
while (buf.length < l) {
|
||||
buf.add(c);
|
||||
}
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
Replace all occurrences of the String `sub` in the String `s` by the
|
||||
String `by`.
|
||||
|
||||
If `sub` is the empty String `""`, `by` is inserted after each character
|
||||
of `s` except the last one. If `by` is also the empty String `""`, `s`
|
||||
remains unchanged.
|
||||
|
||||
If `sub` or `by` are null, the result is unspecified.
|
||||
**/
|
||||
public static function replace(s:String, sub:String, by:String):String {
|
||||
#if java
|
||||
if (sub.length == 0)
|
||||
return s.split(sub).join(by);
|
||||
else
|
||||
return (cast s : java.NativeString).replace(sub, by);
|
||||
#else
|
||||
return s.split(sub).join(by);
|
||||
#end
|
||||
}
|
||||
|
||||
/**
|
||||
Encodes `n` into a hexadecimal representation.
|
||||
|
||||
If `digits` is specified, the resulting String is padded with "0" until
|
||||
its `length` equals `digits`.
|
||||
**/
|
||||
public static function hex(n:Int, ?digits:Int) {
|
||||
#if flash
|
||||
var n:UInt = n;
|
||||
var s:String = untyped n.toString(16);
|
||||
s = s.toUpperCase();
|
||||
#else
|
||||
var s = "";
|
||||
var hexChars = "0123456789ABCDEF";
|
||||
do {
|
||||
s = hexChars.charAt(n & 15) + s;
|
||||
n >>>= 4;
|
||||
} while (n > 0);
|
||||
#end
|
||||
#if python
|
||||
if (digits != null && s.length < digits) {
|
||||
var diff = digits - s.length;
|
||||
for (_ in 0...diff) {
|
||||
s = "0" + s;
|
||||
}
|
||||
}
|
||||
#else
|
||||
if (digits != null)
|
||||
while (s.length < digits)
|
||||
s = "0" + s;
|
||||
#end
|
||||
return s;
|
||||
}
|
||||
|
||||
/**
|
||||
Returns the character code at position `index` of String `s`, or an
|
||||
end-of-file indicator at if `position` equals `s.length`.
|
||||
|
||||
This method is faster than `String.charCodeAt()` on some platforms, but
|
||||
the result is unspecified if `index` is negative or greater than
|
||||
`s.length`.
|
||||
|
||||
End of file status can be checked by calling `StringTools.isEof()` with
|
||||
the returned value as argument.
|
||||
|
||||
This operation is not guaranteed to work if `s` contains the `\0`
|
||||
character.
|
||||
**/
|
||||
public static #if !eval inline #end function fastCodeAt(s:String, index:Int):Int {
|
||||
#if neko
|
||||
return untyped __dollar__sget(s.__s, index);
|
||||
#elseif cpp
|
||||
return untyped s.cca(index);
|
||||
#elseif flash
|
||||
return untyped s.cca(index);
|
||||
#elseif java
|
||||
return (index < s.length) ? cast(_charAt(s, index), Int) : -1;
|
||||
#elseif js
|
||||
return (cast s).charCodeAt(index);
|
||||
#elseif python
|
||||
return if (index >= s.length) -1 else python.internal.UBuiltins.ord(python.Syntax.arrayAccess(s, index));
|
||||
#elseif hl
|
||||
return @:privateAccess s.bytes.getUI16(index << 1);
|
||||
#elseif lua
|
||||
#if lua_vanilla
|
||||
return lua.NativeStringTools.byte(s, index + 1);
|
||||
#else
|
||||
return lua.lib.luautf8.Utf8.byte(s, index + 1);
|
||||
#end
|
||||
#else
|
||||
return untyped s.cca(index);
|
||||
#end
|
||||
}
|
||||
|
||||
/**
|
||||
Returns the character code at position `index` of String `s`, or an
|
||||
end-of-file indicator at if `position` equals `s.length`.
|
||||
|
||||
This method is faster than `String.charCodeAt()` on some platforms, but
|
||||
the result is unspecified if `index` is negative or greater than
|
||||
`s.length`.
|
||||
|
||||
This operation is not guaranteed to work if `s` contains the `\0`
|
||||
character.
|
||||
**/
|
||||
public static #if !eval inline #end function unsafeCodeAt(s:String, index:Int):Int {
|
||||
#if neko
|
||||
return untyped __dollar__sget(s.__s, index);
|
||||
#elseif cpp
|
||||
return untyped s.cca(index);
|
||||
#elseif flash
|
||||
return untyped s.cca(index);
|
||||
#elseif java
|
||||
return cast(_charAt(s, index), Int);
|
||||
#elseif js
|
||||
return (cast s).charCodeAt(index);
|
||||
#elseif python
|
||||
return python.internal.UBuiltins.ord(python.Syntax.arrayAccess(s, index));
|
||||
#elseif hl
|
||||
return @:privateAccess s.bytes.getUI16(index << 1);
|
||||
#elseif lua
|
||||
#if lua_vanilla
|
||||
return lua.NativeStringTools.byte(s, index + 1);
|
||||
#else
|
||||
return lua.lib.luautf8.Utf8.byte(s, index + 1);
|
||||
#end
|
||||
#else
|
||||
return untyped s.cca(index);
|
||||
#end
|
||||
}
|
||||
|
||||
/**
|
||||
Returns an iterator of the char codes.
|
||||
|
||||
Note that char codes may differ across platforms because of different
|
||||
internal encoding of strings in different runtimes.
|
||||
For the consistent cross-platform UTF8 char codes see `haxe.iterators.StringIteratorUnicode`.
|
||||
**/
|
||||
public static inline function iterator(s:String):StringIterator {
|
||||
return new StringIterator(s);
|
||||
}
|
||||
|
||||
/**
|
||||
Returns an iterator of the char indexes and codes.
|
||||
|
||||
Note that char codes may differ across platforms because of different
|
||||
internal encoding of strings in different of runtimes.
|
||||
For the consistent cross-platform UTF8 char codes see `haxe.iterators.StringKeyValueIteratorUnicode`.
|
||||
**/
|
||||
public static inline function keyValueIterator(s:String):StringKeyValueIterator {
|
||||
return new StringKeyValueIterator(s);
|
||||
}
|
||||
|
||||
/**
|
||||
Tells if `c` represents the end-of-file (EOF) character.
|
||||
**/
|
||||
@:noUsing public static inline function isEof(c:Int):Bool {
|
||||
#if (flash || cpp || hl)
|
||||
return c == 0;
|
||||
#elseif js
|
||||
return c != c; // fast NaN
|
||||
#elseif (neko || lua || eval)
|
||||
return c == null;
|
||||
#elseif (java || python)
|
||||
return c == -1;
|
||||
#else
|
||||
return false;
|
||||
#end
|
||||
}
|
||||
|
||||
/**
|
||||
Returns a String that can be used as a single command line argument
|
||||
on Unix.
|
||||
The input will be quoted, or escaped if necessary.
|
||||
**/
|
||||
@:noCompletion
|
||||
@:deprecated('StringTools.quoteUnixArg() is deprecated. Use haxe.SysTools.quoteUnixArg() instead.')
|
||||
public static function quoteUnixArg(argument:String):String {
|
||||
return inline haxe.SysTools.quoteUnixArg(argument);
|
||||
}
|
||||
|
||||
/**
|
||||
Character codes of the characters that will be escaped by `quoteWinArg(_, true)`.
|
||||
**/
|
||||
@:noCompletion
|
||||
@:deprecated('StringTools.winMetaCharacters is deprecated. Use haxe.SysTools.winMetaCharacters instead.')
|
||||
public static var winMetaCharacters:Array<Int> = cast haxe.SysTools.winMetaCharacters;
|
||||
|
||||
/**
|
||||
Returns a String that can be used as a single command line argument
|
||||
on Windows.
|
||||
The input will be quoted, or escaped if necessary, such that the output
|
||||
will be parsed as a single argument using the rule specified in
|
||||
http://msdn.microsoft.com/en-us/library/ms880421
|
||||
|
||||
Examples:
|
||||
```haxe
|
||||
quoteWinArg("abc") == "abc";
|
||||
quoteWinArg("ab c") == '"ab c"';
|
||||
```
|
||||
**/
|
||||
@:noCompletion
|
||||
@:deprecated('StringTools.quoteWinArg() is deprecated. Use haxe.SysTools.quoteWinArg() instead.')
|
||||
public static function quoteWinArg(argument:String, escapeMetaCharacters:Bool):String {
|
||||
return inline haxe.SysTools.quoteWinArg(argument, escapeMetaCharacters);
|
||||
}
|
||||
|
||||
#if java
|
||||
private static inline function _charAt(str:String, idx:Int):java.StdTypes.Char16
|
||||
return (cast str : java.NativeString).charAt(idx);
|
||||
#end
|
||||
|
||||
#if neko
|
||||
private static var _urlEncode = neko.Lib.load("std", "url_encode", 1);
|
||||
private static var _urlDecode = neko.Lib.load("std", "url_decode", 1);
|
||||
#end
|
||||
|
||||
#if utf16
|
||||
static inline var MIN_SURROGATE_CODE_POINT = 65536;
|
||||
|
||||
static inline function utf16CodePointAt(s:String, index:Int):Int {
|
||||
var c = StringTools.fastCodeAt(s, index);
|
||||
if (c >= 0xD800 && c <= 0xDBFF) {
|
||||
c = ((c - 0xD7C0) << 10) | (StringTools.fastCodeAt(s, index + 1) & 0x3FF);
|
||||
}
|
||||
return c;
|
||||
}
|
||||
#end
|
||||
}
|
||||
@@ -14,7 +14,7 @@ import flixel.FlxG;
|
||||
* @see https://twitter.com/Ne_Eo_Twitch
|
||||
*/
|
||||
@:access(away3d.core.managers.Stage3DManager)
|
||||
class Flx3DUtil
|
||||
final class Flx3DUtil
|
||||
{
|
||||
#if THREE_D_SUPPORT
|
||||
/**
|
||||
|
||||
@@ -181,10 +181,12 @@ class FunkinSprite extends FlxSkewedSprite implements IBeatReceiver implements I
|
||||
animateAtlas.flipX = flipX;
|
||||
animateAtlas.flipY = flipY;
|
||||
animateAtlas.shader = shader;
|
||||
animateAtlas.shaderEnabled = shaderEnabled;
|
||||
animateAtlas.antialiasing = antialiasing;
|
||||
animateAtlas.skew = skew;
|
||||
animateAtlas.transformMatrix = transformMatrix;
|
||||
animateAtlas.matrixExposed = matrixExposed;
|
||||
animateAtlas.colorTransform = colorTransform;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ import sys.FileSystem;
|
||||
|
||||
using StringTools;
|
||||
|
||||
class Chart {
|
||||
final class Chart {
|
||||
/**
|
||||
* Default background colors for songs without bg color
|
||||
*/
|
||||
|
||||
@@ -9,7 +9,7 @@ import openfl.Assets;
|
||||
|
||||
using StringTools;
|
||||
|
||||
class EventsData {
|
||||
final class EventsData {
|
||||
public static var defaultEventsList:Array<String> = ["HScript Call", "Camera Movement", "Add Camera Zoom", "Camera Modulo Change", "Camera Flash", "BPM Change", "Scroll Speed Change", "Alt Animation Toggle", "Play Animation"];
|
||||
public static var defaultEventsParams:Map<String, Array<EventParamInfo>> = [
|
||||
"HScript Call" => [
|
||||
@@ -70,6 +70,8 @@ class EventsData {
|
||||
hscriptInterp.variables.set("StrumLine", TStrumLine);
|
||||
hscriptInterp.variables.set("ColorWheel", TColorWheel);
|
||||
hscriptInterp.variables.set("DropDown", Reflect.makeVarArgs((args) -> {return args.length > 0 ? TDropDown([for (arg in args) Std.string(arg)]) : TDropDown(["null"]);}));
|
||||
hscriptInterp.variables.set("Character", TCharacter);
|
||||
hscriptInterp.variables.set("Stage", TStage);
|
||||
|
||||
var hscriptParser:Parser = new Parser();
|
||||
hscriptParser.allowJSON = hscriptParser.allowMetadata = false;
|
||||
@@ -127,4 +129,6 @@ enum EventParamType {
|
||||
TStrumLine;
|
||||
TColorWheel;
|
||||
TDropDown(?options:Array<String>);
|
||||
TCharacter;
|
||||
TStage;
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ package funkin.backend.scripting;
|
||||
import flixel.FlxState;
|
||||
import funkin.backend.scripting.events.*;
|
||||
|
||||
class EventManager {
|
||||
final class EventManager {
|
||||
// map doesnt work for that
|
||||
public static var eventValues:Array<CancellableEvent> = [];
|
||||
public static var eventKeys:Array<Class<CancellableEvent>> = [];
|
||||
|
||||
@@ -11,7 +11,11 @@ import funkin.backend.system.Conductor;
|
||||
class GlobalScript {
|
||||
public static var scripts:ScriptPack;
|
||||
|
||||
private static var initalized:Bool = false;
|
||||
|
||||
public static function init() {
|
||||
if(initalized) return;
|
||||
initalized = true;
|
||||
#if MOD_SUPPORT
|
||||
ModsFolder.onModSwitch.add(onModSwitch);
|
||||
#end
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
package funkin.backend.scripting.events;
|
||||
|
||||
final class HealthIconChangeEvent extends CancellableEvent {
|
||||
/**
|
||||
* Amount
|
||||
*/
|
||||
public var amount:Int;
|
||||
|
||||
/**
|
||||
* The health icon
|
||||
*/
|
||||
public var healthIcon:funkin.game.HealthIcon;
|
||||
}
|
||||
@@ -457,14 +457,7 @@ vec4 flixel_texture2D(sampler2D bitmap, vec2 coord)
|
||||
}
|
||||
|
||||
color = vec4(color.rgb / color.a, color.a);
|
||||
|
||||
mat4 colorMultiplier = mat4(0);
|
||||
colorMultiplier[0][0] = openfl_ColorMultiplierv.x;
|
||||
colorMultiplier[1][1] = openfl_ColorMultiplierv.y;
|
||||
colorMultiplier[2][2] = openfl_ColorMultiplierv.z;
|
||||
colorMultiplier[3][3] = openfl_ColorMultiplierv.w;
|
||||
|
||||
color = clamp(openfl_ColorOffsetv + (color * colorMultiplier), 0.0, 1.0);
|
||||
color = clamp(openfl_ColorOffsetv + (color * openfl_ColorMultiplierv), 0.0, 1.0);
|
||||
|
||||
if (color.a > 0.0)
|
||||
{
|
||||
@@ -491,39 +484,9 @@ vec4 textureCam(sampler2D bitmap, vec2 pos) {
|
||||
return flixel_texture2D(bitmap, camToOg(pos));
|
||||
}";
|
||||
|
||||
public static final fragBody:String = "vec4 color = texture2D (bitmap, openfl_TextureCoordv);
|
||||
|
||||
if (color.a == 0.0) {
|
||||
|
||||
gl_FragColor = vec4 (0.0, 0.0, 0.0, 0.0);
|
||||
|
||||
} else if (openfl_HasColorTransform) {
|
||||
|
||||
color = vec4 (color.rgb / color.a, color.a);
|
||||
|
||||
mat4 colorMultiplier = mat4 (0);
|
||||
colorMultiplier[0][0] = openfl_ColorMultiplierv.x;
|
||||
colorMultiplier[1][1] = openfl_ColorMultiplierv.y;
|
||||
colorMultiplier[2][2] = openfl_ColorMultiplierv.z;
|
||||
colorMultiplier[3][3] = 1.0; // openfl_ColorMultiplierv.w;
|
||||
|
||||
color = clamp (openfl_ColorOffsetv + (color * colorMultiplier), 0.0, 1.0);
|
||||
|
||||
if (color.a > 0.0) {
|
||||
|
||||
gl_FragColor = vec4 (color.rgb * color.a * openfl_Alphav, color.a * openfl_Alphav);
|
||||
|
||||
} else {
|
||||
|
||||
gl_FragColor = vec4 (0.0, 0.0, 0.0, 0.0);
|
||||
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
gl_FragColor = color * openfl_Alphav;
|
||||
|
||||
}";
|
||||
public static final fragBody:String = "vec4 color = flixel_texture2D (bitmap, openfl_TextureCoordv);
|
||||
gl_FragColor = color;
|
||||
";
|
||||
public static final vertHeader:String = "attribute float openfl_Alpha;
|
||||
attribute vec4 openfl_ColorMultiplier;
|
||||
attribute vec4 openfl_ColorOffset;
|
||||
|
||||
@@ -75,18 +75,18 @@ const int EFFECT_TYPE_FLAG = 4;
|
||||
uniform int effectType;
|
||||
|
||||
/**
|
||||
* How fast the waves move over time
|
||||
*/
|
||||
* How fast the waves move over time
|
||||
*/
|
||||
uniform float uSpeed;
|
||||
|
||||
/**
|
||||
* Number of waves over time
|
||||
*/
|
||||
* Number of waves over time
|
||||
*/
|
||||
uniform float uFrequency;
|
||||
|
||||
/**
|
||||
* How much the pixels are going to stretch over the waves
|
||||
*/
|
||||
* How much the pixels are going to stretch over the waves
|
||||
*/
|
||||
uniform float uWaveAmplitude;
|
||||
|
||||
vec2 sineWave(vec2 pt)
|
||||
|
||||
@@ -2,7 +2,7 @@ package funkin.backend.system;
|
||||
|
||||
#if sys
|
||||
import sys.FileSystem;
|
||||
class CommandLineHandler {
|
||||
final class CommandLineHandler {
|
||||
public static function parseCommandLine(cmd:Array<String>) {
|
||||
var i:Int = 0;
|
||||
while(i < cmd.length) {
|
||||
|
||||
@@ -6,7 +6,7 @@ import funkin.backend.utils.NativeAPI.ConsoleColor;
|
||||
import funkin.backend.utils.NativeAPI;
|
||||
import haxe.Log;
|
||||
|
||||
class Logs {
|
||||
final class Logs {
|
||||
private static var __showing:Bool = false;
|
||||
|
||||
public static var nativeTrace = Log.trace;
|
||||
|
||||
@@ -5,6 +5,7 @@ using StringTools;
|
||||
#if macro
|
||||
class BuildInfo {
|
||||
public static function printBuildInfo() {
|
||||
if(haxe.macro.Context.defined("display")) return;
|
||||
var haxeVersion = haxe.macro.Context.definedValue("haxe");
|
||||
Sys.println('--- BUILD INFO ---');
|
||||
Sys.println('Haxe Version: ${haxeVersion}');
|
||||
|
||||
@@ -11,7 +11,7 @@ import lime.media.AudioManager;
|
||||
* hi gray itsa me yoshicrafter29 i fixed it hehe
|
||||
*/
|
||||
@:dox(hide)
|
||||
class AudioSwitchFix {
|
||||
final class AudioSwitchFix {
|
||||
@:noCompletion
|
||||
private static function onStateSwitch(state:FlxState):Void {
|
||||
#if windows
|
||||
|
||||
@@ -7,7 +7,7 @@ import openfl.errors.Error;
|
||||
import openfl.events.ErrorEvent;
|
||||
import openfl.events.UncaughtErrorEvent;
|
||||
|
||||
class CrashHandler {
|
||||
final class CrashHandler {
|
||||
public static function init() {
|
||||
Lib.current.loaderInfo.uncaughtErrorEvents.addEventListener(UncaughtErrorEvent.UNCAUGHT_ERROR, onUncaughtError);
|
||||
#if cpp
|
||||
|
||||
@@ -5,7 +5,7 @@ import openfl.display.BitmapData;
|
||||
import openfl.geom.Point;
|
||||
import openfl.geom.Rectangle;
|
||||
|
||||
class BitmapUtil {
|
||||
final class BitmapUtil {
|
||||
/**
|
||||
* Returns the most present color in a Bitmap.
|
||||
* @param bmap Bitmap
|
||||
|
||||
@@ -42,7 +42,7 @@ import flixel.math.FlxPoint;
|
||||
using StringTools;
|
||||
|
||||
@:allow(funkin.game.PlayState)
|
||||
class CoolUtil
|
||||
final class CoolUtil
|
||||
{
|
||||
public static function getLastExceptionStack():String {
|
||||
return CallStack.toString(CallStack.exceptionStack());
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package funkin.backend.utils;
|
||||
|
||||
import flixel.util.FlxSignal.FlxTypedSignal;
|
||||
import flixel.sound.FlxSound;
|
||||
import flixel.util.typeLimit.OneOfTwo;
|
||||
import funkin.backend.scripting.*; // lazy
|
||||
@@ -16,7 +17,7 @@ import lime.app.Application;
|
||||
import sys.thread.Thread;
|
||||
#end
|
||||
|
||||
class DiscordUtil
|
||||
final class DiscordUtil
|
||||
{
|
||||
public static var currentID(default, set):String = null;
|
||||
public static var discordThread:#if DISCORD_RPC Thread #else Dynamic #end = null;
|
||||
@@ -107,14 +108,28 @@ class DiscordUtil
|
||||
|
||||
public static function loadScript()
|
||||
{
|
||||
#if DISCORD_RPC
|
||||
if (script != null)
|
||||
{
|
||||
call("destroy");
|
||||
script = FlxDestroyUtil.destroy(script);
|
||||
}
|
||||
script = Script.create(Paths.script('data/discord'));
|
||||
script.set("ActivityType", {
|
||||
Playing: ActivityType.Playing,
|
||||
PLAYING: ActivityType.Playing,
|
||||
Streaming: ActivityType.Streaming,
|
||||
STREAMING: ActivityType.Streaming,
|
||||
Watching: ActivityType.Watching,
|
||||
WATCHING: ActivityType.Watching,
|
||||
Listening: ActivityType.Listening,
|
||||
LISTENING: ActivityType.Listening,
|
||||
Competing: ActivityType.Competing,
|
||||
COMPETING: ActivityType.Competing
|
||||
});
|
||||
// script.setParent(DiscordUtil);
|
||||
script.load();
|
||||
#end
|
||||
}
|
||||
|
||||
public static function changePresence(details:String, state:String, ?smallImageKey:String)
|
||||
@@ -218,6 +233,8 @@ class DiscordUtil
|
||||
Utils.safeSetWrapper(dp.joinSecret, data.joinSecret, fixString);
|
||||
Utils.safeSetWrapper(dp.spectateSecret, data.spectateSecret, fixString);
|
||||
Utils.safeSet(dp.instance, data.instance);
|
||||
Utils.safeSet(dp.activityType, data.activityType);
|
||||
Utils.safeSetWrapper(dp.streamUrl, data.streamUrl, fixString);
|
||||
if (data.matchSecret == null && data.joinSecret == null && data.spectateSecret == null)
|
||||
{
|
||||
Utils.safeSetWrapper(dp.button1Label, data.button1Label, fixString);
|
||||
@@ -252,6 +269,7 @@ class DiscordUtil
|
||||
handlers.joinGame = cpp.Function.fromStaticFunction(onJoin);
|
||||
handlers.joinRequest = cpp.Function.fromStaticFunction(onJoinReq);
|
||||
handlers.spectateGame = cpp.Function.fromStaticFunction(onSpectate);
|
||||
handlers.anyResponse = cpp.Function.fromStaticFunction(onAnyResponse);
|
||||
Discord.Initialize(id, cpp.RawPointer.addressOf(handlers), 1, null);
|
||||
stopThread = false;
|
||||
|
||||
@@ -351,7 +369,38 @@ class DiscordUtil
|
||||
var req:DUser = DUser.initRaw(request);
|
||||
call("onJoinRequest", [req]);
|
||||
}
|
||||
|
||||
public static var anyResponse:FlxTypedSignal<String->Void> = new FlxTypedSignal<String->Void>();
|
||||
|
||||
private static function onAnyResponse(data:cpp.ConstCharStar):Void
|
||||
{
|
||||
call("onAnyResponse", [data]);
|
||||
anyResponse.dispatch(data);
|
||||
}
|
||||
#end
|
||||
|
||||
private static function getUUID():String {
|
||||
var uuid = new StringBuf();
|
||||
for (i in 0...16) {
|
||||
uuid.add(StringTools.hex(Math.floor(Math.random() * 16), 1));
|
||||
}
|
||||
return uuid.toString();
|
||||
}
|
||||
|
||||
public static function sendCustomCommand(data:Dynamic) {
|
||||
#if DISCORD_RPC
|
||||
if(data == null) return;
|
||||
if(data.nonce == null) data.nonce = getUUID();
|
||||
var json = Json.stringify(data);
|
||||
Discord.SendCustomCommand(json);
|
||||
#end
|
||||
}
|
||||
|
||||
public static function setDebugMode(mode:Bool) {
|
||||
#if DISCORD_RPC
|
||||
Discord.SetDebugMode(mode);
|
||||
#end
|
||||
}
|
||||
}
|
||||
|
||||
typedef DiscordJson =
|
||||
@@ -476,6 +525,8 @@ typedef DPresence =
|
||||
var ?button1Url:String; /* max 512 bytes */
|
||||
var ?button2Label:String; /* max 32 bytes */
|
||||
var ?button2Url:String; /* max 512 bytes */
|
||||
var ?activityType:ActivityType;
|
||||
var ?streamUrl:String; /* max 512 bytes */
|
||||
}
|
||||
|
||||
typedef DEvents =
|
||||
|
||||
@@ -4,7 +4,7 @@ import flixel.util.FlxColor;
|
||||
import flixel.math.FlxAngle;
|
||||
import flixel.math.FlxPoint;
|
||||
|
||||
class DrawUtil {
|
||||
final class DrawUtil {
|
||||
public static var line:FlxSprite = null;
|
||||
public static var dot:FlxSprite = null;
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ package funkin.backend.utils;
|
||||
import funkin.backend.scripting.MultiThreadedScript;
|
||||
import funkin.backend.scripting.Script;
|
||||
|
||||
class EngineUtil {
|
||||
final class EngineUtil {
|
||||
/**
|
||||
* Starts a new multithreaded script.
|
||||
* This script will share all the variables with the current one, which means already existing callbacks will be replaced by new ones on conflict.
|
||||
|
||||
@@ -2,7 +2,7 @@ package funkin.backend.utils;
|
||||
|
||||
import haxe.Http;
|
||||
|
||||
class HttpUtil
|
||||
final class HttpUtil
|
||||
{
|
||||
public static var userAgent:String = "request";
|
||||
public static function requestText(url:String)
|
||||
|
||||
@@ -3,7 +3,7 @@ package funkin.backend.utils;
|
||||
/**
|
||||
* DOESNT SUPPORT CATEGORIES YET!!
|
||||
*/
|
||||
class IniUtil {
|
||||
final class IniUtil {
|
||||
public static inline function parseAsset(assetPath:String, ?defaultVariables:Map<String, String>)
|
||||
return parseString(Assets.getText(assetPath), defaultVariables);
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ package funkin.backend.utils;
|
||||
import flixel.text.FlxText;
|
||||
import flixel.util.FlxColor;
|
||||
|
||||
class MarkdownUtil {
|
||||
final class MarkdownUtil {
|
||||
public static function applyMarkdownText(text:FlxText, str:String) {
|
||||
text.textField.htmlText = Markdown.markdownToHtml(prepareMarkdown(str));
|
||||
var changes:Array<FlxTextFormatMarkerPair> = [
|
||||
|
||||
@@ -2,7 +2,7 @@ package funkin.backend.utils;
|
||||
|
||||
import haxe.macro.Expr;
|
||||
|
||||
class MathUtil {
|
||||
final class MathUtil {
|
||||
/**
|
||||
* Returns the maximum value in the arguments.
|
||||
* @param args Array of values
|
||||
|
||||
@@ -9,7 +9,7 @@ interface IPrePostDraw {
|
||||
public function postDraw():Void;
|
||||
}
|
||||
|
||||
class MatrixUtil {
|
||||
final class MatrixUtil {
|
||||
public static function getMatrixPosition(sprite:FlxSprite, points:OneOfTwo<FlxPoint, Array<FlxPoint>>, ?camera:FlxCamera, _width:Float = 1, _height:Float = 1):Array<FlxPoint>
|
||||
{
|
||||
//if(_width == -1) _width = sprite.width;
|
||||
|
||||
@@ -14,7 +14,7 @@ import openfl.system.System;
|
||||
|
||||
using StringTools;
|
||||
|
||||
class MemoryUtil {
|
||||
final class MemoryUtil {
|
||||
public static var disableCount:Int = 0;
|
||||
|
||||
public static function askDisable() {
|
||||
|
||||
@@ -14,7 +14,7 @@ package funkin.backend.utils;
|
||||
* - The Function cannot be found in the NDLL
|
||||
* then an empty function will be returned instead, and a message will be shown in logs.
|
||||
*/
|
||||
class NdllUtil {
|
||||
final class NdllUtil {
|
||||
#if NDLLS_SUPPORTED
|
||||
#if windows public static final os:String = "windows"; #end
|
||||
#if linux public static final os:String = "linux"; #end
|
||||
|
||||
@@ -2,7 +2,7 @@ package funkin.backend.utils;
|
||||
|
||||
import openfl.display.Sprite;
|
||||
|
||||
class ShaderResizeFix {
|
||||
final class ShaderResizeFix {
|
||||
public static var doResizeFix:Bool = true;
|
||||
|
||||
public static function init() {
|
||||
|
||||
@@ -3,7 +3,7 @@ package funkin.backend.utils;
|
||||
/**
|
||||
* All types of utils only usable in a sorted array.
|
||||
*/
|
||||
class SortedArrayUtil {
|
||||
final class SortedArrayUtil {
|
||||
/**
|
||||
* Gets the index of a possible new element of an Array of T using an efficient algorithm.
|
||||
* @param array Array of T to check in
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package funkin.backend.utils;
|
||||
|
||||
#if ALLOW_MULTITHREADING
|
||||
class ThreadUtil {
|
||||
final class ThreadUtil {
|
||||
/**
|
||||
* Creates a new Thread with an error handler.
|
||||
* @param func Function to execute
|
||||
|
||||
@@ -2,7 +2,7 @@ package funkin.backend.utils;
|
||||
|
||||
import openfl.Lib;
|
||||
|
||||
class WindowUtils {
|
||||
final class WindowUtils {
|
||||
public static var winTitle(default, set):String;
|
||||
private static function set_winTitle(value:String):String {
|
||||
winTitle = value;
|
||||
|
||||
@@ -15,7 +15,7 @@ typedef TextFormat = { text:String, format:Dynamic }
|
||||
/**
|
||||
* Class made to make XML parsing easier.
|
||||
*/
|
||||
class XMLUtil {
|
||||
final class XMLUtil {
|
||||
|
||||
/**
|
||||
* Applies a property XML node to an object.
|
||||
|
||||
@@ -23,7 +23,7 @@ using StringTools;
|
||||
// import ZipUtils; ZipUtils.uncompressZip(ZipUtils.openZip("E:\\Desktop\\test\\termination lua.ycemod"), "E:\\Desktop\\test\\uncompressed\\");
|
||||
// import ZipUtils; var e = ZipUtils.createZipFile("gjnsdghs.ycemod"); ZipUtils.writeFolderToZip(e, "./mods/Friday Night Funkin'/", "Friday Night Funkin'/"); e.flush(); e.close();
|
||||
|
||||
class ZipUtil {
|
||||
final class ZipUtil {
|
||||
public static var bannedNames:Array<String> = [".git", ".gitignore", ".github", ".vscode", ".gitattributes", "readme.txt"];
|
||||
|
||||
/**
|
||||
|
||||
@@ -2,7 +2,8 @@ package funkin.backend.utils.native;
|
||||
|
||||
#if linux
|
||||
@:cppFileCode("#include <stdio.h>")
|
||||
class Linux {
|
||||
@:dox(hide)
|
||||
final class Linux {
|
||||
@:functionCode('
|
||||
FILE *meminfo = fopen("/proc/meminfo", "r");
|
||||
|
||||
|
||||
@@ -5,7 +5,8 @@ import funkin.backend.utils.NativeAPI.CodeCursor;
|
||||
import openfl.ui.Mouse;
|
||||
|
||||
@:headerInclude('sys/sysctl.h')
|
||||
class Mac
|
||||
@:dox(hide)
|
||||
final class Mac
|
||||
{
|
||||
@:functionCode('
|
||||
int mib [] = { CTL_HW, HW_MEMSIZE };
|
||||
|
||||
@@ -114,7 +114,7 @@ class AudioFixClient : public IMMNotificationClient {
|
||||
AudioFixClient *curAudioFix;
|
||||
')
|
||||
@:dox(hide)
|
||||
class Windows {
|
||||
final class Windows {
|
||||
|
||||
public static var __audioChangeCallback:Void->Void = function() {
|
||||
trace("test");
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package funkin.editors.charter;
|
||||
// ! FUCK YOU CHUF (your biggest fan -lunar) <3
|
||||
|
||||
import flixel.FlxLayer;
|
||||
import funkin.editors.extra.CameraHoverDummy;
|
||||
import flixel.input.keyboard.FlxKey;
|
||||
import flixel.math.FlxPoint;
|
||||
@@ -88,6 +89,8 @@ class Charter extends UIState {
|
||||
public static var clipboard:Array<CharterCopyboardObject> = [];
|
||||
public static var waveformHandler:CharterWaveformHandler;
|
||||
|
||||
public var textLayer:FlxLayer;
|
||||
|
||||
public function new(song:String, diff:String, reload:Bool = true) {
|
||||
super();
|
||||
if (song != null) {
|
||||
@@ -484,6 +487,7 @@ class Charter extends UIState {
|
||||
|
||||
// adds grid and notes so that they're ALWAYS behind the UI
|
||||
add(gridBackdrops);
|
||||
add(textLayer = new FlxLayer());
|
||||
add(eventsBackdrop);
|
||||
add(addEventSpr);
|
||||
add(eventsGroup);
|
||||
|
||||
@@ -6,6 +6,37 @@ import funkin.backend.system.Conductor;
|
||||
import openfl.geom.Rectangle;
|
||||
import funkin.backend.shaders.CustomShader;
|
||||
|
||||
class FlxFastTypedGroup<T:FlxBasic> extends FlxTypedGroup<T> {
|
||||
public function new(?maxSize:Int = 0) {
|
||||
super(maxSize);
|
||||
}
|
||||
|
||||
override public function add(Object:T):T
|
||||
{
|
||||
if (Object == null)
|
||||
{
|
||||
FlxG.log.warn("Cannot add a `null` object to a FlxGroup.");
|
||||
return null;
|
||||
}
|
||||
|
||||
// removed the check for multiple
|
||||
// removed the check for null reuse
|
||||
|
||||
// If the group is full, return the Object
|
||||
if (maxSize > 0 && length >= maxSize)
|
||||
return Object;
|
||||
|
||||
// If we made it this far, we need to add the object to the group.
|
||||
members.push(Object);
|
||||
length++;
|
||||
|
||||
if (_memberAdded != null)
|
||||
_memberAdded.dispatch(Object);
|
||||
|
||||
return Object;
|
||||
}
|
||||
}
|
||||
|
||||
class CharterBackdropGroup extends FlxTypedGroup<CharterBackdrop> {
|
||||
public var strumLineGroup:CharterStrumLineGroup;
|
||||
public var notesGroup:CharterNoteGroup;
|
||||
@@ -56,9 +87,11 @@ class CharterBackdropGroup extends FlxTypedGroup<CharterBackdrop> {
|
||||
|
||||
grid.notesGroup.clear();
|
||||
notesGroup.forEach((n) -> {
|
||||
var onStr:Bool = (n.snappedToStrumline ? n.strumLineID : CoolUtil.boundInt(Std.int((n.x+n.width)/(40*strumLine.keyCount)), 0, strumLineGroup.members.length-1)) == i;
|
||||
if(n.exists && n.visible && onStr)
|
||||
grid.notesGroup.add(n);
|
||||
if(n.exists && n.visible) {
|
||||
var onStr:Bool = (n.snappedToStrumline ? n.strumLineID : CoolUtil.boundInt(Std.int((n.x+n.width)/(40*strumLine.keyCount)), 0, strumLineGroup.members.length-1)) == i;
|
||||
if(onStr)
|
||||
grid.notesGroup.add(n);
|
||||
}
|
||||
});
|
||||
|
||||
grid.active = grid.visible = true;
|
||||
@@ -97,7 +130,7 @@ class CharterBackdropGroup extends FlxTypedGroup<CharterBackdrop> {
|
||||
}
|
||||
}
|
||||
|
||||
class CharterBackdrop extends FlxTypedGroup<Dynamic> {
|
||||
class CharterBackdrop extends FlxTypedGroup<FlxBasic> {
|
||||
public var gridBackDrop:FlxBackdrop;
|
||||
public var topLimit:FlxSprite;
|
||||
public var topSeparator:FlxSprite;
|
||||
@@ -110,7 +143,7 @@ class CharterBackdrop extends FlxTypedGroup<Dynamic> {
|
||||
public var beatSeparator:FlxBackdrop;
|
||||
public var sectionSeparator:FlxBackdrop;
|
||||
|
||||
public var notesGroup:FlxTypedGroup<CharterNote> = new FlxTypedGroup<CharterNote>();
|
||||
public var notesGroup:FlxFastTypedGroup<CharterNote> = new FlxFastTypedGroup<CharterNote>();
|
||||
public var strumLine:CharterStrumline;
|
||||
|
||||
public var gridShader:CustomShader = new CustomShader("engine/charterGrid");
|
||||
@@ -127,7 +160,7 @@ class CharterBackdrop extends FlxTypedGroup<Dynamic> {
|
||||
|
||||
waveformSprite = new FlxSprite().makeSolid(1, 1, 0xFF000000);
|
||||
waveformSprite.scale.set(160, 1);
|
||||
waveformSprite.updateHitbox();
|
||||
waveformSprite.updateHitbox();
|
||||
add(waveformSprite);
|
||||
|
||||
sectionSeparator = new FlxBackdrop(null, Y, 0, 0);
|
||||
|
||||
@@ -65,12 +65,17 @@ class CharterDeleteAnim extends CharterNote {
|
||||
sustainSpr.updateHitbox(); sustainSpr.follow(this, 15, 20);
|
||||
sustainSpr.exists = deleteData.note.susLength != 0; sustainSpr.alpha = .8;
|
||||
|
||||
typeText.text = Std.string(deleteData.note.type);
|
||||
typeText.exists = deleteData.note.type != 0; typeText.alpha = .4;
|
||||
typeText.follow(this, 20 - (typeText.frameWidth/2), 20 - (typeText.frameHeight/2));
|
||||
type = deleteData.note.type;
|
||||
typeAlpha = .4;
|
||||
//typeText.text = Std.string(deleteData.note.type);
|
||||
//typeText.exists = deleteData.note.type != 0; typeAlpha = .4;
|
||||
//typeText.follow(this, 20 - (typeText.frameWidth/2), 20 - (typeText.frameHeight/2));
|
||||
|
||||
for (member in [this, sustainSpr, typeText])
|
||||
member.alpha *= FlxEase.quadInOut(deleteData.time/deleteTime);
|
||||
var mult = FlxEase.quadInOut(deleteData.time/deleteTime);
|
||||
|
||||
for (member in [this, sustainSpr])
|
||||
member.alpha *= mult;
|
||||
typeAlpha *= mult;
|
||||
|
||||
super.draw();
|
||||
}
|
||||
|
||||
@@ -5,6 +5,8 @@ import flixel.util.FlxColor;
|
||||
import funkin.backend.chart.ChartData.ChartEvent;
|
||||
import funkin.backend.chart.EventsData;
|
||||
import funkin.backend.system.Conductor;
|
||||
import funkin.game.Character;
|
||||
import funkin.game.Stage;
|
||||
|
||||
using StringTools;
|
||||
|
||||
@@ -163,6 +165,20 @@ class CharterEventScreen extends UISubstateWindow {
|
||||
var dropdown = new UIDropDown(eventName.x, y, 320, 32, options, Std.int(Math.abs(options.indexOf(cast value))));
|
||||
paramsPanel.add(dropdown); paramsFields.push(dropdown);
|
||||
dropdown;
|
||||
case TCharacter:
|
||||
addLabel();
|
||||
var charFileList = Character.getList(false);
|
||||
var textBox:UIAutoCompleteTextBox = new UIAutoCompleteTextBox(eventName.x, y, cast value);
|
||||
textBox.suggestItems = charFileList;
|
||||
paramsPanel.add(textBox); paramsFields.push(textBox);
|
||||
textBox;
|
||||
case TStage:
|
||||
addLabel();
|
||||
var stageFileList = Stage.getList(false);
|
||||
var textBox:UIAutoCompleteTextBox = new UIAutoCompleteTextBox(eventName.x, y, cast value);
|
||||
textBox.suggestItems = stageFileList;
|
||||
paramsPanel.add(textBox); paramsFields.push(textBox);
|
||||
textBox;
|
||||
default:
|
||||
paramsFields.push(null);
|
||||
null;
|
||||
|
||||
@@ -29,6 +29,8 @@ class CharterNote extends UISprite implements ICharterSelectable {
|
||||
public var selected:Bool = false;
|
||||
public var draggable:Bool = true;
|
||||
|
||||
static var noteTypeTexts:Array<UIText> = [];
|
||||
|
||||
public function new() {
|
||||
super();
|
||||
|
||||
@@ -43,7 +45,7 @@ class CharterNote extends UISprite implements ICharterSelectable {
|
||||
sustainSpr.scale.set(10, 0);
|
||||
members.push(sustainSpr);
|
||||
|
||||
typeText = new UIText(x, y, 0, Std.string(type));
|
||||
this.type = 0;
|
||||
|
||||
cursor = sustainSpr.cursor = CLICK;
|
||||
moves = false;
|
||||
@@ -57,7 +59,22 @@ class CharterNote extends UISprite implements ICharterSelectable {
|
||||
public var step:Float;
|
||||
public var id:Int;
|
||||
public var susLength:Float;
|
||||
public var type:Int;
|
||||
public var type(default, set):Null<Int> = null;
|
||||
public var typeVisible:Bool = true;
|
||||
public var typeAlpha:Float = 1;
|
||||
|
||||
function set_type(v:Null<Int>) {
|
||||
if(v != this.type) {
|
||||
if(noteTypeTexts[v] == null || noteTypeTexts[v].graphic.isDestroyed) {
|
||||
noteTypeTexts[v] = new UIText(x, y, 0, Std.string(v));
|
||||
noteTypeTexts[v].exists = v != 0;
|
||||
noteTypeTexts[v].layer = (Charter.instance != null) ? Charter.instance.textLayer : null;
|
||||
}
|
||||
|
||||
typeText = noteTypeTexts[v];
|
||||
}
|
||||
return this.type = v;
|
||||
}
|
||||
|
||||
public var strumLine:CharterStrumline;
|
||||
public var strumLineID(get, default):Int = -1;
|
||||
@@ -77,9 +94,7 @@ class CharterNote extends UISprite implements ICharterSelectable {
|
||||
this.type = type;
|
||||
if (strumLine != null) this.strumLine = strumLine;
|
||||
|
||||
typeText.exists = type != 0;
|
||||
typeText.text = Std.string(this.type);
|
||||
typeText.follow(this, 20 - (typeText.frameWidth/2), 20 - (typeText.frameHeight/2));
|
||||
sustainSpr.exists = susLength != 0;
|
||||
|
||||
y = step * 40;
|
||||
|
||||
@@ -132,10 +147,12 @@ class CharterNote extends UISprite implements ICharterSelectable {
|
||||
public override function update(elapsed:Float) {
|
||||
super.update(elapsed);
|
||||
|
||||
var sprLength:Float = (40 * (susLength+tempSusLength)) + ((susLength+tempSusLength) != 0 ? (height/2) : 0);
|
||||
sustainSpr.scale.set(10, __susInstaLerp ? sprLength : CoolUtil.fpsLerp(sustainSpr.scale.y, sprLength, 1/2));
|
||||
sustainSpr.updateHitbox();
|
||||
sustainSpr.follow(this, 15, 20);
|
||||
if(susLength != 0) {
|
||||
var sprLength:Float = (40 * (susLength+tempSusLength)) + ((susLength+tempSusLength) != 0 ? (height/2) : 0);
|
||||
sustainSpr.scale.set(10, __susInstaLerp ? sprLength : CoolUtil.fpsLerp(sustainSpr.scale.y, sprLength, 1/2));
|
||||
sustainSpr.updateHitbox();
|
||||
sustainSpr.follow(this, 15, 20);
|
||||
}
|
||||
|
||||
sustainDraggable = false;
|
||||
if (!hovered && susLength != 0) {
|
||||
@@ -143,9 +160,6 @@ class CharterNote extends UISprite implements ICharterSelectable {
|
||||
sustainDraggable = UIState.state.isOverlapping(sustainSpr, @:privateAccess sustainSpr.__rect);
|
||||
}
|
||||
|
||||
if (typeText.exists)
|
||||
typeText.follow(this, 20 - (typeText.frameWidth/2), 20 - (typeText.frameHeight/2));
|
||||
|
||||
if (__passed != (__passed = step < Conductor.curStepFloat)) {
|
||||
if (__passed && FlxG.sound.music.playing && Charter.instance.hitsoundsEnabled(strumLineID))
|
||||
Charter.instance.hitsound.replay();
|
||||
@@ -153,7 +167,7 @@ class CharterNote extends UISprite implements ICharterSelectable {
|
||||
|
||||
if (strumLine != null) {
|
||||
sustainSpr.alpha = alpha = !strumLine.strumLine.visible ? (__passed ? 0.2 : 0.4) : (__passed ? 0.6 : 1);
|
||||
typeText.alpha = !strumLine.strumLine.visible ? (__passed ? 0.4 : 0.6) : (__passed ? 0.8 : 1);
|
||||
typeAlpha = !strumLine.strumLine.visible ? (__passed ? 0.4 : 0.6) : (__passed ? 0.8 : 1);
|
||||
}
|
||||
|
||||
colorTransform.redMultiplier = colorTransform.greenMultiplier = colorTransform.blueMultiplier = selected ? 0.75 : 1;
|
||||
@@ -186,6 +200,10 @@ class CharterNote extends UISprite implements ICharterSelectable {
|
||||
|
||||
drawMembers();
|
||||
drawSuper();
|
||||
if(typeText.exists && typeText.visible) typeText.draw();
|
||||
if(typeText.exists && typeText.visible && typeVisible) {
|
||||
typeText.alpha = typeAlpha;
|
||||
typeText.follow(this, 20 - (typeText.frameWidth/2), 20 - (typeText.frameHeight/2));
|
||||
typeText.draw();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -7,7 +7,7 @@ class CharterNoteHoverer extends CharterNote {
|
||||
super();
|
||||
|
||||
snappedToStrumline = selectable = autoAlpha = false; visible = sustainSpr.visible = false;
|
||||
@:privateAccess __animSpeed = 1.25; typeText.visible = false; alpha = 0.4;
|
||||
@:privateAccess __animSpeed = 1.25; typeVisible = false; alpha = 0.4;
|
||||
}
|
||||
|
||||
@:noCompletion var __mousePos:FlxPoint = FlxPoint.get();
|
||||
@@ -19,7 +19,7 @@ class CharterNoteHoverer extends CharterNote {
|
||||
var inBoundsY:Bool = (__mousePos.y > 0 && __mousePos.y < (Charter.instance.__endStep)*40);
|
||||
if ((__mousePos.x > 0 && __mousePos.x < Charter.instance.strumLines.totalKeyCount * 40 && inBoundsY) && showHoverer) {
|
||||
step = CoolUtil.bound(FlxG.keys.pressed.SHIFT ? ((__mousePos.y-20) / 40) : Charter.instance.quantStep(__mousePos.y/40), 0, Charter.instance.__endStep-1);
|
||||
id = Math.floor(__mousePos.x / 40); y = step * 40; x = id * 40; visible = true; sustainSpr.visible = typeText.visible = false;
|
||||
id = Math.floor(__mousePos.x / 40); y = step * 40; x = id * 40; visible = true; sustainSpr.visible = typeVisible = false;
|
||||
angle = switch(animation.curAnim.curFrame = ((id - Charter.instance.strumLines.getStrumlineFromID(id).startingID) % 4)) {
|
||||
case 0: -90;
|
||||
case 1: 180;
|
||||
@@ -30,9 +30,9 @@ class CharterNoteHoverer extends CharterNote {
|
||||
} else
|
||||
visible = false;
|
||||
case NOTE_DRAG:
|
||||
visible = sustainSpr.visible = typeText.visible = true; __doAnim = false;
|
||||
visible = sustainSpr.visible = typeVisible = true; __doAnim = false;
|
||||
default:
|
||||
visible = sustainSpr.visible = typeText.visible = false; __doAnim = false;
|
||||
visible = sustainSpr.visible = typeVisible = false; __doAnim = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -28,6 +28,8 @@ class UIState extends MusicBeatState {
|
||||
private var __rect:FlxRect;
|
||||
private var __mousePos:FlxPoint;
|
||||
|
||||
static var __point:FlxPoint = new FlxPoint();
|
||||
|
||||
private inline static function get_state()
|
||||
return FlxG.state is UIState ? cast FlxG.state : null;
|
||||
|
||||
@@ -70,17 +72,15 @@ class UIState extends MusicBeatState {
|
||||
|
||||
public function isOverlapping(spr:UISprite, rect:FlxRect) {
|
||||
for(camera in spr.__lastDrawCameras) {
|
||||
var pos = FlxG.mouse.getScreenPosition(camera, FlxPoint.get());
|
||||
var pos = FlxG.mouse.getScreenPosition(camera, __point);
|
||||
__rect.copyFrom(rect);
|
||||
|
||||
__rect.x -= camera.scroll.x * spr.scrollFactor.x;
|
||||
__rect.y -= camera.scroll.y * spr.scrollFactor.y;
|
||||
|
||||
if (((pos.x > __rect.x) && (pos.x < __rect.x + __rect.width)) && ((pos.y > __rect.y) && (pos.y < __rect.y + __rect.height))) {
|
||||
pos.put();
|
||||
return true;
|
||||
}
|
||||
pos.put();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -24,6 +24,11 @@ class HealthIcon extends FlxSprite
|
||||
*/
|
||||
public var healthSteps:Map<Int, Int> = null;
|
||||
|
||||
/**
|
||||
* current animation state
|
||||
*/
|
||||
public var curAnimState:Int = -1;
|
||||
|
||||
/**
|
||||
* Helper for HScript who can't make maps
|
||||
* @param steps Something like this: `[[0, 1], [20, 0]]`
|
||||
@@ -91,7 +96,14 @@ class HealthIcon extends FlxSprite
|
||||
i = icon;
|
||||
}
|
||||
}
|
||||
if (i >= 0) animation.curAnim.curFrame = i;
|
||||
if (i >= 0 && curAnimState != i) {
|
||||
var event = EventManager.get(funkin.backend.scripting.events.HealthIconChangeEvent).recycle(i, this);
|
||||
funkin.backend.scripting.GlobalScript.event("onHealthIconAnimChange", event);
|
||||
if (!event.cancelled)
|
||||
animation.curAnim.curFrame = event.amount;
|
||||
|
||||
curAnimState = event.amount;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1428,6 +1428,8 @@ class PlayState extends MusicBeatState
|
||||
FlxG.sound.music.stop();
|
||||
for (strumLine in strumLines.members) strumLine.vocals.stop();
|
||||
|
||||
deathCounter++;
|
||||
|
||||
openSubState(new GameOverSubstate(event.x, event.y, event.deathCharID, event.isPlayer, event.gameOverSong, event.lossSFX, event.retrySFX));
|
||||
|
||||
scripts.event("onPostGameOver", event);
|
||||
|
||||
@@ -40,7 +40,10 @@ class SplashHandler extends FlxTypedGroup<FunkinSprite> {
|
||||
var __grp:SplashGroup;
|
||||
public function showSplash(name:String, strum:Strum) {
|
||||
__grp = getSplashGroup(name);
|
||||
add(__grp.showOnStrum(strum));
|
||||
var event = funkin.backend.scripting.EventManager.get(funkin.backend.scripting.events.DynamicEvent).recycle(__grp.showOnStrum(strum), strum);
|
||||
event = PlayState.instance.scripts.event("onSplashShown", event);
|
||||
if (!event.cancelled)
|
||||
add(event.value1);
|
||||
|
||||
// max 8 rendered splashes
|
||||
while(members.length > 8)
|
||||
|
||||
@@ -14,6 +14,8 @@ class Strum extends FlxSprite {
|
||||
public var cpu = false; // Unused
|
||||
public var lastHit:Float = -5000;
|
||||
|
||||
public var strumLine = null;
|
||||
|
||||
public var scrollSpeed:Null<Float> = null; // custom scroll speed per strum
|
||||
public var noteAngle:Null<Float> = null;
|
||||
|
||||
|
||||
@@ -148,6 +148,11 @@ class StrumLine extends FlxTypedGroup<Strum> {
|
||||
}
|
||||
}
|
||||
notes.sortNotes();
|
||||
|
||||
var scrollSpeed = strumLine.scrollSpeed;
|
||||
if(scrollSpeed == null) if (PlayState.instance != null) scrollSpeed = PlayState.instance.scrollSpeed;
|
||||
if(scrollSpeed == null) scrollSpeed = 1;
|
||||
notes.limit = 1500 / scrollSpeed;
|
||||
}
|
||||
|
||||
public override function update(elapsed:Float) {
|
||||
@@ -179,10 +184,11 @@ class StrumLine extends FlxTypedGroup<Strum> {
|
||||
if (__updateNote_event.cancelled) return;
|
||||
|
||||
if (__updateNote_event.__updateHitWindow) {
|
||||
daNote.canBeHit = (daNote.strumTime > __updateNote_songPos - (PlayState.instance.hitWindow * daNote.latePressWindow)
|
||||
&& daNote.strumTime < __updateNote_songPos + (PlayState.instance.hitWindow * daNote.earlyPressWindow));
|
||||
var hitWindow = PlayState.instance.hitWindow;
|
||||
daNote.canBeHit = (daNote.strumTime > __updateNote_songPos - (hitWindow * daNote.latePressWindow)
|
||||
&& daNote.strumTime < __updateNote_songPos + (hitWindow * daNote.earlyPressWindow));
|
||||
|
||||
if (daNote.strumTime < __updateNote_songPos - PlayState.instance.hitWindow && !daNote.wasGoodHit)
|
||||
if (daNote.strumTime < __updateNote_songPos - hitWindow && !daNote.wasGoodHit)
|
||||
daNote.tooLate = true;
|
||||
}
|
||||
|
||||
@@ -303,6 +309,7 @@ class StrumLine extends FlxTypedGroup<Strum> {
|
||||
animPrefix = strumAnimPrefix[i % strumAnimPrefix.length];
|
||||
var babyArrow:Strum = new Strum(startingPos.x + ((Note.swagWidth * strumScale * (4 / (data.keyCount != null ? data.keyCount : 4))) * i), startingPos.y);
|
||||
babyArrow.ID = i;
|
||||
babyArrow.strumLine = this;
|
||||
|
||||
if(data.scrollSpeed != null)
|
||||
babyArrow.scrollSpeed = data.scrollSpeed;
|
||||
|
||||
@@ -56,8 +56,13 @@ class VideoCutscene extends Cutscene {
|
||||
parseSubtitles();
|
||||
|
||||
video = new FlxVideo();
|
||||
video.onEndReached.add(video.dispose);
|
||||
video.onEndReached.add(function()
|
||||
{
|
||||
video.dispose();
|
||||
FlxG.removeChild(video);
|
||||
});
|
||||
video.onEndReached.add(close);
|
||||
FlxG.addChildBelowMouse(video);
|
||||
|
||||
//cover = new FlxSprite(0, FlxG.height * 0.85).makeSolid(FlxG.width + 50, FlxG.height + 50, 0xFF000000);
|
||||
//cover.scrollFactor.set(0, 0);
|
||||
|
||||
@@ -196,6 +196,10 @@ class FreeplayState extends MusicBeatState
|
||||
* Path to the currently playing song instrumental.
|
||||
*/
|
||||
public var curPlayingInst:String = null;
|
||||
/**
|
||||
* If it should play the song automatically.
|
||||
*/
|
||||
public var autoplayShouldPlay:Bool = true;
|
||||
#end
|
||||
|
||||
override function update(elapsed:Float)
|
||||
@@ -236,7 +240,17 @@ class FreeplayState extends MusicBeatState
|
||||
autoplayElapsed += elapsed;
|
||||
if (!disableAutoPlay && !songInstPlaying && (autoplayElapsed > timeUntilAutoplay || FlxG.keys.justPressed.SPACE)) {
|
||||
if (curPlayingInst != (curPlayingInst = Paths.inst(songs[curSelected].name, songs[curSelected].difficulties[curDifficulty]))) {
|
||||
var huh:Void->Void = function() FlxG.sound.playMusic(curPlayingInst, 0);
|
||||
var huh:Void->Void = function() {
|
||||
var soundPath = curPlayingInst;
|
||||
var sound = null;
|
||||
if (Assets.exists(soundPath, SOUND) || Assets.exists(soundPath, MUSIC))
|
||||
sound = Assets.getSound(soundPath);
|
||||
else
|
||||
FlxG.log.error('Could not find a Sound asset with an ID of \'$soundPath\'.');
|
||||
|
||||
if (sound != null && autoplayShouldPlay)
|
||||
FlxG.sound.playMusic(sound, 0);
|
||||
}
|
||||
if(!disableAsyncLoading) Main.execAsync(huh);
|
||||
else huh();
|
||||
}
|
||||
@@ -289,6 +303,10 @@ class FreeplayState extends MusicBeatState
|
||||
|
||||
if (event.cancelled) return;
|
||||
|
||||
#if PRELOAD_ALL
|
||||
autoplayShouldPlay = false;
|
||||
#end
|
||||
|
||||
Options.freeplayLastSong = songs[curSelected].name;
|
||||
Options.freeplayLastDifficulty = songs[curSelected].difficulties[curDifficulty];
|
||||
|
||||
|
||||
@@ -19,8 +19,10 @@ class MainMenuState extends MusicBeatState
|
||||
|
||||
var optionShit:Array<String> = CoolUtil.coolTextFile(Paths.txt("config/menuItems"));
|
||||
|
||||
var bg:FlxSprite;
|
||||
var magenta:FlxSprite;
|
||||
var camFollow:FlxObject;
|
||||
var versionText:FunkinText;
|
||||
|
||||
public var canAccessDebugMenus:Bool = true;
|
||||
|
||||
@@ -32,7 +34,7 @@ class MainMenuState extends MusicBeatState
|
||||
|
||||
CoolUtil.playMenuSong();
|
||||
|
||||
var bg:FlxSprite = new FlxSprite(-80).loadAnimatedGraphic(Paths.image('menus/menuBG'));
|
||||
bg = new FlxSprite(-80).loadAnimatedGraphic(Paths.image('menus/menuBG'));
|
||||
add(bg);
|
||||
|
||||
camFollow = new FlxObject(0, 0, 1, 1);
|
||||
@@ -70,15 +72,16 @@ class MainMenuState extends MusicBeatState
|
||||
|
||||
FlxG.camera.follow(camFollow, null, 0.06);
|
||||
|
||||
var versionShit:FunkinText = new FunkinText(5, FlxG.height - 2, 0, 'Codename Engine v${Application.current.meta.get('version')}\nCommit ${funkin.backend.system.macros.GitCommitMacro.commitNumber} (${funkin.backend.system.macros.GitCommitMacro.commitHash})\n[${controls.getKeyName(SWITCHMOD)}] Open Mods menu\n');
|
||||
versionShit.y -= versionShit.height;
|
||||
versionShit.scrollFactor.set();
|
||||
add(versionShit);
|
||||
versionText = new FunkinText(5, FlxG.height - 2, 0, 'Codename Engine v${Application.current.meta.get('version')}\nCommit ${funkin.backend.system.macros.GitCommitMacro.commitNumber} (${funkin.backend.system.macros.GitCommitMacro.commitHash})\n[${controls.getKeyName(SWITCHMOD)}] Open Mods menu\n');
|
||||
versionText.y -= versionText.height;
|
||||
versionText.scrollFactor.set();
|
||||
add(versionText);
|
||||
|
||||
changeItem();
|
||||
}
|
||||
|
||||
var selectedSomethin:Bool = false;
|
||||
var forceCenterX:Bool = true;
|
||||
|
||||
override function update(elapsed:Float)
|
||||
{
|
||||
@@ -127,6 +130,7 @@ class MainMenuState extends MusicBeatState
|
||||
|
||||
super.update(elapsed);
|
||||
|
||||
if (forceCenterX)
|
||||
menuItems.forEach(function(spr:FlxSprite)
|
||||
{
|
||||
spr.screenCenter(X);
|
||||
|
||||
@@ -0,0 +1,347 @@
|
||||
package lime.utils;
|
||||
|
||||
import haxe.ds.ObjectMap;
|
||||
|
||||
|
||||
/**
|
||||
A generic object pool for reusing objects.
|
||||
**/
|
||||
#if !lime_debug
|
||||
@:fileXml('tags="haxe,release"')
|
||||
@:noDebug
|
||||
#end
|
||||
#if !js @:generic #end class ObjectPool<T>
|
||||
{
|
||||
/**
|
||||
The number of active objects in the pool.
|
||||
**/
|
||||
public var activeObjects(default, null):Int;
|
||||
|
||||
/**
|
||||
The number of inactive objects in the pool.
|
||||
**/
|
||||
public var inactiveObjects(default, null):Int;
|
||||
|
||||
/**
|
||||
The total size of the object pool (both active and inactive objects).
|
||||
**/
|
||||
public var size(get, set):Null<Int>;
|
||||
|
||||
@:noCompletion private var __inactiveObject0:T;
|
||||
@:noCompletion private var __inactiveObject1:T;
|
||||
@:noCompletion private var __inactiveObjectList:List<T>;
|
||||
@:noCompletion private var __pool:Map<T, Bool>;
|
||||
@:noCompletion private var __size:Null<Int>;
|
||||
|
||||
/**
|
||||
Creates a new ObjectPool instance.
|
||||
|
||||
@param create A function that creates a new instance of type T.
|
||||
@param clean A function that cleans up an instance of type T before it is reused.
|
||||
@param size The maximum size of the object pool.
|
||||
**/
|
||||
public function new(create:Void->T = null, clean:T->Void = null, size:Null<Int> = null)
|
||||
{
|
||||
__pool = cast new ObjectMap();
|
||||
|
||||
activeObjects = 0;
|
||||
inactiveObjects = 0;
|
||||
|
||||
__inactiveObject0 = null;
|
||||
__inactiveObject1 = null;
|
||||
__inactiveObjectList = new List<T>();
|
||||
|
||||
if (create != null)
|
||||
{
|
||||
this.create = create;
|
||||
}
|
||||
if (clean != null)
|
||||
{
|
||||
this.clean = clean;
|
||||
}
|
||||
if (size != null)
|
||||
{
|
||||
this.size = size;
|
||||
}
|
||||
}
|
||||
/**
|
||||
Adds an object to the object pool.
|
||||
|
||||
@param object The object to add to the pool.
|
||||
**/
|
||||
public function add(object:T):Void
|
||||
{
|
||||
if (object != null && !__pool.exists(object))
|
||||
{
|
||||
__pool.set(object, false);
|
||||
clean(object);
|
||||
__addInactive(object);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
Dynamic function.
|
||||
|
||||
Cleans up an object before returning it to the pool.
|
||||
|
||||
@param object The object to clean up.
|
||||
**/
|
||||
public dynamic function clean(object:T):Void {}
|
||||
|
||||
/**
|
||||
Clears the object pool, removing all objects.
|
||||
**/
|
||||
public function clear():Void
|
||||
{
|
||||
__pool = cast new ObjectMap();
|
||||
|
||||
activeObjects = 0;
|
||||
inactiveObjects = 0;
|
||||
|
||||
__inactiveObject0 = null;
|
||||
__inactiveObject1 = null;
|
||||
__inactiveObjectList.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
Dynamic function.
|
||||
|
||||
Creates a new Object.
|
||||
**/
|
||||
public dynamic function create():T
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
Creates a new object and adds it to the pool, or returns an existing inactive object from the pool.
|
||||
|
||||
@return The object retrieved from the pool, or null if the pool is full and no new objects can be created.
|
||||
**/
|
||||
public function get():T
|
||||
{
|
||||
var object = null;
|
||||
|
||||
if (inactiveObjects > 0)
|
||||
{
|
||||
object = __getInactive();
|
||||
}
|
||||
else if (__size == null || activeObjects < __size)
|
||||
{
|
||||
object = create();
|
||||
|
||||
if (object != null)
|
||||
{
|
||||
__pool.set(object, true);
|
||||
activeObjects++;
|
||||
}
|
||||
}
|
||||
|
||||
return object;
|
||||
}
|
||||
|
||||
/**
|
||||
Releases an active object back into the pool.
|
||||
|
||||
@param object The object to release.
|
||||
**/
|
||||
public function release(object:T):Void
|
||||
{
|
||||
#if lime_pool_debug
|
||||
if (object == null || !__pool.exists(object))
|
||||
{
|
||||
Log.error("Object is not a member of the pool");
|
||||
}
|
||||
else if (!__pool.get(object))
|
||||
{
|
||||
Log.error("Object has already been released");
|
||||
}
|
||||
#end
|
||||
|
||||
activeObjects--;
|
||||
|
||||
if (__size == null || activeObjects + inactiveObjects < __size)
|
||||
{
|
||||
clean(object);
|
||||
__addInactive(object);
|
||||
}
|
||||
else
|
||||
{
|
||||
__pool.remove(object);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
Removes an object from the pool.
|
||||
|
||||
@param object The object to remove from the pool.
|
||||
**/
|
||||
public function remove(object:T):Void
|
||||
{
|
||||
if (object != null && __pool.exists(object))
|
||||
{
|
||||
__pool.remove(object);
|
||||
|
||||
if (__inactiveObject0 == object)
|
||||
{
|
||||
__inactiveObject0 = null;
|
||||
inactiveObjects--;
|
||||
}
|
||||
else if (__inactiveObject1 == object)
|
||||
{
|
||||
__inactiveObject1 = null;
|
||||
inactiveObjects--;
|
||||
}
|
||||
else if (__inactiveObjectList.remove(object))
|
||||
{
|
||||
inactiveObjects--;
|
||||
}
|
||||
else
|
||||
{
|
||||
activeObjects--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@:noCompletion private inline function __addInactive(object:T):Void
|
||||
{
|
||||
#if lime_pool_debug
|
||||
__pool.set(object, false);
|
||||
#end
|
||||
|
||||
if (__inactiveObject0 == null)
|
||||
{
|
||||
__inactiveObject0 = object;
|
||||
}
|
||||
else if (__inactiveObject1 == null)
|
||||
{
|
||||
__inactiveObject1 = object;
|
||||
}
|
||||
else
|
||||
{
|
||||
__inactiveObjectList.add(object);
|
||||
}
|
||||
|
||||
inactiveObjects++;
|
||||
}
|
||||
|
||||
@:noCompletion private inline function __getInactive():T
|
||||
{
|
||||
var object = null;
|
||||
|
||||
if (__inactiveObject0 != null)
|
||||
{
|
||||
object = __inactiveObject0;
|
||||
__inactiveObject0 = null;
|
||||
}
|
||||
else if (__inactiveObject1 != null)
|
||||
{
|
||||
object = __inactiveObject1;
|
||||
__inactiveObject1 = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
object = __inactiveObjectList.pop();
|
||||
|
||||
if (__inactiveObjectList.length > 0)
|
||||
{
|
||||
__inactiveObject0 = __inactiveObjectList.pop();
|
||||
}
|
||||
|
||||
if (__inactiveObjectList.length > 0)
|
||||
{
|
||||
__inactiveObject1 = __inactiveObjectList.pop();
|
||||
}
|
||||
}
|
||||
|
||||
#if lime_pool_debug
|
||||
__pool.set(object, true);
|
||||
#end
|
||||
|
||||
inactiveObjects--;
|
||||
activeObjects++;
|
||||
|
||||
return object;
|
||||
}
|
||||
|
||||
@:noCompletion private function __removeInactive(count:Int):Void
|
||||
{
|
||||
if (count <= 0 || inactiveObjects == 0) return;
|
||||
|
||||
if (__inactiveObject0 != null)
|
||||
{
|
||||
__pool.remove(__inactiveObject0);
|
||||
__inactiveObject0 = null;
|
||||
inactiveObjects--;
|
||||
count--;
|
||||
}
|
||||
|
||||
if (count == 0 || inactiveObjects == 0) return;
|
||||
|
||||
if (__inactiveObject1 != null)
|
||||
{
|
||||
__pool.remove(__inactiveObject1);
|
||||
__inactiveObject1 = null;
|
||||
inactiveObjects--;
|
||||
count--;
|
||||
}
|
||||
|
||||
if (count == 0 || inactiveObjects == 0) return;
|
||||
|
||||
for (object in __inactiveObjectList)
|
||||
{
|
||||
__pool.remove(object);
|
||||
__inactiveObjectList.remove(object);
|
||||
inactiveObjects--;
|
||||
count--;
|
||||
|
||||
if (count == 0 || inactiveObjects == 0) return;
|
||||
}
|
||||
}
|
||||
|
||||
// Get & Set Methods
|
||||
@:noCompletion private function get_size():Null<Int>
|
||||
{
|
||||
return __size;
|
||||
}
|
||||
|
||||
@:noCompletion private function set_size(value:Null<Int>):Null<Int>
|
||||
{
|
||||
if (value == null)
|
||||
{
|
||||
__size = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
var current = inactiveObjects + activeObjects;
|
||||
__size = value;
|
||||
|
||||
if (current > value)
|
||||
{
|
||||
__removeInactive(current - value);
|
||||
}
|
||||
else if (value > current)
|
||||
{
|
||||
var object;
|
||||
|
||||
for (i in 0...(value - current))
|
||||
{
|
||||
object = create();
|
||||
|
||||
if (object != null)
|
||||
{
|
||||
__pool.set(object, false);
|
||||
__inactiveObjectList.add(object);
|
||||
inactiveObjects++;
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user