32 Commits
Author SHA1 Message Date
FuroandGitHub 3a6f4819a9 1.0.1-rc2 2025-08-08 22:41:20 +03:00
FuroandGitHub e24a62a0b7 macOS executable path is different 2025-08-08 21:17:58 +03:00
Ralty 59b54dfbae Fix WINDOW_TITLE_USE_MOD_NAME 2025-08-08 19:06:22 +07:00
Ralty a83f62ab8a Merge remote-tracking branch 'upstream/main' 2025-08-08 18:11:01 +07:00
Frakits f86773223b Merge remote-tracking branch 'origin/main' 2025-08-08 13:57:25 +03:00
Frakits b1133171c7 Merge remote-tracking branch 'origin/main' 2025-08-08 13:57:16 +03:00
Ralty 05006802ee Bring back needsVoices, add vocalsSuffix in meta 2025-08-08 17:54:39 +07:00
Frakits 0eaffe3ea9 you may now disable the updater. 2025-08-08 13:51:35 +03:00
Verwex 03df52c542 flipX and flipY for alphabet XMLs 2025-08-08 12:34:30 +02:00
Verwex 1ca4b64402 Translatable Mod Config Warning
The Translation Strings Used in Editors.xml:
- editor.notNow
- editor.yes
- modConfigWarning.warningTitle
- modConfigWarning.warningDesc
- modConfigWarning.createdTitle
- modConfigWarning.createdDesc

Only English & Polish variants are provided beacuse I speak those.
2025-08-08 11:10:25 +02:00
FrakitsandGitHub b8b8d08be5 Update issue templates 2025-08-08 11:43:28 +03:00
FrakitsandGitHub 406f76af5d improve lagspike? 2025-08-07 23:38:31 +03:00
Verwex 1a855e5061 Freeplay Change Mode Keybind
The Co-Op Mode Switch has been given its own keybind, approved by Frakits.

FOR TRANSLATORS:
The translation string in Options.xml is category.ui.changeMode
2025-08-07 21:13:37 +02:00
FrakitsandGitHub 41474738c7 Update Chart.hx 2025-08-07 21:07:33 +03:00
Ralty bcbfa90f37 Fix Freeplay Difficulty Inst Suffix 2025-08-08 00:38:03 +07:00
Ralty b39f0d993e Whoops 2025-08-08 00:25:23 +07:00
Ralty 65839283e5 Quick fix crash freeplay 2025-08-08 00:19:01 +07:00
Ralty d2a113907a instSuffix, meta.metas for freeplay preview 2025-08-08 00:07:49 +07:00
Ralty d3ef27b9c7 ChartData no longer needs needsVoices (pun) 2025-08-07 23:18:43 +07:00
Ralty 90fc02c128 Oh thats why 2025-08-07 22:43:27 +07:00
Ralty cbaa5a6155 Don't resync Inst too in resyncVocals, stupid 2025-08-07 22:32:30 +07:00
Ralty 1f479a5b16 fix beatHit twice during start 2025-08-07 22:29:44 +07:00
Ralty 967bd4cf01 An attempt to fix stutters for low-end computers 2025-08-07 22:23:45 +07:00
Ralty 3074107322 Async Checking for Updates 2025-08-07 21:54:59 +07:00
Ralty 9c7cc22a47 An attempt to fix skipping the next song randomly 2025-08-07 20:01:23 +07:00
Ralty 7c8dd987dd Fix __readStream out of bound 2025-08-07 20:00:12 +07:00
Verwex 48624554cd Merge branch 'main' of https://github.com/CodenameCrew/CodenameEngine 2025-08-07 13:55:32 +02:00
Verwex a86749953f Camera Position & Camera Zoom Event Icons 2025-08-07 13:55:11 +02:00
KolzeandGitHub 482c213079 prevent crash when vocal suffix doesnt exist (#709)
pls merge adding erects is hell
2025-08-07 14:42:57 +03:00
Verwex 8d8e6e453a Separated volume keybind labels for translations 2025-08-07 13:32:31 +02:00
TilandGitHub d6937e866c fix a buncha stuff in saveSongInfo for SongCreationScreen (#704) 2025-08-07 18:27:08 +07:00
Verwex 9ce1486269 PL Translation - Credits Btn - Kolze's Suggestion 2025-08-07 13:18:04 +02:00
48 changed files with 453 additions and 218 deletions
+8 -2
View File
@@ -2,12 +2,18 @@
name: Bug Report
about: Report a bug or critical performance issue
title: 'Bug Report: [DESCRIBE YOUR BUG IN DETAIL HERE]'
labels: bug
labels: ''
assignees: ''
---
[weed]: <> (MAKE SURE THAT THE ISSUE IS AS COMPLETE AS POSSIBLE. ISSUES THAT HAVE BARELY ANY INFORMATION OR IS ONLY MADE FOR TROLLING WILL BE INSTANTLY CLOSED.)
[weed]: <> (MAKE SURE TO ADD YOUR TARGET OR IT'LL BE IGNORED!)
### What version of the engine are you using?
[weed]: <> (IF YOU COMPILED THE ENGINE PLEASE PROVIDE A COMMIT HASH THAT WE CAN BASE OFF OF)
Version:
### Which target are you using?
[weed]: <> (CHANGE [ ] TO [x] IN FRONT OF YOUR TARGET!)
@@ -17,4 +23,4 @@ labels: bug
### Explain your issue
##### Please check first if your issue haven't already been reported yet.
[weed]: <> (MAKE SURE TO ADD A SCREENSHOT OF THE CRASH HANDLER, IF AVAILABLE, AND STEPS TO REPRODUCE THE BUG!)
[weed]: <> (MAKE SURE TO ADD A SCREENSHOT OF THE CRASH HANDLER, IF AVAILABLE, AND STEPS TO REPRODUCE THE BUG!)
+5 -2
View File
@@ -1,8 +1,11 @@
---
name: Compiling help
about: If you need help compiling the game, and you're running into issues. (Look through the 'compiling help' label in case it's been solved!)
about: If you need help compiling the game, and you're running into issues. (Look
through the 'compiling help' label in case it's been solved!)
title: 'Compiling help: [BRIEF DESCRIPTION / ERROR MESSAGE OUTPUT]'
labels: compiling help
labels: ''
assignees: ''
---
[weed]: <> (MAKE SURE THAT THE ISSUE IS AS COMPLETE AS POSSIBLE. ISSUES THAT HAVE BARELY ANY INFORMATION OR IS ONLY MADE FOR TROLLING WILL BE INSTANTLY CLOSED.)
+4 -1
View File
@@ -2,7 +2,10 @@
name: Enhancement
about: Suggest a new feature
title: 'Enhancement: '
labels: enhancement
labels: ''
assignees: ''
---
#### Please check for duplicates or similar issues before creating this issue.
## What is your suggestion, and why should it be implemented?
+1 -1
View File
@@ -38,7 +38,7 @@ jobs:
uses: actions/upload-artifact@v4
with:
name: Codename Engine (Executable Only)
path: export/release/macos/bin/CodenameEngine
path: export/release/macos/bin/CodenameEngine.app/Contents/MacOS/CodenameEngine
- name: Uploading artifact (entire build)
uses: actions/upload-artifact@v4
with:
+33 -1
View File
@@ -6,5 +6,37 @@
"*.hscript": "haxe",
"*.hsc": "haxe",
"*.hxs": "haxe"
}
},
"lime.targetConfigurations": [
{
"label": "Windows (Test Build)",
"target": "windows",
"args": ["-DTEST_BUILD"]
},
{
"label": "Linux (Test Build)",
"target": "linux",
"args": ["-DTEST_BUILD"]
},
{
"label": "MacOS (Test Build)",
"target": "macos",
"args": ["-DTEST_BUILD"]
},
{
"label": "Windows / Debug (Test Build)",
"target": "windows",
"args": ["-debug", "-DTEST_BUILD"]
},
{
"label": "Linux / Debug (Test Build)",
"target": "linux",
"args": ["-debug", "-DTEST_BUILD"]
},
{
"label": "MacOS / Debug (Test Build)",
"target": "macos",
"args": ["-debug", "-DTEST_BUILD"]
},
]
}
Binary file not shown.
Binary file not shown.

After

Width:  |  Height:  |  Size: 210 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 215 B

+11
View File
@@ -9,6 +9,8 @@
<str id="save">Save</str>
<str id="saveClose">Save &amp; Close</str>
<str id="close">Close</str>
<str id="notNow">Not Now</str>
<str id="yes">Yes</str>
</group>
<group name="UIImageExplorer" prefix="uiImageExplorer.">
@@ -111,6 +113,7 @@
<str id="color">Color</str>
<str id="difficulties">Difficulties</str>
<str id="customValues">Custom Values (Advanced)</str>
<str id="needsVoices">Needs Voices</str>
<str id="opponentMode">Opponent Mode</str>
<str id="coopAllowed">Co-op Mode</str>
@@ -665,4 +668,12 @@
<str id="saveAndExitToMenu">Save &amp; Exit To Menu</str>
<str id="cancel">Cancel</str>
</group>
<group name="ModConfigWarning" prefix="modConfigWarning.">
<str id="warningTitle">Missing mod folder configuration!</str>
<str id="warningDesc">Your mod is currently missing a mod config file!\n\nWould you like to automatically generate one?\n\n(PS: If this is not your mod please disable Developer mode to stop this popup from appearing.)</str>
<str id="createdTitle">Created mod config!</str>
<str id="createdDesc">Your mod config file has been created at {0}!</str>
</group>
</language>
+3
View File
@@ -42,6 +42,7 @@
<str id="back">Back</str>
<str id="reset">Reset</str>
<str id="pause">Pause</str>
<str id="changeMode">Change Mode</str>
</group>
<str id="category.engine">Engine</str>
@@ -50,6 +51,8 @@
</group>
<str id="category.volume">Volume</str>
<str id="volume.up">Up</str>
<str id="volume.down">Down</str>
<str id="volume.mute">Mute</str>
<str id="category.developer">Developer</str>
+1
View File
@@ -105,6 +105,7 @@
<str id="modesAllowed">Modos Permitidos</str>
<str id="difficulties">Difficultades</str>
<str id="customValues">Valores Especiales (Avanzado)</str>
<str id="needsVoices">Necesita Voces</str>
<str id="opponentMode">Modo Oponente</str>
<str id="coopAllowed">Modo Co-op</str>
+2
View File
@@ -49,6 +49,8 @@
</group>
<str id="category.volume">Volumen</str>
<str id="volume.up">Arriba</str>
<str id="volume.down">Abajo</str>
<str id="volume.mute">mutear</str>
<str id="category.developer">Desarrollador</str>
+2 -1
View File
@@ -108,8 +108,9 @@
<str id="color">Colore</str>
<str id="difficulties">Difficoltà</str>
<str id="customValues">Valori Personalizzati (Avanzato)</str>
<str id="needsVoices">Bisogno Voci</str>
<str id="opponentMode">Modalità Avvers.</str>
<str id="opponentMode">Modalità Avvers</str>
<str id="coopAllowed">Modalità Co-op</str>
</group>
+2
View File
@@ -48,6 +48,8 @@
<str id="switchMod">Cambia Mod</str>
</group>
<str id="volume.up">Su</str>
<str id="volume.down">Giù</str>
<str id="volume.mute">Muto</str>
<str id="category.developer">Sviluppatore</str>
+11
View File
@@ -9,6 +9,8 @@
<str id="save">Zapisz</str>
<str id="saveClose">Zapisz i Zamknij</str>
<str id="close">Zamknij</str>
<str id="notNow">Nie Teraz</str>
<str id="yes">Tak</str>
</group>
<group name="UIImageExplorer" prefix="uiImageExplorer.">
@@ -109,6 +111,7 @@
<str id="color">Kolor</str>
<str id="difficulties">Trudności</str>
<str id="customValues">Własna Waluta (Zaawansowane)</str>
<str id="needsVoices">Potrzebne Wokale</str>
<str id="opponentMode">Tryb Przeciwnika</str>
<str id="coopAllowed">Tryb Ko-operacyjny</str>
@@ -653,4 +656,12 @@
<str id="saveAndExitToMenu">Zapisz i Wyjdź do Menu</str>
<str id="cancel">Anuluj</str>
</group>
<group name="ModConfigWarning" prefix="modConfigWarning.">
<str id="warningTitle">Brakujący plik konfiguracyjny modu!</str>
<str id="warningDesc">Twój mod obecnie brakuje jego pliku konfiguracyjnego!\n\nCzy chcesz go automatycznie wygenerować?\n\n(PS: Jeżeli to nie twój mod, wyłącz Tryb Dewelopera, aby zapobiec wyskakiwaniu się tego okienka.)</str>
<str id="createdTitle">Utworzono konfigurację modu!</str>
<str id="createdDesc">Twój plik konfiguracyjny modu został utworzony w {0}!</str>
</group>
</language>
+1 -1
View File
@@ -57,7 +57,7 @@
<str id="credits.allContributors">Wszyscy współpracownicy silnika! - Naciśnij RESET aby zaktualizować listę (Jeden reset co 2 minuty).</str>
</group>
<group name="CreditsMain">
<str id="credits.name">Kredyty</str>
<str id="credits.name">Twórcy</str>
<str id="credits.madePossible">Ludzie którzy to umożliwili!</str>
<str id="credits.selectCodename">Wybierz to aby zobaczyć wszystkich współpracowników!</str>
<str id="credits.selectBase">Wybierz to aby otworzyć stronę itch.io oryginalnej gry!</str>
+3
View File
@@ -38,6 +38,7 @@
<str id="back">Wstecz</str>
<str id="reset">Reset</str>
<str id="pause">Pauza</str>
<str id="changeMode">Zmień Tryb</str>
</group>
<str id="category.engine">Silnik</str>
@@ -46,6 +47,8 @@
</group>
<str id="category.volume">Głośność</str>
<str id="volume.up">Głóśniej</str>
<str id="volume.down">Ciszej</str>
<str id="volume.mute">Wycisz</str>
<str id="category.developer">Deweloper</str>
Binary file not shown.

Before

Width:  |  Height:  |  Size: 65 KiB

After

Width:  |  Height:  |  Size: 68 KiB

@@ -1,15 +1,15 @@
<?xml version="1.0" encoding="UTF-8"?>
<TextureAtlas imagePath="credits.png" width="3020" height="169">
<SubTexture name="credits basic0000" x="1" y="1" width="430" height="114" frameX="-0" frameY="-0" frameWidth="430" frameHeight="114"/>
<SubTexture name="credits basic0001" x="1" y="1" width="430" height="114" frameX="-0" frameY="-0" frameWidth="430" frameHeight="114"/>
<SubTexture name="credits basic0002" x="1" y="1" width="430" height="114" frameX="-0" frameY="-0" frameWidth="430" frameHeight="114"/>
<SubTexture name="credits basic0003" x="433" y="1" width="430" height="114" frameX="-0" frameY="-0" frameWidth="430" frameHeight="114"/>
<SubTexture name="credits basic0004" x="433" y="1" width="430" height="114" frameX="-0" frameY="-0" frameWidth="430" frameHeight="114"/>
<SubTexture name="credits basic0005" x="433" y="1" width="430" height="114" frameX="-0" frameY="-0" frameWidth="430" frameHeight="114"/>
<SubTexture name="credits basic0006" x="865" y="1" width="430" height="114" frameX="-0" frameY="-0" frameWidth="430" frameHeight="114"/>
<SubTexture name="credits basic0007" x="865" y="1" width="430" height="114" frameX="-0" frameY="-0" frameWidth="430" frameHeight="114"/>
<SubTexture name="credits basic0008" x="865" y="1" width="430" height="114" frameX="-0" frameY="-0" frameWidth="430" frameHeight="114"/>
<SubTexture name="credits white0000" x="1297" y="1" width="574" height="164" frameX="-0" frameY="24" frameWidth="574" frameHeight="169"/>
<SubTexture name="credits white0001" x="2446" y="1" width="573" height="167" frameX="-0" frameY="25" frameWidth="574" frameHeight="169"/>
<SubTexture name="credits white0002" x="1873" y="1" width="571" height="165" frameX="-0" frameY="21" frameWidth="574" frameHeight="169"/>
<TextureAtlas imagePath="credits.png" width="3341" height="162">
<SubTexture name="credits basic0000" x="1" y="1" width="440" height="116" frameX="-0" frameY="-0" frameWidth="440" frameHeight="116"/>
<SubTexture name="credits basic0001" x="1" y="1" width="440" height="116" frameX="-0" frameY="-0" frameWidth="440" frameHeight="116"/>
<SubTexture name="credits basic0002" x="1" y="1" width="440" height="116" frameX="-0" frameY="-0" frameWidth="440" frameHeight="116"/>
<SubTexture name="credits basic0003" x="443" y="1" width="440" height="116" frameX="-0" frameY="-0" frameWidth="440" frameHeight="116"/>
<SubTexture name="credits basic0004" x="443" y="1" width="440" height="116" frameX="-0" frameY="-0" frameWidth="440" frameHeight="116"/>
<SubTexture name="credits basic0005" x="443" y="1" width="440" height="116" frameX="-0" frameY="-0" frameWidth="440" frameHeight="116"/>
<SubTexture name="credits basic0006" x="885" y="1" width="440" height="116" frameX="-0" frameY="-0" frameWidth="440" frameHeight="116"/>
<SubTexture name="credits basic0007" x="885" y="1" width="440" height="116" frameX="-0" frameY="-0" frameWidth="440" frameHeight="116"/>
<SubTexture name="credits basic0008" x="885" y="1" width="440" height="116" frameX="-0" frameY="-0" frameWidth="440" frameHeight="116"/>
<SubTexture name="credits white0000" x="2668" y="1" width="672" height="160" frameX="-0" frameY="25" frameWidth="672" frameHeight="160"/>
<SubTexture name="credits white0001" x="1327" y="1" width="669" height="156" frameX="-2" frameY="24" frameWidth="672" frameHeight="160"/>
<SubTexture name="credits white0002" x="1998" y="1" width="668" height="157" frameX="-4" frameY="22" frameWidth="672" frameHeight="160"/>
</TextureAtlas>
+1 -1
View File
@@ -5,7 +5,7 @@
title="Friday Night Funkin' - Codename Engine" main="funkin.backend.system.Main"
file="CodenameEngine" packageName="com.yoshman29.codenameengine"
package="com.yoshman29.codenameengine"
version="1.0.1" company="Yoshman29" />
version="1.0.1-rc2" company="Yoshman29" />
<!--
CHANGE THE SAVE PATH & NAME FOR YOUR MOD HERE!
+7 -6
View File
@@ -38,6 +38,7 @@ class Paths
for (entry in entries) {
if (entry.toLowerCase() == part.toLowerCase()) {
fixedPath += entry + (it != parts.length - 1 ? "/" : "");
break;
}
}
}
@@ -88,18 +89,18 @@ class Paths
inline static public function music(key:String, ?library:String, ?ext:String)
return getPath('music/$key.${ext != null ? ext : Flags.SOUND_EXT}', library);
inline static public function voices(song:String, ?difficulty:String, ?prefix:String = "", ?ext:String) {
inline static public function voices(song:String, ?difficulty:String, ?suffix:String = "", ?ext:String) {
if (difficulty == null) difficulty = Flags.DEFAULT_DIFFICULTY;
if (ext == null) ext = Flags.SOUND_EXT;
var diff = getPath('songs/$song/song/Voices$prefix-${difficulty}.${ext}', null);
return OpenFlAssets.exists(diff) ? diff : getPath('songs/$song/song/Voices$prefix.${ext}', null);
var diff = getPath('songs/$song/song/Voices$suffix-${difficulty}.${ext}', null);
return OpenFlAssets.exists(diff) ? diff : getPath('songs/$song/song/Voices$suffix.${ext}', null);
}
inline static public function inst(song:String, ?difficulty:String, ?prefix:String = "", ?ext:String) {
inline static public function inst(song:String, ?difficulty:String, ?suffix:String = "", ?ext:String) {
if (difficulty == null) difficulty = Flags.DEFAULT_DIFFICULTY;
if (ext == null) ext = Flags.SOUND_EXT;
var diff = getPath('songs/$song/song/Inst$prefix-${difficulty}.${ext}', null);
return OpenFlAssets.exists(diff) ? diff : getPath('songs/$song/song/Inst$prefix.${ext}', null);
var diff = getPath('songs/$song/song/Inst$suffix-${difficulty}.${ext}', null);
return OpenFlAssets.exists(diff) ? diff : getPath('songs/$song/song/Inst$suffix.${ext}', null);
}
static public function image(key:String, ?library:String, checkForAtlas:Bool = false, ?ext:String) {
+32 -16
View File
@@ -83,13 +83,11 @@ class Chart {
return data;
}
public static function loadChartMeta(songName:String, ?difficulty:String, fromMods:Bool = true):ChartMetaData {
if (difficulty == null) difficulty = Flags.DEFAULT_DIFFICULTY;
public static function loadChartMeta(songName:String, difficulty:String = '', fromMods:Bool = true, includeMetaDifficulties:Bool = true):ChartMetaData {
var metaPath = Paths.file('songs/${songName}/meta.json');
var metaDiffPath = Paths.file('songs/${songName}/meta-${difficulty}.json');
var data:ChartMetaData = null;
var data:ChartMetaData = null, fromDifficulty = false;
var fromMods:Bool = fromMods;
for (path in [metaDiffPath, metaPath]) if (Assets.exists(path)) {
fromMods = Paths.assetsTree.existsSpecific(path, "TEXT", MODS);
@@ -98,24 +96,33 @@ class Chart {
tempData.color = CoolUtil.getColorFromDynamic(tempData.color).getDefault(Flags.DEFAULT_COLOR);
data = tempData;
} catch(e) Logs.trace('Failed to load song metadata for ${songName} ($path): ${Std.string(e)}', ERROR);
if (data != null) break;
if (data != null) {
fromDifficulty = path == metaDiffPath;
break;
}
}
if (data == null) data = {
name: songName,
bpm: 100
};
if (data == null) {
data = {
name: songName,
bpm: Flags.DEFAULT_BPM
};
}
else {
data.name = songName;
data.setFieldDefault("bpm", Flags.DEFAULT_BPM);
}
data.name = songName;
data.setFieldDefault("bpm", Flags.DEFAULT_BPM);
data.setFieldDefault("displayName", data.name);
data.setFieldDefault("beatsPerMeasure", Flags.DEFAULT_BEATS_PER_MEASURE);
data.setFieldDefault("stepsPerBeat", Flags.DEFAULT_STEPS_PER_BEAT);
data.setFieldDefault("needsVoices", true);
data.setFieldDefault("icon", Flags.DEFAULT_HEALTH_ICON);
data.setFieldDefault("difficulties", []);
data.setFieldDefault("coopAllowed", Flags.DEFAULT_COOP_ALLOWED);
data.setFieldDefault("opponentModeAllowed", Flags.DEFAULT_OPPONENT_MODE_ALLOWED);
data.setFieldDefault("displayName", data.name);
data.setFieldDefault("instSuffix", "");
data.setFieldDefault("vocalsSuffix", "");
data.setFieldDefault("needsVoices", true);
if (data.difficulties.length <= 0) {
data.difficulties = [for(f in Paths.getFolderContent('songs/${songName}/charts/', false, fromMods ? MODS : SOURCE)) if (Path.extension(f.toUpperCase()) == "JSON") Path.withoutExtension(f)];
@@ -132,6 +139,14 @@ class Chart {
}
}
if (includeMetaDifficulties && !fromDifficulty) {
data.metas = [];
for (difficulty in data.difficulties) {
if (!data.metas.exists(difficulty) && Assets.exists(Paths.file('songs/${songName}/meta-${difficulty}.json')))
data.metas.set(difficulty, loadChartMeta(songName, difficulty, fromMods, false));
}
}
return data;
}
@@ -265,7 +280,7 @@ class Chart {
CoolUtil.safeSaveFile(chartPath, Json.stringify(filteredChart, null, saveSettings.prettyPrint == true ? Flags.JSON_PRETTY_PRINT : null));
if (saveSettings.overrideExistingMeta == true || !FileSystem.exists(metaPath))
if (meta != null && (saveSettings.overrideExistingMeta || !FileSystem.exists(metaPath)))
CoolUtil.safeSaveFile(metaPath, makeMetaSaveable(meta));
#end
return filteredChart;
@@ -305,7 +320,8 @@ class Chart {
public static inline function makeMetaSaveable(meta:ChartMetaData, prettyPrint:Bool = true):String {
var data:Dynamic = Reflect.copy(meta);
if (data.color != null) data.color = new FlxColor(data.color).toWebString(); // dont even ask me - Nex
if (data.color != null) data.color = FlxColor.fromInt(data.color).toWebString(); // dont even ask me - Nex
Reflect.deleteField(data, 'metas');
return Json.stringify(data, null, prettyPrint ? Flags.JSON_PRETTY_PRINT : null);
}
}
@@ -317,4 +333,4 @@ typedef ChartSaveSettings = {
var ?saveGlobalEvents:Bool;
var ?prettyPrint:Bool;
var ?folder:String;
}
}
+3
View File
@@ -28,6 +28,9 @@ typedef ChartMetaData = {
public var ?coopAllowed:Bool;
public var ?opponentModeAllowed:Bool;
public var ?customValues:Dynamic;
public var ?metas:Map<String, ChartMetaData>;
public var ?instSuffix:String;
public var ?vocalsSuffix:String;
public var ?needsVoices:Bool;
}
+1 -1
View File
@@ -67,7 +67,7 @@ class VSliceParser {
var timeChanges = metadata.timeChanges;
result.meta.bpm = timeChanges[0].bpm;
result.meta.needsVoices = true;
result.meta.needsVoices = false;
for (note in data)
{
+6
View File
@@ -22,6 +22,7 @@ enum Control
ACCEPT;
BACK;
PAUSE;
CHANGE_MODE;
//CHEAT;
SWITCHMOD;
@@ -145,6 +146,11 @@ class Controls extends FlxActionSet
@:pressed("reset") public var RESET_HOLD(get, set): Bool;
@:justReleased("reset") public var RESET_R(get, set): Bool;
@:gamepad([FlxGamepadInputID.BACK]) // select button
@:justPressed("change-mode") public var CHANGE_MODE(get, set): Bool;
@:pressed("change-mode") public var CHANGE_MODE_HOLD(get, set): Bool;
@:justReleased("change-mode") public var CHANGE_MODE_R(get, set): Bool;
//@:gamepad([])
//@:justPressed("cheat") public var CHEAT(get, set): Bool;
//@:pressed("cheat") public var CHEAT_HOLD(get, set): Bool;
+5 -1
View File
@@ -196,6 +196,7 @@ class Flags {
public static var DISABLE_WARNING_SCREEN:Bool = true;
public static var DISABLE_TRANSITIONS:Bool = false;
public static var DISABLE_LANGUAGES:Bool = false;
public static var DISABLE_AUTOUPDATER:Bool = false;
@:also(funkin.backend.MusicBeatTransition.script)
public static var DEFAULT_TRANSITION_SCRIPT:String = "";
@@ -285,7 +286,10 @@ class Flags {
}
}
if (!flags.exists("WINDOW_TITLE_USE_MOD_NAME")) flags.set("WINDOW_TITLE_USE_MOD_NAME", flags.exists('TITLE') ? 'false' : 'true');
if (!flags.exists("WINDOW_TITLE_USE_MOD_NAME")) WINDOW_TITLE_USE_MOD_NAME = !flags.exists('TITLE');
else WINDOW_TITLE_USE_MOD_NAME = parseBool(flags.get("WINDOW_TITLE_USE_MOD_NAME"));
flags.remove("WINDOW_TITLE_USE_MOD_NAME");
}
public static function loadFromDatas(datas:Array<String>) {
+23 -29
View File
@@ -12,13 +12,13 @@ import haxe.Exception;
* Requires the `GITHUB_API` macro to be defined.
* This has no authentication, so it's limited to unauthenticated requests, and rate limits.
**/
class GitHub {
final class GitHub {
/**
* Gets all the releases from a specific GitHub repository using the GitHub API.
* @param user The user/organization that owns the repository
* @param repository The repository name
* @param onError Error Callback
* @return Releases
* @param user The user/organization that owns the repository.
* @param repository The repository name.
* @param onError Error Callback.
* @return Releases in Array.
*/
public static function getReleases(user:String, repository:String, ?onError:Exception->Void):Array<GitHubRelease> {
#if GITHUB_API
@@ -38,17 +38,16 @@ class GitHub {
/**
* Gets the contributors list from a specific GitHub repository using the GitHub API.
* @param user The user/organization that owns the repository
* @param repository The repository name
* @param onError Error Callback
* @return Contributors List
* @param user The user/organization that owns the repository.
* @param repository The repository name.
* @param onError Error Callback.
* @return Contributors List as Array.
*/
public static function getContributors(user:String, repository:String, ?onError:Exception->Void):Array<GitHubContributor> {
#if GITHUB_API
try {
var data = Json.parse(HttpUtil.requestText('https://api.github.com/repos/${user}/${repository}/contributors'));
if (!(data is Array))
throw __parseGitHubException(data);
if (!(data is Array)) throw __parseGitHubException(data);
return data;
} catch(e) {
@@ -61,16 +60,15 @@ class GitHub {
/**
* Gets a specific GitHub organization using the GitHub API.
* @param org The organization to get
* @param onError Error Callback
* @return Organization
* @param org The organization to get.
* @param onError Error Callback.
* @return Organization.
*/
public static function getOrganization(org:String, ?onError:Exception->Void):GitHubOrganization {
#if GITHUB_API
try {
var data = Json.parse(HttpUtil.requestText('https://api.github.com/orgs/$org'));
if (Reflect.hasField(data, "documentation_url"))
throw __parseGitHubException(data);
if (Reflect.hasField(data, "documentation_url")) throw __parseGitHubException(data);
return data;
} catch(e) {
@@ -84,16 +82,15 @@ class GitHub {
/**
* Gets the members list from a specific GitHub organization using the GitHub API.
* NOTE: Members use Contributors' structure!
* @param org The organization to get the members from
* @param onError Error Callback
* @return Members List
* @param org The organization to get the members from.
* @param onError Error Callback.
* @return Members List as Array
*/
public static function getOrganizationMembers(org:String, ?onError:Exception->Void):Array<GitHubContributor> {
#if GITHUB_API
try {
var data = Json.parse(HttpUtil.requestText('https://api.github.com/orgs/$org/members'));
if (Reflect.hasField(data, "documentation_url"))
throw __parseGitHubException(data);
if (Reflect.hasField(data, "documentation_url")) throw __parseGitHubException(data);
return data;
} catch(e) {
@@ -107,18 +104,15 @@ class GitHub {
/**
* Gets a specific GitHub user/organization using the GitHub API.
* NOTE: If organization, it will be returned with the structure of a normal user; use `getOrganization` if you specifically want an organization!
* @param user The user/organization to get
* @param onError Error Callback
* @return User/Organization
* @param user The user/organization to get.
* @param onError Error Callback.
* @return User/Organization.
*/
public static function getUser(user:String, ?onError:Exception->Void):GitHubUser {
#if GITHUB_API
try {
var url = 'https://api.github.com/users/$user';
var data = Json.parse(HttpUtil.requestText(url));
if (Reflect.hasField(data, "documentation_url"))
throw __parseGitHubException(data);
var data = Json.parse(HttpUtil.requestText('https://api.github.com/users/$user'));
if (Reflect.hasField(data, "documentation_url")) throw __parseGitHubException(data);
return data;
} catch(e) {
@@ -33,11 +33,9 @@ class FlagMacro {
resetExprs.push(macro $i{"customFlags"} = $v{[]});
for (field in fields) {
var skip = field.meta.hasMeta(":bypass");
if (field.meta.hasMeta(":bypass")) continue;
var hasLazy = field.meta.hasMeta(":lazy");
if (skip) continue;
switch (field.kind) {
case FVar(type, expr):
var isNullable = false;
@@ -24,7 +24,11 @@ class UpdateAvailableScreen extends MusicBeatState {
public function new(check:UpdateCheckCallback) {
super();
this.check = check;
if (Date.now().getTime() - check.date.getTime() > 15000) {
if ((check = UpdateUtil.checkForUpdates(true)).newUpdate) this.check = check;
}
}
public override function create() {
@@ -2,60 +2,97 @@ package funkin.backend.system.updating;
import funkin.backend.system.github.GitHub;
import funkin.backend.system.github.GitHubRelease;
import haxe.io.Path;
import lime.app.Application;
import sys.thread.Mutex;
import sys.thread.Thread;
import sys.FileSystem;
import haxe.io.Path;
using funkin.backend.system.github.GitHub;
class UpdateUtil {
public static var lastUpdateCheck:Null<UpdateCheckCallback>;
private static var __waitCallbacks:Array<UpdateCheckCallback->Void>;
private static var __mutex:Mutex;
public static function init() {
// deletes old bak file if it exists
#if sys
var bakPath = '${Path.withoutExtension(Sys.programPath())}.bak';
if (FileSystem.exists(bakPath))
FileSystem.deleteFile(bakPath);
if (FileSystem.exists(bakPath)) FileSystem.deleteFile(bakPath);
#end
__waitCallbacks = [];
__mutex = new Mutex();
Thread.create(checkForUpdates.bind(true, false));
}
public static function checkForUpdates():UpdateCheckCallback {
var curTag = 'v${Flags.VERSION}';
trace("Current version: " + curTag);
public static function waitForUpdates(force = false, callback:UpdateCheckCallback->Void, lazy = false) {
if (__mutex.tryAcquire()) {
__mutex.release();
if (__shouldCheck(lazy) || force) {
__waitCallbacks.push(callback);
Thread.create(checkForUpdates.bind(force, false));
}
else
callback(lastUpdateCheck);
}
else
__waitCallbacks.push(callback);
}
var error = false;
public static function checkForUpdates(force = false, lazy = false):UpdateCheckCallback {
var wasAcquired = !__mutex.tryAcquire();
if (wasAcquired) __mutex.acquire();
var newUpdates = __doReleaseFiltering(GitHub.getReleases(Flags.REPO_OWNER, Flags.REPO_NAME, function(e) {
if ((!force || wasAcquired) && !__shouldCheck(lazy)) {
__mutex.release();
return lastUpdateCheck;
}
lastUpdateCheck = __checkForUpdates();
__mutex.release();
FlxG.signals.preUpdate.addOnce(__callWaitCallbacks);
return lastUpdateCheck;
}
static function __checkForUpdates():UpdateCheckCallback {
var curTag = 'v${Flags.VERSION}', error = false;
var newUpdates = __doReleaseFiltering(GitHub.getReleases(Flags.REPO_OWNER, Flags.REPO_NAME, (e) -> {
error = true;
}), curTag);
if (error) return {
success: false,
newUpdate: false
};
if (newUpdates.length <= 0) {
return {
success: true,
newUpdate: false
};
var updateCheck:UpdateCheckCallback = {
success: !error,
newUpdate: !error && newUpdates.length > 0,
currentVersionTag: curTag,
date: Date.now()
}
return {
success: true,
newUpdate: true,
currentVersionTag: curTag,
newVersionTag: newUpdates.last().tag_name,
updates: newUpdates
};
if (updateCheck.newUpdate) updateCheck.newVersionTag = (updateCheck.updates = newUpdates).last().tag_name;
return updateCheck;
}
static function __shouldCheck(lazy:Bool):Bool
return lastUpdateCheck == null || !lazy && (!lastUpdateCheck.newUpdate || Date.now().getTime() - lastUpdateCheck.date.getTime() > 1800000);
static function __callWaitCallbacks() {
for (callback in __waitCallbacks) callback(lastUpdateCheck);
__waitCallbacks.resize(0);
}
static var __curVersionPos = -2;
static function __doReleaseFiltering(releases:Array<GitHubRelease>, currentVersionTag:String) {
releases = releases.filterReleases(Options.betaUpdates, false);
if (releases.length <= 0)
return releases;
var newArray:Array<GitHubRelease> = [];
var newArray:Array<GitHubRelease> = [], __curVersionPos = -2;
var skipNextBinaryChecks:Bool = false;
for(index in 0...releases.length) {
@@ -73,12 +110,9 @@ class UpdateUtil {
}
if (containsBinary) {
skipNextBinaryChecks = true; // no need to check for older versions
if (release.tag_name == currentVersionTag) {
__curVersionPos = -1;
}
if (release.tag_name == currentVersionTag) __curVersionPos = -1;
newArray.insert(0, release);
if (__curVersionPos > -2)
__curVersionPos++;
if (__curVersionPos > -2) __curVersionPos++;
}
}
if (__curVersionPos < -1)
@@ -98,4 +132,6 @@ typedef UpdateCheckCallback = {
@:optional var newVersionTag:String;
@:optional var updates:Array<GitHubRelease>;
@:optional var date:Date;
}
+2 -1
View File
@@ -474,7 +474,7 @@ final class AudioAnalyzer {
var n = Math.floor((endPos - startPos) * __toBits);
if (startPos >= time && startPos < backend.bufferTimes[backend.bufferSizes.length - 1] * 1000) {
var pos = Math.floor((startPos - time) * __toBits), buf = backend.bufferDatas[i].buffer, size = backend.bufferSizes[i], c = 0;
while (pos > size) {
while (pos >= size) {
if (++i >= backend.bufferSizes.length) {
n = 0;
break;
@@ -484,6 +484,7 @@ final class AudioAnalyzer {
size = backend.bufferSizes[i];
}
pos -= pos % __sampleSize;
n -= pos % __sampleSize;
while (n > 0) {
callback(getByte(buf, pos, __wordSize), c);
@@ -29,9 +29,6 @@ final class WindowUtils {
public static inline function resetClosing() __triedClosing = false;
@:dox(hide) public static inline function init() {
resetTitle();
resetClosing();
Lib.application.window.onClose.add(function () {
if (preventClosing && !__triedClosing) {
Lib.application.window.onClose.cancel();
+5 -5
View File
@@ -56,9 +56,9 @@ LOGO_TEXT=""
super.createPost();
hadPopup = true;
var substate = new UIWarningSubstate("Missing mod folder configuration!", "Your mod is currently missing a mod config file!\n\nWould you like to automatically generate one?\n\n(PS: If this is not your mod please disable Developer mode to stop this popup from appearing.)", [
var substate = new UIWarningSubstate(TU.translate("modConfigWarning.warningTitle"), TU.translate("modConfigWarning.warningDesc"), [
{
label: "Not Now",
label: TU.translate("editor.notNow"),
color: 0x969533,
onClick: function (_) {
MusicBeatState.skipTransOut = MusicBeatState.skipTransIn = false;
@@ -66,13 +66,13 @@ LOGO_TEXT=""
}
},
{
label: "Yes",
label: TU.translate("editor.yes"),
onClick: function(_) {
var path = '${library.folderPath}/data/config/modpack.ini';
CoolUtil.safeSaveFile(path, defaultModConfigText);
openSubState(new UIWarningSubstate("Created mod config!", "Your mod config file has been created at " + path + "!", [
openSubState(new UIWarningSubstate(TU.translate("modConfigWarning.createdTitle"), TU.translate("modConfigWarning.createdDesc", [path]), [
{
label: "Ok",
label: TU.translate("editor.ok"),
onClick: function (_) {
MusicBeatState.skipTransOut = MusicBeatState.skipTransIn = false;
FlxG.switchState(cast Type.createInstance(goToState, []));
@@ -209,6 +209,9 @@ class AlphabetEditor extends UIState {
scaleX: 1,
scaleY: 1,
flipX: false,
flipY: false,
hasColorMode: false,
colorMode: bigLetter.colorMode
};
@@ -204,6 +204,9 @@ UPPERLETTER and LOWERLETTTER can be used in any backup prefix.";
cos: 1.0,
sin: 0.0,
flipX: false,
flipY: false,
hasColorMode: false,
colorMode: 0
}],
@@ -11,6 +11,9 @@ class GlyphInfoWindow extends UIWindow {
public var angleBox:UINumericStepper;
public var colorModeDrop:UIDropDown;
public var flipXBox:UICheckbox;
public var flipYBox:UICheckbox;
public var outlineTitle:UIText;
public var outlineSep:UISprite;
public var outlineCheck:UICheckbox;
@@ -145,6 +148,9 @@ class GlyphInfoWindow extends UIWindow {
cos: compon.cos,
sin: compon.sin,
flipX: compon.flipX,
flipY: compon.flipY,
hasColorMode: compon.hasColorMode,
colorMode: compon.colorMode
};
@@ -159,6 +165,22 @@ class GlyphInfoWindow extends UIWindow {
}
members.push(outlineCheck);
flipXBox = new UICheckbox(outlineCheck.x, outlineCheck.y - outlineCheck.height - 8, "Flip X?");
flipXBox.onChecked = function(check:Bool) {
compon.flipX = check;
if (outlineCheck.checked)
data.components[compon.outIndex].flipX = check;
}
members.push(flipXBox);
flipYBox = new UICheckbox(flipXBox.x + flipXBox.width + itemMargin * 5, flipXBox.y, "Flip Y?");
flipYBox.onChecked = function(check:Bool) {
compon.flipY = check;
if (outlineCheck.checked)
data.components[compon.outIndex].flipY = check;
}
members.push(flipYBox);
outlineTitle = new UIText(colorModeDrop.x, colorModeDrop.y + colorModeDrop.bHeight + itemMargin, 0, "Outline Data");
members.push(outlineTitle);
outlineSep = new UISprite(x + itemMargin, colorModeDrop.y + colorModeDrop.bHeight + itemMargin + 19);
@@ -239,6 +261,8 @@ class GlyphInfoWindow extends UIWindow {
angleBox.value = com.angle;
@:privateAccess colorModeDrop.index = com.hasColorMode ? com.colorMode : AlphabetEditor.instance.tape.colorMode;
@:privateAccess colorModeDrop.label.text = colorModeDrop.options[colorModeDrop.index];
flipXBox.checked = com.flipX;
flipYBox.checked = com.flipY;
outlineCheck.checked = com.outIndex != null;
outlineBox.label.text = "";
+6 -4
View File
@@ -601,11 +601,13 @@ class Charter extends UIState {
Conductor.setupSong(PlayState.SONG);
noteTypes = PlayState.SONG.noteTypes;
FlxG.sound.setMusic(FlxG.sound.load(Paths.inst(__song, __diff)));
if (PlayState.SONG.meta.needsVoices != false && Assets.exists(Paths.voices(__song, __diff))) // null or true
vocals = FlxG.sound.load(Paths.voices(__song, __diff));
FlxG.sound.setMusic(FlxG.sound.load(Paths.inst(__song, __diff, PlayState.SONG.meta.instSuffix)));
if (Assets.exists(Paths.voices(__song, __diff, PlayState.SONG.meta.vocalsSuffix)))
vocals = FlxG.sound.load(Paths.voices(__song, __diff, PlayState.SONG.meta.vocalsSuffix));
else
vocals = new FlxSound();
vocals.muted = !PlayState.SONG.meta.needsVoices;
vocals.group = FlxG.sound.defaultMusicGroup;
gridBackdrops.createGrids(PlayState.SONG.strumLines.length);
@@ -683,7 +685,7 @@ class Charter extends UIState {
if (FlxG.sound.music.loaded)
wavesToGenerate.push({name: "Inst.ogg", sound: FlxG.sound.music});
if (PlayState.SONG.meta.needsVoices != false && vocals.loaded)
if (vocals.loaded)
wavesToGenerate.push({name: "Voices.ogg", sound: vocals});
for (strumLine in strumLines)
@@ -23,6 +23,7 @@ class CharterMetaDataScreen extends UISubstateWindow {
public var iconSprite:HealthIcon;
public var opponentModeCheckbox:UICheckbox;
public var coopAllowedCheckbox:UICheckbox;
public var needsVoicesCheckbox:UICheckbox;
public var colorWheel:UIColorwheel;
public var difficultiesTextBox:UITextBox;
@@ -68,6 +69,9 @@ class CharterMetaDataScreen extends UISubstateWindow {
denominatorStepper = new UINumericStepper(beatsPerMeasureStepper.x + 30 + 24, beatsPerMeasureStepper.y, Math.floor(16 / metadata.stepsPerBeat), 1, 0, 1, null, 54);
add(denominatorStepper);
needsVoicesCheckbox = new UICheckbox(beatsPerMeasureStepper.x + 100, beatsPerMeasureStepper.y + 6, translate("needsVoices"), metadata.needsVoices);
add(needsVoicesCheckbox);
add(title = new UIText(songNameTextBox.x, songNameTextBox.y + 10 + 46, 0, translate("menusData"), 28));
displayNameTextBox = new UITextBox(title.x, title.y + title.height + 36, metadata.displayName);
@@ -149,6 +153,7 @@ class CharterMetaDataScreen extends UISubstateWindow {
PlayState.SONG.meta = {
name: songNameTextBox.label.text,
bpm: bpmStepper.value,
needsVoices: needsVoicesCheckbox.checked,
beatsPerMeasure: Std.int(beatsPerMeasureStepper.value),
stepsPerBeat: Std.int(16 / denominatorStepper.value),
displayName: displayNameTextBox.label.text,
@@ -161,5 +166,6 @@ class CharterMetaDataScreen extends UISubstateWindow {
};
Charter.instance.updateBPMEvents();
Charter.instance.vocals.muted = !needsVoicesCheckbox.checked;
}
}
@@ -171,7 +171,7 @@ class CharterStrumlineScreen extends UISubstateWindow {
}
}
vocalsSuffixDropDown = new UIDropDown(typeDropdown.x, hudScaleStepper.y + 128, 200, 32, suffixList, strumLine.vocalsSuffix != null && strumLine.vocalsSuffix != "" ? suffixList.indexOf(strumLine.vocalsSuffix) : 0);
vocalsSuffixDropDown = new UIDropDown(typeDropdown.x, hudScaleStepper.y + 128, 200, 32, suffixList, strumLine.vocalsSuffix != null && strumLine.vocalsSuffix != "" && Assets.exists(Paths.voices(Charter.__song.toLowerCase(), "", strumLine.vocalsSuffix)) ? suffixList.indexOf(strumLine.vocalsSuffix) : 0);
add(vocalsSuffixDropDown);
addLabelOn(vocalsSuffixDropDown, TU.translate("charterStrumLine.vocalSuffix"));
@@ -7,6 +7,7 @@ import funkin.backend.chart.ChartData;
import funkin.backend.chart.FNFLegacyParser.SwagSong;
import funkin.backend.chart.PsychParser;
import funkin.backend.chart.VSliceParser;
import funkin.backend.utils.ZipUtil;
import funkin.backend.utils.ZipUtil.ZipReader;
import funkin.game.HealthIcon;
import haxe.Json;
@@ -360,11 +361,11 @@ class SongCreationScreen extends UISubstateWindow {
{
try switch(engineDropdown.index)
{
case 0 /*"V-Slice Project (.fnfc)"*/:
case 2 /*"V-Slice Project (.fnfc)"*/:
var files:Map<String, Any> = [];
for (field in new ZipReader(new BytesInput(importChartFile.file)).read()) {
var fileName = field.fileName;
var fileContent = field.data;
var fileContent = ZipUtil.unzip(field);
files.set(fileName, fileContent);
}
saveFromVSlice(files);
@@ -405,8 +406,8 @@ class SongCreationScreen extends UISubstateWindow {
CoolUtil.safeSaveFile('$songFolder/charts/${songId}.json', Json.stringify(base, Flags.JSON_PRETTY_PRINT));
#end
});
} catch (e) {
openSubState(new UIWarningSubstate("Importing Song/Charts: Error!", Std.string(e), [
} catch (e:haxe.Exception) {
openSubState(new UIWarningSubstate("Importing Song/Charts: Error!", e.details(), [
{label: "Ok", color: 0xFFFF0000, onClick: function(t) {}}
]));
}
+28 -38
View File
@@ -552,7 +552,7 @@ class PlayState extends MusicBeatState
@:noCompletion @:dox(hide) private var _endSongCalled:Bool = false;
@:dox(hide)
var __vocalSyncTimer:Float = 0;
var __vocalSyncTimer:Float = 1;
private function get_accuracy():Float {
if (accuracyPressedNotes <= 0) return -1;
@@ -1113,9 +1113,11 @@ class PlayState extends MusicBeatState
curSong = songData.meta.name.toLowerCase();
curSongID = curSong.replace(" ", "-");
FlxG.sound.setMusic(inst = FlxG.sound.load(Assets.getMusic(Paths.inst(SONG.meta.name, difficulty))));
if (SONG.meta.needsVoices != false && Assets.exists(Paths.voices(SONG.meta.name, difficulty))) // null or true
vocals = FlxG.sound.load(Options.streamedVocals ? Assets.getMusic(Paths.voices(SONG.meta.name, difficulty)) : Paths.voices(SONG.meta.name, difficulty));
FlxG.sound.setMusic(inst = FlxG.sound.load(Assets.getMusic(Paths.inst(SONG.meta.name, difficulty, SONG.meta.instSuffix))));
var vocalsPath = Paths.voices(SONG.meta.name, difficulty, SONG.meta.vocalsSuffix);
if (SONG.meta.needsVoices && Assets.exists(vocalsPath))
vocals = FlxG.sound.load(Options.streamedVocals ? Assets.getMusic(vocalsPath) : vocalsPath);
else
vocals = new FlxSound();
@@ -1224,7 +1226,7 @@ class PlayState extends MusicBeatState
var time = Conductor.songPosition + Conductor.songOffset;
for (strumLine in strumLines.members) strumLine.vocals.play(true, time);
vocals.play(true, time);
inst.play(true, time);
if (!inst.playing) inst.play(true, time);
gameAndCharsCall("onVocalsResync");
}
@@ -1375,20 +1377,19 @@ class PlayState extends MusicBeatState
updateIconPositions();
if (startingSong) {
if (startedCountdown) {
Conductor.songPosition += Conductor.songOffset + elapsed * 1000;
if (Conductor.songPosition >= 0)
startSong();
if (startedCountdown && (Conductor.songPosition += Conductor.songOffset + elapsed * 1000) >= 0) {
Conductor.songPosition = Conductor.songOffset;
startSong();
}
}
else if (FlxG.sound.music != null && (__vocalSyncTimer -= elapsed) < 0) {
__vocalSyncTimer = 1;
var instTime = FlxG.sound.music.getActualTime();
var isOffsync:Bool = vocals.loaded && Math.abs(instTime - vocals.getActualTime()) > 30;
var isOffsync:Bool = vocals.loaded && Math.abs(instTime - vocals.getActualTime()) > 100;
if (!isOffsync) {
for (strumLine in strumLines.members) {
if ((isOffsync = strumLine.vocals.loaded && Math.abs(instTime - strumLine.vocals.getActualTime()) > 30)) break;
if ((isOffsync = strumLine.vocals.loaded && Math.abs(instTime - strumLine.vocals.getActualTime()) > 100)) break;
}
}
@@ -1689,17 +1690,12 @@ class PlayState extends MusicBeatState
endingSong = true;
gameAndCharsCall("onSongEnd");
canPause = false;
inst.volume = 0;
vocals.volume = 0;
for (strumLine in strumLines.members) {
strumLine.vocals.volume = 0;
strumLine.vocals.pause();
}
inst.pause();
vocals.pause();
if (validScore)
{
for (strumLine in strumLines.members) strumLine.vocals.stop();
inst.stop();
vocals.stop();
if (validScore) {
#if !switch
FunkinSave.setSongHighscore(SONG.meta.name, difficulty, {
score: songScore,
@@ -1727,20 +1723,18 @@ class PlayState extends MusicBeatState
* Immediately switches to the next song, or goes back to the Story/Freeplay menu.
*/
public function nextSong() {
if (isStoryMode)
{
if (isStoryMode) {
campaignScore += songScore;
campaignMisses += misses;
campaignAccuracyTotal += accuracy;
campaignAccuracyCount++;
storyPlaylist.shift();
if (storyPlaylist.length <= 0)
{
if (storyPlaylist.length <= 0) {
FlxG.switchState(new StoryMenuState());
if (validScore)
{
if (validScore) {
#if !switch
// TODO: more week info saving
FunkinSave.setWeekHighscore(storyWeek.id, difficulty, {
score: campaignScore,
@@ -1749,29 +1743,23 @@ class PlayState extends MusicBeatState
hits: [],
date: Date.now().toString()
});
#end
}
FlxG.save.flush();
}
else
{
else {
Logs.infos('Loading next song (${storyPlaylist[0].toLowerCase()}/$difficulty)', "PlayState");
registerSmoothTransition();
FlxG.sound.music.stop();
__loadSong(storyPlaylist[0], difficulty);
FlxG.switchState(new PlayState());
}
}
else if (chartingMode)
FlxG.switchState(new funkin.editors.charter.Charter(SONG.meta.name, difficulty, false));
else
{
if (chartingMode)
FlxG.switchState(new funkin.editors.charter.Charter(SONG.meta.name, difficulty, false));
else
FlxG.switchState(new FreeplayState());
}
FlxG.switchState(new FreeplayState());
}
public function registerSmoothTransition() {
@@ -1943,6 +1931,8 @@ class PlayState extends MusicBeatState
}
if (event.deleteNote) strumLine.deleteNote(note);
gameAndCharsEvent("onPostNoteHit", event);
}
public function displayRating(myRating:String, ?evt:NoteHitEvent = null):Void {
+66 -31
View File
@@ -19,6 +19,16 @@ class FreeplayState extends MusicBeatState
*/
public var songs:Array<ChartMetaData> = [];
/**
* Current song metadata
*/
public var curSong:Null<ChartMetaData>;
/**
* Current song metadata difficulty
*/
public var curSongDifficulty:Null<String>;
/**
* Currently selected song
*/
@@ -43,7 +53,7 @@ class FreeplayState extends MusicBeatState
public var diffText:FlxText;
/**
* Text containing the current coop/opponent mode ([TAB] Co-Op mode)
* Text containing the current coop/opponent mode ([KEYBINDS] Co-Op mode)
*/
public var coopText:FlxText;
@@ -107,7 +117,9 @@ class FreeplayState extends MusicBeatState
curSelected = k;
}
}
if (songs[curSelected] != null) {
updateCurSongMeta();
if (curSong != null) {
for(k=>diff in songs[curSelected].difficulties) {
if (diff == Options.freeplayLastDifficulty) {
curDifficulty = k;
@@ -225,7 +237,7 @@ class FreeplayState extends MusicBeatState
if (canSelect) {
changeSelection((controls.UP_P ? -1 : 0) + (controls.DOWN_P ? 1 : 0) - FlxG.mouse.wheel);
changeDiff((controls.LEFT_P ? -1 : 0) + (controls.RIGHT_P ? 1 : 0));
changeCoopMode((FlxG.keys.justPressed.TAB ? 1 : 0)); // TODO: make this configurable
changeCoopMode((controls.CHANGE_MODE ? 1 : 0)); // TODO: make this configurable
// putting it before so that its actually smooth
updateOptionsAlpha();
}
@@ -238,14 +250,14 @@ class FreeplayState extends MusicBeatState
scoreText.x = coopText.x = scoreBG.x + 4;
diffText.x = Std.int(scoreBG.x + ((scoreBG.width - diffText.width) / 2));
interpColor.fpsLerpTo(songs[curSelected].color, 0.0625);
interpColor.fpsLerpTo(curSong.color, 0.0625);
bg.color = interpColor.color;
#if PRELOAD_ALL
var dontPlaySongThisFrame = false;
autoplayElapsed += elapsed;
if (!disableAutoPlay && !songInstPlaying && (autoplayElapsed > timeUntilAutoplay || FlxG.keys.justPressed.SPACE)) {
if (curPlayingInst != (curPlayingInst = Paths.inst(songs[curSelected].name, songs[curSelected].difficulties[curDifficulty]))) {
if (!disableAutoPlay && !songInstPlaying && (autoplayElapsed > timeUntilAutoplay)) {
if (curPlayingInst != (curPlayingInst = Paths.inst(curSong.name, curSongDifficulty, curSong.instSuffix))) {
var streamed = false;
if (Options.streamedMusic) {
var sound = Assets.getMusic(curPlayingInst, true, false);
@@ -253,7 +265,7 @@ class FreeplayState extends MusicBeatState
if (streamed && autoplayShouldPlay) {
FlxG.sound.playMusic(sound, 0);
Conductor.changeBPM(songs[curSelected].bpm, songs[curSelected].beatsPerMeasure, songs[curSelected].stepsPerBeat);
Conductor.changeBPM(curSong.bpm, curSong.beatsPerMeasure, curSong.stepsPerBeat);
}
}
@@ -268,7 +280,7 @@ class FreeplayState extends MusicBeatState
if (sound != null && autoplayShouldPlay) {
FlxG.sound.playMusic(sound, 0);
Conductor.changeBPM(songs[curSelected].bpm, songs[curSelected].beatsPerMeasure, songs[curSelected].stepsPerBeat);
Conductor.changeBPM(curSong.bpm, curSong.beatsPerMeasure, curSong.stepsPerBeat);
}
}
if (!disableAsyncLoading) Main.execAsync(huh);
@@ -302,7 +314,6 @@ class FreeplayState extends MusicBeatState
function updateCoopModes() {
__opponentMode = false;
__coopMode = false;
var curSong = songs[curSelected];
if (curSong.coopAllowed && curSong.opponentModeAllowed) {
__opponentMode = curCoopMode % 2 == 1;
__coopMode = curCoopMode >= 2;
@@ -321,7 +332,7 @@ class FreeplayState extends MusicBeatState
if (songs[curSelected].difficulties.length <= 0) return;
var event = event("onSelect", EventManager.get(FreeplaySongSelectEvent).recycle(songs[curSelected].name, songs[curSelected].difficulties[curDifficulty], __opponentMode, __coopMode));
var event = event("onSelect", EventManager.get(FreeplaySongSelectEvent).recycle(curSong.name, curSongDifficulty, __opponentMode, __coopMode));
if (event.cancelled) return;
@@ -329,17 +340,17 @@ class FreeplayState extends MusicBeatState
autoplayShouldPlay = false;
#end
Options.freeplayLastSong = songs[curSelected].name;
Options.freeplayLastDifficulty = songs[curSelected].difficulties[curDifficulty];
Options.freeplayLastSong = curSong.name;
Options.freeplayLastDifficulty = curSongDifficulty;
PlayState.loadSong(event.song, event.difficulty, event.opponentMode, event.coopMode);
FlxG.switchState(new PlayState());
}
public function convertChart() {
trace('Converting ${songs[curSelected].name} (${songs[curSelected].difficulties[curDifficulty]}) to Codename format...');
var chart = Chart.parse(songs[curSelected].name, songs[curSelected].difficulties[curDifficulty]);
Chart.save('${Main.pathBack}assets/songs/${songs[curSelected].name}', chart, songs[curSelected].difficulties[curDifficulty]);
trace('Converting ${curSong.name} (${curSongDifficulty}) to Codename format...');
var chart = Chart.parse(curSong.name, curSongDifficulty);
Chart.save('${Main.pathBack}assets/songs/${curSong.name}', chart, curSongDifficulty);
}
/**
@@ -351,20 +362,31 @@ class FreeplayState extends MusicBeatState
{
if (change == 0 && !force) return;
var curSong = songs[curSelected];
var validDifficulties = curSong.difficulties.length > 0;
var event = event("onChangeDiff", EventManager.get(MenuChangeEvent).recycle(curDifficulty, validDifficulties ? FlxMath.wrap(curDifficulty + change, 0, curSong.difficulties.length-1) : 0, change));
var song = songs[curSelected];
var validDifficulties = song.difficulties.length > 0;
var event = event("onChangeDiff", EventManager.get(MenuChangeEvent).recycle(curDifficulty, validDifficulties ? FlxMath.wrap(curDifficulty + change, 0, song.difficulties.length-1) : 0, change));
if (event.cancelled) return;
if (event.cancelled) {
if (force) updateCurSongMeta();
return;
}
var prevInstSuffix = curSong.instSuffix;
curDifficulty = event.value;
updateCurSongMeta();
updateScore();
if (curSong.difficulties.length > 1)
diffText.text = '< ${curSong.difficulties[curDifficulty].toUpperCase()} >';
#if PRELOAD_ALL
if (curSong.instSuffix != prevInstSuffix) {
autoplayElapsed = 0;
songInstPlaying = false;
}
#end
if (song.difficulties.length > 1)
diffText.text = '< ${song.difficulties[curDifficulty].toUpperCase()} >';
else
diffText.text = validDifficulties ? curSong.difficulties[curDifficulty].toUpperCase() : "-";
diffText.text = validDifficulties ? song.difficulties[curDifficulty].toUpperCase() : "-";
}
function updateScore() {
@@ -376,7 +398,7 @@ class FreeplayState extends MusicBeatState
var changes:Array<HighscoreChange> = [];
if (__coopMode) changes.push(CCoopMode);
if (__opponentMode) changes.push(COpponentMode);
var saveData = FunkinSave.getSongHighscore(songs[curSelected].name, songs[curSelected].difficulties[curDifficulty], changes);
var saveData = FunkinSave.getSongHighscore(curSong.name, curSongDifficulty, changes);
intendedScore = saveData.score;
}
@@ -397,9 +419,9 @@ class FreeplayState extends MusicBeatState
*/
public function changeCoopMode(change:Int = 0, force:Bool = false) {
if (change == 0 && !force) return;
if (!songs[curSelected].coopAllowed && !songs[curSelected].opponentModeAllowed) return;
if (!curSong.coopAllowed && !curSong.opponentModeAllowed) return;
var bothEnabled = songs[curSelected].coopAllowed && songs[curSelected].opponentModeAllowed;
var bothEnabled = curSong.coopAllowed && curSong.opponentModeAllowed;
var event = event("onChangeCoopMode", EventManager.get(MenuChangeEvent).recycle(curCoopMode, FlxMath.wrap(curCoopMode + change, 0, bothEnabled ? 3 : 1), change));
if (event.cancelled) return;
@@ -408,12 +430,16 @@ class FreeplayState extends MusicBeatState
updateScore();
var key = "[TAB] "; // TODO: make this configurable
var coopBinds = [CoolUtil.keyToString(Options.P1_CHANGE_MODE[0]), CoolUtil.keyToString(Options.P2_CHANGE_MODE[0])].filter(x -> x != "---");
if (coopBinds.length == 2 && coopBinds[1] == coopBinds[0]) coopBinds.pop();
else if (coopBinds.length == 0) coopBinds.push("---");
var key = '[${coopBinds.join(" / ")}] ';
if (bothEnabled) {
coopText.text = key + coopLabels[curCoopMode];
} else {
coopText.text = key + coopLabels[curCoopMode * (songs[curSelected].coopAllowed ? 2 : 1)];
coopText.text = key + coopLabels[curCoopMode * (curSong.coopAllowed ? 2 : 1)];
}
}
@@ -426,7 +452,6 @@ class FreeplayState extends MusicBeatState
{
if (change == 0 && !force) return;
var bothEnabled = songs[curSelected].coopAllowed && songs[curSelected].opponentModeAllowed;
var event = event("onChangeSelection", EventManager.get(MenuChangeEvent).recycle(curSelected, FlxMath.wrap(curSelected + change, 0, songs.length-1), change));
if (event.cancelled) return;
@@ -440,7 +465,7 @@ class FreeplayState extends MusicBeatState
songInstPlaying = false;
#end
coopText.visible = songs[curSelected].coopAllowed || songs[curSelected].opponentModeAllowed;
coopText.visible = curSong.coopAllowed || curSong.opponentModeAllowed;
}
function updateOptionsAlpha() {
@@ -465,6 +490,16 @@ class FreeplayState extends MusicBeatState
item.alpha = selectedAlpha;
}
}
function updateCurSongMeta() {
var song = songs[curSelected];
if (song == null) {
curSong = null;
curSongDifficulty = null;
}
else if ((curSong = song.metas.get(curSongDifficulty = song.difficulties[curDifficulty])) == null)
curSong = song;
}
}
class FreeplaySonglist {
@@ -485,7 +520,7 @@ class FreeplaySonglist {
}
if (songsFound == null) songsFound = Paths.getFolderDirectories("songs", false, source);
if (songsFound.length > 0) {
for (s in songsFound) songs.push(Chart.loadChartMeta(s, Flags.DEFAULT_DIFFICULTY, source == MODS));
for (s in songsFound) songs.push(Chart.loadChartMeta(s, source == MODS));
return false;
}
return true;
+13 -11
View File
@@ -129,7 +129,7 @@ class TitleState extends MusicBeatState
if (pressedEnter && transitioning && skippedIntro) {
FlxG.camera.stopFX();// FlxG.camera.visible = false;
goToMainMenu();
goToMainMenu(false);
}
if (pressedEnter && !transitioning && skippedIntro)
@@ -152,22 +152,24 @@ class TitleState extends MusicBeatState
transitioning = true;
// FlxG.sound.music.stop();
new FlxTimer().start(2, (_) -> goToMainMenu());
new FlxTimer().start(2, (_) -> goToMainMenu(false));
}
function goToMainMenu() {
function goToMainMenu(force = true) {
#if UPDATE_CHECKING
var report = hasCheckedUpdates ? null : funkin.backend.system.updating.UpdateUtil.checkForUpdates();
hasCheckedUpdates = true;
if (!force && !Flags.DISABLE_AUTOUPDATER) {
funkin.backend.system.updating.UpdateUtil.waitForUpdates(false, (report) -> {
hasCheckedUpdates = true;
if (FlxG.state != this) return;
if (report != null && report.newUpdate) {
FlxG.switchState(new funkin.backend.system.updating.UpdateAvailableScreen(report));
} else {
if (!report.newUpdate) goToMainMenu(true);
else FlxG.switchState(new funkin.backend.system.updating.UpdateAvailableScreen(report));
}, true);
}
else
#end {
FlxG.switchState(new MainMenuState());
}
#else
FlxG.switchState(new MainMenuState());
#end
}
public function createCoolText(textArray:Array<String>)
+27 -3
View File
@@ -37,6 +37,9 @@ final class AlphabetComponent {
public var scaleX:Float;
public var scaleY:Float;
public var flipX:Bool;
public var flipY:Bool;
public var hasColorMode:Bool;
public var colorMode:ColorMode;
}
@@ -346,9 +349,9 @@ class Alphabet extends FlxSprite {
_matrix.translate(_frame.frame.width * -0.5, _frame.frame.height * -0.5);
if (__useDrawScale)
_matrix.scale(__drawScale.x, __drawScale.y);
_matrix.scale(__drawScale.x * (__component.flipX ? -1 : 1), __drawScale.y * (__component.flipY ? -1 : 1));
else
_matrix.scale(__component.scaleX, __component.scaleY);
_matrix.scale(__component.scaleX * (__component.flipX ? -1 : 1), __component.scaleY * (__component.flipY ? -1 : 1));
if(__component.shouldRotate)
_matrix.rotateWithTrig(__component.cos, __component.sin);
_matrix.translate(_frame.frame.width * 0.5, _frame.frame.height * 0.5);
@@ -558,6 +561,9 @@ class Alphabet extends FlxSprite {
cos: angleCos,
sin: angleSin,
flipX: node.get("flipX") == "true",
flipY: node.get("flipY") == "true",
hasColorMode: false,
colorMode: 0
}],
@@ -593,6 +599,9 @@ class Alphabet extends FlxSprite {
var colorMode = ["tint", "offsets", "none"].indexOf(component.get("colorMode"));
var xFlip = component.get("flipX") == "true";
var yFlip = component.get("flipY") == "true";
if (component.get("hasOutline") == "true") {
components.insert(startIndex, {
refIndex: componentsPushed,
@@ -608,6 +617,9 @@ class Alphabet extends FlxSprite {
cos: angleCos,
sin: angleSin,
flipX: xFlip,
flipY: yFlip,
hasColorMode: colorMode >= 0,
colorMode: colorMode,
});
@@ -628,6 +640,9 @@ class Alphabet extends FlxSprite {
cos: angleCos,
sin: angleSin,
flipX: xFlip,
flipY: yFlip,
hasColorMode: colorMode >= 0,
colorMode: colorMode,
});
@@ -659,6 +674,9 @@ class Alphabet extends FlxSprite {
var yScale:Float = Std.parseFloat(node.get("scaleY")).getDefaultFloat(1.0);
var advance:Float = Std.parseFloat(node.get("advance"));
var xFlip = node.get("flipX") == "true";
var yFlip = node.get("flipY") == "true";
var colorMode = ["tint", "offsets", "none"].indexOf(node.get("colorMode"));
var components:Array<AlphabetComponent> = [];
@@ -677,6 +695,9 @@ class Alphabet extends FlxSprite {
cos: angleCos,
sin: angleSin,
flipX: xFlip,
flipY: yFlip,
hasColorMode: colorMode >= 0,
colorMode: colorMode,
});
@@ -696,6 +717,9 @@ class Alphabet extends FlxSprite {
cos: angleCos,
sin: angleSin,
flipX: xFlip,
flipY: yFlip,
hasColorMode: colorMode >= 0,
colorMode: colorMode,
});
@@ -758,7 +782,7 @@ class Alphabet extends FlxSprite {
}
private static var alphabetProperties:Array<String> = ["fps", "advance", "lineGap", "forceCasing", "useColorOffsets", "antialiasing"];
private static var componentProperties:Array<String> = ["name", "anim", "x", "y", "scaleX", "scaleY", "angle", "offsetX", "offsetY"];
private static var componentProperties:Array<String> = ["name", "anim", "x", "y", "scaleX", "scaleY", "angle", "offsetX", "offsetY", "flipX", "flipY"];
public function buildXML():Xml {
var xml = Xml.createElement("alphabet");
+3
View File
@@ -117,6 +117,7 @@ class Options
public static var P1_ACCEPT:Array<FlxKey> = [ENTER];
public static var P1_BACK:Array<FlxKey> = [BACKSPACE];
public static var P1_PAUSE:Array<FlxKey> = [ENTER];
public static var P1_CHANGE_MODE:Array<FlxKey> = [TAB];
// Misc
public static var P1_RESET:Array<FlxKey> = [R];
@@ -148,6 +149,7 @@ class Options
public static var P2_ACCEPT:Array<FlxKey> = [SPACE];
public static var P2_BACK:Array<FlxKey> = [ESCAPE];
public static var P2_PAUSE:Array<FlxKey> = [ESCAPE];
public static var P2_CHANGE_MODE:Array<FlxKey> = [];
// Misc
public static var P2_RESET:Array<FlxKey> = [];
@@ -179,6 +181,7 @@ class Options
public static var SOLO_ACCEPT(get, null):Array<FlxKey>;
public static var SOLO_BACK(get, null):Array<FlxKey>;
public static var SOLO_PAUSE(get, null):Array<FlxKey>;
public static var SOLO_CHANGE_MODE(get, null):Array<FlxKey>;
// Misc
public static var SOLO_RESET(get, null):Array<FlxKey>;
@@ -11,7 +11,7 @@ class MiscOptions extends TreeMenuScreen {
#if UPDATE_CHECKING
add(new Checkbox(getNameID('betaUpdates'), getDescID('betaUpdates'), 'betaUpdates'));
add(new TextOption(getNameID('checkForUpdates'), getDescID('checkForUpdates'), () -> {
var report = funkin.backend.system.updating.UpdateUtil.checkForUpdates();
var report = funkin.backend.system.updating.UpdateUtil.checkForUpdates(true);
if (report.newUpdate) FlxG.switchState(new funkin.backend.system.updating.UpdateAvailableScreen(report));
else {
CoolUtil.playMenuSFX(CANCEL);
@@ -97,17 +97,21 @@ class KeybindsOptions extends MusicBeatSubstate {
name: translate("ui.pause"),
control: 'PAUSE'
},
{
name: translate("ui.changeMode"),
control: 'CHANGE_MODE'
},
]
},
{
name: translate("category.volume"),
settings: [
{
name: translate("up"),
name: translate("volume.up"),
control: 'VOLUME_UP'
},
{
name: translate("down"),
name: translate("volume.down"),
control: 'VOLUME_DOWN'
},
{
@@ -25,10 +25,10 @@ import lime.utils.ArrayBufferView;
@:access(lime.media.AudioBuffer)
@:access(lime.utils.ArrayBufferView)
class NativeAudioSource {
private static var STREAM_BUFFER_SAMPLES:Int = 0x1000;
private static var STREAM_MAX_BUFFERS:Int = 32;
private static var STREAM_BUFFER_SAMPLES:Int = 0x4000;
private static var STREAM_MAX_BUFFERS:Int = 8;
private static var STREAM_TIMER_FREQUENCY:Int = 100;
private static var STREAM_BUFFER_FREQUENCY:Int = 6;
private static var STREAM_BUFFER_FREQUENCY:Int = 3;
private var parent:AudioSource;
private var disposed:Bool;