Merge branch 'main' into translations-system
This commit is contained in:
@@ -7,7 +7,7 @@ jobs:
|
||||
build:
|
||||
name: Mac OS Build
|
||||
permissions: write-all
|
||||
runs-on: macos-latest
|
||||
runs-on: macos-13
|
||||
steps:
|
||||
- name: Pulling the new commit
|
||||
uses: actions/checkout@v2
|
||||
|
||||
@@ -11,4 +11,8 @@
|
||||
<anim name="singDOWNmiss" anim="BF NOTE DOWN MISS" fps="24" loop="false" x="-11" y="-19"/>
|
||||
<anim name="hey" anim="BF HEY" fps="24" loop="false" x="7" y="4"/>
|
||||
<anim name="scared" anim="BF idle shaking" fps="24" loop="true" x="-4" y="0"/>
|
||||
</character>
|
||||
<anim name="hit" anim="BF hit" fps="24" loop="false" x="13" y="19"/>
|
||||
<anim name="pre-attack" anim="bf pre attack" fps="24" loop="false" x="-47" y="-36"/>
|
||||
<anim name="attack" anim="boyfriend attack" fps="24" loop="false" x="289" y="270"/>
|
||||
<anim name="dodge" anim="boyfriend dodge" fps="24" loop="false" x="-7" y="-15"/>
|
||||
</character>
|
||||
|
||||
@@ -1 +1,2 @@
|
||||
function onNoteHit(event) if (event.noteType == "Alt Anim Note") event.animSuffix = "-alt";
|
||||
function onNoteHit(event) if (event.noteType == "Alt Anim Note") event.animSuffix = "-alt";
|
||||
function onPlayerMiss(event) if (event.noteType == "Alt Anim Note") event.animSuffix = "-alt";
|
||||
@@ -1 +1,2 @@
|
||||
function onNoteHit(event) if (event.noteType == "No Anim Note") event.animCancelled = true;
|
||||
function onNoteHit(event) if (event.noteType == "No Anim Note") event.animCancelled = true;
|
||||
function onPlayerMiss(event) if (event.noteType == "No Anim Note") event.animCancelled = true;
|
||||
+11
-11
@@ -1,21 +1,21 @@
|
||||
<!DOCTYPE codename-engine-stage>
|
||||
<stage zoom="0.8" name="mall" folder="stages/mall/" startCamPosY="450" startCamPosX="500">
|
||||
<sprite name="bg" x="-1000" y="-500" sprite="bgWalls" scroll="0.2"/>
|
||||
<sprite name="bg" x="-1380" y="-620" sprite="bgWalls" scale="0.8" scroll="0.2"/>
|
||||
|
||||
<high-memory>
|
||||
<sprite name="upperBoppers" x="-240" y="-90" sprite="upperBop" scroll="0.2" type="onbeat" beatInterval="1"/>
|
||||
<sprite name="upperBoppers" x="-400" y="-60" sprite="upperBop" scale="0.85" scroll="0.33" type="onbeat" beatInterval="1"/>
|
||||
</high-memory>
|
||||
<sprite name="bgEscalator" x="-1100" y="-600" sprite="bgEscalator" scroll="0.3" scale="0.9" updateHitbox="true"/>
|
||||
<sprite name="bgEscalator" x="-1090" y="-540" sprite="bgEscalator" scroll="0.3" scale="0.9" updateHitbox="true"/>
|
||||
<sprite name="tree" x="370" y="-250" sprite="christmasTree" scroll="0.4"/>
|
||||
<sprite name="bottomBoppers" x="-300" y="140" sprite="bottomBop" scroll="0.9" type="onbeat" beatInterval="1"/>
|
||||
<sprite name="fgSnow" x="-600" y="700" sprite="fgSnow"/>
|
||||
<sprite name="bottomBoppers" x="-297" y="120" sprite="bottomBop" scroll="0.9" type="onbeat" beatInterval="1"/>
|
||||
<sprite name="fgSnow" x="-600" y="680" sprite="fgSnow"/>
|
||||
|
||||
|
||||
<girlfriend x="540" y="110"/>
|
||||
<dad/>
|
||||
<boyfriend x="1030" camyoffset="-150" camxoffset="-150"/>
|
||||
|
||||
<high-memory>
|
||||
<sprite name="santa" x="-840" y="150" sprite="santa" type="onbeat" beatInterval="1"/>
|
||||
</high-memory>
|
||||
|
||||
|
||||
<girlfriend />
|
||||
<dad />
|
||||
<boyfriend x="870" camyoffset="-200"/>
|
||||
</stage>
|
||||
</stage>
|
||||
|
||||
@@ -89,7 +89,7 @@ class ModsFolderLibrary extends AssetLibrary implements IModsAssetLibrary {
|
||||
return __getFiles(folder, false);
|
||||
|
||||
public function __getFiles(folder:String, folders:Bool = false) {
|
||||
if (!folder.endsWith("/")) folder = folder + "/";
|
||||
if (!folder.endsWith("/")) folder += "/";
|
||||
if (!__parseAsset(folder)) return [];
|
||||
var path = getAssetPath();
|
||||
try {
|
||||
|
||||
@@ -173,7 +173,7 @@ class Paths
|
||||
public static function getFrames(key:String, assetsPath:Bool = false, ?library:String) {
|
||||
if (tempFramesCache.exists(key)) {
|
||||
var frames = tempFramesCache[key];
|
||||
if (frames.parent != null && frames.parent.bitmap != null && frames.parent.bitmap.readable)
|
||||
if (frames != null && frames.parent != null && frames.parent.bitmap != null && frames.parent.bitmap.readable)
|
||||
return frames;
|
||||
else
|
||||
tempFramesCache.remove(key);
|
||||
@@ -271,4 +271,4 @@ class ScriptPathInfo {
|
||||
this.file = file;
|
||||
this.library = library;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,6 +20,8 @@ class ZipFolderLibrary extends AssetLibrary implements IModsAssetLibrary {
|
||||
|
||||
public var zip:SysZip;
|
||||
public var assets:Map<String, SysZipEntry> = [];
|
||||
public var lowerCaseAssets:Map<String, SysZipEntry> = [];
|
||||
public var nameMap:Map<String, String> = [];
|
||||
|
||||
public function new(basePath:String, libName:String, ?modName:String) {
|
||||
this.libName = libName;
|
||||
@@ -33,8 +35,10 @@ class ZipFolderLibrary extends AssetLibrary implements IModsAssetLibrary {
|
||||
|
||||
zip = SysZip.openFromFile(basePath);
|
||||
zip.read();
|
||||
for(entry in zip.entries)
|
||||
assets[entry.fileName.toLowerCase()] = entry;
|
||||
for(entry in zip.entries) {
|
||||
lowerCaseAssets[entry.fileName.toLowerCase()] = assets[entry.fileName.toLowerCase()] = assets[entry.fileName] = entry;
|
||||
nameMap.set(entry.fileName.toLowerCase(), entry.fileName);
|
||||
}
|
||||
|
||||
super();
|
||||
}
|
||||
@@ -86,6 +90,8 @@ class ZipFolderLibrary extends AssetLibrary implements IModsAssetLibrary {
|
||||
}
|
||||
|
||||
_parsedAsset = _parsedAsset.toLowerCase();
|
||||
if(nameMap.exists(_parsedAsset))
|
||||
_parsedAsset = nameMap.get(_parsedAsset);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -105,35 +111,45 @@ class ZipFolderLibrary extends AssetLibrary implements IModsAssetLibrary {
|
||||
return '[ZIP]$basePath/$_parsedAsset';
|
||||
}
|
||||
|
||||
// TODO: rewrite this to 1 function, like ModsFolderLibrary
|
||||
public function getFiles(folder:String):Array<String> {
|
||||
var content:Array<String> = [];
|
||||
|
||||
if (!folder.endsWith("/")) folder = folder + "/";
|
||||
if (!folder.endsWith("/")) folder += "/";
|
||||
if (!__parseAsset(folder)) return [];
|
||||
|
||||
var content:Array<String> = [];
|
||||
|
||||
var checkPath = _parsedAsset.toLowerCase();
|
||||
|
||||
@:privateAccess
|
||||
for(k=>e in assets) {
|
||||
if (k.toLowerCase().startsWith(_parsedAsset)) {
|
||||
for(k=>e in lowerCaseAssets) {
|
||||
if (k.toLowerCase().startsWith(checkPath)) {
|
||||
if(nameMap.exists(k))
|
||||
k = nameMap.get(k);
|
||||
var fileName = k.substr(_parsedAsset.length);
|
||||
if (!fileName.contains("/"))
|
||||
content.push(fileName);
|
||||
if (!fileName.contains("/") && fileName.length > 0)
|
||||
content.pushOnce(fileName);
|
||||
}
|
||||
}
|
||||
return content;
|
||||
}
|
||||
|
||||
public function getFolders(folder:String):Array<String> {
|
||||
var content:Array<String> = [];
|
||||
|
||||
if (!folder.endsWith("/")) folder = folder + "/";
|
||||
if (!folder.endsWith("/")) folder += "/";
|
||||
if (!__parseAsset(folder)) return [];
|
||||
|
||||
var content:Array<String> = [];
|
||||
|
||||
var checkPath = _parsedAsset.toLowerCase();
|
||||
|
||||
@:privateAccess
|
||||
for(k=>e in assets) {
|
||||
if (k.toLowerCase().startsWith(_parsedAsset)) {
|
||||
for(k=>e in lowerCaseAssets) {
|
||||
if (k.toLowerCase().startsWith(checkPath)) {
|
||||
if(nameMap.exists(k))
|
||||
k = nameMap.get(k);
|
||||
var fileName = k.substr(_parsedAsset.length);
|
||||
if (fileName.contains("/")) {
|
||||
var s = fileName.split("/")[0];
|
||||
var index = fileName.indexOf("/");
|
||||
if (index != -1 && fileName.length > 0) {
|
||||
var s = fileName.substr(0, index);
|
||||
content.pushOnce(s);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -56,7 +56,6 @@ class Chart {
|
||||
data.setFieldDefault("name", songName);
|
||||
data.setFieldDefault("beatsPerMeasure", 4);
|
||||
data.setFieldDefault("stepsPerBeat", 4);
|
||||
data.setFieldDefault("needsVoices", true);
|
||||
data.setFieldDefault("icon", "face");
|
||||
data.setFieldDefault("difficulties", []);
|
||||
data.setFieldDefault("coopAllowed", false);
|
||||
|
||||
@@ -20,7 +20,6 @@ typedef ChartMetaData = {
|
||||
public var ?displayName:String;
|
||||
public var ?beatsPerMeasure:Float;
|
||||
public var ?stepsPerBeat:Float;
|
||||
public var ?needsVoices:Bool;
|
||||
public var ?icon:String;
|
||||
public var ?color:Dynamic;
|
||||
public var ?difficulties:Array<String>;
|
||||
|
||||
@@ -48,7 +48,7 @@ class EventsData {
|
||||
}
|
||||
],
|
||||
"Alt Animation Toggle" => [{name: "Enable On Sing Poses", type: TBool, defValue: true}, {name: "Enable On Idle", type: TBool, defValue: true}, {name: "Strumline", type: TStrumLine, defValue: 0}],
|
||||
"Play Animation" => [{name: "Character", type: TStrumLine, defValue: 0}, {name: "Animation", type: TString, defValue: "animation"}],
|
||||
"Play Animation" => [{name: "Character", type: TStrumLine, defValue: 0}, {name: "Animation", type: TString, defValue: "animation"}, {name: "Is forced?", type: TBool, defValue: true}],
|
||||
];
|
||||
|
||||
public static var eventsList:Array<String> = defaultEventsList.copy();
|
||||
|
||||
@@ -41,7 +41,6 @@ class FNFLegacyParser {
|
||||
}
|
||||
|
||||
result.meta.bpm = data.bpm;
|
||||
result.meta.needsVoices = data.needsVoices.getDefault(true);
|
||||
|
||||
var camFocusedBF:Bool = false;
|
||||
var altAnims:Bool = false;
|
||||
@@ -132,7 +131,7 @@ class FNFLegacyParser {
|
||||
if ((swagSection.mustHitSection && strumLine.type == OPPONENT) ||
|
||||
(!swagSection.mustHitSection && strumLine.type == PLAYER))
|
||||
sectionNote[1] += 4;
|
||||
swagSection.sectionNotes.push(note);
|
||||
swagSection.sectionNotes.push(sectionNote);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -145,7 +144,6 @@ class FNFLegacyParser {
|
||||
song: chart.meta.name,
|
||||
notes: null,
|
||||
bpm: chart.meta.bpm,
|
||||
needsVoices: chart.meta.needsVoices,
|
||||
speed: chart.scrollSpeed,
|
||||
|
||||
player1: null,
|
||||
@@ -206,7 +204,6 @@ typedef SwagSong =
|
||||
var song:String;
|
||||
var notes:Array<SwagSection>;
|
||||
var bpm:Float;
|
||||
var needsVoices:Bool;
|
||||
var speed:Float;
|
||||
var ?stage:String;
|
||||
var ?noteTypes:Array<String>;
|
||||
|
||||
@@ -64,7 +64,13 @@ class HScript extends Script {
|
||||
}
|
||||
|
||||
private function importFailedCallback(cl:Array<String>):Bool {
|
||||
var assetsPath = 'assets/source/${cl.join("/")}';
|
||||
if(_importFailedCallback(cl, "") || _importFailedCallback(cl, "source/")) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
private function _importFailedCallback(cl:Array<String>, prefix:String):Bool {
|
||||
var assetsPath = 'assets/$prefix${cl.join("/")}';
|
||||
for(hxExt in ["hx", "hscript", "hsc", "hxs"]) {
|
||||
var p = '$assetsPath.$hxExt';
|
||||
if (__importedPaths.contains(p))
|
||||
|
||||
@@ -428,13 +428,18 @@ class Controls extends FlxActionSet
|
||||
return byName[name].check();
|
||||
}
|
||||
|
||||
public function getKeyName(control:Control):String
|
||||
{
|
||||
return getDialogueName(getActionFromControl(control));
|
||||
}
|
||||
|
||||
public function getDialogueName(action:FlxActionDigital):String
|
||||
{
|
||||
var input = action.inputs[0];
|
||||
return switch input.device
|
||||
{
|
||||
case KEYBOARD: return '[${(input.inputID : FlxKey)}]';
|
||||
case GAMEPAD: return '(${(input.inputID : FlxGamepadInputID)})';
|
||||
case KEYBOARD: return '${(input.inputID : FlxKey)}';
|
||||
case GAMEPAD: return '${(input.inputID : FlxGamepadInputID)}';
|
||||
case device: throw 'unhandled device: $device';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -68,11 +68,6 @@ class MainState extends FlxState {
|
||||
betaWarningShown = true;
|
||||
}
|
||||
|
||||
#if sys
|
||||
sys.FileSystem.createDirectory('./.temp/');
|
||||
#if windows
|
||||
try new funkin.backend.utils.native.HiddenProcess("attrib +h .temp")
|
||||
catch(e) Logs.warn('Failed to set hidden attribute on .temp folder');
|
||||
#end #end
|
||||
CoolUtil.safeSetAttribute('./.temp/', HIDDEN);
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,8 @@
|
||||
package funkin.backend.utils;
|
||||
|
||||
#if sys
|
||||
import sys.FileSystem;
|
||||
#end
|
||||
import flixel.animation.FlxAnimation;
|
||||
import flixel.input.keyboard.FlxKey;
|
||||
import flixel.sound.FlxSound;
|
||||
@@ -62,18 +65,15 @@ class CoolUtil
|
||||
*/
|
||||
@:noUsing public static function deleteFolder(delete:String) {
|
||||
#if sys
|
||||
if (!sys.FileSystem.exists(delete)) return;
|
||||
var files:Array<String> = sys.FileSystem.readDirectory(delete);
|
||||
if (!FileSystem.exists(delete)) return;
|
||||
var files:Array<String> = FileSystem.readDirectory(delete);
|
||||
for(file in files) {
|
||||
if (sys.FileSystem.isDirectory(delete + "/" + file)) {
|
||||
if (FileSystem.isDirectory(delete + "/" + file)) {
|
||||
deleteFolder(delete + "/" + file);
|
||||
sys.FileSystem.deleteDirectory(delete + "/" + file);
|
||||
FileSystem.deleteDirectory(delete + "/" + file);
|
||||
} else {
|
||||
try {
|
||||
sys.FileSystem.deleteFile(delete + "/" + file);
|
||||
} catch(e) {
|
||||
Logs.warn("Could not delete " + delete + "/" + file);
|
||||
}
|
||||
try FileSystem.deleteFile(delete + "/" + file)
|
||||
catch(e) Logs.warn("Could not delete " + delete + "/" + file);
|
||||
}
|
||||
}
|
||||
#end
|
||||
@@ -98,6 +98,22 @@ class CoolUtil
|
||||
#end
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets an attribute to a file or a folder adding eventual missing folders in the path
|
||||
* (WARNING: Only works on `windows`. On other platforms the return code it's always going to be `0` but still creates eventual missing folders if the platforms allows it to).
|
||||
* @param path Path to the file or folder
|
||||
* @param attrib The attribute to set (WARNING: There are some non settable attributes, such as the `COMPRESSED` one)
|
||||
* @param useAbsol If it should use the absolute path (By default it's `true` but if it's `false` you can use files outside from this program's directory for example)
|
||||
* @return The result code: 0 means that it failed setting
|
||||
*/
|
||||
@:noUsing public static inline function safeSetAttribute(path:String, attrib:NativeAPI.FileAttribute, useAbsol:Bool = true) {
|
||||
addMissingFolders(Path.directory(path));
|
||||
|
||||
var result = NativeAPI.setFileAttribute(path, attrib, useAbsol);
|
||||
if(result == 0) Logs.trace('Failed to set attribute to $path with a code of: $result', WARNING);
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates eventual missing folders to the specified `path`
|
||||
*
|
||||
@@ -113,9 +129,8 @@ class CoolUtil
|
||||
|
||||
for (folder in folders) {
|
||||
currentPath += folder + "/";
|
||||
if (!sys.FileSystem.exists(currentPath)) {
|
||||
sys.FileSystem.createDirectory(currentPath);
|
||||
}
|
||||
if (!FileSystem.exists(currentPath))
|
||||
FileSystem.createDirectory(currentPath);
|
||||
}
|
||||
#end
|
||||
return path;
|
||||
|
||||
@@ -41,7 +41,9 @@ class FunkinParentDisabler extends FlxBasic {
|
||||
}
|
||||
}
|
||||
|
||||
public override function update(elapsed:Float) {}
|
||||
public override function update(elapsed:Float) {
|
||||
@:privateAccess for(c in __cameras) c.updateFlashSpritePosition();
|
||||
}
|
||||
public override function draw() {}
|
||||
|
||||
public function reset() {
|
||||
|
||||
@@ -29,6 +29,30 @@ class NativeAPI {
|
||||
#end
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the specified file's (or folder) attribute.
|
||||
*/
|
||||
public static function getFileAttribute(path:String, useAbsol:Bool = true):FileAttribute {
|
||||
#if windows
|
||||
if(useAbsol) path = sys.FileSystem.absolutePath(path);
|
||||
return Windows.getFileAttribute(path);
|
||||
#else
|
||||
return NORMAL;
|
||||
#end
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the specified file's (or folder) attribute. If it fails, the return value is `0`.
|
||||
*/
|
||||
public static function setFileAttribute(path:String, attrib:FileAttribute, useAbsol:Bool = true):Int {
|
||||
#if windows
|
||||
if(useAbsol) path = sys.FileSystem.absolutePath(path);
|
||||
return Windows.setFileAttribute(path, attrib);
|
||||
#else
|
||||
return 0;
|
||||
#end
|
||||
}
|
||||
|
||||
public static function setDarkMode(title:String, enable:Bool) {
|
||||
#if windows
|
||||
Windows.setDarkMode(title, enable);
|
||||
@@ -61,15 +85,13 @@ class NativeAPI {
|
||||
var fg = cast(foregroundColor, Int);
|
||||
var bg = cast(backgroundColor, Int);
|
||||
Windows.setConsoleColors((bg * 16) + fg);
|
||||
#else
|
||||
#if sys
|
||||
#elseif sys
|
||||
Sys.print("\x1b[0m");
|
||||
if(foregroundColor != NONE)
|
||||
Sys.print("\x1b[" + Std.int(consoleColorToANSI(foregroundColor)) + "m");
|
||||
if(backgroundColor != NONE)
|
||||
Sys.print("\x1b[" + Std.int(consoleColorToANSI(backgroundColor) + 10) + "m");
|
||||
#end
|
||||
#end
|
||||
}
|
||||
|
||||
public static function consoleColorToANSI(color:ConsoleColor) {
|
||||
@@ -115,6 +137,26 @@ class NativeAPI {
|
||||
}
|
||||
}
|
||||
|
||||
enum abstract FileAttribute(Int) {
|
||||
// Settables
|
||||
var ARCHIVE = 0x20;
|
||||
var HIDDEN = 0x2;
|
||||
var NORMAL = 0x80;
|
||||
var NOT_CONTENT_INDEXED = 0x2000;
|
||||
var OFFLINE = 0x1000;
|
||||
var READONLY = 0x1;
|
||||
var SYSTEM = 0x4;
|
||||
var TEMPORARY = 0x100;
|
||||
|
||||
// Non Settables
|
||||
var COMPRESSED = 0x800;
|
||||
var DEVICE = 0x40;
|
||||
var DIRECTORY = 0x10;
|
||||
var ENCRYPTED = 0x4000;
|
||||
var REPARSE_POINT = 0x400;
|
||||
var SPARSE_FILE = 0x200;
|
||||
}
|
||||
|
||||
enum abstract ConsoleColor(Int) {
|
||||
var BLACK = 0;
|
||||
var DARKBLUE = 1;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package funkin.backend.utils.native;
|
||||
|
||||
#if windows
|
||||
import funkin.backend.utils.NativeAPI.FileAttribute;
|
||||
import funkin.backend.utils.NativeAPI.MessageBoxIcon;
|
||||
@:buildXml('
|
||||
<target id="haxe">
|
||||
@@ -153,6 +154,22 @@ class Windows {
|
||||
public static function allocConsole() {
|
||||
}
|
||||
|
||||
@:functionCode('
|
||||
return GetFileAttributes(path);
|
||||
')
|
||||
public static function getFileAttribute(path:String):FileAttribute
|
||||
{
|
||||
return NORMAL;
|
||||
}
|
||||
|
||||
@:functionCode('
|
||||
return SetFileAttributes(path, attrib);
|
||||
')
|
||||
public static function setFileAttribute(path:String, attrib:FileAttribute):Int
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@:functionCode('
|
||||
HANDLE console = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||
|
||||
@@ -526,7 +526,7 @@ class Charter extends UIState {
|
||||
noteTypes = PlayState.SONG.noteTypes;
|
||||
|
||||
FlxG.sound.setMusic(FlxG.sound.load(Paths.inst(__song, __diff)));
|
||||
if (PlayState.SONG.meta.needsVoices != false) // null or true
|
||||
if (Assets.exists(Paths.voices(__song, __diff))) // null or true
|
||||
vocals = FlxG.sound.load(Paths.voices(__song, __diff));
|
||||
else
|
||||
vocals = new FlxSound();
|
||||
@@ -602,7 +602,7 @@ class Charter extends UIState {
|
||||
var wavesToGenerate:Array<{name:String, sound:FlxSound}> = [
|
||||
{name: "Inst.ogg", sound: FlxG.sound.music},
|
||||
];
|
||||
if (PlayState.SONG.meta.needsVoices != false)
|
||||
if (@:privateAccess vocals._sound != null) //for some reason Assets.exists returns true regardless if the file actually exists
|
||||
wavesToGenerate.push({name: "Voices.ogg", sound: vocals});
|
||||
|
||||
for (strumLine in strumLines)
|
||||
|
||||
@@ -15,7 +15,6 @@ class CharterMetaDataScreen extends UISubstateWindow {
|
||||
public var bpmStepper:UINumericStepper;
|
||||
public var beatsPerMeasureStepper:UINumericStepper;
|
||||
public var stepsPerBeatStepper :UINumericStepper;
|
||||
public var needsVoicesCheckbox:UICheckbox;
|
||||
public var customPropertiesButtonList:UIButtonList<PropertyButton>;
|
||||
|
||||
public var displayNameTextBox:UITextBox;
|
||||
@@ -64,11 +63,6 @@ class CharterMetaDataScreen extends UISubstateWindow {
|
||||
stepsPerBeatStepper = new UINumericStepper(beatsPerMeasureStepper.x + 30 + 24, beatsPerMeasureStepper.y, metadata.stepsPerBeat, 1, 0, 1, null, 54);
|
||||
add(stepsPerBeatStepper);
|
||||
|
||||
needsVoicesCheckbox = new UICheckbox(stepsPerBeatStepper.x + 80 + 26, stepsPerBeatStepper.y, "Voices", metadata.needsVoices);
|
||||
add(needsVoicesCheckbox);
|
||||
addLabelOn(needsVoicesCheckbox, "Needs Voices");
|
||||
needsVoicesCheckbox.y += 6; needsVoicesCheckbox.x += 4;
|
||||
|
||||
add(title = new UIText(songNameTextBox.x, songNameTextBox.y + 10 + 46, 0, "Menus Data (Freeplay/Story)", 28));
|
||||
|
||||
displayNameTextBox = new UITextBox(title.x, title.y + title.height + 36, metadata.displayName);
|
||||
@@ -97,7 +91,7 @@ class CharterMetaDataScreen extends UISubstateWindow {
|
||||
add(difficulitesTextBox);
|
||||
addLabelOn(difficulitesTextBox, "Difficulties");
|
||||
|
||||
customPropertiesButtonList = new UIButtonList<PropertyButton>(needsVoicesCheckbox.x + needsVoicesCheckbox.width + 105, songNameTextBox.y, 290, 316, '', FlxPoint.get(280, 35), null, 5);
|
||||
customPropertiesButtonList = new UIButtonList<PropertyButton>(stepsPerBeatStepper.x + 80 + 26 + 105, songNameTextBox.y, 290, 316, '', FlxPoint.get(280, 35), null, 5);
|
||||
customPropertiesButtonList.frames = Paths.getFrames('editors/ui/inputbox');
|
||||
customPropertiesButtonList.cameraSpacing = 0;
|
||||
customPropertiesButtonList.addButton.callback = function() {
|
||||
@@ -161,7 +155,6 @@ class CharterMetaDataScreen extends UISubstateWindow {
|
||||
bpm: bpmStepper.value,
|
||||
beatsPerMeasure: Std.int(beatsPerMeasureStepper.value),
|
||||
stepsPerBeat: Std.int(stepsPerBeatStepper.value),
|
||||
needsVoices: needsVoicesCheckbox.checked,
|
||||
displayName: displayNameTextBox.label.text,
|
||||
icon: iconTextBox.label.text,
|
||||
color: colorWheel.curColorString,
|
||||
|
||||
@@ -19,7 +19,6 @@ class SongCreationScreen extends UISubstateWindow {
|
||||
public var bpmStepper:UINumericStepper;
|
||||
public var beatsPerMeasureStepper:UINumericStepper;
|
||||
public var stepsPerBeatStepper :UINumericStepper;
|
||||
public var needsVoicesCheckbox:UICheckbox;
|
||||
public var instExplorer:UIFileExplorer;
|
||||
public var voicesExplorer:UIFileExplorer;
|
||||
|
||||
@@ -83,25 +82,6 @@ class SongCreationScreen extends UISubstateWindow {
|
||||
|
||||
var voicesUIText:UIText = null;
|
||||
|
||||
needsVoicesCheckbox = new UICheckbox(stepsPerBeatStepper.x + 80 + 26, stepsPerBeatStepper.y, "Voices", true);
|
||||
needsVoicesCheckbox.onChecked = function(checked) {
|
||||
if (voicesExplorer == null) return;
|
||||
|
||||
if(!checked) {
|
||||
voicesExplorer.removeFile();
|
||||
voicesExplorer.selectable = voicesExplorer.uploadButton.selectable = false;
|
||||
voicesUIText.text = "Vocal Audio File";
|
||||
} else {
|
||||
voicesExplorer.selectable = voicesExplorer.uploadButton.selectable = true;
|
||||
voicesUIText.applyMarkup(
|
||||
"Vocal Audio File $* Required$",
|
||||
[new FlxTextFormatMarkerPair(new FlxTextFormat(0xFFAD1212), "$")]);
|
||||
}
|
||||
}
|
||||
songDataGroup.add(needsVoicesCheckbox);
|
||||
addLabelOn(needsVoicesCheckbox, "Needs Voices");
|
||||
needsVoicesCheckbox.y += 6; needsVoicesCheckbox.x += 4;
|
||||
|
||||
instExplorer = new UIFileExplorer(songNameTextBox.x, songNameTextBox.y + 32 + 36, null, null, Paths.SOUND_EXT, function (res) {
|
||||
var audioPlayer:UIAudioPlayer = new UIAudioPlayer(instExplorer.x + 8, instExplorer.y + 8, res);
|
||||
instExplorer.members.push(audioPlayer);
|
||||
@@ -195,7 +175,7 @@ class SongCreationScreen extends UISubstateWindow {
|
||||
|
||||
public override function update(elapsed:Float) {
|
||||
if (curPage == 0) {
|
||||
if (instExplorer.file != null && (needsVoicesCheckbox.checked ? voicesExplorer.file != null : true))
|
||||
if (instExplorer.file != null)
|
||||
saveButton.selectable = true;
|
||||
else saveButton.selectable = false;
|
||||
} else
|
||||
@@ -258,7 +238,6 @@ class SongCreationScreen extends UISubstateWindow {
|
||||
bpm: bpmStepper.value,
|
||||
beatsPerMeasure: Std.int(beatsPerMeasureStepper.value),
|
||||
stepsPerBeat: Std.int(stepsPerBeatStepper.value),
|
||||
needsVoices: needsVoicesCheckbox.checked,
|
||||
displayName: displayNameTextBox.label.text,
|
||||
icon: iconTextBox.label.text,
|
||||
color: colorWheel.curColorString,
|
||||
|
||||
@@ -56,6 +56,8 @@ class Character extends FunkinSprite implements IBeatReceiver implements IOffset
|
||||
public var shadowFrame:CharacterShadowFrame;
|
||||
public var idleSuffix:String = "";
|
||||
|
||||
public var anims = ["singLEFT", "singDOWN", "singUP", "singRIGHT"]; // Not making these inline so theyre editable through scripts! - Nex
|
||||
|
||||
public inline function getCameraPosition()
|
||||
{
|
||||
var midpoint = getMidpoint();
|
||||
@@ -68,12 +70,26 @@ class Character extends FunkinSprite implements IBeatReceiver implements IOffset
|
||||
return new FlxPoint(event.x, event.y);
|
||||
}
|
||||
|
||||
var singAnims = ["singLEFT", "singDOWN", "singUP", "singRIGHT"];
|
||||
public inline function getSingAnim(direction:Int, suffix:String = ""):String
|
||||
{
|
||||
return anims[direction % anims.length] + suffix;
|
||||
}
|
||||
|
||||
/**
|
||||
* Like `playSingAnimUnsafe` but checks if the character has the animation with the suffix part, otherwhise tries to play the animation without the suffix part.
|
||||
*/
|
||||
public function playSingAnim(direction:Int, suffix:String = "", Context:PlayAnimContext = SING, Force:Bool = true, Reversed:Bool = false, Frame:Int = 0)
|
||||
{
|
||||
var event = EventManager.get(DirectionAnimEvent).recycle(singAnims[direction % singAnims.length] + suffix, direction, suffix, Context, Reversed, Frame, Force);
|
||||
var event = EventManager.get(DirectionAnimEvent).recycle(getSingAnim(direction, suffix), direction, suffix, Context, Reversed, Frame, Force);
|
||||
script.call("onPlaySingAnim", [event]);
|
||||
if (!event.cancelled)
|
||||
playSingAnimUnsafe(event.direction, hasAnimation(event.animName) ? event.suffix : "", event.context, event.force, event.reversed, event.frame);
|
||||
}
|
||||
|
||||
public function playSingAnimUnsafe(direction:Int, suffix:String = "", Context:PlayAnimContext = SING, Force:Bool = true, Reversed:Bool = false, Frame:Int = 0)
|
||||
{
|
||||
var event = EventManager.get(DirectionAnimEvent).recycle(getSingAnim(direction, suffix), direction, suffix, Context, Reversed, Frame, Force);
|
||||
script.call("onPlaySingAnimUnsafe", [event]);
|
||||
if (!event.cancelled)
|
||||
playAnim(event.animName, event.force, event.context, event.reversed, event.frame);
|
||||
}
|
||||
@@ -161,7 +177,7 @@ class Character extends FunkinSprite implements IBeatReceiver implements IOffset
|
||||
// [ true ][ false ][ true ]
|
||||
// [ false ][ true ][ true ]
|
||||
// [ true ][ true ][ false ]
|
||||
// bros provided evedince :skull:
|
||||
// bros provided evidence :skull:
|
||||
if (isPlayer != playerOffsets && switchAnims)
|
||||
{
|
||||
// character is flipped
|
||||
|
||||
@@ -108,7 +108,7 @@ class GameOverSubstate extends MusicBeatSubstate
|
||||
if (controls.BACK)
|
||||
{
|
||||
if (PlayState.chartingMode && Charter.undos.unsaved)
|
||||
PlayState.instance.saveWarn(false);
|
||||
game.saveWarn(false);
|
||||
else {
|
||||
PlayState.resetSongInfos();
|
||||
if (Charter.instance != null) Charter.instance.__clearStatics();
|
||||
|
||||
@@ -988,7 +988,7 @@ class PlayState extends MusicBeatState
|
||||
curSong = songData.meta.name.toLowerCase();
|
||||
|
||||
inst = FlxG.sound.load(Paths.inst(SONG.meta.name, difficulty));
|
||||
if (SONG.meta.needsVoices != false) // null or true
|
||||
if (Assets.exists(Paths.voices(SONG.meta.name, difficulty))) // null or true
|
||||
vocals = FlxG.sound.load(Paths.voices(SONG.meta.name, difficulty));
|
||||
else
|
||||
vocals = new FlxSound();
|
||||
|
||||
@@ -29,9 +29,9 @@ class StrumLine extends FlxTypedGroup<Strum> {
|
||||
*/
|
||||
public var onNoteUpdate:FlxTypedSignal<NoteUpdateEvent->Void> = new FlxTypedSignal<NoteUpdateEvent->Void>();
|
||||
/**
|
||||
* Signal that triggers whenever a note is being updated. Similar to onNoteUpdate, except strumline specific.
|
||||
* Signal that triggers whenever a note is being deleted. Similar to onNoteDelete, except strumline specific.
|
||||
* To add a listener, do
|
||||
* `strumLine.onNoteUpdate.add(function(e:NoteUpdateEvent) {});`
|
||||
* `strumLine.onNoteDelete.add(function(e:SimpleNoteEvent) {});`
|
||||
*/
|
||||
public var onNoteDelete:FlxTypedSignal<SimpleNoteEvent->Void> = new FlxTypedSignal<SimpleNoteEvent->Void>();
|
||||
/**
|
||||
|
||||
@@ -70,13 +70,12 @@ class MainMenuState extends MusicBeatState
|
||||
|
||||
FlxG.camera.follow(camFollow, null, 0.06);
|
||||
|
||||
var versionText = [
|
||||
var versionShit:FunkinText = new FunkinText(5, FlxG.height - 2, 0, [
|
||||
'Codename Engine v${Application.current.meta.get('version')}',
|
||||
TU.translate("mainMenu.commit", [funkin.backend.system.macros.GitCommitMacro.commitNumber, funkin.backend.system.macros.GitCommitMacro.commitHash]),
|
||||
TU.translate("mainMenu.openMods", ["TAB"]),
|
||||
TU.translate("mainMenu.openMods", [controls.getKeyName(SWITCHMOD)]),
|
||||
''
|
||||
].join('\n');
|
||||
var versionShit:FunkinText = new FunkinText(5, FlxG.height - 2, 0, versionText);
|
||||
].join('\n'));
|
||||
versionShit.y -= versionShit.height;
|
||||
versionShit.scrollFactor.set();
|
||||
add(versionShit);
|
||||
|
||||
@@ -113,7 +113,7 @@ class PauseSubState extends MusicBeatSubstate
|
||||
|
||||
pauseScript.call("postCreate");
|
||||
|
||||
PlayState.instance.updateDiscordPresence();
|
||||
game.updateDiscordPresence();
|
||||
}
|
||||
|
||||
override function update(elapsed:Float)
|
||||
@@ -153,7 +153,7 @@ class PauseSubState extends MusicBeatSubstate
|
||||
close();
|
||||
case "Restart Song":
|
||||
parentDisabler.reset();
|
||||
PlayState.instance.registerSmoothTransition();
|
||||
game.registerSmoothTransition();
|
||||
FlxG.resetState();
|
||||
case "Change Controls":
|
||||
persistentDraw = false;
|
||||
@@ -164,7 +164,7 @@ class PauseSubState extends MusicBeatSubstate
|
||||
FlxG.switchState(new funkin.editors.charter.Charter(PlayState.SONG.meta.name, PlayState.difficulty, false));
|
||||
case "Exit to menu":
|
||||
if (PlayState.chartingMode && Charter.undos.unsaved)
|
||||
PlayState.instance.saveWarn(false);
|
||||
game.saveWarn(false);
|
||||
else {
|
||||
PlayState.resetSongInfos();
|
||||
if (Charter.instance != null) Charter.instance.__clearStatics();
|
||||
@@ -177,16 +177,17 @@ class PauseSubState extends MusicBeatSubstate
|
||||
}
|
||||
override function destroy()
|
||||
{
|
||||
if(FlxG.cameras.list.contains(camera))
|
||||
FlxG.cameras.remove(camera, true);
|
||||
if(camera != FlxG.camera && _cameras != null) {
|
||||
if(FlxG.cameras.list.contains(camera))
|
||||
FlxG.cameras.remove(camera, true);
|
||||
}
|
||||
pauseScript.call("destroy");
|
||||
pauseScript.destroy();
|
||||
|
||||
if (pauseMusic != null)
|
||||
@:privateAccess {
|
||||
FlxG.sound.destroySound(pauseMusic);
|
||||
}
|
||||
|
||||
if(pauseMusic != null) {
|
||||
@:privateAccess
|
||||
FlxG.sound.destroySound(pauseMusic);
|
||||
}
|
||||
super.destroy();
|
||||
}
|
||||
|
||||
|
||||
@@ -51,7 +51,7 @@ class Alphabet extends FlxSpriteGroup
|
||||
|
||||
// TODO: fix this shit refreshing
|
||||
public function refreshAlphabetXML(path:String) {
|
||||
AlphaCharacter.__alphaPath = path;
|
||||
AlphaCharacter.__alphaPath = Paths.getAssetsRoot() + path;
|
||||
try {
|
||||
var xml = new Access(Xml.parse(Assets.getText(path)).firstElement());
|
||||
AlphaCharacter.boldAnims = [];
|
||||
@@ -86,7 +86,7 @@ class Alphabet extends FlxSpriteGroup
|
||||
isBold = bold;
|
||||
|
||||
var alphabetPath = Paths.xml("alphabet");
|
||||
if (alphabetPath != AlphaCharacter.__alphaPath) {
|
||||
if (Paths.getAssetsRoot() + alphabetPath != AlphaCharacter.__alphaPath) {
|
||||
refreshAlphabetXML(alphabetPath);
|
||||
}
|
||||
#if MOD_SUPPORT else {
|
||||
@@ -122,12 +122,16 @@ class Alphabet extends FlxSpriteGroup
|
||||
doSplitWords();
|
||||
|
||||
var xPos:Float = 0;
|
||||
for (character in splitWords)
|
||||
var curRow:Int = 0;
|
||||
for (i=>character in splitWords)
|
||||
{
|
||||
if (lastSprite != null)
|
||||
xPos = lastSprite.x + lastSprite.width - x;
|
||||
|
||||
if (xPosResetted && !(xPosResetted = false)) xPos = 0;
|
||||
|
||||
var letter:AlphaCharacter = new AlphaCharacter(xPos, 0);
|
||||
letter.row = curRow;
|
||||
if (isBold)
|
||||
letter.createBold(character);
|
||||
else
|
||||
@@ -141,6 +145,11 @@ class Alphabet extends FlxSpriteGroup
|
||||
add(letter);
|
||||
|
||||
lastSprite = letter;
|
||||
if (_finalText.fastCodeAt(i) == "\n".code)
|
||||
{
|
||||
xPosResetted = true;
|
||||
curRow += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -247,6 +256,8 @@ class AlphaCharacter extends FlxSprite
|
||||
animation.addByPrefix(letter, boldAnims[letter], 24);
|
||||
animation.play(letter);
|
||||
updateHitbox();
|
||||
|
||||
y += row * height;
|
||||
}
|
||||
|
||||
public function createLetter(letter:String):Void
|
||||
|
||||
@@ -48,8 +48,6 @@ class OptionsMenu extends TreeMenu {
|
||||
bg.antialiasing = true;
|
||||
add(bg);
|
||||
|
||||
Framerate.offset.y = 60;
|
||||
|
||||
main = new OptionsScreen("Options", "Select a category to continue.", [for(o in mainOptions) new TextOption(o.name, o.desc, function() {
|
||||
if (o.substate != null) {
|
||||
persistentUpdate = false;
|
||||
|
||||
@@ -7,6 +7,7 @@ import funkin.backend.FunkinText;
|
||||
import funkin.editors.ui.UIState;
|
||||
import funkin.menus.MainMenuState;
|
||||
import funkin.options.type.OptionType;
|
||||
import funkin.backend.system.framerate.Framerate;
|
||||
|
||||
class TreeMenu extends UIState {
|
||||
public var main:OptionsScreen;
|
||||
@@ -97,6 +98,8 @@ class TreeMenu extends UIState {
|
||||
var menuChangeTween:FlxTween;
|
||||
public override function update(elapsed:Float) {
|
||||
super.update(elapsed);
|
||||
|
||||
Framerate.offset.y = pathBG.height;
|
||||
|
||||
// in case path gets so long it goes offscreen
|
||||
pathLabel.x = lerp(pathLabel.x, Math.max(0, FlxG.width - 4 - pathLabel.width), 0.125);
|
||||
|
||||
@@ -91,7 +91,7 @@ class Assets
|
||||
@usage var bitmap = new Bitmap (Assets.getBitmapData ("image.png"));
|
||||
@param id The ID or asset path for the bitmap
|
||||
@param useCache (Optional) Whether to allow use of the asset cache (Default: true)
|
||||
@param useCache Whenever the image should be immediately pushed to GPU.
|
||||
@param pushToGPU Whenever the image should be immediately pushed to GPU.
|
||||
@return A new BitmapData object
|
||||
**/
|
||||
public static function getBitmapData(id:String, useCache:Bool = true, pushToGPU:Bool = true):BitmapData
|
||||
|
||||
Reference in New Issue
Block a user