Author SHA1 Message Date
33070946bd Merge changes from main (#892)
* repair ScriptableShader.shader = null (#877)

* optimize updateInput (#878)

* optimize updateInput

* resize optimize

* __notePerStrum optimize

* Update StrumLine.hx

* NoteGroup optimize (#881)

* NoteGroup optimize

* CoolUtil.bound faster

* makes commit with github desktop W FRAKITS

* Use fastSinCos for notes (#889)

* Update Strum.hx

* Update Strum.hx

---------

Co-authored-by: HEIHUAa <112499486+HEIHUAa@users.noreply.github.com>
Co-authored-by: Frakits <90983918+Frakits@users.noreply.github.com>
2026-01-28 14:21:21 +00:00
Yoshman29 e9dc588b30 Merge branch 'flixel-animate' of https://www.github.com/CodenameCrew/CodenameEngine into flixel-animate 2026-01-28 13:17:19 +01:00
Yoshman29 355cb303b8 Update libs.xml 2026-01-28 13:16:57 +01:00
Yoshman29 904a5ffe43 flixel-animate** i'm clumsy asf i meant the lib not the class name fuck 2026-01-26 23:40:15 +01:00
Yoshman29 9d7e9a4e91 FlxAnimate pull request merge (empty commit) 2026-01-26 23:37:58 +01:00
Yoshman29 5f9b42d70b applyStageMatrix 2026-01-25 22:55:25 +01:00
Yoshman29 2fa73b35e6 Update Character.hx 2026-01-25 22:41:55 +01:00
Yoshman29 c80b9adf7c Fixed pop-ins 2026-01-25 03:09:24 +01:00
Yoshman29 686e70994b gf sad fix + AliasMacro (for backwards comp, manual impl for now) 2026-01-24 03:57:37 +01:00
Yoshman29 046fe794a2 HOW THE FUCK 2026-01-23 17:27:39 +01:00
Yoshman29 54897d7413 fixed some atlases 2026-01-23 00:24:56 +01:00
Yoshman29 9da24cf851 Fixed culling issues with FlxAnimate (fr this time) 2026-01-22 14:24:09 +01:00
Yoshman29 faf171c91f Added label checkbox in Character Editor 2026-01-22 00:12:06 +01:00
Yoshman29 59e04f86e7 OH YEA BABY NEW FLXANIMATE UPDATE THAT MADE CODE 50X SIMPLER **AGAIN** 💖 /pos 2026-01-21 23:05:55 +01:00
Yoshman29 e3e82e8249 fixed animation swapping on atlas characters 2026-01-21 20:37:13 +01:00
Yoshman29 64c29b0d86 FlxAnimateSettings specification 2026-01-21 20:12:25 +01:00
Yoshman29 ae89e0259a Animate Atlas character editor previews 2026-01-21 18:20:39 +01:00
Yoshman29 9d2fd9dc20 zoomFactor for atlases 2026-01-21 11:52:00 +01:00
Yoshman29 8fe3801ad8 early structure for character editor atlas anim display 2026-01-21 03:15:33 +01:00
Yoshman29 549c578287 fixed zoomFactor + temp fix for atlas popping 2026-01-21 02:31:39 +01:00
Yoshman29 42bfaf065d usable character editor (not entirely fixed) 2026-01-19 15:50:09 +01:00
Yoshman29 abbc562d45 Fixed ghosts for atlases 2026-01-19 14:21:38 +01:00
KolzeandGitHub f6694fe4b9 a simple redirect so old mods wont break that badly (#879)
* Update FunkinSprite.hx

* remove trace
2026-01-19 13:46:22 +01:00
Yoshman29 6caf6341c2 Fixed atlases for mods 2026-01-19 12:35:51 +01:00
Yoshman29 8262086610 useRenderTexture added to xml 2026-01-19 01:22:39 +01:00
Yoshman29 c2bdb1b75c frameOffset for atlases (need testing) 2026-01-19 00:48:59 +01:00
Yoshman29 546db6eaa4 Deleted caches (relaunching actions) 2026-01-18 23:45:07 +01:00
Yoshman29 9ab3059626 Removed flxanimate entirely + Fixed transition + Fixed offsets 2026-01-18 23:34:22 +01:00
Yoshman29 632c80bc9d yes it does blehtty wdym?? 2026-01-18 21:54:04 +01:00
Yoshman29 0c8b766b19 I BROKE THE OFFSETS FUCK
.
2026-01-18 21:43:47 +01:00
Yoshman29 246014af60 added flixel-animate to libs 2026-01-18 19:41:44 +01:00
126 changed files with 95338 additions and 16844 deletions
+81 -81
View File
@@ -11,21 +11,20 @@ jobs:
runs-on: ubuntu-24.04
steps:
- name: Pulling the new commit
uses: actions/checkout@v6
with:
fetch-depth: 0
uses: actions/checkout@v4
- name: Setting up Haxe
uses: krdlab/setup-haxe@v2
with:
haxe-version: 4.3.7
# - name: Restore existing build cache for faster compilation
# uses: actions/cache@v4.2.3
# with:
# # not caching the bin folder to prevent asset duplication and stuff like that
# key: cache-build-linux
# path: |
# .haxelib/
# export/release/linux/obj/
- name: Restore existing build cache for faster compilation
uses: actions/cache@v4.2.3
with:
# not caching the bin folder to prevent asset duplication and stuff like that
key: cache-build-linux
path: |
.haxelib/
export/release/linux/haxe/
export/release/linux/obj/
- run: |
echo "HXCPP_COMPILE_CACHE=~/.hxcpp" >> $GITHUB_ENV
- name: Installing LibVLC
@@ -36,47 +35,47 @@ jobs:
haxe -cp commandline -D analyzer-optimize --run Main setup -s
- name: Building the game
run: |
haxelib run lime rebuild linux -nocolor -nocffi -release
haxelib run lime build linux -DCOMPILE_EXPERIMENTAL
haxelib run lime build linux
# - name: Tar files
# run: tar -zcvf CodenameEngine.tar.gz -C export/release/linux/bin .
- name: Uploading artifact (executable)
uses: actions/upload-artifact@v7
uses: actions/upload-artifact@v4
with:
name: Codename Engine (Executable Only)
path: export/release/linux/bin/CodenameEngine
- name: Uploading artifact (entire build)
uses: actions/upload-artifact@v7
uses: actions/upload-artifact@v4
with:
name: Codename Engine
path: export/release/linux/bin/
# - name: Clearing already existing cache
# uses: actions/github-script@v6
# with:
# script: |
# const caches = await github.rest.actions.getActionsCacheList({
# owner: context.repo.owner,
# repo: context.repo.repo,
# })
# for (const cache of caches.data.actions_caches) {
# if (cache.key == "cache-build-linux") {
# console.log('Clearing ' + cache.key + '...')
# await github.rest.actions.deleteActionsCacheById({
# owner: context.repo.owner,
# repo: context.repo.repo,
# cache_id: cache.id,
# })
# console.log("Cache cleared.")
# }
# }
# - name: Uploading new cache
# uses: actions/cache@v4.2.3
# with:
# # caching again since for some reason it doesnt work with the first post cache shit
# key: cache-build-linux
# path: |
# .haxelib/
# export/release/linux/obj/
- name: Clearing already existing cache
uses: actions/github-script@v6
with:
script: |
const caches = await github.rest.actions.getActionsCacheList({
owner: context.repo.owner,
repo: context.repo.repo,
})
for (const cache of caches.data.actions_caches) {
if (cache.key == "cache-build-linux") {
console.log('Clearing ' + cache.key + '...')
await github.rest.actions.deleteActionsCacheById({
owner: context.repo.owner,
repo: context.repo.repo,
cache_id: cache.id,
})
console.log("Cache cleared.")
}
}
- name: Uploading new cache
uses: actions/cache@v4.2.3
with:
# caching again since for some reason it doesnt work with the first post cache shit
key: cache-build-linux
path: |
.haxelib/
export/release/linux/haxe/
export/release/linux/obj/
# didnt compile debug in the same job or github would have said that job wasn't completed until debug was done too (debug uploads are not essential)
debug_build:
@@ -86,19 +85,20 @@ jobs:
needs: build # since its low priority, it'll run after, so actions will concentrate first on normal builds
steps:
- name: Pulling the new commit
uses: actions/checkout@v6
uses: actions/checkout@v4
- name: Setting up Haxe
uses: krdlab/setup-haxe@v2
with:
haxe-version: 4.3.7
# - name: Restore existing build cache for faster compilation
# uses: actions/cache@v4.2.3
# with:
# # not caching the bin folder to prevent asset duplication and stuff like that
# key: cache-build-linux-debug
# path: |
# .haxelib/
# export/debug/linux/obj/
- name: Restore existing build cache for faster compilation
uses: actions/cache@v4.2.3
with:
# not caching the bin folder to prevent asset duplication and stuff like that
key: cache-build-linux-debug
path: |
.haxelib/
export/debug/linux/haxe/
export/debug/linux/obj/
- run: |
echo "HXCPP_COMPILE_CACHE=~/.hxcpp" >> $GITHUB_ENV
- name: Installing LibVLC
@@ -109,39 +109,39 @@ jobs:
haxe -cp commandline -D analyzer-optimize --run Main setup -s
- name: Building the game
run: |
haxelib run lime rebuild linux -nocolor -nocffi -debug
haxelib run lime build linux -debug -DCOMPILE_EXPERIMENTAL
haxelib run lime build linux -debug
# - name: Tar files
# run: tar -zcvf CodenameEngine.tar.gz -C export/debug/linux/bin .
- name: Uploading artifact (entire build)
uses: actions/upload-artifact@v7
uses: actions/upload-artifact@v4
with:
name: Codename Engine Debug
path: export/debug/linux/bin/
# - name: Clearing already existing cache
# uses: actions/github-script@v6
# with:
# script: |
# const caches = await github.rest.actions.getActionsCacheList({
# owner: context.repo.owner,
# repo: context.repo.repo,
# })
# for (const cache of caches.data.actions_caches) {
# if (cache.key == "cache-build-linux-debug") {
# console.log('Clearing ' + cache.key + '...')
# await github.rest.actions.deleteActionsCacheById({
# owner: context.repo.owner,
# repo: context.repo.repo,
# cache_id: cache.id,
# })
# console.log("Cache cleared.")
# }
# }
# - name: Uploading new cache
# uses: actions/cache@v4.2.3
# with:
# # caching again since for some reason it doesnt work with the first post cache shit
# key: cache-build-linux-debug
# path: |
# .haxelib/
# export/debug/linux/obj/
- name: Clearing already existing cache
uses: actions/github-script@v6
with:
script: |
const caches = await github.rest.actions.getActionsCacheList({
owner: context.repo.owner,
repo: context.repo.repo,
})
for (const cache of caches.data.actions_caches) {
if (cache.key == "cache-build-linux-debug") {
console.log('Clearing ' + cache.key + '...')
await github.rest.actions.deleteActionsCacheById({
owner: context.repo.owner,
repo: context.repo.repo,
cache_id: cache.id,
})
console.log("Cache cleared.")
}
}
- name: Uploading new cache
uses: actions/cache@v4.2.3
with:
# caching again since for some reason it doesnt work with the first post cache shit
key: cache-build-linux-debug
path: |
.haxelib/
export/debug/linux/haxe/
export/debug/linux/obj/
+83 -83
View File
@@ -8,24 +8,23 @@ jobs:
build:
name: Mac OS Build
permissions: write-all
runs-on: macos-26
runs-on: macos-14
steps:
- name: Pulling the new commit
uses: actions/checkout@v6
with:
fetch-depth: 0
uses: actions/checkout@v4
- name: Setting up Haxe
uses: krdlab/setup-haxe@v2
with:
haxe-version: 4.3.7
# - name: Restore existing build cache for faster compilation
# uses: actions/cache@v4.2.3
# with:
# # not caching the bin folder to prevent asset duplication and stuff like that
# key: cache-build-mac
# path: |
# .haxelib/
# export/release/macos/obj/
- name: Restore existing build cache for faster compilation
uses: actions/cache@v4.2.3
with:
# not caching the bin folder to prevent asset duplication and stuff like that
key: cache-build-mac
path: |
.haxelib/
export/release/macos/haxe/
export/release/macos/obj/
- run: |
echo "HXCPP_COMPILE_CACHE=~/.hxcpp" >> $GITHUB_ENV
- name: Installing/Updating libraries
@@ -33,69 +32,70 @@ jobs:
haxe -cp commandline -D analyzer-optimize --run Main setup -s
- name: Building the game
run: |
haxelib run lime rebuild mac -nocolor -nocffi -release
haxelib run lime build mac -DCOMPILE_EXPERIMENTAL
arch -x86_64 haxelib run lime build mac
- name: Tar files
run: tar -zcvf CodenameEngine.tar.gz -C export/release/macos/bin .
- name: Uploading artifact (executable)
uses: actions/upload-artifact@v7
uses: actions/upload-artifact@v4
with:
name: Codename Engine (Executable Only)
path: export/release/macos/bin/CodenameEngine.app/Contents/MacOS/CodenameEngine
- name: Uploading artifact (entire build)
uses: actions/upload-artifact@v7
uses: actions/upload-artifact@v4
with:
name: Codename Engine
path: CodenameEngine.tar.gz
# - name: Clearing already existing cache
# uses: actions/github-script@v6
# with:
# script: |
# const caches = await github.rest.actions.getActionsCacheList({
# owner: context.repo.owner,
# repo: context.repo.repo,
# })
# for (const cache of caches.data.actions_caches) {
# if (cache.key == "cache-build-mac") {
# console.log('Clearing ' + cache.key + '...')
# await github.rest.actions.deleteActionsCacheById({
# owner: context.repo.owner,
# repo: context.repo.repo,
# cache_id: cache.id,
# })
# console.log("Cache cleared.")
# }
# }
# - name: Uploading new cache
# uses: actions/cache@v4.2.3
# with:
# # caching again since for some reason it doesnt work with the first post cache shit
# key: cache-build-mac
# path: |
# .haxelib/
# export/release/macos/obj/
- name: Clearing already existing cache
uses: actions/github-script@v6
with:
script: |
const caches = await github.rest.actions.getActionsCacheList({
owner: context.repo.owner,
repo: context.repo.repo,
})
for (const cache of caches.data.actions_caches) {
if (cache.key == "cache-build-mac") {
console.log('Clearing ' + cache.key + '...')
await github.rest.actions.deleteActionsCacheById({
owner: context.repo.owner,
repo: context.repo.repo,
cache_id: cache.id,
})
console.log("Cache cleared.")
}
}
- name: Uploading new cache
uses: actions/cache@v4.2.3
with:
# caching again since for some reason it doesnt work with the first post cache shit
key: cache-build-mac
path: |
.haxelib/
export/release/macos/haxe/
export/release/macos/obj/
# didnt compile debug in the same job or github would have said that job wasn't completed until debug was done too (debug uploads are not essential)
debug_build:
name: Mac OS Debug Build
permissions: write-all
runs-on: macos-26
runs-on: macos-14
needs: build # since its low priority, it'll run after, so actions will concentrate first on normal builds
steps:
- name: Pulling the new commit
uses: actions/checkout@v6
uses: actions/checkout@v4
- name: Setting up Haxe
uses: krdlab/setup-haxe@v2
with:
haxe-version: 4.3.7
# - name: Restore existing build cache for faster compilation
# uses: actions/cache@v4.2.3
# with:
# # not caching the bin folder to prevent asset duplication and stuff like that
# key: cache-build-mac-debug
# path: |
# .haxelib/
# export/debug/macos/obj/
- name: Restore existing build cache for faster compilation
uses: actions/cache@v4.2.3
with:
# not caching the bin folder to prevent asset duplication and stuff like that
key: cache-build-mac-debug
path: |
.haxelib/
export/debug/macos/haxe/
export/debug/macos/obj/
- run: |
echo "HXCPP_COMPILE_CACHE=~/.hxcpp" >> $GITHUB_ENV
- name: Installing/Updating libraries
@@ -103,39 +103,39 @@ jobs:
haxe -cp commandline -D analyzer-optimize --run Main setup -s
- name: Building the game
run: |
haxelib run lime rebuild mac -nocolor -nocffi -debug
haxelib run lime build mac -debug -DCOMPILE_EXPERIMENTAL
arch -x86_64 haxelib run lime build mac -debug
- name: Tar files
run: tar -zcvf CodenameEngine.tar.gz -C export/debug/macos/bin .
- name: Uploading artifact (entire build)
uses: actions/upload-artifact@v7
uses: actions/upload-artifact@v4
with:
name: Codename Engine Debug
path: CodenameEngine.tar.gz
# - name: Clearing already existing cache
# uses: actions/github-script@v6
# with:
# script: |
# const caches = await github.rest.actions.getActionsCacheList({
# owner: context.repo.owner,
# repo: context.repo.repo,
# })
# for (const cache of caches.data.actions_caches) {
# if (cache.key == "cache-build-mac-debug") {
# console.log('Clearing ' + cache.key + '...')
# await github.rest.actions.deleteActionsCacheById({
# owner: context.repo.owner,
# repo: context.repo.repo,
# cache_id: cache.id,
# })
# console.log("Cache cleared.")
# }
# }
# - name: Uploading new cache
# uses: actions/cache@v4.2.3
# with:
# # caching again since for some reason it doesnt work with the first post cache shit
# key: cache-build-mac-debug
# path: |
# .haxelib/
# export/debug/macos/obj/
- name: Clearing already existing cache
uses: actions/github-script@v6
with:
script: |
const caches = await github.rest.actions.getActionsCacheList({
owner: context.repo.owner,
repo: context.repo.repo,
})
for (const cache of caches.data.actions_caches) {
if (cache.key == "cache-build-mac-debug") {
console.log('Clearing ' + cache.key + '...')
await github.rest.actions.deleteActionsCacheById({
owner: context.repo.owner,
repo: context.repo.repo,
cache_id: cache.id,
})
console.log("Cache cleared.")
}
}
- name: Uploading new cache
uses: actions/cache@v4.2.3
with:
# caching again since for some reason it doesnt work with the first post cache shit
key: cache-build-mac-debug
path: |
.haxelib/
export/debug/macos/haxe/
export/debug/macos/obj/
+7 -9
View File
@@ -21,12 +21,10 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@v6
with:
fetch-depth: 0
uses: actions/checkout@v4
- name: Download Windows Full Build
uses: dawidd6/action-download-artifact@v8
uses: dawidd6/action-download-artifact@v6
with:
workflow: windows.yml
name: Codename Engine
@@ -34,7 +32,7 @@ jobs:
allow_forks: false
- name: Download Windows Executable
uses: dawidd6/action-download-artifact@v8
uses: dawidd6/action-download-artifact@v6
with:
workflow: windows.yml
name: Codename Engine (Executable Only)
@@ -42,7 +40,7 @@ jobs:
allow_forks: false
- name: Download Mac OS Full Build
uses: dawidd6/action-download-artifact@v8
uses: dawidd6/action-download-artifact@v6
with:
workflow: macos.yml
name: Codename Engine
@@ -50,7 +48,7 @@ jobs:
allow_forks: false
- name: Download Mac OS Executable
uses: dawidd6/action-download-artifact@v8
uses: dawidd6/action-download-artifact@v6
with:
workflow: macos.yml
name: Codename Engine (Executable Only)
@@ -58,7 +56,7 @@ jobs:
allow_forks: false
- name: Download Linux Full Build
uses: dawidd6/action-download-artifact@v8
uses: dawidd6/action-download-artifact@v6
with:
workflow: linux.yml
name: Codename Engine
@@ -66,7 +64,7 @@ jobs:
allow_forks: false
- name: Download Linux Executable
uses: dawidd6/action-download-artifact@v8
uses: dawidd6/action-download-artifact@v6
with:
workflow: linux.yml
name: Codename Engine (Executable Only)
+81 -84
View File
@@ -11,21 +11,20 @@ jobs:
runs-on: windows-latest
steps:
- name: Pulling the new commit
uses: actions/checkout@v6
with:
fetch-depth: 0
uses: actions/checkout@v4
- name: Setting up Haxe
uses: krdlab/setup-haxe@v2
with:
haxe-version: 4.3.7
# - name: Restore existing build cache for faster compilation
# uses: actions/cache@v4.2.3
# with:
# # not caching the bin folder to prevent asset duplication and stuff like that
# key: cache-build-windows
# path: |
# .haxelib/
# export/release/windows/obj/
- name: Restore existing build cache for faster compilation
uses: actions/cache@v4.2.3
with:
# not caching the bin folder to prevent asset duplication and stuff like that
key: cache-build-windows
path: |
.haxelib/
export/release/windows/haxe/
export/release/windows/obj/
- run: |
echo "HXCPP_COMPILE_CACHE=~/.hxcpp" >> $GITHUB_ENV
- name: Installing/Updating libraries
@@ -33,47 +32,45 @@ jobs:
haxe -cp commandline -D analyzer-optimize --run Main setup -s --no-vscheck
- name: Building the game
run: |
haxelib run lime rebuild windows -nocolor -nocffi -release
haxelib run lime build windows -DCOMPILE_EXPERIMENTAL
haxelib run lime build windows
- name: Uploading artifact (executable)
uses: actions/upload-artifact@v7
uses: actions/upload-artifact@v4
with:
name: Codename Engine (Executable Only)
path: |
export/release/windows/bin/CodenameEngine.exe
export/release/windows/bin/lime.ndll
path: export/release/windows/bin/CodenameEngine.exe
- name: Uploading artifact (entire build)
uses: actions/upload-artifact@v7
uses: actions/upload-artifact@v4
with:
name: Codename Engine
path: export/release/windows/bin
# - name: Clearing already existing cache
# uses: actions/github-script@v6
# with:
# script: |
# const caches = await github.rest.actions.getActionsCacheList({
# owner: context.repo.owner,
# repo: context.repo.repo,
# })
# for (const cache of caches.data.actions_caches) {
# if (cache.key == "cache-build-windows") {
# console.log('Clearing ' + cache.key + '...')
# await github.rest.actions.deleteActionsCacheById({
# owner: context.repo.owner,
# repo: context.repo.repo,
# cache_id: cache.id,
# })
# console.log("Cache cleared.")
# }
# }
# - name: Uploading new cache
# uses: actions/cache@v4.2.3
# with:
# # caching again since for some reason it doesnt work with the first post cache shit
# key: cache-build-windows
# path: |
# .haxelib/
# export/release/windows/obj/
- name: Clearing already existing cache
uses: actions/github-script@v6
with:
script: |
const caches = await github.rest.actions.getActionsCacheList({
owner: context.repo.owner,
repo: context.repo.repo,
})
for (const cache of caches.data.actions_caches) {
if (cache.key == "cache-build-windows") {
console.log('Clearing ' + cache.key + '...')
await github.rest.actions.deleteActionsCacheById({
owner: context.repo.owner,
repo: context.repo.repo,
cache_id: cache.id,
})
console.log("Cache cleared.")
}
}
- name: Uploading new cache
uses: actions/cache@v4.2.3
with:
# caching again since for some reason it doesnt work with the first post cache shit
key: cache-build-windows
path: |
.haxelib/
export/release/windows/haxe/
export/release/windows/obj/
# didnt compile debug in the same job or github would have said that job wasn't completed until debug was done too (debug uploads are not essential)
debug_build:
@@ -83,20 +80,20 @@ jobs:
needs: build # since its low priority, it'll run after, so actions will concentrate first on normal builds
steps:
- name: Pulling the new commit
uses: actions/checkout@v6
uses: actions/checkout@v4
- name: Setting up Haxe
uses: krdlab/setup-haxe@v2
with:
haxe-version: 4.3.7
# - name: Restore existing build cache for faster compilation
# uses: actions/cache@v4.2.3
# with:
# # not caching the bin folder to prevent asset duplication and stuff like that
# key: cache-build-windows-debug
# path: |
# .haxelib/
# export/debug/windows/haxe/
# export/debug/windows/obj/
- name: Restore existing build cache for faster compilation
uses: actions/cache@v4.2.3
with:
# not caching the bin folder to prevent asset duplication and stuff like that
key: cache-build-windows-debug
path: |
.haxelib/
export/debug/windows/haxe/
export/debug/windows/obj/
- run: |
echo "HXCPP_COMPILE_CACHE=~/.hxcpp" >> $GITHUB_ENV
- name: Installing/Updating libraries
@@ -104,37 +101,37 @@ jobs:
haxe -cp commandline -D analyzer-optimize --run Main setup -s --no-vscheck
- name: Building the game
run: |
haxelib run lime rebuild windows -nocolor -nocffi -debug
haxelib run lime build windows -debug
- name: Uploading artifact (entire build)
uses: actions/upload-artifact@v7
uses: actions/upload-artifact@v4
with:
name: Codename Engine Debug
path: export/debug/windows/bin
# - name: Clearing already existing cache
# uses: actions/github-script@v6
# with:
# script: |
# const caches = await github.rest.actions.getActionsCacheList({
# owner: context.repo.owner,
# repo: context.repo.repo,
# })
# for (const cache of caches.data.actions_caches) {
# if (cache.key == "cache-build-windows-debug") {
# console.log('Clearing ' + cache.key + '...')
# await github.rest.actions.deleteActionsCacheById({
# owner: context.repo.owner,
# repo: context.repo.repo,
# cache_id: cache.id,
# })
# console.log("Cache cleared.")
# }
# }
# - name: Uploading new cache
# uses: actions/cache@v4.2.3
# with:
# # caching again since for some reason it doesnt work with the first post cache shit
# key: cache-build-windows-debug
# path: |
# .haxelib/
# export/debug/windows/obj/
- name: Clearing already existing cache
uses: actions/github-script@v6
with:
script: |
const caches = await github.rest.actions.getActionsCacheList({
owner: context.repo.owner,
repo: context.repo.repo,
})
for (const cache of caches.data.actions_caches) {
if (cache.key == "cache-build-windows-debug") {
console.log('Clearing ' + cache.key + '...')
await github.rest.actions.deleteActionsCacheById({
owner: context.repo.owner,
repo: context.repo.repo,
cache_id: cache.id,
})
console.log("Cache cleared.")
}
}
- name: Uploading new cache
uses: actions/cache@v4.2.3
with:
# caching again since for some reason it doesnt work with the first post cache shit
key: cache-build-windows-debug
path: |
.haxelib/
export/debug/windows/haxe/
export/debug/windows/obj/
+1 -1
View File
@@ -47,7 +47,7 @@ The engine uses [HaxeFlixel](https://haxeflixel.com/) and it mainly features:
---
> [!NOTE]
> Codename Engine as for now supports **Windows x64**, **Mac OS Universal** and **Linux x64**.<br>
> Codename Engine as for now supports **Windows x64**, **Mac OS x64** and **Linux x64**.<br>
> More platforms will soon come, stay tuned!<br>
> - [ ] **Web (HTML5) Support**
> - [ ] **Mobile Support**
+1 -1
View File
@@ -75,7 +75,7 @@ function onCountdown(event) {
};
}
function onRatingsShown(event:RatingsShowEvent) {
function onPlayerHit(event:NoteHitEvent) {
if (!enablePixelUI) return;
event.ratingPrefix = "stages/school/ui/";
event.ratingScale = daPixelZoom * 0.7;
Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.0 KiB

After

Width:  |  Height:  |  Size: 804 B

File diff suppressed because it is too large Load Diff
@@ -1,241 +1,145 @@
{"ATLAS":{"SPRITES":[
{"SPRITE":{"name":"0","x":2564,"y":2,"w":23,"h":144,"rotated":false}},
{"SPRITE":{"name":"1","x":3024,"y":522,"w":42,"h":85,"rotated":false}},
{"SPRITE":{"name":"2","x":2598,"y":1211,"w":67,"h":60,"rotated":false}},
{"SPRITE":{"name":"3","x":3964,"y":932,"w":93,"h":51,"rotated":false}},
{"SPRITE":{"name":"4","x":3691,"y":1016,"w":91,"h":43,"rotated":false}},
{"SPRITE":{"name":"5","x":3877,"y":270,"w":111,"h":95,"rotated":false}},
{"SPRITE":{"name":"6","x":3449,"y":1124,"w":113,"h":127,"rotated":false}},
{"SPRITE":{"name":"7","x":367,"y":1129,"w":382,"h":123,"rotated":false}},
{"SPRITE":{"name":"8","x":2,"y":1122,"w":364,"h":131,"rotated":false}},
{"SPRITE":{"name":"9","x":2,"y":1122,"w":364,"h":131,"rotated":false}},
{"SPRITE":{"name":"10","x":2437,"y":1136,"w":31,"h":31,"rotated":false}},
{"SPRITE":{"name":"11","x":1599,"y":1133,"w":350,"h":113,"rotated":false}},
{"SPRITE":{"name":"12","x":1599,"y":1133,"w":350,"h":113,"rotated":false}},
{"SPRITE":{"name":"13","x":2670,"y":1175,"w":142,"h":94,"rotated":false}},
{"SPRITE":{"name":"14","x":3877,"y":366,"w":122,"h":82,"rotated":false}},
{"SPRITE":{"name":"15","x":1162,"y":1112,"w":436,"h":151,"rotated":false}},
{"SPRITE":{"name":"16","x":1599,"y":985,"w":435,"h":147,"rotated":false}},
{"SPRITE":{"name":"17","x":1599,"y":985,"w":435,"h":147,"rotated":false}},
{"SPRITE":{"name":"18","x":779,"y":692,"w":440,"h":344,"rotated":false}},
{"SPRITE":{"name":"19","x":1550,"y":711,"w":388,"h":273,"rotated":false}},
{"SPRITE":{"name":"20","x":2106,"y":2,"w":376,"h":155,"rotated":false}},
{"SPRITE":{"name":"21","x":2,"y":2,"w":932,"h":689,"rotated":false}},
{"SPRITE":{"name":"22","x":3907,"y":1171,"w":48,"h":36,"rotated":false}},
{"SPRITE":{"name":"23","x":3907,"y":1171,"w":48,"h":36,"rotated":false}},
{"SPRITE":{"name":"24","x":3858,"y":1206,"w":48,"h":40,"rotated":false}},
{"SPRITE":{"name":"25","x":3762,"y":1093,"w":48,"h":64,"rotated":false}},
{"SPRITE":{"name":"26","x":3713,"y":1060,"w":48,"h":66,"rotated":false}},
{"SPRITE":{"name":"27","x":3885,"y":1095,"w":48,"h":67,"rotated":false}},
{"SPRITE":{"name":"28","x":2106,"y":158,"w":48,"h":69,"rotated":false}},
{"SPRITE":{"name":"29","x":2481,"y":498,"w":48,"h":71,"rotated":false}},
{"SPRITE":{"name":"30","x":1162,"y":1037,"w":48,"h":73,"rotated":false}},
{"SPRITE":{"name":"31","x":4018,"y":188,"w":48,"h":76,"rotated":false}},
{"SPRITE":{"name":"32","x":3455,"y":856,"w":48,"h":78,"rotated":false}},
{"SPRITE":{"name":"33","x":3785,"y":1011,"w":48,"h":81,"rotated":false}},
{"SPRITE":{"name":"34","x":4002,"y":1164,"w":48,"h":82,"rotated":false}},
{"SPRITE":{"name":"35","x":3953,"y":1083,"w":48,"h":87,"rotated":false}},
{"SPRITE":{"name":"36","x":3904,"y":1004,"w":48,"h":90,"rotated":false}},
{"SPRITE":{"name":"37","x":4013,"y":1070,"w":48,"h":93,"rotated":false}},
{"SPRITE":{"name":"38","x":3964,"y":984,"w":48,"h":98,"rotated":false}},
{"SPRITE":{"name":"39","x":1550,"y":985,"w":48,"h":103,"rotated":false}},
{"SPRITE":{"name":"40","x":3848,"y":894,"w":50,"h":109,"rotated":false}},
{"SPRITE":{"name":"41","x":3797,"y":894,"w":50,"h":116,"rotated":false}},
{"SPRITE":{"name":"42","x":2777,"y":452,"w":50,"h":121,"rotated":false}},
{"SPRITE":{"name":"43","x":4002,"y":585,"w":50,"h":119,"rotated":false}},
{"SPRITE":{"name":"44","x":3913,"y":886,"w":50,"h":117,"rotated":false}},
{"SPRITE":{"name":"45","x":3059,"y":2,"w":52,"h":129,"rotated":false}},
{"SPRITE":{"name":"46","x":3964,"y":886,"w":123,"h":45,"rotated":false}},
{"SPRITE":{"name":"47","x":1770,"y":228,"w":123,"h":43,"rotated":false}},
{"SPRITE":{"name":"48","x":3424,"y":486,"w":99,"h":35,"rotated":false}},
{"SPRITE":{"name":"49","x":2979,"y":1141,"w":99,"h":34,"rotated":false}},
{"SPRITE":{"name":"50","x":2979,"y":1141,"w":99,"h":34,"rotated":false}},
{"SPRITE":{"name":"51","x":2590,"y":360,"w":99,"h":36,"rotated":false}},
{"SPRITE":{"name":"52","x":3709,"y":783,"w":35,"h":21,"rotated":false}},
{"SPRITE":{"name":"53","x":1220,"y":711,"w":329,"h":400,"rotated":false}},
{"SPRITE":{"name":"54","x":3042,"y":979,"w":112,"h":161,"rotated":false}},
{"SPRITE":{"name":"55","x":2929,"y":163,"w":199,"h":188,"rotated":false}},
{"SPRITE":{"name":"56","x":2175,"y":574,"w":96,"h":158,"rotated":false}},
{"SPRITE":{"name":"57","x":1623,"y":570,"w":277,"h":134,"rotated":false}},
{"SPRITE":{"name":"58","x":1550,"y":1089,"w":38,"h":22,"rotated":false}},
{"SPRITE":{"name":"59","x":3666,"y":152,"w":124,"h":165,"rotated":false}},
{"SPRITE":{"name":"60","x":1623,"y":272,"w":309,"h":297,"rotated":false}},
{"SPRITE":{"name":"61","x":3801,"y":2,"w":121,"h":91,"rotated":false}},
{"SPRITE":{"name":"62","x":3281,"y":522,"w":320,"h":77,"rotated":false}},
{"SPRITE":{"name":"63","x":3921,"y":755,"w":17,"h":22,"rotated":false}},
{"SPRITE":{"name":"64","x":1351,"y":272,"w":162,"h":74,"rotated":false}},
{"SPRITE":{"name":"65","x":3801,"y":94,"w":69,"h":50,"rotated":false}},
{"SPRITE":{"name":"66","x":2273,"y":574,"w":277,"h":174,"rotated":false}},
{"SPRITE":{"name":"67","x":2261,"y":1136,"w":175,"h":123,"rotated":false}},
{"SPRITE":{"name":"68","x":4058,"y":932,"w":32,"h":49,"rotated":false}},
{"SPRITE":{"name":"69","x":3791,"y":270,"w":66,"h":38,"rotated":false}},
{"SPRITE":{"name":"70","x":3079,"y":1141,"w":71,"h":40,"rotated":false}},
{"SPRITE":{"name":"71","x":2690,"y":360,"w":82,"h":29,"rotated":false}},
{"SPRITE":{"name":"72","x":3602,"y":516,"w":88,"h":69,"rotated":false}},
{"SPRITE":{"name":"73","x":4022,"y":806,"w":69,"h":27,"rotated":false}},
{"SPRITE":{"name":"74","x":1894,"y":228,"w":30,"h":40,"rotated":false}},
{"SPRITE":{"name":"75","x":3987,"y":654,"w":10,"h":13,"rotated":false}},
{"SPRITE":{"name":"76","x":3896,"y":1084,"w":7,"h":7,"rotated":false}},
{"SPRITE":{"name":"77","x":3834,"y":1074,"w":9,"h":9,"rotated":false}},
{"SPRITE":{"name":"78","x":3885,"y":1084,"w":10,"h":10,"rotated":false}},
{"SPRITE":{"name":"79","x":3441,"y":736,"w":12,"h":12,"rotated":false}},
{"SPRITE":{"name":"80","x":3811,"y":1128,"w":19,"h":17,"rotated":false}},
{"SPRITE":{"name":"81","x":3971,"y":654,"w":15,"h":14,"rotated":false}},
{"SPRITE":{"name":"82","x":3858,"y":1150,"w":7,"h":7,"rotated":false}},
{"SPRITE":{"name":"83","x":3794,"y":452,"w":5,"h":5,"rotated":false}},
{"SPRITE":{"name":"84","x":3326,"y":751,"w":32,"h":57,"rotated":false}},
{"SPRITE":{"name":"85","x":2583,"y":147,"w":6,"h":7,"rotated":false}},
{"SPRITE":{"name":"86","x":935,"y":617,"w":198,"h":66,"rotated":false}},
{"SPRITE":{"name":"87","x":426,"y":692,"w":352,"h":436,"rotated":false}},
{"SPRITE":{"name":"88","x":3814,"y":452,"w":156,"h":69,"rotated":false}},
{"SPRITE":{"name":"89","x":2829,"y":452,"w":194,"h":197,"rotated":false}},
{"SPRITE":{"name":"90","x":2670,"y":733,"w":181,"h":216,"rotated":false}},
{"SPRITE":{"name":"91","x":3791,"y":150,"w":93,"h":119,"rotated":false}},
{"SPRITE":{"name":"92","x":4000,"y":372,"w":86,"h":75,"rotated":false}},
{"SPRITE":{"name":"93","x":3587,"y":320,"w":113,"h":195,"rotated":false}},
{"SPRITE":{"name":"94","x":2483,"y":2,"w":80,"h":151,"rotated":false}},
{"SPRITE":{"name":"95","x":3493,"y":2,"w":177,"h":149,"rotated":false}},
{"SPRITE":{"name":"96","x":3327,"y":183,"w":9,"h":8,"rotated":false}},
{"SPRITE":{"name":"97","x":3787,"y":452,"w":6,"h":6,"rotated":false}},
{"SPRITE":{"name":"98","x":4002,"y":705,"w":57,"h":48,"rotated":false}},
{"SPRITE":{"name":"99","x":2574,"y":147,"w":8,"h":8,"rotated":false}},
{"SPRITE":{"name":"100","x":3814,"y":605,"w":8,"h":12,"rotated":false}},
{"SPRITE":{"name":"101","x":1589,"y":1089,"w":9,"h":10,"rotated":false}},
{"SPRITE":{"name":"102","x":3701,"y":318,"w":85,"h":140,"rotated":false}},
{"SPRITE":{"name":"103","x":2,"y":692,"w":423,"h":429,"rotated":false}},
{"SPRITE":{"name":"104","x":3533,"y":152,"w":132,"h":167,"rotated":false}},
{"SPRITE":{"name":"105","x":3249,"y":961,"w":66,"h":16,"rotated":false}},
{"SPRITE":{"name":"106","x":3576,"y":808,"w":52,"h":43,"rotated":false}},
{"SPRITE":{"name":"107","x":3858,"y":1163,"w":46,"h":42,"rotated":false}},
{"SPRITE":{"name":"108","x":3834,"y":1011,"w":13,"h":62,"rotated":false}},
{"SPRITE":{"name":"109","x":3654,"y":1025,"w":34,"h":29,"rotated":false}},
{"SPRITE":{"name":"110","x":3811,"y":1150,"w":46,"h":65,"rotated":false}},
{"SPRITE":{"name":"111","x":3701,"y":459,"w":112,"h":46,"rotated":false}},
{"SPRITE":{"name":"112","x":2035,"y":898,"w":226,"h":223,"rotated":false}},
{"SPRITE":{"name":"113","x":3233,"y":351,"w":190,"h":170,"rotated":false}},
{"SPRITE":{"name":"114","x":3654,"y":1060,"w":58,"h":55,"rotated":false}},
{"SPRITE":{"name":"115","x":3885,"y":94,"w":112,"h":94,"rotated":false}},
{"SPRITE":{"name":"116","x":3576,"y":706,"w":132,"h":101,"rotated":false}},
{"SPRITE":{"name":"117","x":1939,"y":493,"w":235,"h":239,"rotated":false}},
{"SPRITE":{"name":"118","x":3242,"y":1150,"w":141,"h":111,"rotated":false}},
{"SPRITE":{"name":"119","x":2822,"y":2,"w":236,"h":160,"rotated":false}},
{"SPRITE":{"name":"120","x":2475,"y":749,"w":194,"h":236,"rotated":false}},
{"SPRITE":{"name":"121","x":3455,"y":736,"w":120,"h":119,"rotated":false}},
{"SPRITE":{"name":"122","x":2551,"y":398,"w":225,"h":189,"rotated":false}},
{"SPRITE":{"name":"123","x":3923,"y":2,"w":130,"h":81,"rotated":false}},
{"SPRITE":{"name":"124","x":3691,"y":621,"w":132,"h":83,"rotated":false}},
{"SPRITE":{"name":"125","x":3531,"y":1025,"w":122,"h":80,"rotated":false}},
{"SPRITE":{"name":"126","x":3814,"y":522,"w":129,"h":82,"rotated":false}},
{"SPRITE":{"name":"127","x":3709,"y":705,"w":125,"h":77,"rotated":false}},
{"SPRITE":{"name":"128","x":3885,"y":189,"w":132,"h":76,"rotated":false}},
{"SPRITE":{"name":"129","x":2035,"y":1122,"w":225,"h":142,"rotated":false}},
{"SPRITE":{"name":"130","x":2777,"y":187,"w":151,"h":264,"rotated":false}},
{"SPRITE":{"name":"131","x":2551,"y":588,"w":277,"h":144,"rotated":false}},
{"SPRITE":{"name":"132","x":3327,"y":197,"w":205,"h":140,"rotated":false}},
{"SPRITE":{"name":"133","x":2175,"y":401,"w":305,"h":172,"rotated":false}},
{"SPRITE":{"name":"134","x":2841,"y":1136,"w":137,"h":129,"rotated":false}},
{"SPRITE":{"name":"135","x":3129,"y":172,"w":197,"h":178,"rotated":false}},
{"SPRITE":{"name":"136","x":2,"y":1122,"w":364,"h":131,"rotated":false}},
{"SPRITE":{"name":"137","x":3424,"y":338,"w":162,"h":147,"rotated":false}},
{"SPRITE":{"name":"138","x":1933,"y":228,"w":223,"h":264,"rotated":false}},
{"SPRITE":{"name":"139","x":1351,"y":2,"w":418,"h":269,"rotated":false}},
{"SPRITE":{"name":"140","x":1599,"y":1133,"w":350,"h":113,"rotated":false}},
{"SPRITE":{"name":"141","x":1599,"y":985,"w":435,"h":147,"rotated":false}},
{"SPRITE":{"name":"142","x":3921,"y":670,"w":80,"h":84,"rotated":false}},
{"SPRITE":{"name":"143","x":2564,"y":147,"w":9,"h":8,"rotated":false}},
{"SPRITE":{"name":"144","x":2437,"y":1168,"w":31,"h":27,"rotated":false}},
{"SPRITE":{"name":"145","x":3664,"y":808,"w":110,"h":88,"rotated":false}},
{"SPRITE":{"name":"146","x":3024,"y":608,"w":43,"h":39,"rotated":false}},
{"SPRITE":{"name":"147","x":3563,"y":1106,"w":55,"h":58,"rotated":false}},
{"SPRITE":{"name":"148","x":3971,"y":619,"w":19,"h":34,"rotated":false}},
{"SPRITE":{"name":"149","x":3416,"y":1250,"w":30,"h":23,"rotated":false}},
{"SPRITE":{"name":"150","x":1939,"y":898,"w":82,"h":85,"rotated":false}},
{"SPRITE":{"name":"151","x":4054,"y":39,"w":34,"h":41,"rotated":false}},
{"SPRITE":{"name":"152","x":3691,"y":516,"w":122,"h":104,"rotated":false}},
{"SPRITE":{"name":"153","x":3762,"y":1060,"w":17,"h":32,"rotated":false}},
{"SPRITE":{"name":"154","x":3745,"y":783,"w":28,"h":20,"rotated":false}},
{"SPRITE":{"name":"155","x":3524,"y":486,"w":45,"h":35,"rotated":false}},
{"SPRITE":{"name":"156","x":4000,"y":266,"w":74,"h":105,"rotated":false}},
{"SPRITE":{"name":"157","x":3775,"y":836,"w":137,"h":57,"rotated":false}},
{"SPRITE":{"name":"158","x":3384,"y":1250,"w":31,"h":23,"rotated":false}},
{"SPRITE":{"name":"159","x":3504,"y":908,"w":24,"h":24,"rotated":false}},
{"SPRITE":{"name":"160","x":4013,"y":984,"w":55,"h":85,"rotated":false}},
{"SPRITE":{"name":"161","x":4054,"y":2,"w":40,"h":36,"rotated":false}},
{"SPRITE":{"name":"162","x":3775,"y":783,"w":163,"h":52,"rotated":false}},
{"SPRITE":{"name":"163","x":1950,"y":1133,"w":73,"h":115,"rotated":false}},
{"SPRITE":{"name":"164","x":3531,"y":962,"w":159,"h":62,"rotated":false}},
{"SPRITE":{"name":"165","x":3811,"y":1093,"w":20,"h":34,"rotated":false}},
{"SPRITE":{"name":"166","x":4067,"y":230,"w":27,"h":32,"rotated":false}},
{"SPRITE":{"name":"167","x":4067,"y":188,"w":24,"h":41,"rotated":false}},
{"SPRITE":{"name":"168","x":3504,"y":856,"w":26,"h":51,"rotated":false}},
{"SPRITE":{"name":"169","x":2929,"y":352,"w":86,"h":98,"rotated":false}},
{"SPRITE":{"name":"170","x":3824,"y":605,"w":146,"h":64,"rotated":false}},
{"SPRITE":{"name":"171","x":1589,"y":1100,"w":9,"h":10,"rotated":false}},
{"SPRITE":{"name":"172","x":3327,"y":172,"w":8,"h":10,"rotated":false}},
{"SPRITE":{"name":"173","x":3939,"y":806,"w":82,"h":27,"rotated":false}},
{"SPRITE":{"name":"174","x":3944,"y":522,"w":141,"h":62,"rotated":false}},
{"SPRITE":{"name":"175","x":3024,"y":352,"w":208,"h":169,"rotated":false}},
{"SPRITE":{"name":"176","x":3129,"y":2,"w":208,"h":169,"rotated":false}},
{"SPRITE":{"name":"177","x":3042,"y":814,"w":206,"h":164,"rotated":false}},
{"SPRITE":{"name":"178","x":3072,"y":522,"w":208,"h":161,"rotated":false}},
{"SPRITE":{"name":"179","x":779,"y":1037,"w":382,"h":218,"rotated":false}},
{"SPRITE":{"name":"180","x":935,"y":357,"w":237,"h":259,"rotated":false}},
{"SPRITE":{"name":"181","x":3155,"y":979,"w":86,"h":203,"rotated":false}},
{"SPRITE":{"name":"182","x":3364,"y":751,"w":90,"h":183,"rotated":false}},
{"SPRITE":{"name":"183","x":3357,"y":961,"w":91,"h":188,"rotated":false}},
{"SPRITE":{"name":"184","x":2852,"y":814,"w":185,"h":122,"rotated":false}},
{"SPRITE":{"name":"185","x":3787,"y":318,"w":89,"h":133,"rotated":false}},
{"SPRITE":{"name":"186","x":2590,"y":187,"w":183,"h":172,"rotated":false}},
{"SPRITE":{"name":"187","x":3242,"y":979,"w":114,"h":151,"rotated":false}},
{"SPRITE":{"name":"188","x":4047,"y":834,"w":42,"h":36,"rotated":false}},
{"SPRITE":{"name":"189","x":1134,"y":617,"w":53,"h":63,"rotated":false}},
{"SPRITE":{"name":"190","x":3166,"y":1183,"w":67,"h":80,"rotated":false}},
{"SPRITE":{"name":"191","x":3848,"y":1004,"w":55,"h":79,"rotated":false}},
{"SPRITE":{"name":"192","x":3834,"y":1084,"w":50,"h":65,"rotated":false}},
{"SPRITE":{"name":"193","x":3629,"y":808,"w":33,"h":43,"rotated":false}},
{"SPRITE":{"name":"194","x":3493,"y":152,"w":26,"h":38,"rotated":false}},
{"SPRITE":{"name":"195","x":3971,"y":585,"w":23,"h":33,"rotated":false}},
{"SPRITE":{"name":"196","x":2530,"y":498,"w":20,"h":24,"rotated":false}},
{"SPRITE":{"name":"197","x":3939,"y":755,"w":121,"h":50,"rotated":false}},
{"SPRITE":{"name":"198","x":3913,"y":836,"w":133,"h":49,"rotated":false}},
{"SPRITE":{"name":"199","x":3971,"y":449,"w":119,"h":69,"rotated":false}},
{"SPRITE":{"name":"200","x":3072,"y":684,"w":253,"h":125,"rotated":false}},
{"SPRITE":{"name":"201","x":3441,"y":600,"w":116,"h":135,"rotated":false}},
{"SPRITE":{"name":"202","x":2475,"y":986,"w":194,"h":224,"rotated":false}},
{"SPRITE":{"name":"203","x":1770,"y":2,"w":335,"h":225,"rotated":false}},
{"SPRITE":{"name":"204","x":2157,"y":158,"w":217,"h":242,"rotated":false}},
{"SPRITE":{"name":"205","x":3249,"y":810,"w":114,"h":150,"rotated":false}},
{"SPRITE":{"name":"206","x":3326,"y":600,"w":114,"h":150,"rotated":false}},
{"SPRITE":{"name":"207","x":3835,"y":670,"w":85,"h":87,"rotated":false}},
{"SPRITE":{"name":"208","x":3671,"y":2,"w":129,"h":147,"rotated":false}},
{"SPRITE":{"name":"209","x":2852,"y":650,"w":219,"h":163,"rotated":false}},
{"SPRITE":{"name":"210","x":3338,"y":2,"w":154,"h":194,"rotated":false}},
{"SPRITE":{"name":"211","x":1514,"y":272,"w":108,"h":68,"rotated":false}},
{"SPRITE":{"name":"212","x":3664,"y":897,"w":132,"h":64,"rotated":false}},
{"SPRITE":{"name":"213","x":2437,"y":1211,"w":160,"h":62,"rotated":false}},
{"SPRITE":{"name":"214","x":3449,"y":935,"w":81,"h":188,"rotated":false}},
{"SPRITE":{"name":"215","x":2841,"y":950,"w":200,"h":185,"rotated":false}},
{"SPRITE":{"name":"216","x":3384,"y":1150,"w":63,"h":99,"rotated":false}},
{"SPRITE":{"name":"217","x":2481,"y":398,"w":63,"h":99,"rotated":false}},
{"SPRITE":{"name":"218","x":3558,"y":600,"w":132,"h":105,"rotated":false}},
{"SPRITE":{"name":"219","x":3531,"y":856,"w":132,"h":105,"rotated":false}},
{"SPRITE":{"name":"220","x":2590,"y":2,"w":231,"h":184,"rotated":false}},
{"SPRITE":{"name":"221","x":2670,"y":950,"w":170,"h":224,"rotated":false}},
{"SPRITE":{"name":"222","x":2273,"y":749,"w":198,"h":144,"rotated":false}},
{"SPRITE":{"name":"223","x":3691,"y":962,"w":93,"h":53,"rotated":false}},
{"SPRITE":{"name":"224","x":2979,"y":1183,"w":186,"h":91,"rotated":false}},
{"SPRITE":{"name":"225","x":1939,"y":733,"w":333,"h":164,"rotated":false}},
{"SPRITE":{"name":"226","x":935,"y":2,"w":415,"h":354,"rotated":false}},
{"SPRITE":{"name":"227","x":1220,"y":357,"w":402,"h":353,"rotated":false}},
{"SPRITE":{"name":"228","x":3998,"y":84,"w":90,"h":103,"rotated":false}},
{"SPRITE":{"name":"229","x":2375,"y":158,"w":214,"h":239,"rotated":false}},
{"SPRITE":{"name":"230","x":2262,"y":898,"w":212,"h":237,"rotated":false}}]},
"meta":{
"app":"Adobe Animate (Better TA Extension)",
"version":"22.0.3.179",
"image":"spritemap1.png",
"format":"RGBA8888",
"size":{"w":4096,"h":1280},
"resolution":"1"
{"ATLAS": {"SPRITES":[
{"SPRITE" : {"name": "0000","x":2847,"y":969,"w":327,"h":397,"rotated": false}},
{"SPRITE" : {"name": "0001","x":2441,"y":894,"w":110,"h":159,"rotated": false}},
{"SPRITE" : {"name": "0002","x":1709,"y":1631,"w":197,"h":185,"rotated": false}},
{"SPRITE" : {"name": "0003","x":408,"y":1773,"w":92,"h":155,"rotated": false}},
{"SPRITE" : {"name": "0004","x":236,"y":1639,"w":274,"h":132,"rotated": false}},
{"SPRITE" : {"name": "0005","x":3427,"y":1495,"w":133,"h":74,"rotated": false}},
{"SPRITE" : {"name": "0006","x":3270,"y":540,"w":121,"h":162,"rotated": false}},
{"SPRITE" : {"name": "0007","x":359,"y":649,"w":518,"h":294,"rotated": false}},
{"SPRITE" : {"name": "0008","x":1957,"y":2,"w":775,"h":483,"rotated": false}},
{"SPRITE" : {"name": "0009","x":1014,"y":2,"w":941,"h":607,"rotated": false}},
{"SPRITE" : {"name": "0010","x":2,"y":2,"w":1010,"h":645,"rotated": false}},
{"SPRITE" : {"name": "0011","x":4059,"y":2,"w":20,"h":142,"rotated": false}},
{"SPRITE" : {"name": "0012","x":4035,"y":616,"w":40,"h":83,"rotated": false}},
{"SPRITE" : {"name": "0013","x":2755,"y":1508,"w":64,"h":57,"rotated": false}},
{"SPRITE" : {"name": "0014","x":3176,"y":1171,"w":91,"h":48,"rotated": false}},
{"SPRITE" : {"name": "0015","x":3245,"y":1737,"w":88,"h":41,"rotated": false}},
{"SPRITE" : {"name": "0016","x":3064,"y":1368,"w":109,"h":93,"rotated": false}},
{"SPRITE" : {"name": "0017","x":3402,"y":2,"w":655,"h":434,"rotated": false}},
{"SPRITE" : {"name": "0018","x":1957,"y":487,"w":626,"h":405,"rotated": false}},
{"SPRITE" : {"name": "0019","x":3402,"y":438,"w":631,"h":438,"rotated": false}},
{"SPRITE" : {"name": "0020","x":1170,"y":1211,"w":393,"h":287,"rotated": false}},
{"SPRITE" : {"name": "0021","x":2847,"y":540,"w":421,"h":427,"rotated": false}},
{"SPRITE" : {"name": "0022","x":2734,"y":2,"w":666,"h":536,"rotated": false}},
{"SPRITE" : {"name": "0023","x":542,"y":1480,"w":224,"h":221,"rotated": false}},
{"SPRITE" : {"name": "0024","x":3657,"y":1736,"w":186,"h":175,"rotated": false}},
{"SPRITE" : {"name": "0025","x":4035,"y":764,"w":56,"h":52,"rotated": false}},
{"SPRITE" : {"name": "0026","x":502,"y":1876,"w":126,"h":104,"rotated": false}},
{"SPRITE" : {"name": "0027","x":1709,"y":1818,"w":135,"h":104,"rotated": false}},
{"SPRITE" : {"name": "0028","x":2,"y":1440,"w":232,"h":236,"rotated": false}},
{"SPRITE" : {"name": "0029","x":3938,"y":1437,"w":139,"h":109,"rotated": false}},
{"SPRITE" : {"name": "0030","x":2755,"y":1627,"w":235,"h":158,"rotated": false}},
{"SPRITE" : {"name": "0031","x":1147,"y":1500,"w":198,"h":233,"rotated": false}},
{"SPRITE" : {"name": "0032","x":2,"y":1860,"w":118,"h":117,"rotated": false}},
{"SPRITE" : {"name": "0033","x":1565,"y":1398,"w":262,"h":221,"rotated": false}},
{"SPRITE" : {"name": "0034","x":3845,"y":1736,"w":222,"h":145,"rotated": false}},
{"SPRITE" : {"name": "0035","x":979,"y":1480,"w":166,"h":292,"rotated": false}},
{"SPRITE" : {"name": "0036","x":3657,"y":1592,"w":276,"h":142,"rotated": false}},
{"SPRITE" : {"name": "0037","x":925,"y":1774,"w":203,"h":138,"rotated": false}},
{"SPRITE" : {"name": "0038","x":236,"y":1467,"w":304,"h":170,"rotated": false}},
{"SPRITE" : {"name": "0039","x":4081,"y":2,"w":1,"h":4,"rotated": false}},
{"SPRITE" : {"name": "0040","x":2441,"y":1055,"w":135,"h":127,"rotated": false}},
{"SPRITE" : {"name": "0041","x":2,"y":1678,"w":195,"h":180,"rotated": false}},
{"SPRITE" : {"name": "0042","x":2751,"y":1787,"w":160,"h":144,"rotated": false}},
{"SPRITE" : {"name": "0043","x":713,"y":945,"w":243,"h":261,"rotated": false}},
{"SPRITE" : {"name": "0044","x":2375,"y":1508,"w":378,"h":121,"rotated": false}},
{"SPRITE" : {"name": "0045","x":3064,"y":1495,"w":361,"h":128,"rotated": false}},
{"SPRITE" : {"name": "0046","x":713,"y":1211,"w":455,"h":267,"rotated": false}},
{"SPRITE" : {"name": "0047","x":2,"y":1112,"w":272,"h":326,"rotated": false}},
{"SPRITE" : {"name": "0048","x":3710,"y":878,"w":249,"h":557,"rotated": false}},
{"SPRITE" : {"name": "0049","x":2585,"y":540,"w":260,"h":701,"rotated": false}},
{"SPRITE" : {"name": "0050","x":1014,"y":611,"w":291,"h":598,"rotated": false}},
{"SPRITE" : {"name": "0051","x":1307,"y":611,"w":315,"h":540,"rotated": false}},
{"SPRITE" : {"name": "0052","x":2,"y":649,"w":355,"h":461,"rotated": false}},
{"SPRITE" : {"name": "0053","x":359,"y":945,"w":352,"h":373,"rotated": false}},
{"SPRITE" : {"name": "0054","x":3064,"y":1625,"w":347,"h":110,"rotated": false}},
{"SPRITE" : {"name": "0055","x":2039,"y":1248,"w":379,"h":216,"rotated": false}},
{"SPRITE" : {"name": "0056","x":2827,"y":1368,"w":235,"h":257,"rotated": false}},
{"SPRITE" : {"name": "0057","x":2100,"y":1879,"w":140,"h":92,"rotated": false}},
{"SPRITE" : {"name": "0058","x":776,"y":1886,"w":119,"h":79,"rotated": false}},
{"SPRITE" : {"name": "0059","x":1565,"y":1249,"w":434,"h":147,"rotated": false}},
{"SPRITE" : {"name": "0060","x":276,"y":1320,"w":433,"h":145,"rotated": false}},
{"SPRITE" : {"name": "0061","x":3270,"y":878,"w":438,"h":342,"rotated": false}},
{"SPRITE" : {"name": "0062","x":3176,"y":1222,"w":385,"h":271,"rotated": false}},
{"SPRITE" : {"name": "0063","x":3563,"y":1437,"w":373,"h":153,"rotated": false}},
{"SPRITE" : {"name": "0064","x":3245,"y":1786,"w":202,"h":122,"rotated": false}},
{"SPRITE" : {"name": "0065","x":3449,"y":1786,"w":202,"h":122,"rotated": false}},
{"SPRITE" : {"name": "0066","x":3064,"y":1861,"w":150,"h":91,"rotated": false}},
{"SPRITE" : {"name": "0067","x":3938,"y":1548,"w":151,"h":95,"rotated": false}},
{"SPRITE" : {"name": "0068","x":3845,"y":1883,"w":140,"h":90,"rotated": false}},
{"SPRITE" : {"name": "0069","x":2913,"y":1861,"w":149,"h":92,"rotated": false}},
{"SPRITE" : {"name": "0070","x":630,"y":1886,"w":144,"h":87,"rotated": false}},
{"SPRITE" : {"name": "0071","x":3935,"y":1645,"w":152,"h":87,"rotated": false}},
{"SPRITE" : {"name": "0072","x":2441,"y":1243,"w":384,"h":263,"rotated": false}},
{"SPRITE" : {"name": "0073","x":1624,"y":611,"w":248,"h":277,"rotated": false}},
{"SPRITE" : {"name": "0074","x":1624,"y":894,"w":413,"h":353,"rotated": false}},
{"SPRITE" : {"name": "0075","x":2039,"y":894,"w":400,"h":352,"rotated": false}},
{"SPRITE" : {"name": "0076","x":1829,"y":1466,"w":330,"h":163,"rotated": false}},
{"SPRITE" : {"name": "0077","x":3987,"y":1883,"w":90,"h":103,"rotated": false}},
{"SPRITE" : {"name": "0078","x":2161,"y":1466,"w":212,"h":238,"rotated": false}},
{"SPRITE" : {"name": "0079","x":768,"y":1480,"w":209,"h":236,"rotated": false}},
{"SPRITE" : {"name": "0080","x":2242,"y":1879,"w":86,"h":84,"rotated": false}},
{"SPRITE" : {"name": "0081","x":879,"y":649,"w":126,"h":148,"rotated": false}},
{"SPRITE" : {"name": "0082","x":1908,"y":1631,"w":216,"h":168,"rotated": false}},
{"SPRITE" : {"name": "0083","x":2575,"y":1631,"w":174,"h":192,"rotated": false}},
{"SPRITE" : {"name": "0084","x":1130,"y":1901,"w":106,"h":75,"rotated": false}},
{"SPRITE" : {"name": "0085","x":3961,"y":1367,"w":129,"h":63,"rotated": false}},
{"SPRITE" : {"name": "0086","x":1829,"y":1398,"w":158,"h":60,"rotated": false}},
{"SPRITE" : {"name": "0087","x":3270,"y":704,"w":123,"h":148,"rotated": false}},
{"SPRITE" : {"name": "0088","x":3961,"y":878,"w":123,"h":148,"rotated": false}},
{"SPRITE" : {"name": "0089","x":199,"y":1773,"w":207,"h":142,"rotated": false}},
{"SPRITE" : {"name": "0090","x":1238,"y":1901,"w":91,"h":53,"rotated": false}},
{"SPRITE" : {"name": "0091","x":276,"y":1112,"w":65,"h":96,"rotated": false}},
{"SPRITE" : {"name": "0092","x":276,"y":1210,"w":65,"h":96,"rotated": false}},
{"SPRITE" : {"name": "0093","x":2518,"y":1825,"w":136,"h":102,"rotated": false}},
{"SPRITE" : {"name": "0094","x":1537,"y":1857,"w":136,"h":102,"rotated": false}},
{"SPRITE" : {"name": "0095","x":3427,"y":1592,"w":228,"h":192,"rotated": false}},
{"SPRITE" : {"name": "0096","x":1541,"y":1621,"w":166,"h":234,"rotated": false}},
{"SPRITE" : {"name": "0097","x":512,"y":1703,"w":205,"h":171,"rotated": false}},
{"SPRITE" : {"name": "0098","x":2126,"y":1706,"w":205,"h":171,"rotated": false}},
{"SPRITE" : {"name": "0099","x":719,"y":1718,"w":204,"h":166,"rotated": false}},
{"SPRITE" : {"name": "0100","x":1147,"y":1735,"w":206,"h":164,"rotated": false}},
{"SPRITE" : {"name": "0101","x":1874,"y":611,"w":79,"h":185,"rotated": false}},
{"SPRITE" : {"name": "0102","x":3176,"y":969,"w":83,"h":200,"rotated": false}},
{"SPRITE" : {"name": "0103","x":3563,"y":1222,"w":86,"h":180,"rotated": false}},
{"SPRITE" : {"name": "0104","x":3961,"y":1028,"w":89,"h":186,"rotated": false}},
{"SPRITE" : {"name": "0105","x":2375,"y":1631,"w":198,"h":183,"rotated": false}},
{"SPRITE" : {"name": "0106","x":1908,"y":1801,"w":190,"h":120,"rotated": false}},
{"SPRITE" : {"name": "0107","x":2656,"y":1825,"w":86,"h":145,"rotated": false}},
{"SPRITE" : {"name": "0108","x":1355,"y":1724,"w":180,"h":170,"rotated": false}},
{"SPRITE" : {"name": "0109","x":3961,"y":1216,"w":111,"h":149,"rotated": false}},
{"SPRITE" : {"name": "0110","x":4035,"y":516,"w":39,"h":98,"rotated": false}},
{"SPRITE" : {"name": "0111","x":1307,"y":1153,"w":118,"h":47,"rotated": false}},
{"SPRITE" : {"name": "0112","x":1427,"y":1153,"w":120,"h":43,"rotated": false}},
{"SPRITE" : {"name": "0113","x":2441,"y":1184,"w":120,"h":41,"rotated": false}},
{"SPRITE" : {"name": "0114","x":3216,"y":1910,"w":95,"h":33,"rotated": false}},
{"SPRITE" : {"name": "0115","x":3563,"y":1404,"w":95,"h":31,"rotated": false}},
{"SPRITE" : {"name": "0116","x":3563,"y":1404,"w":95,"h":31,"rotated": false}},
{"SPRITE" : {"name": "0117","x":2330,"y":1906,"w":95,"h":34,"rotated": false}},
{"SPRITE" : {"name": "0118","x":2585,"y":487,"w":131,"h":47,"rotated": false}},
{"SPRITE" : {"name": "0119","x":4059,"y":258,"w":31,"h":18,"rotated": false}},
{"SPRITE" : {"name": "0120","x":1355,"y":1896,"w":122,"h":66,"rotated": false}},
{"SPRITE" : {"name": "0121","x":2992,"y":1737,"w":251,"h":122,"rotated": false}},
{"SPRITE" : {"name": "0122","x":879,"y":799,"w":114,"h":133,"rotated": false}},
{"SPRITE" : {"name": "0123","x":1347,"y":1500,"w":192,"h":222,"rotated": false}},
{"SPRITE" : {"name": "0124","x":4035,"y":818,"w":39,"h":34,"rotated": false}},
{"SPRITE" : {"name": "0125","x":4035,"y":701,"w":50,"h":61,"rotated": false}},
{"SPRITE" : {"name": "0126","x":1874,"y":798,"w":64,"h":78,"rotated": false}},
{"SPRITE" : {"name": "0127","x":4035,"y":438,"w":52,"h":76,"rotated": false}},
{"SPRITE" : {"name": "0128","x":958,"y":934,"w":46,"h":63,"rotated": false}},
{"SPRITE" : {"name": "0129","x":4059,"y":146,"w":30,"h":41,"rotated": false}},
{"SPRITE" : {"name": "0130","x":4059,"y":189,"w":23,"h":35,"rotated": false}},
{"SPRITE" : {"name": "0131","x":4059,"y":226,"w":20,"h":30,"rotated": false}},
{"SPRITE" : {"name": "0132","x":4059,"y":278,"w":17,"h":22,"rotated": false}},
{"SPRITE" : {"name": "0133","x":2333,"y":1816,"w":183,"h":88,"rotated": false}}
]},
"meta": {
"app": "Adobe Animate",
"version": "21.0.1.37179",
"image": "spritemap1.png",
"format": "RGBA8888",
"size": {"w":4093,"h":1988},
"resolution": "1"
}
}
}
Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 MiB

After

Width:  |  Height:  |  Size: 642 KiB

+3 -9
View File
@@ -203,7 +203,6 @@
<str id="paste">Paste</str>
<str id="cut">Cut</str>
<str id="delete">Delete</str>
<str id="deletestacked">Delete Stacked Notes</str>
</group>
<str id="topBar.chart">Chart</str>
@@ -224,7 +223,6 @@
<str id="resetZoom">Reset Zoom</str>
<str id="showSectionsSeparator">Show Sections Separator</str>
<str id="showBeatsSeparator">Show Beats Separator</str>
<str id="showCameraHighlights">Show Camera Highlights</str>
<str id="rainbowWaveforms">Rainbow Waveforms</str>
<str id="lowDetailWaveforms">Low Detail Waveforms</str>
<str id="scrollLeft">Scroll Left</str>
@@ -238,9 +236,8 @@
<str id="goEnd">Go forward to the end</str> <!-- used to be "Go to the end" -->
<str id="addOpponentCamera">Add camera on Opponent</str>
<str id="addPlayerCamera">Add camera on Player</str>
<str id="inst">Instrumental</str>
<str id="voices">Voices</str>
<str id="hitsounds">Hitsounds</str>
<str id="muteInst">Mute instrumental</str>
<str id="muteVoices">Mute voices</str>
</group>
<group name="BookmarkMenu" prefix="bookmarks.">
@@ -253,7 +250,6 @@
<str id="editBookmarkListTitle">Bookmark List</str>
<str id="editBookmarksTitle">Edit Bookmarks</str>
<str id="newBookmarkName">New Bookmark</str>
<str id="bookmarkList">Bookmark List</str>
</group>
<str id="topBar.note">Note</str>
@@ -262,7 +258,6 @@
<str id="subtractSustainLength">Subtract sustain length</str>
<str id="selectAll">Select all</str>
<str id="selectMeasure">Select measure</str>
<str id="noteTypesList">Note Types List</str>
<str id="editNoteTypesList">Edit Note Types List</str>
</group>
<str id="topBar.snap">Snap</str>
@@ -313,9 +308,8 @@
<group name="StrumlineOptions" prefix="strumLine.">
<str id="button-name">Options ↓</str>
<str id="waveforms">Waveforms</str>
<str id="hitsounds">Hitsounds</str>
<str id="vocals">Vocals</str>
<str id="muteVocals">Mute Vocals</str>
<str id="edit">Edit</str>
<str id="delete">Delete</str>
+1 -2
View File
@@ -16,7 +16,7 @@
<str id="animation">Animación</str>
<str id="atlas">Atlas</str>
<str id="spritemap">Mapa de sprites</str>
<str id="directory">Ubicación ({0}/)</str>
<str id="found">{0} {1} Encontrado</str>
<str id="foundplural">{0} {1} Encontrados</str>
@@ -188,7 +188,6 @@
<str id="paste">Pegar</str>
<str id="cut">Cortar</str>
<str id="delete">Eliminar</str>
<str id="deletestacked">Eliminar Notas Apiladas</str>
</group>
<str id="topBar.chart">Chart</str>
-1
View File
@@ -194,7 +194,6 @@
<str id="paste">Incolla</str>
<str id="cut">Taglia</str>
<str id="delete">Elimina</str>
<str id="deletestacked">Elimina Note Impilate</str>
</group>
<str id="topBar.chart">Chart</str>
+3 -4
View File
@@ -18,7 +18,7 @@
<str id="animation">Animacja</str>
<str id="atlas">Atlas</str>
<str id="spritemap">Mapa sprite'ów</str>
<str id="directory">Folder ({0}/)</str>
<str id="found">{0} {1} Znaleziono</str>
<str id="foundplural">{0} {1} Znaleziono</str>
@@ -87,7 +87,7 @@
<group name="CharterEventScreen" prefix="charterEventScreen.">
<str id="title-creating">Stwórz Grupę Wydarzeń</str>
<str id="title-editing">Edytuj Grupę Wydarzeń</str>
<str id="noEvent">Brak Wydarzenia</str>
<str id="strumLine.format">Struma #{0} ({1})</str>
@@ -197,7 +197,6 @@
<str id="paste">Wklej</str>
<str id="cut">Wytnij</str>
<str id="delete">Usuń</str>
<str id="deletestacked">Usuń Notatki Ułożone w Stos</str>
</group>
<str id="topBar.chart">Mapa</str>
@@ -224,7 +223,7 @@
<str id="scrollRight">Przewiń w Prawo</str>
<str id="scrollReset">Resetuj Przewinięcie</str>
</group>
<str id="topBar.song">Ścieżka</str>
<group name="SongMenu" prefix="song.">
<str id="goStart">Wróć do początku</str>
-1
View File
@@ -203,7 +203,6 @@
<str id="paste">Colar</str>
<str id="cut">Cortar</str>
<str id="delete">Apagar</str>
<str id="deletestacked">Apagar Notas Empilhadas</str>
</group>
<str id="topBar.chart">Mapa</str>
+2 -16
View File
@@ -1,5 +1,6 @@
#pragma header
#version 120 // this shader doesnt require expensive calculations
// Used in charter by waveforms
const vec3 gradient1 = vec3(114.0/255.0, 81.0/255.0, 135.0/255.0);
const vec3 gradient2 = vec3(144.0/255.0, 80.0/255.0, 186.0/255.0);
@@ -17,21 +18,6 @@ uniform vec2 waveformSize;
uniform bool lowDetail;
float catmullRom(
float p0, float p1, float p2, float p3,
float t
) {
float t2 = t * t;
float t3 = t2 * t;
return 0.5 * (
(2.0 * p1) +
(-p0 + p2) * t +
(2.0*p0 - 5.0*p1 + 4.0*p2 - p3) * t2 +
(-p0 + 3.0*p1 - 3.0*p2 + p3) * t3
);
}
float getAmplitude(vec2 pixel) {
float pixelID = floor((pixel.y+pixelOffset)/3.0);
+2 -2
View File
@@ -12,7 +12,7 @@ function create() {
tankman = new FunkinSprite(game.dad.x + game.dad.globalOffset.x + 520, game.dad.y + game.dad.globalOffset.y + 225);
tankman.antialiasing = true;
tankman.loadSprite(Paths.image('game/cutscenes/tank/guns-tankman'));
tankman.addAnim('tank', 'TANK TALK 2', 0, false);
tankman.animateAtlas.anim.addBySymbol('tank', 'TANK TALK 2', 0, false);
game.insert(game.members.indexOf(game.dad), tankman);
game.dad.visible = false;
@@ -65,4 +65,4 @@ function destroy() {
for(thing in [tankman, tankTalk]) thing.destroy();
if(destroyDistorto) distorto.destroy();
FlxTween.tween(FlxG.camera, {zoom: game.defaultCamZoom}, 0.7, {ease: FlxEase.quadInOut, startDelay: 0});
}
}
+9 -13
View File
@@ -33,20 +33,20 @@ function create() {
tankman = new FunkinSprite(game.dad.x + game.dad.globalOffset.x + 520, game.dad.y + game.dad.globalOffset.y + 225);
tankman.antialiasing = true;
tankman.loadSprite(Paths.image('game/cutscenes/tank/stress-tankman'));
tankman.addAnim('p1', 'TANK TALK 3 P1 UNCUT', 0, false);
tankman.addAnim('p2', 'TANK TALK 3 P2 UNCUT', 0, false);
tankman.animateAtlas.anim.addBySymbol('p1', 'TANK TALK 3 P1 UNCUT', 0, false);
tankman.animateAtlas.anim.addBySymbol('p2', 'TANK TALK 3 P2 UNCUT', 0, false);
tankman.playAnim('p1');
pico = new FunkinSprite(game.gf.x + game.gf.globalOffset.x - 615, game.gf.y + game.gf.globalOffset.y - 130);
pico = new FunkinSprite(game.gf.x + game.gf.globalOffset.x + 150, game.gf.y + game.gf.globalOffset.y + 395);
pico.antialiasing = true;
pico.loadSprite(Paths.image('game/cutscenes/tank/stress-pico'));
pico.addAnim('die', 'die', 24, false, null, null, 0, 0, null, true);
pico.addAnim('saves', 'saves', 24, false, null, null, 0, 0, null, true);
pico.addAnim('idle', 'idle', 24, true, null, null, 0, 0, null, true);
pico.animateAtlas.anim.addBySymbol('die', 'GF Time to Die sequence', 24, false);
pico.animateAtlas.anim.addBySymbol('saves', 'Pico Saves them sequence', 24, false);
pico.animateAtlas.anim.addBySymbol('idle', 'Pico Dual Wield on Speaker idle', 24, true);
pico.scrollFactor.set(0.95, 0.95);
pico.playAnim("idle");
pico.visible = false;
game.insert(game.members.indexOf(game.gf) + 2, pico);
game.insert(game.members.indexOf(game.gf) + 1, pico);
game.insert(game.members.indexOf(game.dad) + 1, tankman);
game.dad.visible = false;
@@ -59,11 +59,7 @@ function update(elapsed) {
lipSync(tankman, 0, 16750);
if (stressCutscene.time > 15100) {
step = 1;
//focusOn(game.gf);
game.camFollow.x += 350;
game.camFollow.y -= 200;
focusOn(game.gf);
pico.visible = true;
pico.playAnim('die', true);
@@ -122,7 +118,7 @@ function update(elapsed) {
}
function lipSync(char:FunkinSprite, begin:Float, end:Float) {
char.anim.curAnim.curFrame = Std.int(FlxMath.remapToRange(stressCutscene.time, begin, end, 0, char.anim.curAnim.numFrames - 1));
char.animateAtlas.anim.curFrame = Std.int(FlxMath.remapToRange(stressCutscene.time, begin, end, 0, char.animateAtlas.anim.length-1));
}
function focusOn(char, snap:Bool = false) {
+3 -3
View File
@@ -12,8 +12,8 @@ function create() {
tankman = new FunkinSprite(game.dad.x + game.dad.globalOffset.x + 520, game.dad.y + game.dad.globalOffset.y + 225);
tankman.antialiasing = true;
tankman.loadSprite(Paths.image('game/cutscenes/tank/ugh-tankman'));
tankman.addAnim('1', 'TANK TALK 1 P1', 0, false);
tankman.addAnim('2', 'TANK TALK 1 P2', 0, false);
tankman.animateAtlas.anim.addBySymbol('1', 'TANK TALK 1 P1', 0, false);
tankman.animateAtlas.anim.addBySymbol('2', 'TANK TALK 1 P2', 0, false);
game.insert(game.members.indexOf(game.dad), tankman);
game.dad.visible = false;
@@ -77,4 +77,4 @@ function destroy() {
for(timer in timers) timer.cancel();
for(thing in [tankTalk1, tankTalk2, bfBeep, tankman]) thing.destroy();
if(destroyDistorto) distorto.destroy();
}
}
+17
View File
@@ -0,0 +1,17 @@
[General]
sample-type=float32
stereo-mode=speakers
hrtf=false
cf_level=0
output-limiter=false
front-stabilizer=false
volume-adjust=0
period_size=441
sources=512
sends=64
dither=false
[decoder]
hq-mode=true
distance-comp=true
nfc=false
+29 -26
View File
@@ -3,32 +3,36 @@
<if cond="update || all">
<print pretty="true">Preparing installation...</print>
<!-- Hxcpp & OpenFL & Lime (Required for Flixel) -->
<git name="hxcpp" url="https://github.com/CodenameCrew/cne-hxcpp"/>
<git name="openfl" url="https://github.com/CodenameCrew/cne-openfl" ref="cne" skipDeps="true"/>
<git name="lime" url="https://github.com/CodenameCrew/cne-lime" ref="cne" skipDeps="true"/>
<!-- OpenFL & Lime (Required for Flixel) -->
<git name="openfl" url="https://github.com/CodenameCrew/cne-openfl" ref="old" />
<lib name="lime" version="8.1.2" />
<!-- <git name="lime" url="https://github.com/CodenameCrew/cne-lime" /> disabled for now until fixed -->
<!-- Flixel -->
<git name="flixel" url="https://github.com/CodenameCrew/cne-flixel" ref="cne" skipDeps="true"/>
<git name="flixel-addons" url="https://github.com/CodenameCrew/cne-flixel-addons" ref="dev" skipDeps="true"/>
<!-- CNE Core Libraries -->
<git name="flixel-animate" url="https://github.com/CodenameCrew/cne-flixel-animate" skipDeps="true"/>
<git name="hscript-improved" url="https://github.com/CodenameCrew/hscript-improved" ref="codename-dev"/>
<git name="away3d" url="https://github.com/CodenameCrew/away3d" skipDeps="true"/>
<git name="flixel" url="https://github.com/CodenameCrew/cne-flixel" />
<git name="flixel-addons" url="https://github.com/CodenameCrew/cne-flixel-addons" />
<!-- Other Libraries -->
<git name="hxdiscord_rpc" url="https://github.com/CodenameCrew/cne-hxdiscord_rpc" skipDeps="true"/>
<git name="hxvlc" url="https://github.com/CodenameCrew/cne-hxvlc" skipDeps="true"/>
<lib name="funkin-modchart" skipDeps="true"/>
<lib name="hxcpp-debug-server"/>
<lib name="format"/>
<lib name="markdown"/>
<lib name="nape-haxe4"/>
<git name="hscript-improved" url="https://github.com/CodenameCrew/hscript-improved" ref="codename-dev" />
<git name="hxdiscord_rpc" url="https://github.com/CodenameCrew/cne-hxdiscord_rpc" skipDeps="true" />
<lib name="funkin-modchart" version="1.2.4" skipDeps="true" />
<lib name="hxvlc" version="1.9.3" skipDeps="true" />
<!-- Documentation and other features -->
<git name="away3d" url="https://github.com/CodenameCrew/away3d" />
<!-- <lib name="dox" /> -->
<lib name="markdown" />
<lib name="hxcpp-debug-server" />
<lib name="nape-haxe4" />
<!-- For building lime -->
<!-- <lib global="true" name="hxp"/> <lib name="hxp"/> -->
<!-- <lib global="true" name="format"/> <lib name="format"/> -->
<lib global="true" name="hxp" /> <lib name="hxp" />
<lib global="true" name="format" /> <lib name="format" />
<git name="hxcpp" url="https://github.com/CodenameCrew/cne-hxcpp" />
<git name="flixel-animate" url="https://github.com/MaybeMaru/flixel-animate/" skipDeps="true"/>
</if>
<!-- Hxcpp Building -->
@@ -37,16 +41,15 @@
<!-- Build hxcpp -->
<cmd inLib="hxcpp" dir="tools/hxcpp" cmd="haxe compile.hxml" />
<cmd inLib="lime" cmd="haxelib run lime rebuild hxcpp"/>
</if>
<!-- Lime Building -->
<if cond="build-lime">
<!-- <if cond="build-lime || all">
<print pretty="true">Rebuilding Lime...</print>
<!-- Build lime -->
<cmd inLib="lime" dir="tools" cmd="haxe tools.hxml"/>
<cmd inLib="lime" cmd="haxelib run lime rebuild $PLATFORM -nocolor -nocffi -release"/>
</if>
<!- - Build lime - ->
<cmd inLib="lime" cmd="haxelib run lime rebuild . $PLATFORM -release -clean" />
<cmd inLib="lime" dir="tools" cmd="haxe tools.hxml" />
</if> -->
</libraries>
+148 -147
View File
@@ -1,54 +1,81 @@
<?xml version="1.0" encoding="utf-8"?>
<project xmlns="http://lime.software/project/1.0.4" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://lime.software/project/1.0.4 http://lime.software/xsd/project-1.0.4.xsd">
<!-- ____________________________ Window Settings ___________________________ -->
<project>
<!-- _________________________ Application Settings _________________________ -->
<app
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" />
<!-- CHANGE THE SAVE PATH & NAME FOR YOUR MOD HERE! -->
<!--
CHANGE THE SAVE PATH & NAME FOR YOUR MOD HERE!
-->
<haxedef name="SAVE_PATH" value="CodenameEngine"/>
<haxedef name="SAVE_NAME" value="save-default"/>
<haxedef name="SAVE_OPTIONS_PATH" value="CodenameEngine"/>
<haxedef name="SAVE_OPTIONS_NAME" value="options"/>
<!-- Comment this out to disable commit number on FPS -->
<define name="SHOW_BUILD_ON_FPS"/>
<!--Switch Export with Unique ApplicationID and Icon-->
<set name="APP_ID" value="0x0100f6c013bbc000" />
<!-- Comment this out to disable dark mode windows -->
<define name="DARK_MODE_WINDOW"/>
<!-- DO NOT REMOVE!! ALLOWS ME TO DO REGIONS LIKE IN C# AND KEEP CODE ORGANIZED. REMOVING IT WOULD BREAK THE ENGINE -->
<haxedef name="REGION" />
<!-- Should it checks for updates? -->
<define name="UPDATE_CHECKING" if="desktop" unless="hl || debug"/>
<!--The flixel preloader is not accurate in Chrome. You can use it regularly if you embed the swf into a html file
or you can set the actual size of your file manually at "FlxPreloaderBase-onUpdate-bytesTotal"-->
<app preloader="flixel.system.FlxPreloader" />
<!--Minimum without FLX_NO_GAMEPAD: 11.8, without FLX_NO_NATIVE_CURSOR: 11.2-->
<set name="SWF_VERSION" value="11.8" />
<!-- ____________________________ Window Settings ___________________________ -->
<!--These window settings apply to all targets-->
<window width="1280" height="720" background="#000000" hardware="true" vsync="false" />
<!--HTML5-specific-->
<window if="html5" resizable="true" />
<!--Desktop-specific-->
<window if="desktop" orientation="landscape" fullscreen="false" resizable="true" vsync="false"/>
<window if="mac" allow-high-dpi="true" />
<!--Mobile-specific-->
<window if="mobile" orientation="landscape" fullscreen="true" width="0" height="0" resizable="false"/>
<!--Switch-specific-->
<window if="switch" orientation="landscape" fullscreen="true" width="0" height="0" resizable="true" />
<!-- _________________________________ Engine Settings _______________________________ -->
<!-- Comment this out to disable updates !-->
<define name="UPDATE_CHECKING" unless="web || hl || neko || debug"/>
<!-- Comment this out to disable Discord RPC !-->
<section if="cpp">
<!-- Compile With Discord Rich Presence -->
<!-- TODO: Update Discord RPC Library & Rewrite DiscordUtil -->
<!-- <define name="DISCORD_RPC" if="desktop"/> -->
<!-- Compile with video support (hxvlc) -->
<define name="VIDEO_CUTSCENES" if="desktop || mobile"/>
<define name="DISCORD_RPC" if="desktop"/>
</section>
<!-- Uncomment this to achive faster compile times, will strip extraneous and commonly unused features. -->
<!-- <define name="STRIPPED_COMPILE" unless="debug"/> -->
<!-- Compile with _theo0p's modcharting tools -->
<define name="MODCHARTING_FEATURES" unless="STRIPPED_COMPILE"/>
<!-- Comment this out to disable modcharting features -->
<define name="MODCHARTING_FEATURES" unless="STRIPPED_COMPILE" />
<!-- Comment this out to disable GitHub API integration !-->
<define name="GITHUB_API" unless="web || hl"/>
<!-- Comment this out to prevent unused classes from being compiled.
Reduces compilation time at the cost of limited HScript flexibility. !-->
<define name="COMPILE_ALL_CLASSES" unless="STRIPPED_COMPILE"/>
<define name="COMPILE_ALL_CLASSES" unless="STRIPPED_COMPILE" />
<!-- Comment this out to prevent extending classes in hscript.
Reduces compilation time at the cost of limited HScript flexibility. !-->
<define name="CUSTOM_CLASSES" unless="STRIPPED_COMPILE"/>
<!-- Comment this out to disable abstract support in hscript. !-->
<haxedef name="HSCRIPT_ABSTRACT_SUPPORT" unless="STRIPPED_COMPILE" />
<define name="CUSTOM_CLASSES" unless="STRIPPED_COMPILE" />
<!-- Comment this out to disable multithreading !-->
<haxedef name="ALLOW_MULTITHREADING" if="cpp"/>
<haxedef name="ALLOW_MULTITHREADING" unless="web || flash" />
<!-- Comment this out to disable mods folder !-->
<haxedef name="MOD_SUPPORT" unless="web"/>
@@ -57,60 +84,36 @@
<haxedef name="TRANSLATIONS_SUPPORT"/>
<!-- Comment this out to disable global scripts !-->
<haxedef name="GLOBAL_SCRIPT"/>
<haxedef name="GLOBAL_SCRIPT" />
<!-- Comment this out to disable softcoded states and state scripting !-->
<haxedef name="SOFTCODED_STATES"/>
<haxedef name="SOFTCODED_STATES" />
<!-- Comment this out to disable support for custom ndlls. !-->
<haxedef name="NDLLS_SUPPORTED" unless="web || iphonesim || hl"/>
<haxedef name="NDLLS_SUPPORTED" unless="web || iphonesim || hl" />
<!-- Comment this out to use the default OpenFL assets system for the assets/ folder. !-->
<define name="USE_ADAPTED_ASSETS" unless="web"/>
<define name="USE_ADAPTED_ASSETS" unless="web" />
<!-- Comment this out to disable support for Away3D Flixel Integration (reduces compile times) !-->
<define name="THREE_D_SUPPORT" unless="STRIPPED_COMPILE"/>
<define name="THREE_D_SUPPORT" unless="STRIPPED_COMPILE" />
<!-- Comment this out to disable support for Nape integration (flixel.addons.nape) (reduces compile times) !-->
<define name="NAPE_ENABLED" unless="STRIPPED_COMPILE"/>
<define name="NAPE_ENABLED" unless="STRIPPED_COMPILE" />
<!-- Comment this out to disable video cutscene support, in case of compilation errors. !-->
<section if="cpp">
<define name="VIDEO_CUTSCENES" if="desktop || android" />
</section>
<!-- Comment this out to disable commit number on FPS -->
<define name="SHOW_BUILD_ON_FPS" />
<!-- Comment this out to disable dark mode windows -->
<define name="DARK_MODE_WINDOW"/>
<!-- Disable a optimization, to allow reflection to use more functions -->
<define name="FLX_NO_GENERIC"/>
<!-- _________________________ Application Settings _________________________ -->
<app
title="Friday Night Funkin' - Codename Engine"
file="CodenameEngine" packageName="com.yoshman29.codenameengine"
package="com.yoshman29.codenameengine"
version="1.0.2" company="Yoshman29"/>
<!--Switch Export with Unique ApplicationID and Icon-->
<set name="APP_ID" value="0x0100f6c013bbc000"/>
<!--<set name="SWF_VERSION" value="11.8"/>-->
<!--Place custom nodes like icons here (higher priority to override the HaxeFlixel icon)-->
<icon path="art/icon16.png" size='16'/>
<icon path="art/icon32.png" size='24'/>
<icon path="art/icon32.png" size='32'/>
<icon path="art/icon64.png" size='40'/>
<icon path="art/icon64.png" size='48'/>
<icon path="art/icon64.png" size='64'/>
<icon path="art/iconOG.png" size='96'/>
<icon path="art/iconOG.png" size='128'/>
<icon path="art/iconOG.png" size='196'/>
<icon path="art/iconOG.png" size='256'/>
<icon path="art/iconOG.png" size='512'/>
<icon path="art/iconOG.png" size='768'/>
<icon path="art/iconOG.png" size='1024'/>
<!-- ____________________________ Window Settings ___________________________ -->
<window hardware="true" colorDepth="32" background="" fps="0" orientation="landscape"/>
<window unless="desktop" width="0" height="0" vsync="true" fullscreen="true"/>
<window if="desktop" type="opengl" width="1280" height="720" fullscreen="false" resizable="true" vsync="false" allowHighDPI="true"/>
<window if="mobile" resizable="false"/>
<window if="html5 || switch" resizable="true"/>
<define name="FLX_NO_GENERIC" />
<!-- _______________________________ Libraries ______________________________ -->
@@ -118,46 +121,32 @@
<define name="disable-version-check"/>
<haxedef name="disable-version-check"/>
<haxelib name="flixel"/>
<haxedef name="no-deprecation-warnings"/>
<haxedef name="DISCORD_DISABLE_IO_THREAD" if="DISCORD_RPC"/>
<haxelib name="flixel-addons"/>
<haxelib name="flixel-animate"/>
<haxelib name="flixel" />
<haxelib name="flixel-addons" />
<haxelib name="hscript-improved"/>
<haxelib name="format" unless="STRIPPED_COMPILE"/>
<haxelib name="markdown" unless="STRIPPED_COMPILE"/>
<haxelib name="hxvlc" if="VIDEO_CUTSCENES"/>
<haxelib name="hxdiscord_rpc" if="DISCORD_RPC"/>
<haxelib name="away3d" if="THREE_D_SUPPORT"/>
<haxelib name="hxvlc" if="VIDEO_CUTSCENES" />
<haxelib name="away3d" if="THREE_D_SUPPORT" />
<haxelib name="format" />
<haxelib name="flixel-animate" />
<haxelib name="nape-haxe4" if="NAPE_ENABLED"/>
<haxelib name="hxcpp-debug-server" if="debug"/>
<haxelib name="hscript-improved" />
<haxelib name="hxdiscord_rpc" if="DISCORD_RPC"/>
<haxelib name="markdown" />
<haxelib name="hxcpp-debug-server" if="TEST_BUILD debug" />
<!-- ____________________________ Source Settings ___________________________ -->
<!-- _____________________________ Path Settings ____________________________ -->
<!-- DO NO REMOVE THIS -->
<haxedef name="REGION"/>
<set name="BUILD_DIR" value="export/debug" if="debug" />
<set name="BUILD_DIR" value="export/release" unless="debug" />
<set name="BUILD_DIR" value="export/debug-32bit" if="debug 32bit" />
<set name="BUILD_DIR" value="export/release-32bit" if="32bit" unless="debug" />
<set name="BUILD_DIR" value="latest/" if="beta" />
<set name="BUILD_DIR" value="export/debug" if="debug"/>
<set name="BUILD_DIR" value="export/release" unless="debug"/>
<set name="BUILD_DIR" value="export/debug-32bit" if="debug 32bit"/>
<set name="BUILD_DIR" value="export/release-32bit" if="32bit" unless="debug"/>
<set name="BUILD_DIR" value="latest/" if="beta"/>
<app main="funkin.backend.system.Main" preloader="flixel.system.FlxPreloader"/>
<source name="source"/>
<section if="MODCHARTING_FEATURES">
<haxedef name="FM_ENGINE" value="CODENAME"/>
<haxedef name="FM_ENGINE_VERSION" value="1.0"/>
<haxelib name="funkin-modchart" />
<haxeflag name="--macro" value="modchart.backend.macros.Macro.includeFiles()"/>
</section>
<!-- ____________________________ Assets Settings ___________________________ -->
<source name="source" />
<define name="PRELOAD_ALL" unless="web" />
<define name="NO_PRELOAD_ALL" unless="PRELOAD_ALL"/>
@@ -173,69 +162,81 @@
<assets path='mods' rename='mods' embed='false' />
</section>
<!-- <assets path='art/readme.txt' rename='do NOT readme.txt' /> -->
<assets path='art/readme.txt' rename='do NOT readme.txt' />
<!-- OpenAL config -->
<section if="desktop">
<assets path="building/alsoft.txt" rename="plugins/alsoft.ini" type="text" if="windows"/>
<assets path="building/alsoft.txt" rename="plugins/alsoft.conf" type="text" unless="windows"/>
</section>
<!-- ______________________________ Haxedefines _____________________________ -->
<haxeflag name="--macro" value="funkin.backend.system.macros.Macros.initMacros()"/>
<haxeflag name="--macro" value="funkin.backend.system.macros.BuildInfo.printBuildInfo()" />
<haxedef name="VIDEO_CUTSCENES" if="VIDEO_CUTSCENES"/>
<haxedef name="UPDATE_CHECKING" if="UPDATE_CHECKING"/>
<haxedef name="DISCORD_RPC" if="DISCORD_RPC"/>
<haxedef name="SHOW_BUILD_ON_FPS" if="SHOW_BUILD_ON_FPS"/>
<haxedef name="SOFTCODED_CLASSES" if="SOFTCODED_CLASSES"/>
<haxedef name="USE_ADAPTED_ASSETS" if="USE_ADAPTED_ASSETS"/>
<haxedef name="FLX_NO_FOCUS_LOST_SCREEN" />
<!-- Backward Compatibilities -->
<haxedef name="FLX_HEALTH" />
<!--Disable the Flixel core debugger. Automatically gets set whenever you compile in release mode!-->
<haxedef name="FLX_NO_DEBUG" unless="debug" />
<haxedef name="FLX_NO_FOCUS_LOST_SCREEN"/>
<haxedef name="FLX_NO_DEBUG" unless="debug"/>
<haxedef name="analyzer-optimize" unless="debug" />
<haxedef name="DISCORD_DISABLE_IO_THREAD" if="DISCORD_RPC"/>
<haxedef name="VIDEO_CUTSCENES" if="VIDEO_CUTSCENES" />
<haxedef name="UPDATE_CHECKING" if="UPDATE_CHECKING" />
<haxedef name="DISCORD_RPC" if="DISCORD_RPC" />
<haxedef name="SHOW_BUILD_ON_FPS" if="SHOW_BUILD_ON_FPS" />
<haxedef name="SOFTCODED_CLASSES" if="SOFTCODED_CLASSES" />
<haxedef name="USE_ADAPTED_ASSETS" if="USE_ADAPTED_ASSETS" />
<haxedef name="openfl_dpi_aware" if="openfl_dpi_aware" />
<!-- <haxedef name="no-deprecation-warnings"/> -->
<!-- Disable it for more concise compilation errors. -->
<haxedef name="message.reporting" value="pretty" />
<!-- _________________________________ Custom _______________________________ -->
<!--Place custom nodes like icons here (higher priority to override the HaxeFlixel icon)-->
<icon path="art/icon16.png" size='16'/>
<icon path="art/icon32.png" size='24'/>
<icon path="art/icon32.png" size='32'/>
<icon path="art/icon64.png" size='40'/>
<icon path="art/icon64.png" size='48'/>
<icon path="art/icon64.png" size='64'/>
<icon path="art/iconOG.png" size='96' />
<icon path="art/iconOG.png" size='128' />
<icon path="art/iconOG.png" size='196' />
<icon path="art/iconOG.png" size='256' />
<icon path="art/iconOG.png" size='512' />
<icon path="art/iconOG.png" size='768' />
<icon path="art/iconOG.png" size='1024' />
<!-- _________________________________ Additional stuff _______________________________ -->
<section if="MODCHARTING_FEATURES">
<haxedef name="FM_ENGINE" value="CODENAME"/>
<haxedef name="FM_ENGINE_VERSION" value="1.0"/>
<haxelib name="funkin-modchart" />
<haxeflag name="--macro" value="modchart.backend.macros.Macro.includeFiles()"/>
</section>
<section if="COMPILE_ALL_CLASSES">
<haxeflag name="-dce" value="no" />
<haxeflag name="--macro" value="funkin.backend.system.macros.Macros.addAdditionalClasses()" />
</section>
<haxeflag name="--macro" value="funkin.backend.system.macros.Macros.initMacros()" />
<!-- Fix compiling issues -->
<setenv name="HAXEPATH" value="./"/>
<!-- Haxe 4.3.0+: Enable pretty syntax errors and stuff. -->
<haxedef name="message.reporting" value="pretty"/>
<haxedef name="HXCPP_M32" if="32bit" />
<haxedef name="HXCPP_DEBUG_LINK" if="debug" />
<haxedef name="HXCPP_CHECK_POINTER" />
<haxedef name="HXCPP_STACK_LINE" />
<haxedef name="HXCPP_CATCH_SEGV" />
<!-- for logging -->
<haxedef name="hscriptPos"/>
<section if="COMPILE_ALL_CLASSES">
<haxeflag name="-dce" value="no"/>
<haxeflag name="--macro" value="funkin.backend.system.macros.Macros.addAdditionalClasses()"/>
</section>
<haxeflag name="--macro" value="funkin.backend.system.macros.BuildInfo.printBuildInfo()"/>
<haxedef name="hscriptPos" /> <!-- for logging -->
<!-- EXPERMENTAL FEATURES -->
<haxedef name="EXPERIMENTAL_FLXGRAPHIC_DESTROY_FIX" />
<haxedef name="openfl_dpi_aware"/>
<haxedef name="openfl_pool_events"/>
<haxedef name="openfl-enable-handle-error"/>
<haxedef name="openfl_no_custom_render_event"/>
<haxedef name="HXCPP_M32" if="32bit"/>
<haxedef name="HXCPP_DEBUG_LINK" if="debug"/>
<!--C++20 seems doesn't seem to do anything but causes compile warnings-->
<!--<haxedef name="HXCPP_CPP20"/>-->
<!--<haxedef name="HXCPP_CLANG"/>-->
<!--<haxedef name="HXCPP_MSVC"/>-->
<!-- This seems to causes more harm, but to be fair, its mostly because of text rendering -->
<!-- This is really important to fix honestly, try to get cairo text rendering not memory leak in the future -->
<!--<haxedef name="HXCPP_GC_BIG_BLOCKS"/>-->
<!--<haxedef name="HXCPP_GC_GENERATIONAL"/>-->
<!--<haxedef name="HXCPP_GC_DYNAMIC_SIZE"/>-->
<haxedef name="HXCPP_CHECK_POINTER"/>
<haxedef name="HXCPP_STACK_LINE"/>
<haxedef name="HXCPP_STACK_TRACE"/>
<!--<haxedef name="HXCPP_CATCH_SEGV"/>-->
<haxedef name="HXCPP_FAST_LINK" unless="debug"/>
<!--<haxedef name="HXCPP_ALIGN_ALLOC"/>--> <!--hxcpp.h(71): warning C4005: 'HXCPP_ALIGN_ALLOC': macro redefinition-->
<haxedef name="analyzer-optimize" unless="debug"/>
</project>
+960
View File
@@ -0,0 +1,960 @@
package flixel.sound;
import lime.media.AudioBuffer;
import lime.media.AudioSource;
import lime.media.AudioManager;
import openfl.Assets;
import openfl.events.IEventDispatcher;
import openfl.events.Event;
import openfl.media.Sound;
import openfl.media.SoundChannel;
import openfl.media.SoundTransform;
import openfl.media.SoundMixer;
import openfl.net.URLRequest;
import openfl.utils.AssetType;
#if flash11
import openfl.utils.ByteArray;
#end
import flixel.math.FlxMath;
import flixel.math.FlxPoint;
import flixel.system.FlxAssets.FlxSoundAsset;
import flixel.tweens.FlxTween;
import flixel.util.FlxDestroyUtil;
import flixel.util.FlxSignal;
import flixel.util.FlxStringUtil;
import flixel.FlxBasic;
import flixel.FlxObject;
import flixel.FlxG;
@:access(flixel.FlxGame)
class FlxSound extends FlxBasic {
/**
* The x position of this sound in world coordinates.
* Only really matters if you are doing proximity/panning stuff.
*/
public var x:Float;
/**
* The y position of this sound in world coordinates.
* Only really matters if you are doing proximity/panning stuff.
*/
public var y:Float;
/**
* Whether or not this sound should be automatically destroyed when you switch states.
*/
public var persist:Bool;
/**
* Whether or not the sound is currently playing.
*/
public var playing(get, never):Bool;
/**
* Set volume to a value between 0 and 1* to change how this sound is.
*/
public var volume(get, set):Float;
/**
* Whether to make this sound muted or not.
*/
public var muted(default, set):Bool;
#if FLX_PITCH
/**
* Set pitch, which also alters the playback speed. Default is 1.
* @since 5.0.0
*/
public var pitch(get, set):Float;
/**
* Alters the pitch of the sound depends on the current FlxG.timeScale. Default is true.
* @since raltyMod
*/
public var timeScaleBased:Bool;
#end
/**
* Pan amount. -1 = full left, 1 = full right. Proximity based panning overrides this.
*/
public var pan(get, set):Float;
/**
* The position in runtime of the music playback in milliseconds.
* If set while paused, changes only come into effect after a `resume()` call.
*/
public var time(get, set):Float;
/**
* The offset for this FlxSound.
* Useful for just generally offsetting this sound without affecting time.
* @since raltyMod
*/
public var offset(get, set):Float;
/**
* The length of the sound in milliseconds.
* @since 4.2.0
*/
public var length(get, never):Float;
/**
* The latency of the sound in milliseconds.
* @since raltyMod
*/
//public var latency(get, never):Float;
/**
* Whether or not this sound should loop.
*/
public var looped(default, set):Bool;
/**
* In case of looping, the point (in milliseconds) from where to restart the sound when it loops back
* @since 4.1.0
*/
public var loopTime(default, set):Float;
/**
* At which point to stop playing the sound, in milliseconds.
* If not set / `null`, the sound completes normally.
* @since 4.2.0
*/
public var endTime(default, set):Null<Float>;
/**
* The sound's "target" (for proximity and panning).
* @since raltyMod
*/
public var target:Null<FlxObject>;
/**
* The maximum effective radius of this sound (for proximity and panning).
* @since raltyMod
*/
public var radius:Float;
/**
* Whether the proximity alters the pan or not.
* @since raltyMod
*/
public var proximityPan:Bool;
/**
* Controls how much this object is affected by camera scrolling. `0` = no movement (e.g. a static sound),
* This is only useful if used with proximity (Initialized once proximity is used).
* @since raltyMod
*/
public var scrollFactor(default, null):FlxPoint;
/**
* Stores for how much channels are in the loaded sound.
* @since raltyMod
*/
public var channels(get, never):Int;
/**
* Whether or not this sound is stereo instead of mono.
* @since raltyMod
*/
public var stereo(get, never):Bool;
/**
* Whether or not this sound is loaded yet.
* @since raltyMod
*/
public var loaded(get, never):Bool;
/**
* Whether or not this sound is streamed, only vorbis though.
* @since raltyMod
*/
public var streamed(get, never):Bool;
/**
* Stores the average wave amplitude of both stereo channels.
* @since raltyMod
*/
public var amplitude(get, never):Float;
/**
* Just the amplitude of the left stereo channel
* @since raltyMod
*/
public var amplitudeLeft(get, never):Float;
/**
* Just the amplitude of the right stereo channel
* @since raltyMod
*/
public var amplitudeRight(get, never):Float;
/**
* The ID3 song name. Defaults to null. Currently only works for MP3 streamed sounds.
*/
public var name(default, null):String;
/**
* The ID3 artist name. Defaults to null. Currently only works for MP3 streamed sounds.
*/
public var artist(default, null):String;
/**
* Whether to call `destroy()` when the sound has finished playing.
*/
public var autoDestroy:Bool;
/**
* Signal that is dispatched on sound complete.
* Seperates the looping from onComplete.
*/
public final onFinish:FlxSignal;
/**
* Tracker for sound complete callback. If assigned, will be called
* each time when sound reaches its end.
*/
//@:deprecated("`FlxSound.onComplete` is deprecated! Use `FlxSound.onFinish` instead.")
public var onComplete:Void->Void;
/**
* The sound group this sound belongs to
*/
public var group(default, set):FlxSoundGroup;
/**
* Stores the sound lime AudioBuffer if exists.
* @since raltyMod
*/
public var buffer(get, never):AudioBuffer;
/**
* The tween used to fade this sound's volume in and out (set via `fadeIn()` and `fadeOut()`)
* @since 4.1.0
*/
public var fadeTween:FlxTween;
@:allow(flixel.system.frontEnds.SoundFrontEnd.load) var _sound:Sound;
var _transform:SoundTransform;
var _channel:SoundChannel;
var _source:AudioSource;
var _paused:Bool;
var _volume:Float;
var _volumeAdjust:Float;
var _pan:Float;
var _panAdjust:Float;
var _time:Float;
var _offset:Float;
var _timeInterpolation:Float;
var _lastTime:Null<Float>; // FlxG.game.getTicks(), in MS
var _length:Float;
#if FLX_PITCH
var _pitch:Float;
var _timeScaleAdjust:Float;
var _realPitch:Float;
#end
var _amplitudeLeft:Float;
var _amplitudeRight:Float;
var _amplitudeUpdate:Bool;
var _alreadyPaused:Null<Bool>;
public function new() {
super();
onFinish = new FlxSignal();
revive();
}
/**
* Resets this FlxSound properties.
*
* @param clean Whether if this FlxSound also needs to be cleaned up too.
*/
public function reset():Void {
if (_source != null) stop();
onFinish.removeAll();
x = y = 0;
@:bypassAccessor muted = false;
@:bypassAccessor looped = false;
@:bypassAccessor loopTime = 0;
@:bypassAccessor endTime = null;
autoDestroy = false;
visible = false;
target = null;
radius = 0;
proximityPan = true;
onComplete = null;
timeScaleBased = true;
_alreadyPaused = null;
_cameras = null;
_lastTime = null;
_paused = false;
_offset = _length = _time = 0;
_volume = _volumeAdjust = 1;
_amplitudeLeft = _amplitudeRight = 0;
_amplitudeUpdate = true;
#if FLX_PITCH _pitch = _realPitch = _timeScaleAdjust = 1; #end
if (_transform == null) _transform = new SoundTransform();
}
/**
* Internal cleanup function for cleaning up this FlxSound.
* @since raltyMod
*/
function cleanup(destroySound:Bool, resetPosition:Bool = true) @:privateAccess {
active = false;
_lastTime = null;
if (destroySound) {
scrollFactor = FlxDestroyUtil.put(scrollFactor);
if (group != null) group.remove(this);
if (_channel != null) {
_channel.stop();
_channel = null;
}
if (_source != null) {
_source.onComplete.remove(stopped);
_source.onLoop.remove(source_looped);
_source.dispose();
}
_source = null;
_sound = null;
if (autoDestroy) persist = false;
reset();
}
else if (_channel != null && _channel.__isValid) {
if (resetPosition) {
_source.stop();
_time = 0;
_paused = false;
}
else if (!_paused) {
get_time();
_source.pause();
}
}
}
/**
* Handles fade out, fade in, panning, proximity, and amplitude operations each frame.
*/
override function update(elapsed:Float):Void {
if (!playing) return;
var timeScaleTarget = timeScaleBased ? FlxG.timeScale : 1.0;
if (_timeScaleAdjust != timeScaleTarget) {
_timeScaleAdjust = timeScaleTarget;
pitch = _pitch;
if (_channel == null) return;
}
_amplitudeUpdate = true;
// Distance-based volume control
if (target != null) {
var targetPosition = target.getPosition(), position = getPosition();
var camera = camera;
if (camera != null) {
targetPosition.subtract(camera.scroll.x * target.scrollFactor.x, camera.scroll.y * target.scrollFactor.y);
if (scrollFactor != null) position.subtract(camera.scroll.x * scrollFactor.x, camera.scroll.y * scrollFactor.y);
else position.subtract(camera.scroll.x, camera.scroll.y);
}
var radialMultiplier = targetPosition.distanceTo(position) / radius;
// Make it so it affects the 3d position of the source and not just the panning?
_volumeAdjust = 1 - FlxMath.bound(radialMultiplier, 0, 1);
if (proximityPan) _panAdjust = (position.x - targetPosition.x) / radius;
targetPosition.put();
position.put();
}
else
_volumeAdjust = 1.0;
updateTransform();
}
override function revive() {
reset();
super.revive();
}
override function kill() {
super.kill();
reset();
}
override function destroy() {
super.destroy();
cleanup(true);
}
/**
* One of the main setup functions for sounds, this function loads a sound from an embedded MP3.
*
* @param embeddedSound An embedded Class object representing an MP3 file.
* @param looped Whether or not this sound should loop endlessly.
* @param autoDestroy Whether or not this FlxSound instance should be destroyed when the sound finishes playing.
* Default value is false, but `FlxG.sound.play()` and `FlxG.sound.stream()` will set it to true by default.
* @param onComplete Called when the sound finished playing
* @return This FlxSound instance (nice for chaining stuff together, if you're into that).
*/
public function loadEmbedded(embeddedSound:FlxSoundAsset, looped = false, autoDestroy = false, ?onComplete:Void->Void):FlxSound {
if (!exists || embeddedSound == null) return this;
cleanup(true);
if ((embeddedSound is Sound)) _sound = embeddedSound;
else if ((embeddedSound is Class)) _sound = Type.createInstance(embeddedSound, []);
else if ((embeddedSound is String)) {
if (Assets.exists(embeddedSound, AssetType.MUSIC) && this == FlxG.sound.music)
_sound = Assets.getMusic(embeddedSound);
else if (Assets.exists(embeddedSound, AssetType.SOUND))
_sound = Assets.getSound(embeddedSound);
else
FlxG.log.error('Could not find a Sound asset with an ID of \'$embeddedSound\'.');
}
return init(looped, autoDestroy, onComplete);
}
/**
* One of the main setup functions for sounds, this function loads a sound from a URL.
*
* @param soundURL A string representing the URL of the MP3 file you want to play.
* @param looped Whether or not this sound should loop endlessly.
* @param autoDestroy Whether or not this FlxSound instance should be destroyed when the sound finishes playing.
* Default value is false, but `FlxG.sound.play()` and `FlxG.sound.stream()` will set it to true by default.
* @param onComplete Called when the sound finished playing
* @param onLoad Called when the sound finished loading.
* @return This FlxSound instance (nice for chaining stuff together, if you're into that).
*/
public function loadStream(soundURL:String, looped = false, autoDestroy = false, ?onComplete:Void->Void, ?onLoad:Void->Void):FlxSound {
if (!exists) return this;
cleanup(true);
_sound = new Sound();
_sound.addEventListener(Event.ID3, gotID3);
var loadCallback:Event->Void = null;
loadCallback = function(e:Event)
{
(e.target : IEventDispatcher).removeEventListener(e.type, loadCallback);
// Check if the sound was destroyed before calling. Weak ref doesn't guarantee GC.
if (_sound == e.target)
{
_length = _sound.length;
if (onLoad != null)
onLoad();
}
}
// Use a weak reference so this can be garbage collected if destroyed before loading.
_sound.addEventListener(Event.COMPLETE, loadCallback, false, 0, true);
_sound.load(new URLRequest(soundURL));
return init(looped, autoDestroy, onComplete);
}
#if flash11
/**
* One of the main setup functions for sounds, this function loads a sound from a ByteArray.
*
* @param bytes A ByteArray object.
* @param looped Whether or not this sound should loop endlessly.
* @param autoDestroy Whether or not this FlxSound instance should be destroyed when the sound finishes playing.
* Default value is false, but `FlxG.sound.play()` and `FlxG.sound.stream()` will set it to true by default.
* @return This FlxSound instance (nice for chaining stuff together, if you're into that).
*/
public function loadByteArray(bytes:ByteArray, looped = false, autoDestroy = false, ?onComplete:Void->Void):FlxSound {
if (!exists) return this;
cleanup(true);
_sound = new Sound();
_sound.addEventListener(Event.ID3, gotID3);
_sound.loadCompressedDataFromByteArray(bytes, bytes.length);
return init(looped, autoDestroy, onComplete);
}
#end
function init(?looped:Null<Bool>, ?autoDestroy:Null<Bool>, ?onComplete:Null<Void->Void>):FlxSound {
if (looped != null) this.looped = looped;
if (autoDestroy != null) this.autoDestroy = autoDestroy;
if (onComplete != null) this.onComplete = onComplete;
if (_sound != null) makeChannel();
else _length = 0;
endTime = null;
return this;
}
/**
* Call inbetween init function if a sound asset does exists.
* @since raltyMod
*/
function makeChannel() @:privateAccess {
if (_sound.__buffer == null) return;
if (_source != null) _source.dispose();
if (_channel == null) (_source = (_channel = new SoundChannel(null)).__source = new AudioSource(_sound.__buffer)).gain = 0;
else {
(_source = new AudioSource(_sound.__buffer)).gain = 0;
_channel.__dispose();
_channel.__source = _source;
SoundMixer.__registerSoundChannel(_channel);
}
_length = _source.length;
_source.onComplete.add(stopped);
_source.onLoop.add(source_looped);
_channel.__soundTransform = _transform;
_channel.__isValid = true;
}
/**
* Call after adjusting the volume to update the sound channel's settings.
*/
@:allow(flixel.sound.FlxSoundGroup)
function updateTransform() {
_transform.volume = calcTransformVolume();
_transform.pan = _pan + _panAdjust;
if (_channel != null) _channel.soundTransform = _transform;
}
public function calcTransformVolume():Float {
if (muted) return 0.0;
#if FLX_SOUND_SYSTEM
if (FlxG.sound.muted) return 0.0;
// TODO: when flixel-cne is updated, enable this
//return FlxG.sound.applySoundCurve(FlxG.sound.volume * volume);
return FlxG.sound.volume * getActualVolume();
#else
return getActualVolume();
#end
}
/**
* Call this function if you want this sound's volume to change
* based on distance from a particular FlxObject.
*
* @param X The X position of the sound.
* @param Y The Y position of the sound.
* @param TargetObject The object you want to track.
* @param Radius The maximum distance this sound can travel.
* @param Pan Whether panning should be used in addition to the volume changes.
* @return This FlxSound instance (nice for chaining stuff together, if you're into that).
*/
public function proximity(x = 0.0, y = 0.0, ?targetObject:FlxObject, ?radius:Float, pan = true, ?scrollFactor:FlxPoint):FlxSound {
setPosition(x, y);
if (targetObject != null) this.target = targetObject;
if (radius != null) this.radius = radius;
proximityPan = pan;
if (this.scrollFactor == null) this.scrollFactor = FlxPoint.get(1, 1);
if (scrollFactor != null) this.scrollFactor.copyFrom(scrollFactor);
return this;
}
/**
* Helper function to set the coordinates of this object.
* Sound positioning is used in conjunction with proximity/panning.
*
* @param x The new x position
* @param y The new y position
*/
public function setPosition(x = 0.0, y = 0.0):Void {
this.x = x;
this.y = y;
}
/**
* Returns the world position of this object.
*
* @param result Optional arg for the returning point.
* @return The world position of this object.
* @since raltyMod
*/
public function getPosition(?result:FlxPoint):FlxPoint {
if (result == null)
result = FlxPoint.get();
return result.set(x, y);
}
/**
* Call this function to play the sound - also works on paused sounds.
*
* @param forceRestart Whether to start the sound over or not.
* Default value is false, meaning if the sound is already playing or was
* paused when you call play(), it will continue playing from its current
* position, NOT start again from the beginning.
* @param startTime At which point to start playing the sound, in milliseconds.
* @param endTime At which point to stop playing the sound, in milliseconds.
* If not set / `null`, the sound completes normally.
*/
public function play(forceRestart = false, startTime = 0.0, ?endTime:Null<Float>):FlxSound {
if (!exists) return this;
if (forceRestart) cleanup(false, true);
else if (playing) return this;
if (endTime != null) this.endTime = endTime;
if (_paused) resume();
else startSound(startTime);
return this;
}
/**
* Unpause a sound. Only works on sounds that have been paused.
* @return This FlxSound instance (nice for chaining stuff together, if you're into that).
*/
public function resume():FlxSound {
if (_paused) startSound(_time);
return this;
}
/**
* Call this function to pause this sound.
* @return This FlxSound instance (nice for chaining stuff together, if you're into that).
*/
public function pause():FlxSound {
if (!playing) return this;
cleanup(false, false);
_paused = true;
return this;
}
/**
* Call this function to stop this sound.
* @return This FlxSound instance (nice for chaining stuff together, if you're into that).
*/
public function stop():FlxSound {
cleanup(autoDestroy, true);
return this;
}
/**
* Helper function that tweens this sound's volume.
*
* @param duration The amount of time the fade-out operation should take.
* @param to The volume to tween to, 0 by default.
* @return This FlxSound instance (nice for chaining stuff together, if you're into that).
*/
public function fadeOut(duration = 1.0, to = 0.0, ?onComplete:FlxTween->Void):FlxSound {
if (fadeTween != null) fadeTween.cancel();
fadeTween = FlxTween.num(volume, to, duration, {onComplete: onComplete}, volumeTween);
return this;
}
/**
* Helper function that tweens this sound's volume.
* If the sound wasn't playing at all, it'll play before the tween starts.
*
* @param duration The amount of time the fade-in operation should take.
* @param from The volume to tween from, 0 by default.
* @param to The volume to tween to, 1 by default.
* @return This FlxSound instance (nice for chaining stuff together, if you're into that).
*/
public function fadeIn(duration = 1.0, from = 0.0, to = 1.0, ?onComplete:FlxTween->Void):FlxSound {
if (!playing) play();
if (fadeTween != null) fadeTween.cancel();
fadeTween = FlxTween.num(from, to, duration, {onComplete: onComplete}, volumeTween);
return this;
}
function volumeTween(f:Float) volume = f;
/**
* Returns the currently selected "real" volume of the sound (takes fades and proximity into account).
*
* @return The adjusted volume of the sound.
*/
public inline function getActualVolume():Float
return (group != null ? group.getVolume() : 1.0) * _volume * _volumeAdjust;
#if FLX_PITCH
/**
* Returns the currently selected "real" pitch of the sound.
*
* @return The adjusted pitch of the sound.
*/
public inline function getActualPitch():Float
return _realPitch;
#end
/**
* Returns the actual time coming from the internal, can be used for detecting sync error.
*
* @return The actual time of the sound.
*/
public inline function getActualTime():Float {
get_time();
return _time;
}
/**
* An internal helper function used to attempt to start playing
* the sound and populate the _channel variable.
*/
function startSound(startTime:Float) @:privateAccess {
if (_sound == null) return;
_paused = false;
_time = startTime;
_lastTime = FlxG.game.getTicks();
if (_channel == null || !_channel.__isValid || _source == null #if lime_cffi || _source.__backend.disposed #end)
makeChannel();
if (_channel != null) {
#if FLX_PITCH
_timeScaleAdjust = timeScaleBased ? FlxG.timeScale : 1.0;
pitch = _pitch;
#end
updateTransform();
_channel.__lastPeakTime = -10;
_channel.__leftPeak = 0;
_channel.__rightPeak = 0;
#if lime_cffi _source.__backend.playing = true; #end
_source.offset = 0;
_source.currentTime = startTime + _offset;
#if !lime_cffi _source.play(); #end
looped = looped;
loopTime = loopTime;
endTime = endTime;
active = true;
}
else {
exists = false;
active = false;
}
}
function stopped() {
onFinish.dispatch();
if (onComplete != null) onComplete();
if (looped) {
cleanup(false);
play(false, loopTime, endTime);
}
else cleanup(autoDestroy);
}
function source_looped() {
if (onComplete != null) onComplete();
if (!looped) {
cleanup(autoDestroy);
_lastTime = FlxG.game.getTicks();
_time = loopTime;
}
else if (_source != null)
_source.loops = 999;
}
/**
* Internal event handler for ID3 info (i.e. fetching the song name).
*/
function gotID3(_) {
name = _sound.id3.songName;
artist = _sound.id3.artist;
_sound.removeEventListener(Event.ID3, gotID3);
}
#if FLX_SOUND_SYSTEM
@:allow(flixel.system.frontEnds.SoundFrontEnd)
function onFocus() if (!_alreadyPaused) {
resume();
_alreadyPaused = null;
}
@:allow(flixel.system.frontEnds.SoundFrontEnd)
function onFocusLost() if (_alreadyPaused == null && !(_alreadyPaused = _paused)) pause();
#end
function set_group(value:FlxSoundGroup):FlxSoundGroup {
if (value != null) value.add(this);
else group.remove(this);
return group;
}
inline function get_playing():Bool @:privateAccess
return _source != null && _source.playing;
inline function get_volume():Float
return _volume;
inline function set_volume(v:Float):Float {
_volume = FlxMath.bound(v, 0, 10);
updateTransform();
return _volume;
}
inline function set_muted(v:Bool):Bool {
muted = v;
updateTransform();
return muted;
}
inline function get_pan():Float return _pan;
inline function set_pan(v:Float):Float {
_pan = FlxMath.bound(v, -1, 1);
updateTransform();
return _pan;
}
inline function get_loaded():Bool
return buffer != null;
inline function get_streamed():Bool
@:privateAccess return #if lime_vorbis _sound != null && _sound.__buffer.__srcVorbisFile != null #else false #end;
inline function get_buffer():AudioBuffer
@:privateAccess return _sound != null ? _sound.__buffer : null;
inline function update_amplitude():Void @:privateAccess {
if (_channel != null && _channel.__updatePeaks(get_time()) && _amplitudeUpdate) {
_amplitudeUpdate = false;
_amplitudeLeft = _channel.__leftPeak;
_amplitudeRight = _channel.__rightPeak;
}
}
inline function get_amplitudeLeft():Float {
update_amplitude();
return _amplitudeLeft;
}
inline function get_amplitudeRight():Float {
update_amplitude();
return _amplitudeRight;
}
inline function get_amplitude():Float {
update_amplitude();
return Math.max(_amplitudeLeft, _amplitudeRight);//if (stereo) (_amplitudeLeft + _amplitudeRight) * 0.5; else _amplitudeLeft;
}
inline function get_channels():Int
@:privateAccess return (buffer != null) ? buffer.channels : 0;
inline function get_stereo():Bool
return channels > 1;
#if FLX_PITCH
inline function get_pitch():Float
return _pitch;
inline function set_pitch(v:Float):Float {
_realPitch = (_pitch = v) * _timeScaleAdjust;
if (_source != null) _source.pitch = _realPitch;
return _pitch;
}
#end
function set_looped(v:Bool):Bool {
if (playing) _source.loops = v ? 999 : 0;
return looped = v;
}
function set_loopTime(v:Float):Float {
if (playing) _source.loopTime = v;
return loopTime = v;
}
function set_endTime(v:Null<Float>):Null<Float> {
if (playing) {
if (v != null && v > 0 && v < _length) _source.length = v;
else _source.length = null;
}
return endTime = v;
}
inline function getFakeTime():Float {
if (_source.playing && _realPitch > 0 && _lastTime != null)
return _time + (FlxG.game.getTicks() - _lastTime) * _realPitch * _timeInterpolation;
else
return _time;
}
function get_time():Float {
if (_source == null || /*AudioManager.context == null*/funkin.backend.system.Main.audioDisconnected) return _time;
final sourceTime = _source.currentTime - _source.offset - _offset;
if (!_source.playing || _realPitch <= 0) {
_lastTime = null;
return _time = sourceTime;
}
final fakeTime = getFakeTime();
if (sourceTime != _time) {
_lastTime = FlxG.game.getTicks();
if ((_timeInterpolation = 1 - Math.min(fakeTime - sourceTime, 1000) * 0.001) < 1 && _timeInterpolation > .9)
return _time = fakeTime;
else {
_timeInterpolation = 1;
return _time = sourceTime;
}
}
else
return fakeTime;
}
function set_time(time:Float):Float @:privateAccess {
time = FlxMath.bound(time, _offset, length - 1);
if (_channel != null && _realPitch > 0) {
if (!_channel.__isValid) {
cleanup(false, true);
startSound(time);
}
else {
_source.offset = 0;
_source.currentTime = time + _offset;
}
}
_lastTime = null;
return _time = time;
}
function get_offset():Float return _offset;
function set_offset(offset:Float):Float {
if (_offset == (_offset = offset)) return offset;
//time = time + _offset;
return offset;
}
function get_length():Float return _length - _offset;
//function get_latency():Float return _source != null ? _source.latency : 0;
override function toString():String {
return FlxStringUtil.getDebugString([
LabelValuePair.weak("playing", playing),
LabelValuePair.weak("time", time),
LabelValuePair.weak("length", length),
LabelValuePair.weak("volume", volume),
LabelValuePair.weak("pitch", pitch)
]);
}
}
+120
View File
@@ -0,0 +1,120 @@
package flixel.sound;
/**
* A way of grouping sounds for things such as collective volume control
*/
class FlxSoundGroup
{
/**
* The sounds in this group
*/
public var sounds:Array<FlxSound> = [];
/**
* The volume of this group
*/
public var volume(default, set):Float;
/**
* Whether or not this group is muted
*/
public var muted(default, set):Bool;
/**
* Create a new sound group
* @param volume The initial volume of this group
*/
public function new(volume:Float = 1)
{
this.volume = volume;
}
/**
* Add a sound to this group, will remove the sound from any group it is currently in
* @param sound The sound to add to this group
* @return True if sound was successfully added, false otherwise
*/
public function add(sound:FlxSound):Bool
{
if (!sounds.contains(sound))
{
// remove from prev group
if (sound.group != null)
sound.group.sounds.remove(sound);
sounds.push(sound);
@:bypassAccessor
sound.group = this;
sound.updateTransform();
return true;
}
return false;
}
/**
* Remove a sound from this group
* @param sound The sound to remove
* @return True if sound was successfully removed, false otherwise
*/
public function remove(sound:FlxSound):Bool
{
if (sounds.contains(sound))
{
@:bypassAccessor
sound.group = null;
sounds.remove(sound);
sound.updateTransform();
return true;
}
return false;
}
/**
* Call this function to pause all sounds in this group.
* @since 4.3.0
*/
public function pause():Void
{
for (sound in sounds)
sound.pause();
}
/**
* Unpauses all sounds in this group. Only works on sounds that have been paused.
* @since 4.3.0
*/
public function resume():Void
{
for (sound in sounds)
sound.resume();
}
/**
* Returns the volume of this group, taking `muted` in account.
* @return The volume of the group or 0 if the group is muted.
*/
public function getVolume():Float
{
return muted ? 0.0 : volume;
}
function set_volume(volume:Float):Float
{
this.volume = volume;
for (sound in sounds)
{
sound.updateTransform();
}
return volume;
}
function set_muted(value:Bool):Bool
{
muted = value;
for (sound in sounds)
{
sound.updateTransform();
}
return muted;
}
}
+18 -12
View File
@@ -173,17 +173,6 @@ class FunkinSprite extends FlxAnimate implements IBeatReceiver implements IOffse
{
}
public override function draw() {
// re-implementing the `onDraw` functionality from `FlxSprite` since `FlxAnimate` didn't have this, so we have to add it back in ourselves
if (this.isAnimate && this.__drawOverrided) {
this.__drawOverrided = false;
this.onDraw(this);
this.__drawOverrided = true;
return;
}
super.draw();
}
// ANIMATE ATLAS DRAWING
#if REGION
@@ -366,9 +355,26 @@ class FunkinSprite extends FlxAnimate implements IBeatReceiver implements IOffse
}
override function prepareDrawMatrix(matrix:FlxMatrix, camera:FlxCamera):Void {
matrix.translate(-origin.x, -origin.y);
if (frameOffsetAngle != null && frameOffsetAngle != angle)
{
var angleOff = (frameOffsetAngle - angle) * FlxAngle.TO_RAD;
var cos = Math.cos(angleOff);
var sin = Math.sin(angleOff);
// cos doesnt need to be negated
matrix.rotateWithTrig(cos, -sin);
matrix.translate(-frameOffset.x, -frameOffset.y);
matrix.rotateWithTrig(cos, sin);
}
else
matrix.translate(-frameOffset.x, -frameOffset.y);
matrix.translate(origin.x, origin.y);
super.prepareDrawMatrix(matrix, camera);
if (__shouldDoZoomFactor()) {
if(__shouldDoZoomFactor()) {
__prepareZoomFactor(_rect2, camera);
matrix.setTo(
matrix.a * _rect2.width, matrix.b * _rect2.height,
+4 -83
View File
@@ -1,98 +1,19 @@
package funkin.backend;
import flixel.FlxCamera;
import flixel.FlxG;
import flixel.math.FlxMath;
import flixel.text.FlxText;
import flixel.util.FlxColor;
import funkin.backend.system.Flags;
class FunkinText extends FlxText
{
public var zoomFactor:Float = 1;
public var zoomFactorEnabled:Bool = true;
public function new(X:Float = 0, Y:Float = 0, FieldWidth:Float = 0, ?Text:String, ?Size:Int, Border:Bool = true)
{
if (Size == null)
Size = Flags.DEFAULT_FONT_SIZE;
class FunkinText extends FlxText {
public function new(X:Float = 0, Y:Float = 0, FieldWidth:Float = 0, ?Text:String, ?Size:Int, Border:Bool = true) {
if (Size == null) Size = Flags.DEFAULT_FONT_SIZE;
super(X, Y, FieldWidth, Text, Size);
setFormat(Paths.font(Flags.DEFAULT_FONT), Size, FlxColor.WHITE);
if (Border)
{
if (Border) {
borderStyle = OUTLINE;
borderSize = 1;
borderColor = 0xFF000000;
}
}
private inline function __shouldDoZoomFactor():Bool
{
return zoomFactorEnabled && zoomFactor != 1;
}
private inline function __getZoomScaleX(camera:FlxCamera):Float
{
return (camera.scaleX > 0 ? Math.max : Math.min)(0, FlxMath.lerp(1 / camera.scaleX, 1, zoomFactor));
}
private inline function __getZoomScaleY(camera:FlxCamera):Float
{
return (camera.scaleY > 0 ? Math.max : Math.min)(0, FlxMath.lerp(1 / camera.scaleY, 1, zoomFactor));
}
private inline function __getZoomAnchorX(camera:FlxCamera):Float
{
if (Flags.USE_LEGACY_ZOOM_FACTOR)
return camera.width * 0.5;
return camera.width * 0.5 + camera.scroll.x * scrollFactor.x;
}
private inline function __getZoomAnchorY(camera:FlxCamera):Float
{
if (Flags.USE_LEGACY_ZOOM_FACTOR)
return camera.height * 0.5;
return camera.height * 0.5 + camera.scroll.y * scrollFactor.y;
}
override public function draw():Void
{
if (!__shouldDoZoomFactor())
{
super.draw();
return;
}
var camera:FlxCamera = this.camera;
if (camera == null)
camera = FlxG.camera;
var oldX:Float = x;
var oldY:Float = y;
var oldScaleX:Float = scale.x;
var oldScaleY:Float = scale.y;
var zoomScaleX:Float = __getZoomScaleX(camera);
var zoomScaleY:Float = __getZoomScaleY(camera);
var anchorX:Float = __getZoomAnchorX(camera);
var anchorY:Float = __getZoomAnchorY(camera);
x = (x - anchorX) * zoomScaleX + anchorX;
y = (y - anchorY) * zoomScaleY + anchorY;
scale.set(scale.x * zoomScaleX, scale.y * zoomScaleY);
super.draw();
x = oldX;
y = oldY;
scale.set(oldScaleX, oldScaleY);
}
}
@@ -9,17 +9,6 @@ import haxe.ds.Map;
class AssetsLibraryList extends AssetLibrary {
public var libraries:Array<AssetLibrary> = [];
public var cleanLibraries(get, never):Array<AssetLibrary>;
function get_cleanLibraries():Array<AssetLibrary> {
return [for (l in libraries) getCleanLibrary(l)];
}
// is true if any library in `libraries` contains some kind of compressed library.
public var hasCompressedLibrary(get, never):Bool;
function get_hasCompressedLibrary():Bool {
for (l in libraries) if (getCleanLibrary(l).isCompressed) return true;
return false;
}
@:allow(funkin.backend.system.Main)
@:allow(funkin.backend.system.MainState)
@@ -30,9 +19,6 @@ class AssetsLibraryList extends AssetLibrary {
public var transLib:TranslatedAssetLibrary;
#end
private var cachePaths:Map<String, AssetLibrary> = [];
private var cacheNonExistentPaths:Map<String, Int> = [];
public function removeLibrary(lib:AssetLibrary) {
if (lib != null) {
libraries.remove(lib);
@@ -56,29 +42,12 @@ class AssetsLibraryList extends AssetLibrary {
public function existsSpecific(id:String, type:String, source:AssetSource = BOTH) {
if (!id.startsWith("assets/") && existsSpecific('assets/$id', type, source))
return true;
// There's a mod that heavily relies on addons and that can causes lags just to get a path.
final sec = Date.now().getSeconds();
if (cacheNonExistentPaths.exists(id) && sec - 10 < cacheNonExistentPaths.get(id)) {
return false;
}
else if (cachePaths.exists(id)) {
if (cachePaths.get(id).exists(id, type)) return true;
else cachePaths.remove(id);
}
for(k=>l in libraries) {
if (shouldSkipLib(l, source)) continue;
if (l.exists(id, type)) {
//trace("EXISTS", id, type, source);
cachePaths.set(id, l);
return true;
}
}
//trace("DOESNT EXISTS", id, type, source);
cacheNonExistentPaths.set(id, sec);
return false;
}
public override inline function exists(id:String, type:String):Bool
@@ -217,7 +186,6 @@ class AssetsLibraryList extends AssetLibrary {
public function reset() {
unloadLibraries();
cachePaths.clear();
libraries = [];
// adds default libraries in again
+21 -13
View File
@@ -83,11 +83,10 @@ class ModsFolder {
*/
public static function loadModLib(path:String, force:Bool = false, ?modName:String) {
#if MOD_SUPPORT
for (ext in Flags.ALLOWED_ZIP_EXTENSIONS) {
if (!FileSystem.exists('$path.$ext')) continue;
return loadLibraryFromZip('$path'.toLowerCase(), '$path.$ext', force, modName);
}
return loadLibraryFromFolder('$path'.toLowerCase(), '$path', force, modName);
if (FileSystem.exists('$path.zip'))
return loadLibraryFromZip('$path'.toLowerCase(), '$path.zip', force, modName);
else
return loadLibraryFromFolder('$path'.toLowerCase(), '$path', force, modName);
#else
return null;
@@ -97,16 +96,27 @@ class ModsFolder {
public static function getModsList():Array<String> {
var mods:Array<String> = [];
#if MOD_SUPPORT
// Mods directory does not exist yet, create it
if (!FileSystem.exists(modsPath)) FileSystem.createDirectory(modsPath);
if (!FileSystem.exists(modsPath)) {
// Mods directory does not exist yet, create it
FileSystem.createDirectory(modsPath);
}
final modsList:Array<String> = FileSystem.readDirectory(modsPath);
if (modsList == null || modsList.length <= 0) return mods;
if (modsList == null || modsList.length <= 0)
return mods;
for (modFolder in modsList) {
if (FileSystem.isDirectory(modsPath + modFolder)) mods.push(modFolder);
else if (Flags.ALLOWED_ZIP_EXTENSIONS.contains(Path.extension(modFolder))) mods.push(Path.withoutExtension(modFolder));
if (FileSystem.isDirectory(modsPath + modFolder)) {
mods.push(modFolder);
} else {
var ext = Path.extension(modFolder).toLowerCase();
switch(ext) {
case 'zip':
// is a zip mod!!
mods.push(Path.withoutExtension(modFolder));
}
}
}
#end
return mods;
@@ -118,9 +128,7 @@ class ModsFolder {
#if TRANSLATIONS_SUPPORT
if(skipTranslated && (l is TranslatedAssetLibrary)) continue;
#end
// No need to check for it being a `ScriptedAssetLibrary`, if `ScriptedAssetLibrary` extends ModsFolderLibrary, which implements `IModsAssetLibrary`
// If you have to revert this change then uhhhhh wasn't me, trust 🙏
if (/*l is ScriptedAssetLibrary ||*/ l is IModsAssetLibrary) libs.push(cast(l, IModsAssetLibrary));
if (l is ScriptedAssetLibrary || l is IModsAssetLibrary) libs.push(cast(l, IModsAssetLibrary));
}
return libs;
}
@@ -47,7 +47,7 @@ class MultiFramesCollection extends FlxFramesCollection
public function addFrames(collection:FlxFramesCollection) {
if (collection == null || collection.frames == null) return;
collection.parent.incrementUseCount();
collection.parent.useCount++;
parentedFrames.push(collection);
for(f in collection.frames) {
@@ -63,7 +63,7 @@ class MultiFramesCollection extends FlxFramesCollection
if(parentedFrames != null) {
for(collection in parentedFrames) {
if(collection != null)
collection.parent.decrementUseCount();
collection.parent.useCount--;
}
parentedFrames = null;
}
+1 -1
View File
@@ -194,7 +194,7 @@ class Paths
return FlxAtlasFrames.fromAseprite('$key.${ext != null ? ext : Flags.IMAGE_EXT}', '$key.json');
inline static public function getAssetsRoot():String
return ModsFolder.currentModFolder != null ? '${ModsFolder.modsPath}${ModsFolder.currentModFolder}' : #if (sys && TEST_BUILD) './${Main.pathBack}assets/' #else './assets' #end;
return ModsFolder.currentModFolder != null ? '${ModsFolder.modsPath}${ModsFolder.currentModFolder}' : #if (sys && TEST_BUILD) './${Main.pathBack}assets/' #else './assets' #end;
/**
* Gets frames at specified path.
@@ -1,14 +1,11 @@
package funkin.backend.assets;
import funkin.backend.system.Flags;
import haxe.io.Path;
import lime.graphics.Image;
import lime.media.AudioBuffer;
import lime.text.Font;
import lime.utils.Bytes;
import openfl.utils.AssetLibrary;
import sys.io.File;
#if MOD_SUPPORT
import funkin.backend.utils.SysZip.SysZipEntry;
@@ -18,16 +15,15 @@ class ZipFolderLibrary extends AssetLibrary implements IModsAssetLibrary {
public var basePath:String;
public var modName:String;
public var libName:String;
public var useImageCache:Bool = false;
public var prefix = 'assets/';
public var zip:SysZip;
public var assets:Map<String, SysZipEntry> = [];
public var lowerCaseAssets:Map<String, SysZipEntry> = [];
public var nameMap:Map<String, String> = [];
public var PRELOAD_VIDEOS:Bool = true;
public function new(basePath:String, libName:String, ?modName:String, ?preloadVideos:Bool = true) {
public function new(basePath:String, libName:String, ?modName:String) {
this.libName = libName;
this.basePath = basePath;
@@ -35,59 +31,24 @@ class ZipFolderLibrary extends AssetLibrary implements IModsAssetLibrary {
this.modName = (modName == null) ? libName : modName;
zip = SysZip.openFromFile(basePath);
zip.read();
for(entry in zip.entries) {
if (entry.fileName.length < 0 || entry.fileName.endsWith("/")) continue;
if (entry.fileName.length < 0 || entry.fileName.endsWith("/"))
continue;
var name:String = entry.fileName.toLowerCase(); // calling .toLowerCase a million times is never the solution
lowerCaseAssets[name] = assets[name] = assets[entry.fileName] = entry;
nameMap.set(name, entry.fileName);
lowerCaseAssets[entry.fileName.toLowerCase()] = assets[entry.fileName.toLowerCase()] = assets[entry.fileName] = entry;
nameMap.set(entry.fileName.toLowerCase(), entry.fileName);
}
super();
isCompressed = true;
// don't override default value of true if the file exists.
// by default `PRELOAD_VIDEOS` is true so you will never need to add this file, but in the case of it being false this is a backup method.
PRELOAD_VIDEOS = (!PRELOAD_VIDEOS) ? exists("assets/data/PRECACHE_VIDEOS", "TEXT") : PRELOAD_VIDEOS;
// if (PRELOAD_VIDEOS) precacheVideos(); // we do this in `MainState` now to handle for `Flags.VIDEO_EXT` :)
}
public function precacheVideos() {
_videoExtensions = [Flags.VIDEO_EXT];
videoCacheRemap = [];
for (entry in zip.entries) {
var name = entry.fileName.toLowerCase();
if (_videoExtensions.contains(Path.extension(name))) getPath(prefix+name);
}
var count:Int = 0;
for (_ in videoCacheRemap.keys()) count++;
if (count <= 0) return;
trace('Precached $count video${(count == 1) ? "" : "s"}');
}
// Now we have supports for videos in ZIP!!
public var _videoExtensions:Array<String> = [Flags.VIDEO_EXT];
public var videoCacheRemap:Map<String, String> = [];
public function getVideoRemap(originalPath:String):String {
if (!_videoExtensions.contains(Path.extension(_parsedAsset))) return originalPath;
if (videoCacheRemap.exists(originalPath)) return videoCacheRemap.get(originalPath);
// We adding the length of the string to counteract folder in folder naming duplicates.
var newPath = './.temp/${_parsedAsset.length}-zipvideo-${_parsedAsset.split("/").pop()}';
File.saveBytes(newPath, unzip(assets[_parsedAsset]));
videoCacheRemap.set(originalPath, newPath);
return newPath;
}
function toString():String {
return '(ZipFolderLibrary: $libName/$modName | ${zip.entries.length} entries | Detected Video Extensions: ${_videoExtensions.join(", ")})';
return '(ZipFolderLibrary: $libName/$modName)';
}
public var _parsedAsset:String;
public override function getAudioBuffer(id:String):AudioBuffer {
__parseAsset(id);
return AudioBuffer.fromBytes(unzip(assets[_parsedAsset]));
@@ -110,12 +71,15 @@ class ZipFolderLibrary extends AssetLibrary implements IModsAssetLibrary {
return getAssetPath();
}
public inline function unzip(f:SysZipEntry) return (f == null) ? null : zip.unzipEntry(f);
public inline function unzip(f:SysZipEntry)
return f == null ? null : zip.unzipEntry(f);
public function __parseAsset(asset:String):Bool {
if (!asset.startsWith(prefix)) return false;
_parsedAsset = asset.substr(prefix.length);
if (ModsFolder.useLibFile) {
if(ModsFolder.useLibFile) {
var file = new haxe.io.Path(_parsedAsset);
if(file.file.startsWith("LIB_")) {
var library = file.file.substr(4);
@@ -126,7 +90,8 @@ class ZipFolderLibrary extends AssetLibrary implements IModsAssetLibrary {
}
_parsedAsset = _parsedAsset.toLowerCase();
if (nameMap.exists(_parsedAsset)) _parsedAsset = nameMap.get(_parsedAsset);
if(nameMap.exists(_parsedAsset))
_parsedAsset = nameMap.get(_parsedAsset);
return true;
}
@@ -141,8 +106,9 @@ class ZipFolderLibrary extends AssetLibrary implements IModsAssetLibrary {
return assets[_parsedAsset] != null;
}
private inline function getAssetPath() {
return getVideoRemap('$basePath/$_parsedAsset');
private function getAssetPath() {
trace('[ZIP]$basePath/$_parsedAsset');
return '[ZIP]$basePath/$_parsedAsset';
}
// TODO: rewrite this to 1 function, like ModsFolderLibrary
@@ -191,6 +157,18 @@ class ZipFolderLibrary extends AssetLibrary implements IModsAssetLibrary {
return content;
}
public override function list(type:String):Array<String> { return [for(k=>e in nameMap) '$prefix$e']; }
public override function list(type:String):Array<String> {
return[for(k=>e in nameMap) '$prefix$e'];
}
// Backwards compat
@:noCompletion public var zipPath(get, set):String;
@:noCompletion private inline function get_zipPath():String {
return basePath;
}
@:noCompletion private inline function set_zipPath(value:String):String {
return basePath = value;
}
}
#end
+3 -7
View File
@@ -278,13 +278,9 @@ class Chart {
var filteredChart = filterChartForSaving(chart, saveSettings.saveMetaInChart, saveSettings.saveLocalEvents, saveSettings.saveGlobalEvents && saveSettings.seperateGlobalEvents != true);
#if sys
var songPath = saveSettings.songFolder == null ? 'songs/${chart.meta.name}' : saveSettings.songFolder, variantSuffix = variant != null && variant != "" ? '-$variant' : "", difficultySuffix = difficulty != null && difficulty != "" ? '-$difficulty' : "";
var metaPath = 'meta$variantSuffix.json', altMetaPath = 'meta${variantSuffix}${difficultySuffix}.json', prettyPrint = saveSettings.prettyPrint == true ? Flags.JSON_PRETTY_PRINT : null, temp:String;
if ((temp = Paths.assetsTree.getPath('assets/$songPath/$altMetaPath')) != null) { //check for difficulty specific
songPath = temp.substr(0, temp.length - altMetaPath.length - 1);
metaPath = temp;
}
else if ((temp = Paths.assetsTree.getPath('assets/$songPath/$metaPath')) != null) {
var songPath = saveSettings.songFolder == null ? 'songs/${chart.meta.name}' : saveSettings.songFolder, variantSuffix = variant != null && variant != "" ? '-$variant' : "";
var metaPath = 'meta$variantSuffix.json', prettyPrint = saveSettings.prettyPrint == true ? Flags.JSON_PRETTY_PRINT : null, temp:String;
if ((temp = Paths.assetsTree.getPath('assets/$songPath/$metaPath')) != null) {
songPath = temp.substr(0, temp.length - metaPath.length - 1);
metaPath = temp;
}
+1 -1
View File
@@ -75,7 +75,7 @@ class EventsData {
defValue: "In"
},
{name: "Mode", type: TDropDown(['direct', 'stage']), defValue: "direct"},
{name: "Multiplicative?", type: TBool, defValue: false}
{name: "Multiplicative?", type: TBool, defValue: true}
],
"Camera Modulo Change" => [
{name: "Modulo Interval", type: TInt(1, 9999999, 1), defValue: 4},
@@ -106,15 +106,12 @@ class GlobalScript {
public static function onModSwitch(newMod:String) {
destroy();
scripts = new ScriptPack("GlobalScript");
for (lib in funkin.backend.assets.ModsFolder.getLoadedModsLibs()) {
var modName = lib.modName;
var path = Paths.script('data/global/LIB_$modName');
for (i in funkin.backend.assets.ModsFolder.getLoadedMods()) {
var path = Paths.script('data/global/LIB_$i');
var script = Script.create(path);
if (script is DummyScript) continue;
script.remappedNames.set(script.fileName, '$modName:${script.fileName}');
// so you can get the current mod's library in GloablScript :)
// you should not make this a static variable then all scripts will try to reference the 1 static variable, which will be overwritten :yoikes:
script.set("MOD_LIBRARY", lib);
if (script is DummyScript)
continue;
script.remappedNames.set(script.fileName, '$i:${script.fileName}');
scripts.add(script);
script.load();
}
+1 -1
View File
@@ -89,7 +89,7 @@ class Script extends FlxBasic implements IFlxDestroyable {
"Paths" => funkin.backend.assets.Paths,
"Conductor" => funkin.backend.system.Conductor,
"FunkinShader" => funkin.backend.shaders.FunkinShader,
"CustomShader" => funkin.backend.shaders.CustomShader, // deprecated
"CustomShader" => funkin.backend.shaders.CustomShader,
"FunkinText" => funkin.backend.FunkinText,
"FlxAnimate" => animate.FlxAnimate,
"FunkinSprite" => funkin.backend.FunkinSprite,
@@ -4,6 +4,7 @@ import flixel.util.FlxDestroyUtil.IFlxDestroyable;
@:allow(funkin.backend.scripting.ScriptPack)
@:autoBuild(funkin.backend.system.macros.EventMacro.build())
@:noCustomClass
class CancellableEvent implements IFlxDestroyable {
@:dox(hide) public var cancelled:Bool = false;
@:dox(hide) private var __continueCalls:Bool = true;
@@ -53,4 +54,4 @@ class CancellableEvent implements IFlxDestroyable {
public function destroy() {
data = null;
}
}
}
@@ -1,96 +0,0 @@
package funkin.backend.scripting.events.gameplay;
import flixel.math.FlxPoint;
import flixel.tweens.FlxTween;
final class RatingsShowEvent extends CancellableEvent
{
/**
* Rating sprite (may be null)
*/
public var ratingSprite:Null<FlxSprite>;
/**
* Number sprite (may be null)
*/
public var numberSprite:Null<FlxSprite>;
/**
* Combo sprite (may be null)
*/
public var comboSprite:Null<FlxSprite>;
/**
* Scale of combo numbers. (may be null)
*/
public var numScale:Null<Float> = 0.5;
/**
* Whenever antialiasing should be enabled on combo numbers. (may be null)
*/
public var numAntialiasing:Null<Bool> = true;
/**
* Scale of the rating sprites. (may be null)
*/
public var ratingScale:Null<Float> = 0.7;
/**
* Whenever antialiasing should be enabled on ratings. (may be null)
*/
public var ratingAntialiasing:Null<Bool> = true;
/**
* Prefix of the rating sprite path. Defaults to "game/score/"
*/
public var ratingPrefix:String;
/**
* Suffix of the rating sprite path.
*/
public var ratingSuffix:String;
/**
* The sprite's acceleration.
*/
public var acceleration:Float;
/**
* A FlxPoint which x or y properties preposition the sprites current velocity.
*/
public var velocity:FlxPoint;
/**
* The duration of the sprite's alpha tween.
*/
public var tweenDuration:Float;
/**
* The start delay of the sprite's alpha tween.
*/
public var startDelay:Float;
/**
* Whenever the Rating sprites should be shown or not.
*/
public var displayRating:Bool;
/**
* Whenever the Rating sprites should be shown or not.
*/
public var displayNumbers:Bool;
/**
* Whenever the Combo sprite should be shown or not (like old Week 7 patches).
*/
public var displayCombo:Bool;
/**
* Whether the sprite should be tweened or not.
*/
public var playTween:Bool;
/**
* The amount of spacing for the combo numbers. (may be null)
*/
public var numSpacing:Null<Float>;
/**
* The position of the sprite.
*/
public var position:FlxPoint;
/**
* Whether to reset the sprite or not.
*/
public var resetSprite:Bool;
/**
* The rating name of the rating sprite. (may be null)
*/
public var rating:Null<String>;
/**
* The FlxTween instance. (null before "onPostRatingsShown")
*/
public var tween:Null<FlxTween>;
}
@@ -34,11 +34,11 @@ final class NoteHitEvent extends CancellableEvent {
/**
* Whenever the Rating sprites should be shown or not.
*/
public var displayRating:Null<Bool>;
public var displayRating:Bool;
/**
* Whenever the Combo sprite should be shown or not (like old Week 7 patches).
*/
public var displayCombo:Null<Bool>;
public var displayCombo:Bool;
/**
* Note that has been pressed
*/
@@ -66,11 +66,11 @@ final class NoteHitEvent extends CancellableEvent {
/**
* Prefix of the rating sprite path. Defaults to "game/score/"
*/
public var ratingPrefix:Null<String>;
public var ratingPrefix:String;
/**
* Suffix of the rating sprite path.
*/
public var ratingSuffix:Null<String>;
public var ratingSuffix:String;
/**
* Direction of the press (0 = Left, 1 = Down, 2 = Up, 3 = Right)
*/
@@ -98,19 +98,19 @@ final class NoteHitEvent extends CancellableEvent {
/**
* Scale of combo numbers.
*/
public var numScale:Null<Float>;
public var numScale:Float = 0.5;
/**
* Whenever antialiasing should be enabled on combo number.
*/
public var numAntialiasing:Null<Bool>;
public var numAntialiasing:Bool = true;
/**
* Scale of ratings.
*/
public var ratingScale:Null<Float>;
public var ratingScale:Float = 0.7;
/**
* Whenever antialiasing should be enabled on ratings.
*/
public var ratingAntialiasing:Null<Bool>;
public var ratingAntialiasing:Bool = true;
/**
* Whenever the animation should be forced to play (if it's null it will be forced based on the sprite's data xml, if it has one).
*/
@@ -0,0 +1,36 @@
package funkin.backend.shaders;
import flixel.util.FlxColor;
import openfl.display.ShaderParameter;
@:dox(hide)
typedef BlendModeShader =
{
var uBlendColor:ShaderParameter<Float>;
}
@:dox(hide)
class BlendModeEffect
{
public var shader(default, null):BlendModeShader;
@:isVar
public var color(default, set):FlxColor;
public function new(shader:BlendModeShader, color:FlxColor):Void
{
shader.uBlendColor.value = [];
this.shader = shader;
this.color = color;
}
function set_color(color:FlxColor):FlxColor
{
shader.uBlendColor.value[0] = color.redFloat;
shader.uBlendColor.value[1] = color.greenFloat;
shader.uBlendColor.value[2] = color.blueFloat;
shader.uBlendColor.value[3] = color.alphaFloat;
return this.color = color;
}
}
@@ -1,766 +0,0 @@
package funkin.backend.shaders;
import haxe.Timer;
import openfl.filters.BitmapFilter;
import openfl.filters.BitmapFilterShader;
import openfl.display.BitmapData;
import openfl.display.DisplayObjectRenderer;
import openfl.display.BlendMode;
import openfl.display.Shader;
import openfl.geom.Point;
import openfl.geom.Rectangle;
/**
This BloomEffect was modified by heihua based on openfl.filters.BlurFilter.
The BloomEffect class applies a bloom/glow visual effect to display objects.
A bloom effect extracts bright areas from an image, blurs them, and combines
them back to create a glowing halo around bright objects. This effect is
commonly used to simulate intense light, emissive materials, or to add a
dreamy, atmospheric quality to scenes.
The effect consists of three stages:
1. Extraction - Bright pixels above a threshold are extracted
2. Blurring - The extracted bright areas are blurred horizontally and vertically
3. Combination - The blurred result is blended back with the original image
**/
@:noCustomClass
class BloomEffect extends BitmapFilter
{
@:noCompletion private static var __blurShader:BlurShader;
@:noCompletion private static var __combineShader:CombineShader;
@:noCompletion private static var __extractShader:ExtractShader;
@:noCompletion private static var __extractLowShader:ExtractLowShader;
/**
Values that are a power of 2 (such as 2, 4, 8, 16 and 32) are optimized to render
more quickly than other values.
**/
public var blurX(get, set):Float;
/**
Values that are a power of 2 (such as 2, 4, 8, 16 and 32) are optimized to render
more quickly than other values.
**/
public var blurY(get, set):Float;
/**
The downscaling factor for bloom rendering. Higher values significantly reduce
GPU performance cost, but setting values too high may cause noticeable flickering.
Recommended range is 8-24.
**/
public var quality(get, set):Float;
/**
The intensity of the bloom effect. Higher values produce more pronounced bloom.
**/
public var strength(get, set):Float;
/**
The brightness threshold for bloom extraction. Pixels brighter than this value
will contribute to the bloom effect. Value range is 0.0 to 1.0.
**/
public var threshold(get, set):Float;
/**
The smoothness of the threshold transition in blur shader.
Higher values create a smoother transition for brightness correction.
Value range is 0.0 to 1.0. Default is 0.1.
**/
public var smoothness(get, set):Float;
/**
Enables extended rendering area to avoid edge artifacts. Enabling this option
will increase performance cost. Generally not required when rendering to camera.
**/
public var extension(get, set):Bool;
/**
Low-quality pixel sampling mode. Disabling it significantly reduces screen flickering,
with minimal performance impact on desktop platforms but a higher
performance cost on non-desktop platforms.
**/
public var useLowQualityExtract(get, set):Bool;
/**
The weights for calculating brightness (RGB to grayscale).
Order: [Red, Green, Blue]. Default is [0.2126, 0.7152, 0.0722].
**/
public var weights(get, set):Array<Float>;
/**
The blend mode used when combining the bloom with the original image.
BlendMode currently supports: (BlendMode.ADD, BlendMode.ALPHA, BlendMode.HARDLIGHT,
BlendMode.LIGHTEN, BlendMode.MULTIPLY, BlendMode.OVERLAY, BlendMode.SCREEN,
BlendMode.COLORDODGE, BlendMode.SOFTLIGHT).
Default is BlendMode.ADD.
**/
public var blendMode(get, set):BlendMode;
@:noCompletion private var __blurX:Float;
@:noCompletion private var __blurY:Float;
@:noCompletion private var __horizontalPasses:Int;
@:noCompletion private var __quality:Float;
@:noCompletion private var __verticalPasses:Int;
@:noCompletion private var __strength:Float;
@:noCompletion private var __threshold:Float;
@:noCompletion private var __smoothness:Float;
@:noCompletion private var __extension:Bool;
@:noCompletion private var __useLowQualityExtract:Bool;
@:noCompletion private var __weights:Array<Float>;
@:noCompletion private var __blendMode:BlendMode;
#if openfljs
@:noCompletion private static function __init__()
{
untyped Object.defineProperties(BloomEffect.prototype, {
"blurX": {
get: untyped #if haxe4 js.Syntax.code #else __js__ #end ("function () { return this.get_blurX (); }"),
set: untyped #if haxe4 js.Syntax.code #else __js__ #end ("function (v) { return this.set_blurX (v); }")
},
"blurY": {
get: untyped #if haxe4 js.Syntax.code #else __js__ #end ("function () { return this.get_blurY (); }"),
set: untyped #if haxe4 js.Syntax.code #else __js__ #end ("function (v) { return this.set_blurY (v); }")
},
"quality": {
get: untyped #if haxe4 js.Syntax.code #else __js__ #end ("function () { return this.get_quality (); }"),
set: untyped #if haxe4 js.Syntax.code #else __js__ #end ("function (v) { return this.set_quality (v); }")
},
"strength": {
get: untyped #if haxe4 js.Syntax.code #else __js__ #end ("function () { return this.get_strength (); }"),
set: untyped #if haxe4 js.Syntax.code #else __js__ #end ("function (v) { return this.set_strength (v); }")
},
"threshold": {
get: untyped #if haxe4 js.Syntax.code #else __js__ #end ("function () { return this.get_threshold (); }"),
set: untyped #if haxe4 js.Syntax.code #else __js__ #end ("function (v) { return this.set_threshold (v); }")
},
"useLowQualityExtract": {
get: untyped #if haxe4 js.Syntax.code #else __js__ #end ("function () { return this.get_useLowQualityExtract (); }"),
set: untyped #if haxe4 js.Syntax.code #else __js__ #end ("function (v) { return this.set_useLowQualityExtract (v); }")
},
"weights": {
get: untyped #if haxe4 js.Syntax.code #else __js__ #end ("function () { return this.get_weights (); }"),
set: untyped #if haxe4 js.Syntax.code #else __js__ #end ("function (v) { return this.set_weights (v); }")
},
"blendMode": {
get: untyped #if haxe4 js.Syntax.code #else __js__ #end ("function () { return this.get_blendMode (); }"),
set: untyped #if haxe4 js.Syntax.code #else __js__ #end ("function (v) { return this.set_blendMode (v); }")
},
});
}
#end
/**
Initializes the bloom filter with the specified parameters.
@param blurX The amount to blur horizontally.
@param blurY The amount to blur vertically.
@param quality The downscaling factor for bloom rendering (higher values reduce
GPU cost but may cause flickering if too high).
@param strength The intensity of the bloom effect.
@param threshold The brightness threshold for bloom extraction (0.0 to 1.0).
@param smoothness The smoothness of threshold transition in blur (0.0 to 1.0).
@param useLowQualityExtract Enables performance-optimized extraction with
potentially more flickering.
**/
public function new(blurX:Float = 50, blurY:Float = 50, quality:Float = 8, strength:Float = 0.6, threshold:Float = 0.6, smoothness:Float = 0.1, useLowQualityExtract:Bool = true)
{
super();
if (__blurShader == null) __blurShader = new BlurShader();
if (__combineShader == null) __combineShader = new CombineShader();
if (__extractShader == null) __extractShader = new ExtractShader();
if (__extractLowShader == null) __extractLowShader = new ExtractLowShader();
this.blurX = blurX;
this.blurY = blurY;
this.quality = quality;
this.strength = strength;
this.threshold = threshold;
this.smoothness = smoothness;
this.extension = false;
this.useLowQualityExtract = useLowQualityExtract;
this.weights = [0.2126, 0.7152, 0.0722];
this.blendMode = BlendMode.ADD;
__needSecondBitmapData = true;
__preserveObject = true;
__renderDirty = true;
}
public override function clone():BitmapFilter
{
var cloned = new BloomEffect(__blurX, __blurY, __quality, __strength, __threshold, __smoothness, __useLowQualityExtract);
cloned.weights = __weights != null ? __weights.copy() : [0.2126, 0.7152, 0.0722];
cloned.blendMode = __blendMode;
return cloned;
}
@:noCompletion private override function __applyFilter(bitmapData:BitmapData, sourceBitmapData:BitmapData, sourceRect:Rectangle, destPoint:Point):BitmapData
{
trace("Due to technical limitations, I'm unable to implement the bitmapData rendering method. If you know how to implement it, you're welcome to contribute this feature.");
return sourceBitmapData;
}
@:noCompletion private override function __initShader(renderer:DisplayObjectRenderer, pass:Int, sourceBitmapData:BitmapData):Shader
{
final numBlurPasses = __horizontalPasses + __verticalPasses;
switch pass
{
case 0:
if (__useLowQualityExtract)
{
__extractLowShader.uThreshold.value[0] = __threshold;
__extractLowShader.uSmoothness.value[0] = __smoothness;
__extractLowShader.uQuality.value[0] = __quality;
__extractLowShader.uWeights.value = __weights;
return __extractLowShader;
}
else
{
__extractShader.uThreshold.value[0] = __threshold;
__extractShader.uSmoothness.value[0] = __smoothness;
__extractShader.uQuality.value[0] = __quality;
__extractShader.uWeights.value = __weights;
return __extractShader;
}
case _ if (pass <= numBlurPasses):
final blurPass = pass - 1;
final isHorizontal = blurPass < __horizontalPasses;
final scalePass = isHorizontal ? blurPass : blurPass - __horizontalPasses;
final scale = Math.pow(0.5, scalePass >> 1);
final blurRadius = isHorizontal ? blurX * scale : blurY * scale;
if (isHorizontal)
{
__blurShader.uRadius.value[0] = blurRadius / __quality;
__blurShader.uRadius.value[1] = 0.0;
}
else
{
__blurShader.uRadius.value[0] = 0.0;
__blurShader.uRadius.value[1] = blurRadius / __quality;
}
__blurShader.uQuality.value[0] = __quality;
__blurShader.uStrength.value[0] = Math.pow(__strength, 1.0 / numBlurPasses);
return __blurShader;
default:
__combineShader.sourceBitmap.input = sourceBitmapData;
__combineShader.uThreshold.value[0] = __threshold;
__combineShader.uQuality.value[0] = __quality;
__combineShader.uBlendMode.value[0] = cast __blendMode;
return __combineShader;
}
}
// Get & Set Methods
@:noCompletion private function get_blurX():Float
{
return __blurX;
}
@:noCompletion private function set_blurX(value:Float):Float
{
if (value != __blurX)
{
__blurX = value;
__renderDirty = true;
if (!__extension)
{
// Setting it to 1 prevents bloom flickering at the screen edges
__leftExtension = 1;
__rightExtension = 1;
}
else
{
__leftExtension = (value > 0 ? Math.ceil(value) : 0);
__rightExtension = __leftExtension;
}
__horizontalPasses = (value <= 0) ? 0 : Math.ceil(value * 0.0625 / quality) + 1;
__numShaderPasses = __horizontalPasses + __verticalPasses + 2;
}
return value;
}
@:noCompletion private function get_blurY():Float
{
return __blurY;
}
@:noCompletion private function set_blurY(value:Float):Float
{
if (value != __blurY)
{
__blurY = value;
__renderDirty = true;
if (!__extension)
{
// Setting it to 1 prevents bloom flickering at the screen edges
__topExtension = 1;
__bottomExtension = 1;
}
else
{
__topExtension = (value > 0 ? Math.ceil(value) : 0);
__bottomExtension = __topExtension;
}
__verticalPasses = (value <= 0) ? 0 : Math.ceil(value * 0.0625 / quality) + 1;
__numShaderPasses = __horizontalPasses + __verticalPasses + 2;
}
return value;
}
@:noCompletion private function get_quality():Float
{
return __quality;
}
@:noCompletion private function set_quality(value:Float):Float
{
__horizontalPasses = (__blurX <= 0) ? 0 : Math.round(__blurX * 0.125 / value) + 1;
__verticalPasses = (__blurY <= 0) ? 0 : Math.round(__blurY * 0.125 / value) + 1;
__numShaderPasses = __horizontalPasses + __verticalPasses + 2;
if (value != __quality)
__renderDirty = true;
return __quality = value;
}
@:noCompletion private function get_strength():Float
{
return __strength;
}
@:noCompletion private function set_strength(value:Float):Float
{
if (value != __strength)
{
__strength = value;
__renderDirty = true;
}
return value;
}
@:noCompletion private function get_threshold():Float
{
return __threshold;
}
@:noCompletion private function set_threshold(value:Float):Float
{
if (value != __threshold)
{
__threshold = value;
__renderDirty = true;
}
return value;
}
@:noCompletion private function get_smoothness():Float
{
return __smoothness;
}
@:noCompletion private function set_smoothness(value:Float):Float
{
if (value != __smoothness)
{
__smoothness = value;
__renderDirty = true;
}
return value;
}
@:noCompletion private function get_extension():Bool
{
return __extension;
}
@:noCompletion private function set_extension(value:Bool):Bool
{
if (value != __extension)
{
__extension = value;
if (!value)
__leftExtension = __rightExtension = __topExtension = __bottomExtension = 0;
else
{
__leftExtension = __rightExtension = (__blurX > 0 ? Math.ceil(__blurX) : 0);
__topExtension = __bottomExtension = (__blurY > 0 ? Math.ceil(__blurY) : 0);
}
__renderDirty = true;
}
return value;
}
@:noCompletion private function get_useLowQualityExtract():Bool
{
return __useLowQualityExtract;
}
@:noCompletion private function set_useLowQualityExtract(value:Bool):Bool
{
if (value != __useLowQualityExtract)
{
__useLowQualityExtract = value;
__renderDirty = true;
}
return value;
}
@:noCompletion private function get_weights():Array<Float>
{
return __weights;
}
@:noCompletion private function set_weights(value:Array<Float>):Array<Float>
{
if (value != __weights)
{
__weights = value;
__renderDirty = true;
}
return value;
}
@:noCompletion private function get_blendMode():BlendMode
{
return __blendMode;
}
@:noCompletion private function set_blendMode(value:BlendMode):BlendMode
{
if (value != __blendMode)
{
__blendMode = value;
__renderDirty = true;
}
return value;
}
}
private class BlurShader extends BitmapFilterShader
{
@:glFragmentSource("
uniform sampler2D openfl_Texture;
uniform float uStrength;
varying mat2 vBlurCoord0;
varying mat2 vBlurCoord1;
varying vec2 vBlurCoord2;
varying mat2 vBlurCoord3;
varying mat2 vBlurCoord4;
varying float invQuality;
void main(void) {
if ((all(greaterThanEqual(vBlurCoord2, vec2(0.0))) && all(lessThanEqual(vBlurCoord2, vec2(1.0)))) == false) return;
vec4 sum = texture2D(openfl_Texture, clamp(vBlurCoord0[0], 0.0, invQuality)) * 0.028532;
sum += texture2D(openfl_Texture, clamp(vBlurCoord0[1], 0.0, invQuality)) * 0.067234;
sum += texture2D(openfl_Texture, clamp(vBlurCoord1[0], 0.0, invQuality)) * 0.124009;
sum += texture2D(openfl_Texture, clamp(vBlurCoord1[1], 0.0, invQuality)) * 0.179044;
sum += texture2D(openfl_Texture, clamp(vBlurCoord2, 0.0, invQuality)) * 0.202360;
sum += texture2D(openfl_Texture, clamp(vBlurCoord3[0], 0.0, invQuality)) * 0.179044;
sum += texture2D(openfl_Texture, clamp(vBlurCoord3[1], 0.0, invQuality)) * 0.124009;
sum += texture2D(openfl_Texture, clamp(vBlurCoord4[0], 0.0, invQuality)) * 0.067234;
sum += texture2D(openfl_Texture, clamp(vBlurCoord4[1], 0.0, invQuality)) * 0.028532;
gl_FragColor = sum * uStrength;
}
")
@:glVertexSource("
attribute vec4 openfl_Position;
attribute vec2 openfl_TextureCoord;
uniform mat4 openfl_Matrix;
uniform vec2 uRadius;
uniform vec2 uTextureSize;
uniform float uQuality;
varying mat2 vBlurCoord0;
varying mat2 vBlurCoord1;
varying vec2 vBlurCoord2;
varying mat2 vBlurCoord3;
varying mat2 vBlurCoord4;
varying float invQuality;
void main(void) {
vec4 pos = openfl_Position;
invQuality = 1.0 / uQuality;
pos.xy *= invQuality;
gl_Position = openfl_Matrix * pos;
vec2 r = uRadius / uTextureSize;
vec2 coord = openfl_TextureCoord * invQuality;
vBlurCoord0[0] = coord - r;
vBlurCoord0[1] = coord - r * 0.25;
vBlurCoord1[0] = coord - r * 0.5;
vBlurCoord1[1] = coord - r * 0.75;
vBlurCoord2 = coord;
vBlurCoord3[0] = coord + r * 0.25;
vBlurCoord3[1] = coord + r * 0.5;
vBlurCoord4[0] = coord + r * 0.75;
vBlurCoord4[1] = coord + r;
}
")
public function new()
{
super();
uStrength.value = [1.0];
uRadius.value = [0, 0];
uQuality.value = [8];
uTextureSize.value = [1, 1];
}
@:noCompletion private override function __update():Void
{
#if !macro
uTextureSize.value[0] = __texture.input.width;
uTextureSize.value[1] = __texture.input.height;
#end
super.__update();
}
}
private class ExtractLowShader extends BitmapFilterShader
{
@:glFragmentSource("
uniform sampler2D openfl_Texture;
uniform float uThreshold;
uniform float uSmoothness;
uniform vec3 uWeights;
varying vec2 vTexCoord;
void main(void) {
if ((all(greaterThanEqual(vTexCoord, vec2(0.0))) && all(lessThanEqual(vTexCoord, vec2(1.0)))) == false) return;
vec4 texel = texture2D(openfl_Texture, vTexCoord);
float brightness = min(dot(texel.rgb, uWeights), 1.0);
float mask = smoothstep(uThreshold, uThreshold + uSmoothness, brightness);
gl_FragColor = texel * mask;
}
")
@:glVertexSource("
attribute vec4 openfl_Position;
attribute vec2 openfl_TextureCoord;
uniform mat4 openfl_Matrix;
uniform vec2 openfl_TextureSize;
uniform float uQuality;
varying vec2 vTexCoord;
void main(void) {
vec4 pos = openfl_Position;
pos.xy /= uQuality;
gl_Position = openfl_Matrix * pos;
vTexCoord = openfl_TextureCoord;
}
")
public function new()
{
super();
uThreshold.value = [0.6];
uSmoothness.value = [0.1];
uQuality.value = [8];
uWeights.value = [0.2126, 0.7152, 0.0722];
}
}
private class ExtractShader extends BitmapFilterShader
{
@:glFragmentSource("
uniform sampler2D openfl_Texture;
uniform vec2 openfl_TextureSize;
uniform float uThreshold;
uniform float uSmoothness;
uniform float uQuality;
uniform vec3 uWeights;
varying vec2 vTexCoord;
varying vec4 border;
void main(void) {
if ((all(greaterThanEqual(vTexCoord, border.xy)) && all(lessThanEqual(vTexCoord, border.zw))) == false) return;
float quality = floor(uQuality) / 2.0;
vec2 texelSize = 1.0 / openfl_TextureSize;
vec4 accumulated = vec4(0.0);
int sampleCount = 0;
for (float dx = -quality; dx <= quality; dx += 2.0) {
for (float dy = -quality; dy <= quality; dy += 2.0) {
vec2 sampleCoord = vTexCoord + vec2(dx, dy) * texelSize;
vec4 texel = texture2D(openfl_Texture, sampleCoord);
float brightness = min(dot(texel.rgb, uWeights), 1.0);
float mask = smoothstep(uThreshold, uThreshold + uSmoothness, brightness);
accumulated += texel * mask;
sampleCount++;
}
}
gl_FragColor = accumulated / float(sampleCount);
}
")
@:glVertexSource("
attribute vec4 openfl_Position;
attribute vec2 openfl_TextureCoord;
uniform mat4 openfl_Matrix;
uniform vec2 openfl_TextureSize;
uniform float uQuality;
varying vec2 vTexCoord;
varying vec4 border;
void main(void) {
vec4 pos = openfl_Position;
pos.xy /= uQuality;
vec2 size = 1.0 / openfl_TextureSize * uQuality;
border = vec4(size, vec2(1.0) - size);
gl_Position = openfl_Matrix * pos;
vTexCoord = openfl_TextureCoord;
}
")
public function new()
{
super();
uThreshold.value = [0.6];
uSmoothness.value = [0.1];
uQuality.value = [8];
uWeights.value = [0.2126, 0.7152, 0.0722];
}
}
private class CombineShader extends BitmapFilterShader
{
@:glFragmentSource("
uniform sampler2D openfl_Texture;
uniform sampler2D sourceBitmap;
uniform float uThreshold;
uniform int uBlendMode;
varying vec4 textureCoords;
vec4 blendScreen(vec4 src, vec4 bloom) {
return vec4(1.0) - (vec4(1.0) - src) * (vec4(1.0) - bloom);
}
vec4 blendMultiply(vec4 src, vec4 bloom) {
return src * bloom;
}
vec4 blendLighten(vec4 src, vec4 bloom) {
return max(src, bloom);
}
vec4 blendOverlay(vec4 src, vec4 bloom) {
vec4 result = vec4(0.0);
for (int i = 0; i < 4; i++) {
if (src[i] < 0.5) {
result[i] = 2.0 * src[i] * bloom[i];
} else {
result[i] = 1.0 - 2.0 * (1.0 - src[i]) * (1.0 - bloom[i]);
}
}
return result;
}
vec4 blendColorDodge(vec4 src, vec4 bloom) {
vec4 result = vec4(0.0);
for (int i = 0; i < 4; i++) {
if (bloom[i] < 1.0) {
result[i] = min(1.0, src[i] / (1.0 - bloom[i]));
} else {
result[i] = 1.0;
}
}
return result;
}
vec4 blendSoftLight(vec4 src, vec4 bloom) {
vec4 result = vec4(0.0);
for (int i = 0; i < 4; i++) {
if (bloom[i] < 0.5) {
result[i] = src[i] - (1.0 - 2.0 * bloom[i]) * src[i] * (1.0 - src[i]);
} else {
float d = (src[i] <= 0.25) ? ((16.0 * src[i] - 12.0) * src[i] + 4.0) * src[i] : sqrt(src[i]);
result[i] = src[i] + (2.0 * bloom[i] - 1.0) * (d - src[i]);
}
}
return result;
}
vec4 blendAlpha(vec4 src, vec4 bloom) {
return src + bloom * (1.0 - src.a);
}
void main(void) {
vec4 src = texture2D(sourceBitmap, textureCoords.xy);
vec4 bloom = texture2D(openfl_Texture, textureCoords.zw);
vec4 result;
if(uBlendMode == 0)
result = src + bloom;
else if(uBlendMode == 1)
result = blendAlpha(src, bloom);
else if(uBlendMode == 5)
result = blendOverlay(src, bloom);
else if(uBlendMode == 8)
result = blendLighten(src, bloom);
else if(uBlendMode == 9)
result = blendMultiply(src, bloom);
else if(uBlendMode == 11)
result = blendOverlay(src, bloom);
else if(uBlendMode == 12)
result = blendScreen(src, bloom);
else if(uBlendMode == 15)
result = blendColorDodge(src, bloom);
else if(uBlendMode == 17)
result = blendSoftLight(src, bloom);
else
result = src + bloom;
gl_FragColor = result;
}
")
@:glVertexSource("
attribute vec4 openfl_Position;
attribute vec2 openfl_TextureCoord;
uniform mat4 openfl_Matrix;
uniform vec2 openfl_TextureSize;
uniform float uQuality;
varying vec4 textureCoords;
void main(void) {
gl_Position = openfl_Matrix * openfl_Position;
textureCoords = vec4(openfl_TextureCoord, openfl_TextureCoord / uQuality);
}
")
public function new()
{
super();
uQuality.value = [8];
uThreshold.value = [0.6];
uBlendMode.value = [0];
}
}
+32 -8
View File
@@ -2,15 +2,39 @@ package funkin.backend.shaders;
import openfl.Assets;
@:deprecated("Use funkin.backend.shaders.FunkinShader.fromFile instead.")
/**
* Class for custom shaders.
*
* To create one, create a `shaders` folder in your assets/mod folder, then add a file named `my-shader.frag` or/and `my-shader.vert`.
*
* Non-existent shaders will only load the default one, and throw a warning in the console.
*
* To access the shader's uniform variables, use `shader.variable`
*/
class CustomShader extends FunkinShader {
@:isVar
public var path(get, set):String;
inline function get_path():String return path != null ? path : _fragmentFilePath + _vertexFilePath;
inline function set_path(v:Null<String>):String return path = cast v;
public var path:String = "";
public function new(name:String, ?glslVersion:String) {
super();
loadShaderFile(Paths.fragShader(name), Paths.vertShader(name), glslVersion);
/**
* Creates a new custom shader
* @param name Name of the frag and vert files.
* @param glslVersion GLSL version to use. Defaults to `120`.
*/
public function new(name:String, glslVersion:String = null) {
if (glslVersion == null) glslVersion = Flags.DEFAULT_GLSL_VERSION;
var fragShaderPath = Paths.fragShader(name);
var vertShaderPath = Paths.vertShader(name);
var fragCode = Assets.exists(fragShaderPath) ? Assets.getText(fragShaderPath) : null;
var vertCode = Assets.exists(vertShaderPath) ? Assets.getText(vertShaderPath) : null;
fileName = name;
fragFileName = fragShaderPath;
vertFileName = vertShaderPath;
path = fragShaderPath+vertShaderPath;
if (fragCode == null && vertCode == null)
Logs.error('Shader "$name" couldn\'t be found.');
super(fragCode, vertCode, glslVersion);
}
}
+600 -251
View File
@@ -1,65 +1,533 @@
package funkin.backend.shaders;
import haxe.io.Path;
import flixel.graphics.FlxGraphic;
import flixel.system.FlxAssets.FlxShader;
import flixel.util.FlxSignal.FlxTypedSignal;
import haxe.Exception;
import hscript.IHScriptCustomBehaviour;
import lime.utils.Float32Array;
import openfl.display.BitmapData;
import openfl.display.Shader;
import openfl.display.ShaderInput;
import openfl.display.ShaderParameter;
import openfl.display.ShaderParameterType;
import openfl.display.ShaderPrecision;
import openfl.display.ShaderInput;
import openfl.display3D._internal.GLProgram;
import openfl.display3D._internal.GLShader;
import openfl.display3D.Program3D;
import openfl.utils._internal.Log;
import openfl.utils.GLSLSourceAssembler;
import flixel.addons.display.FlxRuntimeShader;
import flixel.graphics.FlxGraphic;
import flixel.util.FlxSignal.FlxTypedSignal;
import flixel.util.FlxStringUtil;
using StringTools;
@:access(openfl.display3D.Context3D)
@:access(openfl.display3D.Program3D)
@:access(openfl.display.ShaderInput)
@:access(openfl.display.ShaderParameter)
class FunkinShader extends FlxRuntimeShader implements IHScriptCustomBehaviour {
class FunkinShader extends FlxShader implements IHScriptCustomBehaviour {
private static var __instanceFields = Type.getInstanceFields(FunkinShader);
private static var FRAGMENT_SHADER = 0;
private static var VERTEX_SHADER = 1;
public var onGLUpdate:FlxTypedSignal<Void->Void> = new FlxTypedSignal<Void->Void>();
public var onProcessGLData:FlxTypedSignal<(String, String)->Void> = new FlxTypedSignal<(String, String)->Void>();
public function new(?fragmentSource:String, ?vertexSource:String, ?version:String) {
super(fragmentSource, vertexSource, version ?? (fragmentSource != null || vertexSource != null ? Flags.DEFAULT_GLSL_VERSION : null));
public var glslVer:String = Flags.DEFAULT_GLSL_VERSION;
public var fileName:String = "FunkinShader";
public var fragFileName:String = "FunkinShader";
public var vertFileName:String = "FunkinShader";
public var shaderPrefix:String = "";
public var fragmentPrefix:String = "";
public var vertexPrefix:String = "";
/**
* Creates a new shader from the specified fragment and vertex source.
* Accepts `#pragma header`.
* @param frag Fragment source (pass `null` to use default)
* @param vert Vertex source (pass `null` to use default)
* @param glslVer Version of GLSL to use (defaults to 120)
*/
public override function new(frag:String, vert:String, glslVer:String = null) {
if (glslVer == null) glslVer = Flags.DEFAULT_GLSL_VERSION;
if (frag == null) frag = ShaderTemplates.defaultFragmentSource;
if (vert == null) vert = ShaderTemplates.defaultVertexSource;
this.glFragmentSource = frag;
this.glVertexSource = vert;
this.glslVer = glslVer;
super();
}
public static function fromFile(fragmentPath:String, ?vertexPath:String, ?version:String):FunkinShader {
return new FunkinShader().loadShaderFile(fragmentPath, vertexPath, version);
static var IMPORT_REGEX = ~/#import\s+<(.*)>/;
private function processImports(value:String, type:Int):String
{
while(IMPORT_REGEX.match(value))
{
var importPath = IMPORT_REGEX.matched(1);
var importSource = Assets.getText("assets/shaders/" + importPath);
if(importSource == null) {
var fileName = type == FRAGMENT_SHADER ? fragFileName : vertFileName;
Logs.traceColored([
Logs.logText('[Shader] ', RED),
Logs.logText('Failed to import shader ${importPath} in ${fileName}', RED),
]);
} else {
value = value.replace(IMPORT_REGEX.matched(0), importSource);
}
}
return value;
}
public function loadShaderFile(fragmentPath:String, ?vertexPath:String, ?version:String):FunkinShader {
if (vertexPath == null) {
final idx = fragmentPath.lastIndexOf(".");
if (idx == -1) vertexPath = fragmentPath;
else vertexPath = fragmentPath.substr(0, idx);
static var ERROR_POS_REGEX = ~/(\d+):(\d+): (.*)/g;
static var ERROR_REGEX = ~/ERROR: (\d+):(\d+): (.*)/g;
static var ERROR_REGEX_2 = ~/(\d+)\((\d+)\) : error ([^:]+): (.*)/g;
@:noCompletion private override function __createGLShader(source:String, type:Int):GLShader
{
var gl = __context.gl;
var shader = gl.createShader(type);
gl.shaderSource(shader, source);
gl.compileShader(shader);
var shaderInfoLog = gl.getShaderInfoLog(shader);
var hasInfoLog = shaderInfoLog != null && StringTools.trim(shaderInfoLog) != "";
var compileStatus = gl.getShaderParameter(shader, gl.COMPILE_STATUS);
if (hasInfoLog || compileStatus == 0)
{
var isVertexShader = type == gl.VERTEX_SHADER;
var messageBuf = new StringBuf();
messageBuf.add((compileStatus == 0) ? "Error" : "Info");
if(isVertexShader) {
messageBuf.add(" compiling vertex shader");
if(vertFileName != null && vertFileName.length > 0) {
messageBuf.add(" (" + vertFileName + ")");
}
} else {
messageBuf.add(" compiling fragment shader");
if(fragFileName != null && fragFileName.length > 0) {
messageBuf.add(" (" + fragFileName + ")");
}
}
messageBuf.add("\n");
var errorPositions = [];
var regex = null;
var tmp = shaderInfoLog;
if(shaderInfoLog.contains(" : error ")) {
regex = ERROR_REGEX_2;
while(regex.match(tmp)) {
errorPositions.push(new ShaderErrorPosition(regex.matched(2), regex.matched(1), regex.matched(4)));
tmp = regex.matchedRight();
}
} else if(shaderInfoLog.contains("ERROR: ")) {
regex = ERROR_REGEX;
while(regex.match(tmp)) {
errorPositions.push(new ShaderErrorPosition(regex.matched(2), regex.matched(1), regex.matched(3)));
tmp = regex.matchedRight();
}
} else {
regex = ERROR_POS_REGEX;
while(regex.match(tmp)) {
errorPositions.push(new ShaderErrorPosition(regex.matched(2), regex.matched(1), regex.matched(3)));
tmp = regex.matchedRight();
}
}
var splitSource = source.split("\n");
for(error in errorPositions) {
messageBuf.add("ERROR: Line: " + error.line);
if(error.column > 0) {
messageBuf.add(", Column: " + error.column);
}
messageBuf.add(", " + error.message);
if(error.line < splitSource.length) {
messageBuf.add("\nLine: ");
messageBuf.add(splitSource[error.line-1].trim());
}
messageBuf.add("\n\n");
}
var hasErrorPosition = errorPositions.length > 0;
if(hasErrorPosition) {
messageBuf.add("Raw shader info log:\n");
}
messageBuf.add(shaderInfoLog);
messageBuf.add("\n");
messageBuf.add(source);
var message = messageBuf.toString();
if (compileStatus == 0) Log.error(message);
else if (hasInfoLog) Log.debug(message);
}
fragmentPath = FlxRuntimeShader._getPath(fragmentPath, false);
vertexPath = FlxRuntimeShader._getPath(vertexPath, true);
_fromFile(fragmentPath, vertexPath, version ?? (fragmentPath != null || vertexPath != null ? Flags.DEFAULT_GLSL_VERSION : null));
return shader;
}
return this;
@:noCompletion private override function __createGLProgram(vertexSource:String, fragmentSource:String):GLProgram
{
var program:GLProgram = null;
try
{
var gl = __context.gl;
var vertexShader = __createGLShader(vertexSource, gl.VERTEX_SHADER);
var fragmentShader = __createGLShader(fragmentSource, gl.FRAGMENT_SHADER);
program = gl.createProgram();
// Fix support for drivers that don't draw if attribute 0 is disabled
for (param in __paramFloat)
{
if (param.name.indexOf("Position") > -1 && StringTools.startsWith(param.name, "openfl_"))
{
gl.bindAttribLocation(program, 0, param.name);
break;
}
}
gl.attachShader(program, vertexShader);
gl.attachShader(program, fragmentShader);
gl.linkProgram(program);
if (gl.getProgramParameter(program, gl.LINK_STATUS) == 0)
{
var messageBuf = new StringBuf();
messageBuf.add("Unable to initialize the shader program");
messageBuf.add("\n");
messageBuf.add(gl.getProgramInfoLog(program));
var message = messageBuf.toString();
Log.error(message);
}
}
catch (error:Dynamic)
{
Logs.traceColored([
Logs.logText('[Shader] ', BLUE),
Logs.logText('Failed to compile shader ${fileName}: ', RED),
Logs.logText(Std.string(error))
], TRACE);
}
return program;
return program;
}
var glRawFragmentSource:String;
var glRawVertexSource:String;
@:noCompletion override private function set_glFragmentSource(value:String):String
{
if(value == null)
value = ShaderTemplates.defaultFragmentSource;
glRawFragmentSource = value;
value = processImports(value, FRAGMENT_SHADER);
value = value.replace("#pragma header", ShaderTemplates.fragHeader).replace("#pragma body", ShaderTemplates.fragBody);
if (value != __glFragmentSource)
{
__glSourceDirty = true;
}
return __glFragmentSource = value;
}
@:noCompletion override private function set_glVertexSource(value:String):String
{
if(value == null)
value = ShaderTemplates.defaultVertexSource;
glRawVertexSource = value;
value = processImports(value, VERTEX_SHADER);
var useBackCompat:Bool = true;
for (regex in ShaderTemplates.vertBackCompatVarList) if (!regex.match(value)) {
useBackCompat = false;
break;
}
var header = useBackCompat ? ShaderTemplates.vertHeaderBackCompat : ShaderTemplates.vertHeader;
var body = useBackCompat ? ShaderTemplates.vertBodyBackCompat : ShaderTemplates.vertBody;
value = value.replace("#pragma header", header).replace("#pragma body", body);
if (value != __glVertexSource)
{
__glSourceDirty = true;
}
return __glVertexSource = value;
}
override function __updateGL():Void {
onGLUpdate.dispatch();
super.__updateGL();
}
@:noCompletion private override function __initGL():Void
{
if (__glSourceDirty || __paramBool == null)
{
__glSourceDirty = false;
program = null;
__inputBitmapData = new Array();
__paramBool = new Array();
__paramFloat = new Array();
__paramInt = new Array();
__processGLData(glVertexSource, "attribute");
__processGLData(glVertexSource, "uniform");
__processGLData(glFragmentSource, "uniform");
}
if (__context != null && program == null)
{
var prefixBuf = new StringBuf();
prefixBuf.add('#version ${glslVer}\n');
prefixBuf.add(shaderPrefix);
var gl = __context.gl;
prefixBuf.add("#ifdef GL_ES\n");
if (precisionHint == FULL) {
prefixBuf.add("#ifdef GL_FRAGMENT_PRECISION_HIGH\n");
prefixBuf.add("precision highp float;\n");
prefixBuf.add("#else\n");
prefixBuf.add("precision mediump float;\n");
prefixBuf.add("#endif\n");
} else {
prefixBuf.add("precision lowp float;\n");
}
prefixBuf.add("#endif\n");
var prefix = prefixBuf.toString();
var vertex = prefix + vertexPrefix + glVertexSource;
var fragment = prefix + fragmentPrefix + glFragmentSource;
var id = vertex + fragment;
if (__context.__programs.exists(id))
{
program = __context.__programs.get(id);
}
else
{
program = __context.createProgram(GLSL);
program.__glProgram = __createGLProgram(vertex, fragment);
__context.__programs.set(id, program);
}
if (program != null)
{
glProgram = program.__glProgram;
for (input in __inputBitmapData) {
if (input.__isUniform) {
input.index = gl.getUniformLocation(glProgram, input.name);
} else {
input.index = gl.getAttribLocation(glProgram, input.name);
}
}
for (parameter in __paramBool) {
if (parameter.__isUniform) {
parameter.index = gl.getUniformLocation(glProgram, parameter.name);
} else {
parameter.index = gl.getAttribLocation(glProgram, parameter.name);
}
}
for (parameter in __paramFloat) {
if (parameter.__isUniform) {
parameter.index = gl.getUniformLocation(glProgram, parameter.name);
} else {
parameter.index = gl.getAttribLocation(glProgram, parameter.name);
}
}
for (parameter in __paramInt) {
if (parameter.__isUniform) {
parameter.index = gl.getUniformLocation(glProgram, parameter.name);
} else {
parameter.index = gl.getAttribLocation(glProgram, parameter.name);
}
}
}
// initInstance(vertex, fragment); // btw make sure to disable the prefixes for ._isInstance
}
}
var __cancelNextProcessGLData:Bool = false;
@:noCompletion private override function __processGLData(source:String, storageType:String):Void
{
onProcessGLData.dispatch(source, storageType);
if (__cancelNextProcessGLData != (__cancelNextProcessGLData = false))
return;
var lastMatch = 0, position, regex, name, type;
if (storageType == "uniform")
{
regex = ~/uniform ([A-Za-z0-9]+) ([A-Za-z0-9_]+)/;
}
else
{
regex = ~/attribute ([A-Za-z0-9]+) ([A-Za-z0-9_]+)/;
}
while (regex.matchSub(source, lastMatch))
{
type = regex.matched(1);
name = regex.matched(2);
if (StringTools.startsWith(name, "gl_"))
{
continue;
}
var isUniform = (storageType == "uniform");
registerParameter(name, type, isUniform);
position = regex.matchedPos();
lastMatch = position.pos + position.len;
}
}
function registerParameter(name:String, type:String, isUniform:Bool):Void
{
if (StringTools.startsWith(type, "sampler"))
{
var input = new ShaderInput<BitmapData>();
input.name = name;
input.__isUniform = isUniform;
__inputBitmapData.push(input);
switch (name)
{
case "openfl_Texture":
__texture = input;
case "bitmap":
__bitmap = input;
default:
}
Reflect.setField(__data, name, input);
try{Reflect.setField(this, name, input);} catch(e) {}
}
else if (!Reflect.hasField(__data, name) || Reflect.field(__data, name) == null)
{
var parameterType:ShaderParameterType = switch (type)
{
case "bool": BOOL;
case "double", "float": FLOAT;
case "int", "uint": INT;
case "bvec2": BOOL2;
case "bvec3": BOOL3;
case "bvec4": BOOL4;
case "ivec2", "uvec2": INT2;
case "ivec3", "uvec3": INT3;
case "ivec4", "uvec4": INT4;
case "vec2", "dvec2": FLOAT2;
case "vec3", "dvec3": FLOAT3;
case "vec4", "dvec4": FLOAT4;
case "mat2", "mat2x2": MATRIX2X2;
case "mat2x3": MATRIX2X3;
case "mat2x4": MATRIX2X4;
case "mat3x2": MATRIX3X2;
case "mat3", "mat3x3": MATRIX3X3;
case "mat3x4": MATRIX3X4;
case "mat4x2": MATRIX4X2;
case "mat4x3": MATRIX4X3;
case "mat4", "mat4x4": MATRIX4X4;
default: null;
}
var length = switch (parameterType)
{
case BOOL2, INT2, FLOAT2: 2;
case BOOL3, INT3, FLOAT3: 3;
case BOOL4, INT4, FLOAT4, MATRIX2X2: 4;
case MATRIX3X3: 9;
case MATRIX4X4: 16;
default: 1;
}
var arrayLength = switch (parameterType)
{
case MATRIX2X2: 2;
case MATRIX3X3: 3;
case MATRIX4X4: 4;
default: 1;
}
switch (parameterType)
{
case BOOL, BOOL2, BOOL3, BOOL4:
var parameter = new ShaderParameter<Bool>();
parameter.name = name;
parameter.type = parameterType;
parameter.__arrayLength = arrayLength;
parameter.__isBool = true;
parameter.__isUniform = isUniform;
parameter.__length = length;
__paramBool.push(parameter);
if (name == "openfl_HasColorTransform")
{
__hasColorTransform = parameter;
}
Reflect.setField(__data, name, parameter);
try{Reflect.setField(this, name, parameter);} catch(e) {}
case INT, INT2, INT3, INT4:
var parameter = new ShaderParameter<Int>();
parameter.name = name;
parameter.type = parameterType;
parameter.__arrayLength = arrayLength;
parameter.__isInt = true;
parameter.__isUniform = isUniform;
parameter.__length = length;
__paramInt.push(parameter);
Reflect.setField(__data, name, parameter);
try{Reflect.setField(this, name, parameter);} catch(e) {}
default:
var parameter = new ShaderParameter<Float>();
parameter.name = name;
parameter.type = parameterType;
parameter.__arrayLength = arrayLength;
#if lime
if (arrayLength > 0) parameter.__uniformMatrix = new Float32Array(arrayLength * arrayLength);
#end
parameter.__isFloat = true;
parameter.__isUniform = isUniform;
parameter.__length = length;
__paramFloat.push(parameter);
if (StringTools.startsWith(name, "openfl_"))
{
switch (name)
{
case "openfl_Alpha": __alpha = parameter;
case "openfl_ColorMultiplier": __colorMultiplier = parameter;
case "openfl_ColorOffset": __colorOffset = parameter;
case "openfl_Matrix": __matrix = parameter;
case "openfl_Position": __position = parameter;
case "openfl_TextureCoord": __textureCoord = parameter;
case "openfl_TextureSize": __textureSize = parameter;
default:
}
}
Reflect.setField(__data, name, parameter);
try{Reflect.setField(this, name, parameter);} catch(e) {}
}
}
}
#if REGION /* IHScriptCustomBehaviour */
public function hget(name:String):Dynamic {
if (__glSourceDirty) __init();
if (__thisHasField(name) || __thisHasField('get_${name}')) return Reflect.getProperty(this, name);
else if (!Reflect.hasField(__data, name)) return null;
final field:Dynamic = Reflect.field(__data, name);
if (__instanceFields.contains(name) || __instanceFields.contains('get_${name}'))
return Reflect.getProperty(this, name);
if (!Reflect.hasField(data, name))
return null;
var field:Dynamic = Reflect.field(data, name);
var cl:String = Type.getClassName(Type.getClass(field));
// little problem we are facing boys...
@@ -78,19 +546,17 @@ class FunkinShader extends FlxRuntimeShader implements IHScriptCustomBehaviour {
}
public function hset(name:String, val:Dynamic):Dynamic {
if (__glSourceDirty) __init();
if (__thisHasField(name) || __thisHasField('set_${name}')) {
if (__instanceFields.contains(name) || __instanceFields.contains('set_${name}')) {
Reflect.setProperty(this, name, val);
return val;
}
else if (!Reflect.hasField(__data, name)) {
// ??? huh
Reflect.setField(__data, name, val);
if (!Reflect.hasField(data, name)) {
Reflect.setField(data, name, val);
return val;
}
var field = Reflect.field(__data, name);
var field = Reflect.field(data, name);
var cl = Type.getClassName(Type.getClass(field));
var isNotNull = val != null;
// cant do "field is ShaderInput" for some reason
@@ -132,193 +598,9 @@ class FunkinShader extends FlxRuntimeShader implements IHScriptCustomBehaviour {
return val;
}
#end
override function __updateGL():Void {
onGLUpdate.dispatch();
super.__updateGL();
}
override function __createAssembler():Void {
__glSourceAssembler = new FunkinShaderSourceAssembler(this);
}
override function toString():String {
return __cacheProgramId != null ? 'FunkinShader(${__cacheProgramId})' : 'FunkinShader';
}
#if REGION /* Deprecated */
public var shaderPrefix:String = "";
public var fragmentPrefix:String = "";
public var vertexPrefix:String = "";
#end
#if REGION /* Backward Compatibility */
private static var __instanceFields = Type.getInstanceFields(FunkinShader);
private static var FRAGMENT_SHADER = 0;
private static var VERTEX_SHADER = 1;
public var fileName(get, set):String;
inline function get_fileName():String return _fragmentFilePath ?? _vertexFilePath ?? "FunkinShader";
inline function set_fileName(v:String):String return _fragmentFilePath = _vertexFilePath = v;
public var fragFileName(get, set):String;
inline function get_fragFileName():String return _fragmentFilePath ?? "FunkinShader";
inline function set_fragFileName(v:String):String return _fragmentFilePath = v;
public var vertFileName(get, set):String;
inline function get_vertFileName():String return _vertexFilePath ?? "FunkinShader";
inline function set_vertFileName(v:String):String return _vertexFilePath = v;
public var glslVer(get, set):String;
inline function get_glslVer():String return glVersion;
inline function set_glslVer(v:String):String return glVersion = v;
public var glRawFragmentSource(get, set):String;
inline function get_glRawFragmentSource():String return __glFragmentSourceRaw;
inline function set_glRawFragmentSource(v:String):String return __glFragmentSourceRaw = v;
public var glRawVertexSource(get, set):String;
inline function get_glRawVertexSource():String return __glVertexSourceRaw;
inline function set_glRawVertexSource(v:String):String return __glVertexSourceRaw = v;
function thisHasField(v:String):Bool return __thisHasField(v);
function registerParameter(name:String, type:String, isUniform:Bool) {
__registerParameter(name, Shader.getParameterTypeFromGLSL(type, false), StringTools.startsWith(type, "sampler"), 1, null, isUniform, null);
}
// Unused... cne-openfl uses a different system
var __cancelNextProcessGLData:Bool = false;
public var onProcessGLData:FlxTypedSignal<(String, String)->Void> = new FlxTypedSignal<(String, String)->Void>();
#end
}
class FunkinShaderSourceAssembler extends FlxRuntimeShader.FlxShaderSourceAssembler {
final funkinParent:FunkinShader;
public function new(parent:FunkinShader) {
super(funkinParent = parent);
}
override function __appendIncludes(source:String, isVertex:Bool, ?includedKeys:Map<String, Bool>):String
{
if (includedKeys == null) includedKeys = [];
source = GLSLSourceAssembler.__getIncludeFinder().map(source, (regex:EReg) ->
{
var key = regex.matched(1);
if (includedKeys.get(key)) return '/*Recursive include $key*/\n';
var include = __getIncludeSource(key, isVertex);
if (include == null) return '/*Unknown include $key*/\n';
includedKeys.set(key, true);
return '/*include $key*/\n' + __appendIncludes(include, isVertex, includedKeys);
});
return __getImportCompatibilityFinder().map(source, (regex:EReg) ->
{
var key = regex.matched(1);
if (includedKeys.get(key)) return '/*Recursive import $key*/\n';
var include = __getIncludeSource(key, isVertex);
if (include == null) return '/*Unknown import $key*/\n';
includedKeys.set(key, true);
return '/*import $key*/\n' + __appendIncludes(include, isVertex, includedKeys);
});
}
override function __getIncludeSource(include:String, fromVertex:Bool):Null<String> {
final path = Paths.getPath('shaders/' + include);
if (Assets.exists(path)) return Assets.getText(path);
final fallback = __getIncludeSource(include, fromVertex);
if (fallback != null) return fallback;
Logs.traceColored([
Logs.logText('[Shader] ', RED),
Logs.logText('Failed to import shader $include', RED),
]);
return null;
}
override function __appendPrefix(source:String, versionNumber:Int, versionProfile:String, extensions:Map<String, String>, isVertex:Bool,
precisionHint:Null<ShaderPrecision>):String
{
var result = super.__appendPrefix(null, versionNumber, versionProfile, extensions, isVertex, precisionHint) + "\n";
result += funkinParent.shaderPrefix + "\n" + (isVertex ? funkinParent.vertexPrefix : funkinParent.fragmentPrefix) + "\n";
if (source != null) {
if (!isVertex && versionNumber >= 300 && versionProfile != "compatibility" && !StringTools.contains(source, "out vec4")) {
result += "out vec4 openfl_FragColor;\n";
}
result += source;
}
return result;
}
private static inline function __getImportCompatibilityFinder():EReg {
return ~/#import\s+(?|"([^"]+)"|'([^']+)'|<(.*)>|([^\s]+))/g;
}
}
#if REGION /* Backward Compatibility */
class ShaderTemplates {
public static final vertHeader:String = "attribute float openfl_Alpha;
attribute vec4 openfl_ColorMultiplier;
attribute vec4 openfl_ColorOffset;
attribute vec4 openfl_Position;
attribute vec2 openfl_TextureCoord;
varying float openfl_Alphav;
varying vec4 openfl_ColorMultiplierv;
varying vec4 openfl_ColorOffsetv;
varying vec2 openfl_TextureCoordv;
uniform mat4 openfl_Matrix;
uniform bool openfl_HasColorTransform;
uniform vec2 openfl_TextureSize;
attribute float alpha;
attribute vec4 colorMultiplier;
attribute vec4 colorOffset;
uniform bool hasColorTransform;";
public static final vertBody:String = "openfl_TextureCoordv = openfl_TextureCoord;
if (hasColorTransform)
{
openfl_Alphav = openfl_Alpha * colorMultiplier.a;
if (openfl_HasColorTransform)
{
openfl_ColorOffsetv = (openfl_ColorOffset / 255.0 * colorMultiplier) + (colorOffset / 255.0);
openfl_ColorMultiplierv = openfl_ColorMultiplier * vec4(colorMultiplier.rgb, 1.0);
}
else
{
openfl_ColorOffsetv = colorOffset / 255.0;
openfl_ColorMultiplierv = vec4(colorMultiplier.rgb, 1.0);
}
}
else
{
openfl_Alphav = openfl_Alpha * alpha;
if (openfl_HasColorTransform)
{
openfl_ColorOffsetv = (openfl_ColorOffset + colorOffset) / 255.0;
openfl_ColorMultiplierv = openfl_ColorMultiplier;
}
else
{
openfl_ColorOffsetv = colorOffset / 255.0;
openfl_ColorMultiplierv = vec4(1.0);
}
}";
public static final fragHeader:String = "varying float openfl_Alphav;
varying vec4 openfl_ColorMultiplierv;
varying vec4 openfl_ColorOffsetv;
@@ -327,27 +609,35 @@ varying vec2 openfl_TextureCoordv;
uniform bool openfl_HasColorTransform;
uniform vec2 openfl_TextureSize;
uniform sampler2D bitmap;
uniform bool hasTransform;
uniform bool hasColorTransform;
uniform bool premultiplyAlpha;
vec4 apply_flixel_transform(vec4 color)
{
if (!hasTransform) return color;
else if (color.a <= 0.0 || openfl_Alphav == 0.0) return vec4(0.0);
vec4 applyFlixelEffects(vec4 color) {
if(!hasTransform) {
return color;
}
// this is just solely for ASTC compressed textures.
// ...also in flixel_texture2D, it also converts to linear alpha anyway.
if (!premultiplyAlpha) color.rgb /= color.a;
if(color.a == 0.0) {
return vec4(0.0, 0.0, 0.0, 0.0);
}
if(!hasColorTransform) {
return color * openfl_Alphav;
}
color.rgb = color.rgb / color.a;
color = clamp(openfl_ColorOffsetv + (color * openfl_ColorMultiplierv), 0.0, 1.0);
return vec4(color.rgb * color.a * openfl_Alphav, color.a * openfl_Alphav);
}
#define applyFlixelEffects(color) apply_flixel_transform(color)
vec4 flixel_texture2D(sampler2D bitmap, vec2 coord)
{
return apply_flixel_transform(texture2D(bitmap, coord));
if(color.a > 0.0) {
return vec4(color.rgb * color.a * openfl_Alphav, color.a * openfl_Alphav);
}
return vec4(0.0, 0.0, 0.0, 0.0);
}
vec4 flixel_texture2D(sampler2D bitmap, vec2 coord) {
vec4 color = texture2D(bitmap, coord);
return applyFlixelEffects(color);
}
uniform vec4 _camSize;
@@ -368,17 +658,53 @@ vec4 textureCam(sampler2D bitmap, vec2 pos) {
return flixel_texture2D(bitmap, camToOg(pos));
}";
public static final fragBody:String = "gl_FragColor = flixel_texture2D(bitmap, openfl_TextureCoordv);
if (gl_FragColor.a == 0.0) discard;";
public static final fragBody:String = "gl_FragColor = flixel_texture2D(bitmap, openfl_TextureCoordv);";
public static final vertHeader:String = "attribute float openfl_Alpha;
attribute vec4 openfl_ColorMultiplier;
attribute vec4 openfl_ColorOffset;
attribute vec4 openfl_Position;
attribute vec2 openfl_TextureCoord;
public static final vertBackCompatVarList:Array<EReg> = [
~/attribute float alpha/,
~/attribute vec4 colorMultiplier/,
~/attribute vec4 colorOffset/,
~/uniform bool hasColorTransform/
];
varying float openfl_Alphav;
varying vec4 openfl_ColorMultiplierv;
varying vec4 openfl_ColorOffsetv;
varying vec2 openfl_TextureCoordv;
public static final vertHeaderBackCompat:String = "attribute float openfl_Alpha;
uniform mat4 openfl_Matrix;
uniform bool openfl_HasColorTransform;
uniform vec2 openfl_TextureSize;
attribute float alpha;
attribute vec4 colorMultiplier;
attribute vec4 colorOffset;
uniform bool hasColorTransform;";
public static final vertBody:String = "openfl_Alphav = openfl_Alpha;
openfl_TextureCoordv = openfl_TextureCoord;
if(openfl_HasColorTransform) {
openfl_ColorMultiplierv = openfl_ColorMultiplier;
openfl_ColorOffsetv = openfl_ColorOffset / 255.0;
}
openfl_Alphav = openfl_Alpha * alpha;
if(hasColorTransform) {
openfl_ColorOffsetv = colorOffset / 255.0;
openfl_ColorMultiplierv = colorMultiplier;
}
gl_Position = openfl_Matrix * openfl_Position;";
// TODO: make this ignore comments
public static final vertBackCompatVarList:Array<EReg> = [
~/attribute float alpha/,
~/attribute vec4 colorMultiplier/,
~/attribute vec4 colorOffset/,
~/uniform bool hasColorTransform/
];
public static final vertHeaderBackCompat:String = "attribute float openfl_Alpha;
attribute vec4 openfl_ColorMultiplier;
attribute vec4 openfl_ColorOffset;
attribute vec4 openfl_Position;
@@ -393,7 +719,7 @@ uniform mat4 openfl_Matrix;
uniform bool openfl_HasColorTransform;
uniform vec2 openfl_TextureSize;";
public static final vertBodyBackCompat:String = "openfl_Alphav = openfl_Alpha;
public static final vertBodyBackCompat:String = "openfl_Alphav = openfl_Alpha;
openfl_TextureCoordv = openfl_TextureCoord;
if(openfl_HasColorTransform) {
@@ -402,8 +728,19 @@ if(openfl_HasColorTransform) {
}
gl_Position = openfl_Matrix * openfl_Position;";
public static final defaultVertexSource:String = "#pragma header
void main(void) {
#pragma body
}";
public static final defaultFragmentSource:String = "#pragma header
void main(void) {
#pragma body
}";
}
#end
class ShaderTypeException extends Exception {
var has:Class<Dynamic>;
@@ -416,4 +753,16 @@ class ShaderTypeException extends Exception {
this.name = name;
super('ShaderTypeException - Tried to set the shader uniform "${name}" as a ${Type.getClassName(has)}, but the shader uniform is a ${Std.string(want)}.');
}
}
class ShaderErrorPosition {
public var column:Int;
public var line:Int;
public var message:String;
public function new(line:String, column:String, message:String) {
this.line = Std.parseInt(line);
this.column = Std.parseInt(column);
this.message = message;
}
}
@@ -1,471 +0,0 @@
package funkin.backend.shaders;
import haxe.Exception;
import haxe.io.Path;
import flixel.graphics.FlxGraphic;
import flixel.system.FlxAssets.FlxShader;
import flixel.util.FlxSignal.FlxTypedSignal;
import flixel.util.FlxStringUtil;
import hscript.IHScriptCustomBehaviour;
import openfl.display.BitmapData;
import openfl.display.ShaderInput;
import openfl.display.ShaderParameter;
import openfl.display.ShaderParameterType;
import openfl.display.Shader;
import openfl.display3D._internal.GLProgram;
import openfl.display3D._internal.GLShader;
import openfl.display3D.Program3D;
import openfl.utils._internal.Log;
using StringTools;
@:access(openfl.display3D.Context3D)
@:access(openfl.display3D.Program3D)
@:access(openfl.display.ShaderInput)
@:access(openfl.display.ShaderParameter)
class FunkinShader extends FlxShader implements IHScriptCustomBehaviour {
#if REGION /* Backward Compatibility */
private static var __instanceFields = Type.getInstanceFields(FunkinShader);
private static var FRAGMENT_SHADER = 0;
private static var VERTEX_SHADER = 1;
public var glslVer(get, set):String;
inline function get_glslVer():String return glVersion;
inline function set_glslVer(v:String):String return glVersion = v;
public var glRawFragmentSource(get, set):String;
inline function get_glRawFragmentSource():String return __glFragmentSourceRaw;
inline function set_glRawFragmentSource(v:String):String return __glFragmentSourceRaw = v;
public var glRawVertexSource(get, set):String;
inline function get_glRawVertexSource():String return __glVertexSourceRaw;
inline function set_glRawVertexSource(v:String):String return __glVertexSourceRaw = v;
// Unused... cne-openfl uses a different system
var __cancelNextProcessGLData:Bool = false;
public var onProcessGLData:FlxTypedSignal<(String, String)->Void> = new FlxTypedSignal<(String, String)->Void>();
#end
public static function getShaderCode(key:String, isFragment = true):Null<String> {
var path = "shaders/" + key;
key = Path.withoutExtension(key);
final ext = Path.extension(path);
if (ext == "") path = path + (isFragment ? ".frag" : ".vert");
else isFragment = ext != "vert";
path = Paths.getPath(path);
return Assets.exists(path) ? Assets.getText(path) : null;
}
private static function processGLSLText(source:String, glVersion:String, isFragment:Bool, ?pragmas:Map<String, String>):String
return Shader.processGLSLText(_processGLSLText(source, glVersion, isFragment, pragmas), glVersion, isFragment);
private static function _processGLSLText(source:String, glVersion:String, isFragment:Bool, ?pragmas:Map<String, String>):String {
if (pragmas != null) {
final pragmaKeyword = ~/#pragma\s+(\w+)/g;
source = pragmaKeyword.map(source, (_) -> {
var name = pragmaKeyword.matched(1), pragma:String;
if (pragmas.exists(name)) pragma = pragmas.get(name);
else {
if (name != "header" && name != "body") return '#pragma $name';
pragma = "";
}
return _processGLSLText(pragma, glVersion, isFragment, pragmas);
});
}
inline function tryGetShaderCode(key:String) {
final s = getShaderCode(key, isFragment);
if (s == null) {
Logs.traceColored([
Logs.logText('[Shader] ', RED),
Logs.logText('Failed to import shader $key', RED),
]);
return "";
}
return s;
}
final includeKeyword = ~/#include ['"](.+)['"]/g;
final importKeyword = ~/#import\s+<(.*)>/g;
source = importKeyword.map(source, (_) ->
return _processGLSLText(tryGetShaderCode(importKeyword.matched(1)), glVersion, isFragment, pragmas) ?? "");
return source = includeKeyword.map(source, (_) ->
return _processGLSLText(tryGetShaderCode(includeKeyword.matched(1)), glVersion, isFragment, pragmas) ?? "");
}
private static var __defaultsAvailable:Bool;
private static var __glFragmentSourceDefault:String;
private static var __glVertexSourceDefault:String;
private static var __glFragmentPragmasDefault:Map<String, String>;
private static var __glVertexPragmasDefault:Map<String, String>;
private static var __glFragmentExtensionsDefault:Array<ShaderExtension>;
private static var __glVertexExtensionsDefault:Array<ShaderExtension>;
public var onGLUpdate:FlxTypedSignal<Void->Void> = new FlxTypedSignal<Void->Void>();
public var fileName:String = "FunkinShader";
public var fragFileName:String = "FunkinShader";
public var vertFileName:String = "FunkinShader";
public var shaderPrefix:String = "";
public var fragmentPrefix:String = "";
public var vertexPrefix:String = "";
private var __immediate:Bool;
public function new(?fragmentSource:String, ?vertexSource:String, ?version:String,
?fragmentExtensions:Array<ShaderExtension>, ?vertexExtensions:Array<ShaderExtension>, immediate = false
) {
if (!__defaultsAvailable) {
__glFragmentSourceDefault = __glFragmentSourceRaw;
__glVertexSourceDefault = __glVertexSourceRaw;
__glFragmentPragmasDefault = __glFragmentPragmas.copy();
__glVertexPragmasDefault = __glVertexPragmas.copy();
__glFragmentExtensionsDefault = __glFragmentExtensions ?? [];
__glVertexExtensionsDefault = __glVertexExtensions ?? [];
__defaultsAvailable = true;
}
__immediate = immediate;
if (version != null) glVersion = version;
if (vertexExtensions != null) glVertexExtensions = vertexExtensions;
if (fragmentExtensions != null) glFragmentExtensions = fragmentExtensions;
if (vertexSource != null) glVertexSource = vertexSource;
if (fragmentSource != null) glFragmentSource = fragmentSource;
super();
if (!__isGenerated) {
__isGenerated = true;
__init();
}
}
public function loadShader(name:String, ?version:String, immediate = false):FunkinShader {
final fragment = getShaderCode(name, true), vertex = getShaderCode(name, false);
glVersion = version;
glVertexSource = vertex ?? __glVertexSourceDefault;
glFragmentSource = fragment ?? __glFragmentSourceDefault;
if (immediate) __init();
return this;
}
override function __initGL():Void {
if (__immediate) {
__context = FlxG.stage.context3D;
__enable();
}
super.__initGL();
}
override function __updateGL():Void {
onGLUpdate.dispatch();
super.__updateGL();
}
public function hget(name:String):Dynamic {
if (__glSourceDirty || __data == null) __init();
if (thisHasField(name) || thisHasField('get_${name}')) return Reflect.getProperty(this, name);
else if (!Reflect.hasField(__data, name)) return null;
final field:Dynamic = Reflect.field(__data, name);
var cl:String = Type.getClassName(Type.getClass(field));
// little problem we are facing boys...
// cant do "field is ShaderInput" because ShaderInput has the @:generic metadata
// aka instead of ShaderInput<Float> it gets built as ShaderInput_Float
// this should be fine tho because we check the class, and the fields don't vary based on the type
// thanks for looking in the code cne fans :D!! -lunar
if (cl.startsWith("openfl.display.ShaderParameter"))
return (field.__length > 1) ? field.value : field.value[0];
else if (cl.startsWith("openfl.display.ShaderInput"))
return field.input;
return field;
}
public function hset(name:String, val:Dynamic):Dynamic {
if (__glSourceDirty || __data == null) __init();
if (thisHasField(name) || thisHasField('set_${name}')) {
Reflect.setProperty(this, name, val);
return val;
}
else if (!Reflect.hasField(__data, name)) {
// ??? huh
Reflect.setField(__data, name, val);
return val;
}
var field = Reflect.field(__data, name);
var cl = Type.getClassName(Type.getClass(field));
var isNotNull = val != null;
// cant do "field is ShaderInput" for some reason
if (cl.startsWith("openfl.display.ShaderParameter")) {
if (field.__length <= 1) {
// that means we wait for a single number, instead of an array
if (field.__isInt && isNotNull && !(val is Int)) {
throw new ShaderTypeException(name, Type.getClass(val), 'Int');
return null;
} else
if (field.__isBool && isNotNull && !(val is Bool)) {
throw new ShaderTypeException(name, Type.getClass(val), 'Bool');
return null;
} else
if (field.__isFloat && isNotNull && !(val is Float)) {
throw new ShaderTypeException(name, Type.getClass(val), 'Float');
return null;
}
return field.value = isNotNull ? [val] : null;
} else {
if (isNotNull && !(val is Array)) {
throw new ShaderTypeException(name, Type.getClass(val), Array);
return null;
}
return field.value = val;
}
} else if (cl.startsWith("openfl.display.ShaderInput")) {
// shader input!!
var bitmap:BitmapData;
if (!isNotNull) bitmap = null;
else if (val is BitmapData) bitmap = val;
else if (val is FlxGraphic) bitmap = val.bitmap;
else {
throw new ShaderTypeException(name, Type.getClass(val), BitmapData);
return null;
}
field.input = bitmap;
}
return val;
}
override function __buildSourcePrefix(isFragment:Bool):String {
var result = super.__buildSourcePrefix(isFragment) + '\n$shaderPrefix';
return isFragment ? result + '\n$fragmentPrefix' : result + '\n$vertexPrefix';
}
override function set_glFragmentExtensions(value:Array<ShaderExtension>):Array<ShaderExtension> {
if (value == null) value = __glFragmentExtensionsDefault;
if (value != __glFragmentExtensions) __glSourceDirty = true;
return __glFragmentExtensions = value;
}
override function set_glVertexExtensions(value:Array<ShaderExtension>):Array<ShaderExtension> {
if (value == null) value = __glVertexExtensionsDefault;
if (value != __glVertexExtensions) __glSourceDirty = true;
return __glVertexExtensions = value;
}
override function set_glVersion(value:Null<String>):String {
if (value == null || value == "") value = Flags.DEFAULT_GLSL_VERSION;
if ((__glVersionRaw = value) != __glVersion) {
__glSourceDirty = true;
if (__glVertexSourceRaw != null) __glVertexSource = processGLSLText(__glVertexSourceRaw, value, false, __glVertexPragmas);
if (__glFragmentSourceRaw != null) __glFragmentSource = processGLSLText(__glFragmentSourceRaw, value, true, __glFragmentPragmas);
}
return __glVersion = value;
}
override function set_glFragmentSource(value:String):String {
if (value == null || value == "") value = __glFragmentSourceDefault;
if ((__glFragmentSourceRaw = value) != null) {
if (__glVersion != (__glVersion = Shader.getGLSLTextVersion(value, __glVersionRaw)))
__glSourceDirty = true;
value = processGLSLText(value, __glVersion, true, __glFragmentPragmas);
}
if (value != __glFragmentSource) __glSourceDirty = true;
return __glFragmentSource = value;
}
override function set_glVertexSource(value:String):String {
if (value == null || value == "") value = __glVertexSourceDefault;
if ((__glVertexSourceRaw = value) != null) {
if (__glVersion != (__glVersion = Shader.getGLSLTextVersion(value, __glVersionRaw)))
__glSourceDirty = true;
value = processGLSLText(value, __glVersion, false, __glVertexPragmas);
}
if (value != __glVertexSource) __glSourceDirty = true;
return __glVertexSource = value;
}
override function set_glFragmentPragmas(value:Map<String, String>):Map<String, String> {
if (value == null) value = __glFragmentPragmasDefault;
if (value != __glFragmentPragmas)
__glSourceDirty = true;
return __glFragmentPragmas = value;
}
override function set_glVertexPragmas(value:Map<String, String>):Map<String, String> {
if (value == null) value = __glVertexPragmasDefault;
if (value != __glVertexPragmas)
__glSourceDirty = true;
return __glVertexPragmas = value;
}
function registerParameter(name:String, type:String, isUniform:Bool):Void {
__registerParameter(name, Program3D.getParameterTypeFromGLString(type, 1), 1, -1, isUniform, false, null);
}
public function toString():String
return FlxStringUtil.getDebugString([for (field in Reflect.fields(data)) LabelValuePair.weak(field, Reflect.field(data, field))]);
}
class ShaderTemplates {
#if REGION /* Backward Compatibility */
public static final vertHeader:String = "attribute float openfl_Alpha;
attribute vec4 openfl_ColorMultiplier;
attribute vec4 openfl_ColorOffset;
attribute vec4 openfl_Position;
attribute vec2 openfl_TextureCoord;
varying float openfl_Alphav;
varying vec4 openfl_ColorMultiplierv;
varying vec4 openfl_ColorOffsetv;
varying vec2 openfl_TextureCoordv;
uniform mat4 openfl_Matrix;
uniform bool openfl_HasColorTransform;
uniform vec2 openfl_TextureSize;
attribute float alpha;
attribute vec4 colorMultiplier;
attribute vec4 colorOffset;
uniform bool hasColorTransform;";
public static final vertBody:String = "openfl_TextureCoordv = openfl_TextureCoord;
if (hasColorTransform) {
openfl_Alphav = openfl_Alpha * colorMultiplier.a;
if (openfl_HasColorTransform) {
openfl_ColorOffsetv = (openfl_ColorOffset / 255.0 * colorMultiplier) + (colorOffset / 255.0);
openfl_ColorMultiplierv = openfl_ColorMultiplier * vec4(colorMultiplier.rgb, 1.0);
}
else {
openfl_ColorOffsetv = colorOffset / 255.0;
openfl_ColorMultiplierv = vec4(colorMultiplier.rgb, 1.0);
}
}
else {
openfl_Alphav = openfl_Alpha * alpha;
if (openfl_HasColorTransform) {
openfl_ColorOffsetv = (openfl_ColorOffset + colorOffset) / 255.0;
openfl_ColorMultiplierv = openfl_ColorMultiplier;
}
else {
openfl_ColorOffsetv = colorOffset / 255.0;
openfl_ColorMultiplierv = vec4(1.0);
}
}";
public static final fragHeader:String = "varying float openfl_Alphav;
varying vec4 openfl_ColorMultiplierv;
varying vec4 openfl_ColorOffsetv;
varying vec2 openfl_TextureCoordv;
uniform bool openfl_HasColorTransform;
uniform vec2 openfl_TextureSize;
uniform sampler2D bitmap;
uniform bool hasTransform;
uniform bool hasColorTransform;
vec4 apply_flixel_transform(vec4 color) {
if (!hasTransform) return color;
else if (color.a <= 0.0 || openfl_Alphav == 0.0) return vec4(0.0);
color.rgb /= color.a;
color = clamp(openfl_ColorOffsetv + (color * openfl_ColorMultiplierv), 0.0, 1.0);
return vec4(color.rgb * color.a * openfl_Alphav, color.a * openfl_Alphav);
}
#define applyFlixelEffects(color) apply_flixel_transform(color)
vec4 flixel_texture2D(sampler2D bitmap, vec2 coord) {
return apply_flixel_transform(texture2D(bitmap, coord));
}
uniform vec4 _camSize;
float map(float value, float min1, float max1, float min2, float max2) {
return min2 + (value - min1) * (max2 - min2) / (max1 - min1);
}
vec2 getCamPos(vec2 pos) {
vec4 size = _camSize / vec4(openfl_TextureSize, openfl_TextureSize);
return vec2(map(pos.x, size.x, size.x + size.z, 0.0, 1.0), map(pos.y, size.y, size.y + size.w, 0.0, 1.0));
}
vec2 camToOg(vec2 pos) {
vec4 size = _camSize / vec4(openfl_TextureSize, openfl_TextureSize);
return vec2(map(pos.x, 0.0, 1.0, size.x, size.x + size.z), map(pos.y, 0.0, 1.0, size.y, size.y + size.w));
}
vec4 textureCam(sampler2D bitmap, vec2 pos) {
return flixel_texture2D(bitmap, camToOg(pos));
}";
public static final fragBody:String = "gl_FragColor = flixel_texture2D(bitmap, openfl_TextureCoordv);
if (gl_FragColor.a == 0.0) discard;";
public static final vertBackCompatVarList:Array<EReg> = [
~/attribute float alpha/,
~/attribute vec4 colorMultiplier/,
~/attribute vec4 colorOffset/,
~/uniform bool hasColorTransform/
];
public static final vertHeaderBackCompat:String = "attribute float openfl_Alpha;
attribute vec4 openfl_ColorMultiplier;
attribute vec4 openfl_ColorOffset;
attribute vec4 openfl_Position;
attribute vec2 openfl_TextureCoord;
varying float openfl_Alphav;
varying vec4 openfl_ColorMultiplierv;
varying vec4 openfl_ColorOffsetv;
varying vec2 openfl_TextureCoordv;
uniform mat4 openfl_Matrix;
uniform bool openfl_HasColorTransform;
uniform vec2 openfl_TextureSize;";
public static final vertBodyBackCompat:String = "openfl_Alphav = openfl_Alpha;
openfl_TextureCoordv = openfl_TextureCoord;
if(openfl_HasColorTransform) {
openfl_ColorMultiplierv = openfl_ColorMultiplier;
openfl_ColorOffsetv = openfl_ColorOffset / 255.0;
}
gl_Position = openfl_Matrix * openfl_Position;";
#end
}
class ShaderTypeException extends Exception {
var has:Class<Dynamic>;
var want:Class<Dynamic>;
var name:String;
public function new(name:String, has:Class<Dynamic>, want:Dynamic) {
this.has = has;
this.want = want;
this.name = name;
super('ShaderTypeException - Tried to set the shader uniform "${name}" as a ${Type.getClassName(has)}, but the shader uniform is a ${Std.string(want)}.');
}
}
+8 -13
View File
@@ -11,11 +11,6 @@ import openfl.display.BlendMode;
import openfl.geom.ColorTransform;
import openfl.geom.Point;
import openfl.geom.Rectangle;
import openfl.display.TriangleCulling;
import openfl.display3D.Context3DWrapMode;
import openfl.display3D.Context3DCompareMode;
import openfl.filters.BitmapFilter;
import openfl.filters.ShaderFilter;
class FakeCamera extends FlxCamera {
public static final instance = new FakeCamera();
@@ -26,14 +21,14 @@ class FakeCamera extends FlxCamera {
visible = false;
}
override function startTrianglesBatch(graphic:FlxGraphic, smoothing:Bool = false, isColored:Bool = false, ?blend:BlendMode, ?hasColorOffsets:Bool, ?shader:FlxShader, ?wrapMode:Context3DWrapMode, ?depthCompareMode:Context3DCompareMode, ?culling:TriangleCulling) { return null;}
override function startQuadBatch(graphic:FlxGraphic, colored:Bool, hasColorOffsets:Bool = false, ?blend:BlendMode, smooth:Bool = false, ?shader:FlxShader, ?wrapMode:Context3DWrapMode, ?depthCompareMode:Context3DCompareMode) { return null;}
override function startTrianglesBatch(graphic:FlxGraphic, smoothing:Bool = false, isColored:Bool = false, ?blend:BlendMode, ?hasColorOffsets:Bool, ?shader:FlxShader) { return null;}
override function startQuadBatch(graphic:FlxGraphic, colored:Bool, hasColorOffsets:Bool = false, ?blend:BlendMode, smooth:Bool = false, ?shader:FlxShader) { return null;}
override function clearDrawStack() {}
override function render() {}
public override function drawPixels(?frame:FlxFrame, ?pixels:BitmapData, matrix:FlxMatrix, ?transform:ColorTransform, ?blend:BlendMode, ?smoothing:Bool = false, ?shader:FlxShader, ?wrapMode:Context3DWrapMode, ?depthCompareMode:Context3DCompareMode) {}
public override function copyPixels(?frame:FlxFrame, ?pixels:BitmapData, ?sourceRect:Rectangle, destPoint:Point, ?transform:ColorTransform, ?blend:BlendMode, ?smoothing:Bool = false, ?shader:FlxShader, ?wrapMode:Context3DWrapMode, ?depthCompareMode:Context3DCompareMode) {}
public override function drawTriangles(graphic:FlxGraphic, vertices:DrawData<Float>, indices:DrawData<Int>, uvtData:DrawData<Float>, ?colors:DrawData<Int>, ?position:FlxPoint, ?blend:BlendMode, repeat:Bool = false, smoothing:Bool = false, ?transform:ColorTransform, ?shader:FlxShader, ?wrapMode:Context3DWrapMode, ?depthCompareMode:Context3DCompareMode, ?culling:TriangleCulling):Void {}
public override function drawPixels(?frame:FlxFrame, ?pixels:BitmapData, matrix:FlxMatrix, ?transform:ColorTransform, ?blend:BlendMode, ?smoothing:Bool = false, ?shader:FlxShader) {}
public override function copyPixels(?frame:FlxFrame, ?pixels:BitmapData, ?sourceRect:Rectangle, destPoint:Point, ?transform:ColorTransform, ?blend:BlendMode, ?smoothing:Bool = false, ?shader:FlxShader) {}
public override function drawTriangles(graphic:FlxGraphic, vertices:DrawData<Float>, indices:DrawData<Int>, uvtData:DrawData<Float>, ?colors:DrawData<Int>, ?position:FlxPoint, ?blend:BlendMode, repeat:Bool = false, smoothing:Bool = false, ?transform:ColorTransform, ?shader:FlxShader):Void {}
public override function update(elapsed:Float) {}
@@ -46,10 +41,10 @@ class FakeCallCamera extends FakeCamera {
public static final instance = new FakeCallCamera();
public var ignoreDraws:Bool = false;
public dynamic function onDraw(?frame:FlxFrame, ?pixels:BitmapData, matrix:FlxMatrix, ?transform:ColorTransform, ?blend:BlendMode, ?smoothing:Bool = false, ?shader:FlxShader, ?wrapMode:Context3DWrapMode, ?depthCompareMode:Context3DCompareMode) {
public dynamic function onDraw(?frame:FlxFrame, ?pixels:BitmapData, matrix:FlxMatrix, ?transform:ColorTransform, ?blend:BlendMode, ?smoothing:Bool = false, ?shader:FlxShader) {
}
override function drawPixels(?frame:FlxFrame, ?pixels:BitmapData, matrix:FlxMatrix, ?transform:ColorTransform, ?blend:BlendMode, ?smoothing:Bool = false, ?shader:FlxShader, ?wrapMode:Context3DWrapMode, ?depthCompareMode:Context3DCompareMode) {
if (!ignoreDraws) onDraw(frame, pixels, matrix, transform, blend, smoothing, shader, wrapMode, depthCompareMode);
override function drawPixels(?frame:FlxFrame, ?pixels:BitmapData, matrix:FlxMatrix, ?transform:ColorTransform, ?blend:BlendMode, ?smoothing:Bool = false, ?shader:FlxShader) {
if (!ignoreDraws) onDraw(frame, pixels, matrix, transform, blend, smoothing, shader);
}
}
+2 -27
View File
@@ -19,10 +19,8 @@ class Flags {
// -- Codename's Addon Config --
@:bypass public static var addonFlags:Map<String, Dynamic> = [];
public static var CURRENT_API_VERSION:Int = 2;
// -- Codename's ZipFolderLibrary Config --
public static var ALLOWED_ZIP_EXTENSIONS:Array<String> = ["zip"];
public static var CURRENT_API_VERSION:Int = 2;
// -- Codename's Mod Config --
public static var MOD_NAME:String = "";
@@ -106,10 +104,6 @@ class Flags {
public static var DEFAULT_BEATS_PER_MEASURE:Int = 4;
public static var DEFAULT_STEPS_PER_BEAT:Int = 4;
public static var DEFAULT_LOOP_TIME:Float = 0.0;
public static var ICONS_AUTOPOSITION:Bool = true;
@:lazy public static var DEFAULT_SOUND_TIME_SCALED_PITCH:Null<Bool> = null;
@:lazy public static var USE_FLXTRAIL_FRAMES:Null<Bool> = null;
public static var SUPPORTED_CHART_RUNTIME_FORMATS:Array<String> = ["Legacy", "Psych Engine"];
public static var SUPPORTED_CHART_FORMATS:Array<String> = ["BaseGame"];
@@ -134,9 +128,6 @@ class Flags {
@:also(funkin.game.PlayState.opponentMode)
public static var DEFAULT_OPPONENT_MODE:Bool = false;
public static var EARLY_HIT_WINDOW_RANGE:Float = 1.0; // was 0.5 for easier early hitting, but now 1 to demotivate mashing and getting away with it.
public static var LATE_HIT_WINDOW_RANGE:Float = 1.0;
public static var SHITS_BREAK_COMBO:Bool = true;
public static var USE_LEGACY_TIMING:Null<Bool> = null;
public static var DEFAULT_NOTE_MS_LIMIT:Float = 1500;
@@ -170,8 +161,6 @@ class Flags {
// Font configuration
public static var DEFAULT_FONT:String = "vcr.ttf";
public static var DEFAULT_FONT_SIZE:Int = 16;
public static var DEFAULT_ALT_ANIM_SUFFIX:String = "-alt";
// to translate these you need to convert them into ids
// Resume -> pause.resume
@@ -286,7 +275,7 @@ class Flags {
public static var DEFAULT_CHARACTER_GHOSTDISABLE_SOUND:String = "editors/character/ghostDisable";
public static var DEFAULT_CHARACTER_GHOSTENABLE_SOUND:String = "editors/character/ghostEnable";
@:lazy public static var DEFAULT_GLSL_VERSION:String = null;
public static var DEFAULT_GLSL_VERSION:String = "120";
@:also(funkin.backend.utils.HttpUtil.userAgent)
public static var USER_AGENT:String = 'request';
// -- End of Codename's Default Flags --
@@ -321,20 +310,6 @@ class Flags {
if (USE_LEGACY_TIMING == null) USE_LEGACY_TIMING = MOD_API_VERSION < 2;
if (USE_LEGACY_ZOOM_FACTOR == null) USE_LEGACY_ZOOM_FACTOR = MOD_API_VERSION < 2;
if (SUSTAINS_AS_ONE_NOTE == null) SUSTAINS_AS_ONE_NOTE = MOD_API_VERSION >= 2;
if (DEFAULT_GLSL_VERSION == null) {
if (MOD_API_VERSION < 2) {
DEFAULT_GLSL_VERSION = #if (android || mac || web) "100" #else "120" #end;
Logs.warn("Blend Mode Extensions won't work in MOD_API_VERSION below than 2");
}
else {
DEFAULT_GLSL_VERSION = openfl.utils.GLSLSourceAssembler.getDefaultVersion();
}
}
if (DEFAULT_SOUND_TIME_SCALED_PITCH == null) DEFAULT_SOUND_TIME_SCALED_PITCH = MOD_API_VERSION >= 2;
if (USE_FLXTRAIL_FRAMES == null) USE_FLXTRAIL_FRAMES = MOD_API_VERSION < 2;
flixel.sound.FlxSound.defaultTimeScaledPitch = cast DEFAULT_SOUND_TIME_SCALED_PITCH;
flixel.addons.effects.FlxTrail.defaultDelayBackwardCompatibility = cast USE_FLXTRAIL_FRAMES;
}
public static function loadFromDatas(datas:Array<String>):Map<String, String> {
+5 -4
View File
@@ -8,12 +8,13 @@ class FunkinGame extends FlxGame {
super.switchState();
// draw once to put all images in gpu then put the last update time to now to prevent lag spikes or whatever
draw();
ticks = getTicks();
_total = ticks = getTicks();
skipNextTickUpdate = true;
}
override function __enterFrame(deltaTime:Float) {
if (skipNextTickUpdate != (skipNextTickUpdate = false)) ticks = getTicks();
super.__enterFrame(deltaTime);
public override function onEnterFrame(t) {
if (skipNextTickUpdate != (skipNextTickUpdate = false))
_total = ticks = getTicks();
super.onEnterFrame(t);
}
}
@@ -36,7 +36,7 @@ class GraphicCacheSprite extends FlxSprite {
if (graphic == null) return;
// make their useCount one time higher to prevent them from auto being cleared from cache
graphic.incrementUseCount();
graphic.useCount++;
graphic.destroyOnNoUse = false;
cachedGraphics.push(graphic);
nonRenderedCachedGraphics.push(graphic);
@@ -45,7 +45,7 @@ class GraphicCacheSprite extends FlxSprite {
public override function destroy() {
for(g in cachedGraphics) {
g.destroyOnNoUse = true;
g.decrementUseCount();
g.useCount--;
}
graphic = null;
super.destroy();
+1 -1
View File
@@ -115,7 +115,7 @@ final class Logs {
Sys.print(t.text);
}
NativeAPI.setConsoleColors();
Sys.println("\r");
Sys.print("\r\n");
__showing = false;
#elseif mobile
while(__showing) {
+21 -6
View File
@@ -24,8 +24,8 @@ import openfl.utils.AssetLibrary;
import sys.FileSystem;
import sys.io.File;
#if android
import extension.androidtools.content.Context;
import extension.androidtools.os.Build;
import android.content.Context;
import android.os.Build;
#end
class Main extends Sprite
@@ -33,7 +33,7 @@ class Main extends Sprite
public static var instance:Main;
public static var modToLoad:String = null;
public static var forceGPUOnlyBitmapsOff:Bool = false;
public static var forceGPUOnlyBitmapsOff:Bool = #if desktop false #else true #end;
public static var noTerminalColor:Bool = false;
public static var verbose:Bool = false;
@@ -58,11 +58,9 @@ class Main extends Sprite
// You can pretty much ignore everything from here on - your code should go in your states.
public static function preInit() {
#if sys
funkin.backend.utils.NativeAPI.registerAsDPICompatible();
funkin.backend.system.CommandLineHandler.parseCommandLine(Sys.args());
funkin.backend.system.Main.fixWorkingDirectory();
#end
}
public function new()
@@ -97,10 +95,16 @@ class Main extends Sprite
// DEPRECATED
@:dox(hide) public static function execAsync(func:Void->Void) ThreadUtil.execAsync(func);
private static function getTimer():Int {
return time = Lib.getTimer();
}
public static function loadGameSettings() {
WindowUtils.init();
SaveWarning.init();
MemoryUtil.init();
@:privateAccess
FlxG.game.getTimer = getTimer;
FunkinCache.init();
Paths.assetsTree = new AssetsLibraryList();
@@ -125,11 +129,12 @@ class Main extends Sprite
funkin.options.PlayerSettings.init();
Options.load();
game.focusLostFramerate = 30;
FlxG.fixedTimestep = false;
FlxG.scaleMode = scaleMode = new FunkinRatioScaleMode();
Conductor.init();
AudioSwitchFix.init();
EventManager.init();
FlxG.signals.focusGained.add(onFocus);
FlxG.signals.preStateSwitch.add(onStateSwitch);
@@ -201,6 +206,16 @@ class Main extends Sprite
// manual asset clearing since base openfl one does'nt clear lime one
// does'nt clear bitmaps since flixel fork does it auto
@:privateAccess {
// clear uint8 pools
for(length=>pool in openfl.display3D.utils.UInt8Buff._pools) {
for(b in pool.clear())
b.destroy();
}
openfl.display3D.utils.UInt8Buff._pools.clear();
}
MemoryUtil.clearMajor();
}
+14 -44
View File
@@ -7,14 +7,12 @@ import flixel.FlxState;
import funkin.backend.assets.AssetsLibraryList;
import funkin.backend.assets.ModsFolder;
import funkin.backend.assets.ModsFolderLibrary;
import funkin.backend.assets.ZipFolderLibrary;
import funkin.backend.chart.EventsData;
import funkin.backend.system.framerate.Framerate;
import funkin.editors.ModConfigWarning;
import funkin.menus.TitleState;
import haxe.io.Path;
@dox(hide)
typedef AddonInfo = {
var name:String;
@@ -42,7 +40,6 @@ class MainState extends FlxState {
ControlsUtil.resetCustomControls();
FlxG.bitmap.reset();
FlxG.sound.destroy(true);
FlxG.sound.resetCache();
Paths.assetsTree.reset();
@@ -60,32 +57,12 @@ class MainState extends FlxState {
var _highPriorityAddons:Array<AddonInfo> = [];
var _noPriorityAddons:Array<AddonInfo> = [];
var quick_modsPath = ModsFolder.modsPath + ModsFolder.currentModFolder;
// handing if the loading mod (before it's properly loaded) is a compressed mod
// we just need to use `Paths.assetsTree.hasCompressedLibrary` to complete valid checks for actual loaded compressed mods
var isZipMod = false;
// If we know it's a compressed mod, then we can check if it's using the `cnemod` folder path.
// All it is really is a folder with the mod's name, then a compressed file called "cnemod.[zip|7z|rar|etc]"
var isCneMod = false;
// We are doing it like this because think about it: it's 1 for loop lol
// We just need to know if any of these values is true, so if only one is true and we are not close to being done in the loop, that's fine.
//
for (ext in Flags.ALLOWED_ZIP_EXTENSIONS) {
if (FileSystem.exists(quick_modsPath+"."+ext)) isZipMod = true;
if (FileSystem.exists(quick_modsPath+"/cnemod."+ext)) isCneMod = true;
if (isZipMod && isCneMod) break;
}
// We get the addons folder from relative space (`./`) and then our mod's addons.
var addonPaths = [
ModsFolder.addonsPath,
// So to check the mod's addons folder, we need to decompress it. Which is impossible* in this stage of the loading library process.
// TODO: Write a function when the library is loaded to decompress the contents and then load the libraries :)
( (ModsFolder.currentModFolder != null && !isZipMod) ?
quick_modsPath + "/addons/" : null
(
ModsFolder.currentModFolder != null ?
ModsFolder.modsPath + ModsFolder.currentModFolder + "/addons/" :
null
)
];
@@ -95,8 +72,12 @@ class MainState extends FlxState {
for (addon in FileSystem.readDirectory(path)) {
if (!FileSystem.isDirectory(path + addon)) {
if (Flags.ALLOWED_ZIP_EXTENSIONS.contains(Path.extension(addon))) addon = Path.withoutExtension(addon);
else continue;
switch(Path.extension(addon).toLowerCase()) {
case 'zip':
addon = Path.withoutExtension(addon);
default:
continue;
}
}
var data:AddonInfo = {
@@ -119,14 +100,9 @@ class MainState extends FlxState {
#if MOD_SUPPORT
for (addon in _lowPriorityAddons)
loadLib(addon.path, ltrim(addon.name, "[LOW]"));
if (ModsFolder.currentModFolder != null) {
// isCneMod is a guarentee to be a zip mod because we just checked for it, so this will always load as a CompressedLibrary
if (isCneMod)
loadLib(quick_modsPath + "/cnemod", ModsFolder.currentModFolder);
else
loadLib(quick_modsPath, ModsFolder.currentModFolder);
}
if (ModsFolder.currentModFolder != null)
loadLib(ModsFolder.modsPath + ModsFolder.currentModFolder, ModsFolder.currentModFolder);
for (addon in _noPriorityAddons)
loadLib(addon.path, addon.name);
@@ -158,15 +134,9 @@ class MainState extends FlxState {
CoolUtil.safeAddAttributes('./.temp/', NativeAPI.FileAttribute.HIDDEN);
#end
for (lib in ModsFolder.getLoadedModsLibs()) {
if (!(lib is ZipFolderLibrary)) continue;
if (cast(lib, ZipFolderLibrary).PRELOAD_VIDEOS) cast(lib, ZipFolderLibrary).precacheVideos();
}
var startState:Class<FlxState> = Flags.DISABLE_WARNING_SCREEN ? TitleState : funkin.menus.WarningState;
// In this case if the mod we just loaded a compressed modpack, we can't edit or modify files without decompressing it.
if (Options.devMode && Options.allowConfigWarning && !isZipMod) {
if (Options.devMode && Options.allowConfigWarning) {
var lib:ModsFolderLibrary;
for (e in Paths.assetsTree.libraries) if ((lib = cast AssetsLibraryList.getCleanLibrary(e)) is ModsFolderLibrary
&& lib.modName == ModsFolder.currentModFolder)
@@ -1,14 +1,62 @@
package funkin.backend.system;
import lime.graphics.Image;
import lime.graphics.cairo.CairoImageSurface;
import openfl.display.BitmapData;
import openfl.geom.Rectangle;
@:deprecated("Use openfl.display.BitmapData.toHardware instead.")
class OptimizedBitmapData extends BitmapData {
@SuppressWarnings("checkstyle:Dynamic")
@:noCompletion private override function __fromImage(image:#if lime Image #else Dynamic #end):Void
{
super.__fromImage(image);
toHardware();
#if lime
if (image != null && image.buffer != null)
{
this.image = image;
width = image.width;
height = image.height;
rect = new Rectangle(0, 0, image.width, image.height);
__textureWidth = width;
__textureHeight = height;
#if sys
image.format = BGRA32;
image.premultiplied = true;
#end
__isValid = true;
readable = true;
if(FlxG.stage.context3D != null) {
lock();
getTexture(FlxG.stage.context3D);
getSurface();
readable = true;
this.image = null;
// @:privateAccess
// if (FlxG.bitmap.__doNotDelete)
// MemoryUtil.clearMinor();
}
}
#end
}
@SuppressWarnings("checkstyle:Dynamic")
@:dox(hide) public override function getSurface():#if lime CairoImageSurface #else Dynamic #end
{
#if lime
if (__surface == null)
{
__surface = CairoImageSurface.fromImage(image);
}
return __surface;
#else
return null;
#end
}
}
@@ -8,12 +8,7 @@ class RotatingSpriteGroup extends FlxSpriteGroup {
if (maxSize <= 0)
return super.recycle(ObjectClass, ObjectFactory, Force, Revive);
if (group.members.length < maxSize)
{
if (ObjectFactory != null) return add(ObjectFactory());
if (ObjectClass != null) return add(Type.createInstance(ObjectClass, []));
return null;
}
return group.recycleCreateObject(ObjectClass, ObjectFactory);
var spr = group.members.shift();
group.members.push(spr);
if (Revive)
@@ -14,7 +14,7 @@ class AssetTreeInfo extends FramerateCategory {
super("Asset Libraries Tree Info");
}
public override function __enterFrame(t:Float) {
public override function __enterFrame(t:Int) {
if (alpha <= 0.05) return;
if ((lastUpdateTime += FlxG.rawElapsed) < 1)
@@ -6,23 +6,15 @@ import openfl.text.TextField;
class CodenameBuildField extends TextField {
public function new() {
super();
defaultTextFormat = Framerate.textFormat;
autoSize = LEFT;
multiline = wordWrap = false;
reload();
}
public function reload() {
defaultTextFormat = Framerate.textFormat;
#if TEST_BUILD
text = '${Flags.VERSION_MESSAGE} (Test Build)';
#elseif COMPILE_EXPERIMENTAL
text = '${Flags.VERSION_MESSAGE} (Experimental Build)';
#else
text = '${Flags.VERSION_MESSAGE}';
#end
#if (debug || COMPILE_EXPERIMENTAL)
#if debug
text += '\n${Flags.COMMIT_MESSAGE}';
#end
}
@@ -5,7 +5,7 @@ class ConductorInfo extends FramerateCategory {
super("Conductor Info");
}
public override function __enterFrame(t:Float) {
public override function __enterFrame(t:Int) {
if (alpha <= 0.05) return;
_text = 'Current Song Position: ${Math.floor(Conductor.songPosition * 1000) / 1000}';
_text += '\n - ${Conductor.curBeat} beats';
@@ -7,7 +7,7 @@ class FlixelInfo extends FramerateCategory {
super("Flixel Info");
}
public override function __enterFrame(t:Float) {
public override function __enterFrame(t:Int) {
if (alpha <= 0.05) return;
@@ -76,7 +76,6 @@ class Framerate extends Sprite {
}
public function reload() {
textFormat = new TextFormat(fontName, 12, -1);
for(c in categories)
c.reload();
#if SHOW_BUILD_ON_FPS
@@ -101,7 +100,7 @@ class Framerate extends Sprite {
var debugAlpha:Float = 0;
public override function __enterFrame(t:Float) {
public override function __enterFrame(t:Int) {
alpha = CoolUtil.fpsLerp(alpha, debugMode > 0 ? 1 : 0, 0.5);
debugAlpha = CoolUtil.fpsLerp(debugAlpha, debugMode > 1 ? 1 : 0, 0.5);
@@ -40,11 +40,9 @@ class FramerateCategory extends Sprite {
this.text.y = this.title.y + this.title.height + 2;
}
public function reload() {
for(label in [this.title, this.text]) label.defaultTextFormat = new TextFormat(Framerate.fontName, label == this.title ? 18 : 12, -1);
}
public function reload() {}
public override function __enterFrame(t:Float) {
public override function __enterFrame(t:Int) {
if (alpha <= 0.05) return;
super.__enterFrame(t);
@@ -35,11 +35,10 @@ class FramerateCounter extends Sprite {
}
public function reload() {
for (label in [fpsNum, fpsLabel]) label.defaultTextFormat = new TextFormat(Framerate.fontName, label == fpsNum ? 18 : 12, -1);
lastUpdateTime = 0;
}
public override function __enterFrame(t:Float) {
public override function __enterFrame(t:Int) {
if (alpha <= 0.05) return;
super.__enterFrame(t);
@@ -23,18 +23,16 @@ class MemoryCounter extends Sprite {
label.y = 0;
label.text = "MEM";
label.multiline = label.wordWrap = false;
label.defaultTextFormat = Framerate.textFormat;
label.defaultTextFormat = new TextFormat(Framerate.fontName, 12, -1);
label.selectable = false;
addChild(label);
}
memoryPeakText.alpha = 0.5;
}
public function reload() {
for(label in [memoryText, memoryPeakText]) label.defaultTextFormat = Framerate.textFormat;
}
public function reload() {}
public override function __enterFrame(t:Float) {
public override function __enterFrame(t:Int) {
if (alpha <= 0.05) return;
super.__enterFrame(t);
@@ -9,7 +9,7 @@ class StatsInfo extends FramerateCategory {
super("Asset Libraries Tree Info");
}
public override function __enterFrame(t:Float) {
public override function __enterFrame(t:Int) {
if (alpha <= 0.05) return;
_text = "totalDC: " + Context3DStats.totalDrawCalls();
_text += "\nstageDC: " + Context3DStats.contextDrawCalls(DrawCallContext.STAGE);
@@ -169,7 +169,7 @@ class SystemInfo extends FramerateCategory {
super("System Info");
}
public override function __enterFrame(t:Float) {
public override function __enterFrame(t:Int) {
if (alpha <= 0.05) return;
_text = __formattedSysText;
@@ -58,9 +58,6 @@ class Macros {
final macroPath = 'funkin.backend.system.macros.Macros';
Compiler.addMetadata('@:build($macroPath.buildLimeAssetLibrary())', 'lime.utils.AssetLibrary');
Compiler.addMetadata('@:build($macroPath.buildLimeApplication())', 'lime.app.Application');
Compiler.addMetadata('@:build($macroPath.buildLimeWindow())', 'lime.ui.Window');
Compiler.addMetadata('@:build($macroPath.buildOpenflAssets())', 'openfl.utils.Assets');
//Adds Compat for #if hscript blocks when you have hscript improved
if (Context.defined("hscript_improved") && !Context.defined("hscript")) {
@@ -72,60 +69,6 @@ class Macros {
final fields:Array<Field> = Context.getBuildFields(), pos:Position = Context.currentPos();
fields.push({name: 'tag', access: [APublic], pos: pos, kind: FVar(macro :funkin.backend.assets.AssetSource)});
fields.push({name: 'isCompressed', access: [APublic], pos: pos, kind: FVar(macro :Bool, macro false)});
return fields;
}
public static function buildLimeApplication():Array<Field> {
final fields:Array<Field> = Context.getBuildFields(), pos:Position = Context.currentPos();
for (f in fields) switch (f.kind) {
case FFun(func): switch (f.name) {
case "exec": switch (func.expr.expr) {
case EBlock(exprs): exprs.insert(1, macro funkin.backend.system.Main.preInit());
default:
}
}
default:
}
return fields;
}
public static function buildLimeWindow():Array<Field> {
final fields:Array<Field> = Context.getBuildFields(), pos:Position = Context.currentPos();
if (!Context.defined("DARK_MODE_WINDOW")) return fields;
for (f in fields) switch (f.kind) {
case FFun(func): switch (f.name) {
case "new": switch (func.expr.expr) {
case EBlock(exprs): exprs.push(macro funkin.backend.utils.NativeAPI.setDarkMode(title, true));
default:
}
}
default:
}
return fields;
}
public static function buildOpenflAssets():Array<Field> {
final fields:Array<Field> = Context.getBuildFields(), pos:Position = Context.currentPos();
for (f in fields) switch (f.name) {
case "allowHardwareTextures": fields.remove(f);
default:
}
fields.push({name: 'allowHardwareTextures', access: [APublic, AStatic], pos: pos, kind: FProp("get", "set", macro :Bool)});
fields.push({name: '__allowHardwareTextures', access: [APrivate, AStatic], pos: pos, kind: FVar(macro :Null<Bool>)});
fields.push({name: "get_allowHardwareTextures", access: [APublic, AStatic, AInline], pos: pos, kind: FFun({ret: macro :Bool, args: [], expr: macro {
return __allowHardwareTextures != null ? __allowHardwareTextures : !funkin.backend.system.Main.forceGPUOnlyBitmapsOff && funkin.options.Options.gpuOnlyBitmaps;
}})});
fields.push({name: "set_allowHardwareTextures", access: [APublic, AStatic, AInline], pos: pos, kind: FFun({ret: macro :Bool, args: [{name: "value", type: macro :Bool}], expr: macro {
__allowHardwareTextures = value;
return get_allowHardwareTextures();
}})});
return fields;
}
@@ -0,0 +1,31 @@
package funkin.backend.system.modules;
import haxe.io.Path;
/*
* A class that simply points OpenALSoft to a custom configuration file when
* the game starts up.
*
* The config overrides a few global OpenALSoft settings with the aim of
* improving audio quality on desktop targets.
*/
@:keepInit class ALSoftConfig
{
#if desktop
static function __init__():Void
{
var origin:String = #if hl Sys.getCwd() #else Sys.programPath() #end;
var configPath:String = Path.directory(Path.withoutExtension(origin));
#if windows
configPath += "/plugins/alsoft.ini";
#elseif mac
configPath = Path.directory(configPath) + "/Resources/plugins/alsoft.conf";
#else
configPath += "/plugins/alsoft.conf";
#end
Sys.putEnv("ALSOFT_CONF", configPath);
}
#end
}
@@ -0,0 +1,69 @@
package funkin.backend.system.modules;
import flixel.FlxState;
import flixel.sound.FlxSound;
import funkin.backend.utils.NativeAPI;
import lime.media.AudioManager;
import lime.media.AudioSource;
import haxe.Timer;
/**
* if you are stealing this keep this comment at least please lol
*
* hi gray itsa me yoshicrafter29 i fixed it hehe
*/
@:dox(hide)
class AudioSwitchFix {
public static function onAudioDisconnected() @:privateAccess {
var sources:Array<{source:AudioSource, playing:Bool, time:Float, gain:Float, pitch:Float, position:lime.math.Vector4}> = [];
for (source in AudioSource.activeSources) {
var wasPlaying = source.playing;
sources.push({
source: source,
playing: wasPlaying,
time: source.currentTime,
gain: source.gain,
pitch: source.pitch,
position: source.position
});
source.__backend.dispose();
if (wasPlaying) source.__backend.playing = true;
}
AudioManager.shutdown();
AudioManager.init();
// #if !lime_doc_gen
// if (AudioManager.context.type == OPENAL)
// {
// var alc = AudioManager.context.openal;
// var device = alc.openDevice();
// var ctx = alc.createContext(device);
// alc.makeContextCurrent(ctx);
// alc.processContext(ctx);
// }
// #end
for (d in sources) {
d.source.__backend.init();
d.source.currentTime = d.time;
d.source.gain = d.gain;
d.source.pitch = d.pitch;
d.source.position = d.position;
if (d.playing) d.source.play();
}
Main.changeID++;
Main.audioDisconnected = false;
}
private static var timer:Timer;
private static function onRun() if (Main.audioDisconnected) onAudioDisconnected();
public static function init() {
NativeAPI.registerAudio();
if (timer == null) (timer = new Timer(1000)).run = onRun;
}
}
+402 -448
View File
@@ -1,94 +1,67 @@
package funkin.backend.utils;
#if lime_openal
import sys.thread.Mutex;
import flixel.sound.FlxSound;
import lime.media.AudioBuffer;
import lime.utils.ArrayBufferView.ArrayBufferIO;
import lime.utils.ArrayBuffer;
import flixel.sound.FlxSound;
import flixel.sound.FlxSoundData;
#if (lime_cffi && lime_vorbis)
import lime.media.vorbis.Vorbis;
import lime.media.vorbis.VorbisFile;
#end
typedef ReadCallback = Int->Int->Void;
typedef WindowFunction = Float->Float;
#if (target.threaded)
import sys.thread.Mutex;
#end
final class WindowFunctions {
static inline final TWO_PI:Float = 6.283185307179586;
static inline final FOUR_PI:Float = 12.566370614359172;
static inline final SIX_PI:Float = 18.84955592153876;
static inline final EIGHT_PI:Float = 25.132741228718345;
public static inline function triangular(x:Float):Float
return 1.0 - Math.abs(x - 0.5) * 2.0;
public static inline function hann(x:Float):Float
return 0.5 - 0.5 * FlxMath.fastCos(TWO_PI * x);
public static inline function hamming(x:Float):Float
return 0.53836 - 0.46164 * FlxMath.fastCos(TWO_PI * x);
public static inline function blackmanNuttall(x:Float):Float
return 0.3635819 - 0.4891775 * FlxMath.fastCos(TWO_PI * x) + 0.1365995 * FlxMath.fastCos(FOUR_PI * x)
- 0.0106411 * FlxMath.fastCos(SIX_PI * x);
public static inline function blackmanHarris(x:Float):Float
return 0.4243801 - 0.4973406 * FlxMath.fastCos(TWO_PI * x) + 0.0782793 * FlxMath.fastCos(FOUR_PI * x);
public static inline function flatTop(x:Float):Float
return 0.21557895 - 0.41663158 * FlxMath.fastCos(TWO_PI * x) + 0.277263158 * FlxMath.fastCos(FOUR_PI * x)
+ 0.083578947 * FlxMath.fastCos(SIX_PI * x) + 0.006947368 * FlxMath.fastCos(EIGHT_PI * x);
}
enum abstract TimeUnit(Int) from Int to Int {
var MILLISECOND = 0;
var SECOND = 1;
var SAMPLE = 2;
}
typedef AudioAnalyzerCallback = Int->Int->Void;
/**
* An utility that analyze FlxSound,
* An utility that analyze FlxSounds,
* can be used to make waveform or real-time audio visualizer.
*
* FlxSound.amplitude works so if any case if your only checking for peak of current time, use that instead.
*/
final class AudioAnalyzer {
/**
* Get bytes from an audio buffer with specified position and wordSize
* @param buffer The audio buffer to get byte from.
* @param position The specified position to get the byte from the audio buffer.
* @param wordSize How many bytes to get with to one byte (Usually it's bitsPerSample / 8 or bitsPerSample >> 3).
* @param buffer The audio buffer to get byte from.
* @param position The specified position to get the byte from the audio buffer.
* @param wordSize How many bytes to get with to one byte (Usually it's bitsPerSample / 8 or bitsPerSample >> 3).
* @return Byte from the audio buffer with specified position.
*/
public static function getByte(buffer:ArrayBuffer, position:Int, wordSize:Int):Int {
if (wordSize == 2) return ArrayBufferIO.getInt16(buffer, position);
if (wordSize == 2) return inline ArrayBufferIO.getInt16(buffer, position);
else if (wordSize == 3) {
wordSize = ArrayBufferIO.getUint16(buffer, position) | (buffer.get(position + 2) << 16);
if (wordSize & 0x800000 != 0) return wordSize - 0x1000000;
else return wordSize;
var b = inline ArrayBufferIO.getUint16(buffer, position) | (buffer.get(position + 2) << 16);
if (b & 0x800000 != 0) return b - 0x1000000;
else return b;
}
else if (wordSize == 4) return ArrayBufferIO.getInt32(buffer, position);
else return ArrayBufferIO.getInt8(buffer, position);
else if (wordSize == 4) return inline ArrayBufferIO.getInt32(buffer, position);
else return inline ArrayBufferIO.getUint8(buffer, position) - 128;
}
/**
* Gets spectrum from the frequencies with specified sample rate.
* @param frequencies Frequencies input.
* @param sampleRate Sample Rate input.
* @param barCount How much bars to get.
* @param spectrum The output for getting the values, to avoid memory leaks (Optional).
* @param ratio How much ratio for smoothen the values from the previous spectrum values (Optional, use FlxMath.getElapsedLerp(1 - ratio) to simulate web AnalyserNode.smoothingTimeConstant, 0.35 of smoothingTime works most of the time).
* @param minDb The minimum decibels to cap (Optional, default -63.0, -120 is pure silence).
* @param maxDb The maximum decibels to cap (Optional, default -10.0, Above 0 is not recommended).
* @param minFreq The minimum frequency to cap (Optional, default 20.0, Below 8.0 is not recommended).
* @param maxFreq The maximum frequency to cap (Optional, default 20000.0, Above 23000.0 is not recommended).
* @return Output of spectrum/bars that ranges from 0 to 1.
* Gets levels from the frequencies with specified sample rate.
* @param frequencies Frequencies input.
* @param sampleRate Sample Rate input.
* @param barCount How much bars to get.
* @param levels The output for getting the values, to avoid memory leaks (Optional).
* @param ratio How much ratio for smoothen the values from the previous levels values (Optional, use CoolUtil.getFPSRatio(1 - ratio) to simulate web AnalyserNode.smoothingTimeConstant, 0.35 of smoothingTime works most of the time).
* @param minDb The minimum decibels to cap (Optional, default -63.0, -120 is pure silence).
* @param maxDb The maximum decibels to cap (Optional, default -10.0, Above 0 is not recommended).
* @param minFreq The minimum frequency to cap (Optional, default 20.0, Below 8.0 is not recommended).
* @param maxFreq The maximum frequency to cap (Optional, default 22000.0, Above 23000.0 is not recommended).
* @return Output of levels/bars that ranges from 0 to 1.
*/
public static function getSpectrumFromFrequencies(frequencies:Array<Float>, sampleRate:Int, barCount:Int, ?spectrum:Array<Float>, ratio = 0.0, minDb = -63.0, maxDb = -10.0, minFreq = 20.0, maxFreq = 20000.0):Array<Float> {
if (spectrum == null) spectrum = [];
if (spectrum.length != barCount) spectrum.resize(barCount);
public static function getLevelsFromFrequencies(frequencies:Array<Float>, sampleRate:Int, barCount:Int, ?levels:Array<Float>, ratio = 0.0, minDb = -63.0, maxDb = -10.0, minFreq = 20.0, maxFreq = 22000.0):Array<Float> {
if (levels == null) levels = [];
levels.resize(barCount);
var logMin = Math.log(minFreq), n = frequencies.length - 1;
var logRange = Math.log(maxFreq) - logMin, dbRangeRate = 1 / (maxDb - minDb), rate = frequencies.length * 2 / sampleRate;
var logMin = Math.log(minFreq), logMax = Math.log(maxFreq);
var logRange = logMax - logMin, dbRange = maxDb - minDb, n = frequencies.length;
inline function calculateScale(i:Int)
return FlxMath.bound(Math.exp(logMin + (logRange * i / (barCount + 1))) * rate, 0, n);
return CoolUtil.bound(Math.exp(logMin + (logRange * i / (barCount + 1))) * n * 2 / sampleRate, 0, n - 1);
var s1 = calculateScale(0), s2;
var i1 = Math.floor(s1), i2;
@@ -108,146 +81,138 @@ final class AudioAnalyzer {
}
i1 = Math.floor(s1 = s2);
v = FlxMath.bound((Math.log(v) * 8.685889638065035 - minDb) * dbRangeRate, 0, 1);
if (ratio > 0 && ratio < 1 && v < spectrum[i]) spectrum[i] -= (spectrum[i] - v) * ratio;
else spectrum[i] = v;
v = CoolUtil.bound(((20 * Math.log(v) / 2.302585092994046) - minDb) / dbRange, 0, 1);
if (ratio > 0 && ratio < 1 && v < levels[i]) levels[i] -= (levels[i] - v) * ratio;
else levels[i] = v;
}
return spectrum;
return levels;
}
/**
* Gets levels from the frequencies with specified sample rate.
* @param frequencies Frequencies input.
* @param sampleRate Sample Rate input.
* @param barCount How much bars to get.
* @param levels The output for getting the values, to avoid memory leaks (Optional).
* @param ratio How much ratio for smoothen the values from the previous levels values (Optional, use CoolUtil.getFPSRatio(1 - ratio) to simulate web AnalyserNode.smoothingTimeConstant, 0.35 of smoothingTime works most of the time).
* @param minDb The minimum decibels to cap (Optional, default -63.0, -120 is pure silence).
* @param maxDb The maximum decibels to cap (Optional, default -10.0, Above 0 is not recommended).
* @param minFreq The minimum frequency to cap (Optional, default 20.0, Below 8.0 is not recommended).
* @param maxFreq The maximum frequency to cap (Optional, default 22000.0, Above 23000.0 is not recommended).
* @return Output of levels/bars that ranges from 0 to 1.
*
* deprecated, use getSpectrumFromFrequencies instead.
*/
@:deprecated("Use getSpectrumFromFrequencies instead of getLevelsFromFrequencies.")
public static function getLevelsFromFrequencies(frequencies:Array<Float>, sampleRate:Int, barCount:Int, ?levels:Array<Float>, ratio = 0.0, minDb = -63.0, maxDb = -10.0, minFreq = 20.0, maxFreq = 22000.0):Array<Float>
return inline getSpectrumFromFrequencies(frequencies, sampleRate, barCount, levels, ratio, minDb, maxDb, minFreq, maxFreq);
static final _permutations:Map<Int, Array<Int>> = [];
static final _twiddleReals:Map<Int, Array<Float>> = [];
static final _twiddleImags:Map<Int, Array<Float>> = [];
static final _reals:Array<Array<Float>> = [];
static final _imags:Array<Array<Float>> = [];
static var _freqCalculating:Int = 0;
static final _mutex = new Mutex();
static var __reverseIndices:Array<Array<Int>> = [];
static var __windows:Array<Array<Float>> = [];
static var __twiddleReals:Array<Array<Float>> = [];
static var __twiddleImags:Array<Array<Float>> = [];
static var __freqReals:Array<Array<Float>> = [];
static var __freqImags:Array<Array<Float>> = [];
static var __freqCalculating:Int = 0;
#if (target.threaded)
static var __mutex:Mutex = new Mutex();
#end
/**
* Gets frequencies from the samples.
* @param samples The samples (can be from FunkinAudioAnalyzer.getSamples).
* @param window The windowing function to use when passed.
* @param frequencies The output for getting the frequencies, to avoid memory leaks (Optional).
* @return Output of frequencies.
* @param samples The samples (can be from AudioAnalyzer.getSamples).
* @param fftN How much samples for the fft to get, Has to be power of two, or it won't work.
* @param useWindowing Should fft related stuff use blackman windowing? (Web AnalyzerNode windowing), Most of the time it's not worth it.
* @param frequencies The output for getting the frequencies, to avoid memory leaks (Optional).
* @return Output of frequencies.
*/
public static function getFrequenciesFromSamples(samples:Array<Float>, ?window:WindowFunction, ?frequencies:Array<Float>, ?fftN:Int):Array<Float> {
if (fftN == null) fftN = samples.length;
public static function getFrequenciesFromSamples(samples:Array<Float>, fftN = 2048, useWindowing = false, ?frequencies:Array<Float>):Array<Float> {
var log = Math.floor(Math.log(fftN) / 0.6931471805599453);
if (log == 0) throw "AudioAnalyzer.getFrequenciesFromSamples: Cannot insert a fftN of 1";
var bits = 0;
while ((fftN >>= 1) > 0) bits++;
if (bits == 0) throw "FunkinAudioAnalyzer.getFrequenciesFromSamples: Cannot insert a sample length or fftN of 1";
var i = log - 1;
fftN = 1 << log;
fftN = 1 << bits;
var fftN2 = fftN >> 1, n = fftN - 1;
var permutation:Array<Int>, twiddleReal:Array<Float>, twiddleImag:Array<Float>;
_mutex.acquire();
var real:Array<Float> = _reals[_freqCalculating], imag:Array<Float> = _imags[_freqCalculating];
if (real == null) {
_reals.push(real = []);
_imags.push(imag = []);
#if (target.threaded) __mutex.acquire(); #end
var reals:Array<Float> = __freqReals[__freqCalculating], imags:Array<Float> = __freqImags[__freqCalculating];
if (reals == null) {
__freqReals.push(reals = []);
__freqImags.push(imags = []);
}
_freqCalculating++;
__freqCalculating++;
if (_permutations.exists(bits)) {
permutation = _permutations.get(bits);
twiddleReal = _twiddleReals.get(bits);
twiddleImag = _twiddleImags.get(bits);
}
else {
(permutation = []).resize(fftN);
(twiddleReal = []).resize(fftN2);
(twiddleImag = []).resize(fftN2);
var reverseIndices:Array<Int> = __reverseIndices[i];
var windows:Array<Float> = __windows[i];
var twiddleReals:Array<Float> = __twiddleReals[i];
var twiddleImags:Array<Float> = __twiddleImags[i];
var ang:Float;
if (reverseIndices == null) {
__reverseIndices.resize(log);
__windows.resize(log);
__twiddleReals.resize(log);
__twiddleImags.resize(log);
(reverseIndices = []).resize(fftN);
(windows = []).resize(fftN);
(twiddleReals = []).resize(fftN);
(twiddleImags = []).resize(fftN);
var f;
for (i in 0...fftN) {
permutation[i] = _bitReverse(i, bits);
if (i < fftN2) {
twiddleReal[i] = Math.cos((ang = -6.283185307179586 * i / n));
twiddleImag[i] = Math.sin(ang);
}
f = 2 * Math.PI * (i / fftN);
windows[i] = 0.42 - 0.5 * Math.cos(f) + 0.08 * Math.cos(2 * f);
reverseIndices[i] = __bitReverse(i, log);
twiddleReals[i] = Math.cos(-f);
twiddleImags[i] = Math.sin(-f);
}
_permutations.set(bits, permutation);
_twiddleReals.set(bits, twiddleReal);
_twiddleImags.set(bits, twiddleImag);
__reverseIndices[i] = reverseIndices;
__windows[i] = windows;
__twiddleReals[i] = twiddleReals;
__twiddleImags[i] = twiddleImags;
}
_mutex.release();
#if (target.threaded) __mutex.release(); #end
if (fftN > real.length) {
real.resize(fftN);
imag.resize(fftN);
if (fftN > reals.length) {
reals.resize(fftN);
imags.resize(fftN);
}
if (frequencies == null) frequencies = [];
if (frequencies.length != fftN2) frequencies.resize(fftN2);
frequencies.resize(1 << i);
var tr = 1 / n;
for (i in 0...fftN) {
real[permutation[i]] = samples[i];
if (window != null) real[permutation[i]] *= window(i * tr);
imag[i] = 0;
i = samples.length;
while (i > 0) {
i--;
if (useWindowing) reals[reverseIndices[i]] = samples[i] * windows[i];
else reals[reverseIndices[i]] = samples[i];
imags[i] = 0;
}
var half = 1, g:Int, b:Int, r:Int, i0:Int, i1:Int, ti:Float;
while (fftN2 > 0) {
g = 0;
while (g < fftN) {
b = r = 0;
while (b < half) {
i1 = (i0 = g + b) + half;
tr = real[i1] * twiddleReal[r] - imag[i1] * twiddleImag[r];
ti = real[i1] * twiddleImag[r] + imag[i1] * twiddleReal[r];
real[i1] = real[i0] - tr;
imag[i1] = imag[i0] - ti;
real[i0] += tr;
imag[i0] += ti;
b++;
r += fftN2;
var size = 1, n = fftN, half = 1, k, i0, i1, t, tr:Float, ti:Float;
while ((size <<= 1) < fftN) {
n >>= 1;
i = 0;
while (i < fftN) {
k = 0;
while (k < half) {
i1 = (i0 = i + k) + half;
t = (k * n) % fftN;
tr = reals[i1] * twiddleReals[t] - imags[i1] * twiddleImags[t];
ti = reals[i1] * twiddleImags[t] + imags[i1] * twiddleReals[t];
reals[i1] = reals[i0] - tr;
imags[i1] = imags[i0] - ti;
reals[i0] += tr;
imags[i0] += ti;
k++;
}
g += half << 1;
i += size;
}
half <<= 1;
fftN2 >>= 1;
half = size;
}
tr = 1.0 / fftN;
i0 = frequencies.length - 1;
for (i in 1...i0) frequencies[i] = 2 * Math.sqrt(real[i] * real[i] + imag[i] * imag[i]) * tr;
frequencies[0] = Math.sqrt(real[0] * real[0] + imag[0] * imag[0]) * tr;
frequencies[i0] = Math.sqrt(real[i0] * real[i0] + imag[i0] * imag[i0]) * tr;
i = 1 << (log - 1);
while (i > 1) {
i--;
frequencies[i] = 2 * Math.sqrt(reals[i] * reals[i] + imags[i] * imags[i]) * tr;
}
frequencies[0] = Math.sqrt(reals[0] * reals[0] + imags[0] * imags[0]) * tr;
_mutex.acquire();
_freqCalculating--;
_mutex.release();
#if (target.threaded) __mutex.acquire(); #end
__freqCalculating--;
#if (target.threaded) __mutex.release(); #end
return frequencies;
}
static function _bitReverse(x:Int, bits:Int):Int {
var y = 0, i = bits;
static function __bitReverse(x:Int, log:Int):Int {
var y = 0, i = log;
while (i > 0) {
y = (y << 1) | (x & 1);
x >>= 1;
@@ -262,357 +227,346 @@ final class AudioAnalyzer {
public var sound:FlxSound;
/**
* The current data from sound.
*/
public var data(default, null):FlxSoundData;
/**
* How much samples for the fourier transform to get.
* How much samples for the fft to get.
* Usually for getting the levels or frequencies of the sound.
*
* Has to be power of two, or it won't work.
*/
public var fftN:Int;
/**
* Should fft related stuff use blackman windowing? (Web AnalyzerNode windowing).
* Most of the time looks bad with this.
*/
public var useWindowingFFT:Bool;
/**
* The current buffer from sound.
*/
public var buffer(default, null):AudioBuffer;
/**
* The current byteSize from buffer.
* Example the byteSize of 16 BitsPerSample is 32768 (1 << (16 - 1))
* Example the byteSize of 16 BitsPerSample is 32768 (1 << 16-1)
*/
public var byteSize(default, null):Int;
var _sampleSize:Int;
var _mins:Array<Int> = [];
var _maxs:Array<Int> = [];
//var _decoder:FunkinAudioDecoder;
//var _buffer:ArrayBuffer;
//var _bufferLen:Int;
//var _bufferLastSize:Int;
//var _bufferLastSample:Int;
var _sampleIndex:Int;
var _sampleChannel:Int;
var _sampleValue:Int;
var _sampleValueGain:Float;
var _sampleOutputMerge:Bool;
var _sampleOutputLength:Int;
var _sampleOutput:Array<Float>;
var _freqSamples:Array<Float>;
var _frequencies:Array<Float>;
var __toBits:Float;
var __wordSize:Int;
var __sampleSize:Int;
public function new(sound:FlxSound, fftN = 4096) {
this.sound = sound;
this.fftN = fftN;
_check();
}
#if (lime_cffi && lime_vorbis)
var __vorbis:VorbisFile;
var __buffer:ArrayBuffer;
var __bufferSize:Int;
var __bufferLastSize:Int;
var __bufferTime:Float;
var __bufferLastTime:Float;
#end
function _check() {
if (sound != null && !sound.data.isDestroyed) {
if (sound.data != data)
{
byteSize = 1 << ((data = sound.data).bitsPerSample - 1);
_sampleSize = data.channels * (data.bitsPerSample >> 3);
_mins.resize(data.channels);
_maxs.resize(data.channels);
//_decoder?.destroy();
}
}
else data = null;
}
// analyze
var __min:Array<Int> = [];
var __max:Array<Int> = [];
var __minByte:Int;
var __maxByte:Int;
// samples
var __sampleIndex:Int;
var __sampleChannel:Int;
var __sampleToValue:Float;
var __sampleOutputMerge:Bool;
var __sampleOutputLength:Int;
var __sampleOutput:Array<Float>;
// frequencies
var __freqSamples:Array<Float>;
var __frequencies:Array<Float>;
/**
* Gets spectrum from an attached sound from position.
* @param pos Position to get (Optional).
* @param timeUnit TimeUnit to use for positions (Optional).
* @param gain How much gain multiplier will it affect the output. (Optional, default 1.0).
* @param barCount How much bars to get.
* @param spectrum The output for getting the values, to avoid memory leaks (Optional).
* @param ratio How much ratio for smoothen the values from the previous spectrum values (Optional, use FlxMath.getElapsedLerp(1 - ratio) to simulate web AnalyserNode.smoothingTimeConstant, 0.35 of smoothingTime works most of the time).
* @param minDb The minimum decibels to cap (Optional, default -63.0, -120 is pure silence).
* @param maxDb The maximum decibels to cap (Optional, default -10.0, Above 0 is not recommended).
* @param minFreq The minimum frequency to cap (Optional, default 20.0, Below 8.0 is not recommended).
* @param maxFreq The maximum frequency to cap (Optional, default 20000.0, Above 23000.0 is not recommended).
* @return Output of spectrum/bars that ranges from 0 to 1.
* Creates an analyzer for specified FlxSound
* @param sound An FlxSound to analyze.
* @param fftN How much samples for fft to get (Optional, default 2048, 4096 is recommended for highest quality).
* @param useWindowingFFT Should fft related stuff use blackman windowing? (Web AnalyzerNode windowing).
*/
public function getSpectrum(?pos:Float, ?timeUnit:TimeUnit, ?gain:Float, ?window:WindowFunction, barCount:Int, ?spectrum:Array<Float>, ?ratio:Float, ?minDb:Float, ?maxDb:Float, ?minFreq:Float, ?maxFreq:Float):Array<Float> {
return getSpectrumFromFrequencies(_frequencies = getFrequencies(pos, timeUnit, gain, window, _frequencies), data.sampleRate, barCount, spectrum, ratio, minDb, maxDb, minFreq, maxFreq);
public function new(sound:FlxSound, fftN = 2048, useWindowingFFT = false) {
this.sound = sound;
this.fftN = fftN;
this.useWindowingFFT = useWindowingFFT;
__check();
}
function __check() if (sound != null && sound.buffer != buffer) {
byteSize = 1 << ((buffer = sound.buffer).bitsPerSample - 1);
#if (lime_cffi && lime_vorbis)
__vorbis = null;
__bufferLastSize = 0;
__bufferTime = Math.NaN;
__bufferLastTime = Math.NaN;
#end
__toBits = buffer.sampleRate / 1000 * (__sampleSize = buffer.channels * (__wordSize = buffer.bitsPerSample >> 3));
__min.resize(buffer.channels);
__max.resize(buffer.channels);
}
/**
* Gets levels from an attached FlxSound from startPos, basically a minimized of frequencies.
* @param startPos Start Position to get from sound in milliseconds.
* @param volume How much volume multiplier will it affect the output. (Optional, default 1.0).
* @param barCount How much bars to get.
* @param levels The output for getting the values, to avoid memory leaks (Optional).
* @param ratio How much ratio for smoothen the values from the previous levels values (Optional, use CoolUtil.getFPSRatio(1 - ratio) to simulate web AnalyserNode.smoothingTimeConstant, 0.35 of smoothingTime works most of the time).
* @param minDb The minimum decibels to cap (Optional, default -63.0, -120 is pure silence).
* @param maxDb The maximum decibels to cap (Optional, default -10.0, Above 0 is not recommended).
* @param minFreq The minimum frequency to cap (Optional, default 20.0, Below 8.0 is not recommended).
* @param maxFreq The maximum frequency to cap (Optional, default 22000.0, Above 23000.0 is not recommended).
* @return Output of levels/bars that ranges from 0 to 1.
*
* deprecated, use getLevels instead.
* @param startPos Start Position to get from sound in milliseconds.
* @param volume How much volume multiplier will it affect the output. (Optional, default 1.0).
* @param barCount How much bars to get.
* @param levels The output for getting the values, to avoid memory leaks (Optional).
* @param ratio How much ratio for smoothen the values from the previous levels values (Optional, use CoolUtil.getFPSRatio(1 - ratio) to simulate web AnalyserNode.smoothingTimeConstant, 0.35 of smoothingTime works most of the time).
* @param minDb The minimum decibels to cap (Optional, default -63.0, -120 is pure silence).
* @param maxDb The maximum decibels to cap (Optional, default -10.0, Above 0 is not recommended).
* @param minFreq The minimum frequency to cap (Optional, default 20.0, Below 8.0 is not recommended).
* @param maxFreq The maximum frequency to cap (Optional, default 22000.0, Above 23000.0 is not recommended).
* @return Output of levels/bars that ranges from 0 to 1.
*/
@:deprecated("Use getSpectrum instead of getLevels.")
public function getLevels(?startPos:Float, ?volume:Float, barCount:Int, ?levels:Array<Float>, ?ratio:Float, ?minDb:Float, ?maxDb:Float, ?minFreq:Float, ?maxFreq:Float):Array<Float>
return inline getSpectrum(startPos, MILLISECOND, volume, null, barCount, levels, ratio, minDb, maxDb, minFreq, maxFreq);
return inline getLevelsFromFrequencies(__frequencies = getFrequencies(startPos, volume, __frequencies), buffer.sampleRate, barCount, levels, ratio, minDb, maxDb, minFreq, maxFreq);
/**
* Gets frequencies from an attached sound from position.
* @param pos Position to get. (Optional).
* @param timeUnit TimeUnit to use for positions. (Optional).
* @param gain How much gain multiplier will it affect the output. (Optional, default 1.0).
* @param window The windowing function to use when passed.
* @param frequencies The output for getting the frequencies, to avoid memory leaks (Optional).
* @return Output of frequencies.
* Gets frequencies from an attached FlxSound from startPos.
* @param startPos Start Position to get from sound in milliseconds.
* @param volume How much volume multiplier will it affect the output. (Optional, default 1.0).
* @param frequencies The output for getting the frequencies, to avoid memory leaks (Optional).
* @return Output of frequencies.
*/
public function getFrequencies(?pos:Float, ?timeUnit:TimeUnit, ?gain:Float, ?window:WindowFunction, ?frequencies:Array<Float>):Array<Float> {
if (pos == null) {
if (sound == null) return frequencies;
_check();
if ((pos = sound.time / 1000 * data.sampleRate - fftN) < 0) pos = 0;
timeUnit = SAMPLE;
}
return getFrequenciesFromSamples(_freqSamples = getSamples(pos, timeUnit, fftN, true, -1, gain, _freqSamples), window, frequencies);
}
public function getFrequencies(?startPos:Float, ?volume:Float, ?frequencies:Array<Float>):Array<Float>
return inline getFrequenciesFromSamples(__freqSamples = getSamples(startPos != null ? startPos : sound.time, fftN, true, -1, volume, __freqSamples), fftN, useWindowingFFT, frequencies);
/**
* Analyzes an attached sound from startPos to endPos in milliseconds to get the amplitudes.
* @param startPos Start Position to get.
* @param endPos End Position to get.
* @param timeUnit TimeUnit to use for positions.
* @param outOrOutMins The output minimum value from the analyzer, indices is in channels (0 to -0.5 -> 0 to 0.5) (Optional, if outMax doesn't get passed in, it will be [min, max] with all channels combined instead).
* @param outMaxs The output maximum value from the analyzer, indices is in channels (Optional).
* @return Output of amplitude from given position.
* Analyzes an attached FlxSound from startPos to endPos in milliseconds to get the amplitudes.
* @param startPos Start Position to get from sound in milliseconds.
* @param endPos End Position to get from sound in milliseconds.
* @param outOrOutMin The output minimum value from the analyzer, indices is in channels (0 to -0.5 -> 0 to 0.5) (Optional, if outMax doesn't get passed in, it will be [min, max] with all channels combined instead).
* @param outMax The output maximum value from the analyzer, indices is in channels (Optional).
* @return Output of amplitude from given position.
*/
public function analyze(startPos:Float, endPos:Float, ?timeUnit:TimeUnit, ?outOrOutMins:Array<Float>, ?outMaxs:Array<Float>):Float {
var hasOut = outOrOutMins != null;
var hasTwoOut = hasOut && outMaxs != null;
public function analyze(startPos:Float, endPos:Float, ?outOrOutMin:Array<Float>, ?outMax:Array<Float>):Float {
var hasOut = outOrOutMin != null;
var hasTwoOut = hasOut && outMax != null;
_check();
var conversion:Float = switch (timeUnit) {
case SAMPLE: 1;
case SECOND: data.sampleRate;
default: data.sampleRate / 1000;
}
for (i in 0...data.channels) _mins[i] = _maxs[i] = -0x7FFFFFFF;
if (startPos > endPos) _read(Math.floor(startPos * conversion), Math.floor(endPos * conversion), _analyzeRead);
if (hasTwoOut) for (i in 0...buffer.channels) __min[i] = __max[i] = 0;
__minByte = __maxByte = 0;
var min = -0x7FFFFFFF, max = -0x7FFFFFFF, v = 1 / byteSize, f:Float;
for (i in 0...data.channels) {
if (hasTwoOut) {
if ((f = _mins[i] * v) > outOrOutMins[i]) outOrOutMins[i] = f;
if ((f = _maxs[i] * v) > outMaxs[i]) outMaxs[i] = f;
__check();
__read(startPos, endPos, hasTwoOut ? __analyzeCallback : __analyzeCallbackSimple);
if (hasOut) {
var f:Float;
if (hasTwoOut) for (i in 0...buffer.channels) {
if (outOrOutMin[i] < (f = __min[i] / byteSize)) outOrOutMin[i] = f;
if (outMax[i] < (f = __max[i] / byteSize)) outMax[i] = f;
}
else {
outOrOutMin.resize(2);
if (outOrOutMin[0] < (f = __minByte / byteSize)) outOrOutMin[0] = f;
if (outOrOutMin[1] < (f = __maxByte / byteSize)) outOrOutMin[1] = f;
}
if (_maxs[i] > max) max = _maxs[i];
if (_mins[i] > min) min = _mins[i];
}
if (hasOut && outMaxs == null) {
if ((f = min * v) > outOrOutMins[0]) outOrOutMins[0] = f;
if ((f = max * v) > outOrOutMins[1]) outOrOutMins[1] = f;
}
return (max + min) * v;
return (__maxByte + __minByte) / byteSize;
}
function _analyzeRead(b:Int, c:Int) ((b > _maxs[c]) ? (_maxs[c] = b) : (if (-b > _mins[c]) (_mins[c] = -b)));
function __analyzeCallback(b:Int, c:Int):Void
((b > __max[c]) ? (if ((__max[c] = b) > __maxByte) (__maxByte = b)) : (if (-b > __min[c]) (if ((__min[c] = -b) > __minByte) (__minByte = __min[c]))));
function __analyzeCallbackSimple(b:Int, c:Int):Void
((b > __maxByte) ? (__maxByte = b) : (if (-b > __minByte) (__minByte = -b)));
/**
* Gets samples from startPos with given length of samples.
* @param startPos Start Position to get.
* @param timeUnit TimeUnit to use for positions.
* @param length Length of Samples.
* @param mono Merge all of the byte channels of samples in one channel instead (Optional).
* @param channel What channels to get from? (-1 == All Channels, Optional, this will be ignored if mono is enabled).
* @param gain How much gain multiplier will it affect the output. (Optional, default 1.0).
* @param output An Output that gets passed into this function, usually for to avoid memory leaks (Optional).
* @param outputMerge Merge with previous values (Optional, default false).
* @return Output of samples.
* @param startPos Start Position to get from sound in milliseconds.
* @param length Length of Samples.
* @param mono Merge all of the byte channels of samples in one channel instead (Optional).
* @param channel What channels to get from? (-1 == All Channels, Optional, this will be ignored if mono is enabled).
* @param volume How much volume multiplier will it affect the output. (Optional, default 1.0).
* @param output An Output that gets passed into this function, usually for to avoid memory leaks (Optional).
* @param outputMerge Merge with previous values (Optional, default false).
* @return Output of samples.
*/
public function getSamples(startPos:Float, ?timeUnit:TimeUnit, length:Int, mono = true, channel = -1, gain = 1.0, ?output:Array<Float>, ?outputMerge = false):Array<Float> {
_check();
((!mono && channel == -1) ? (_sampleOutputLength = length * data.channels) : (_sampleOutputLength = length));
if (((output == null) ? (_sampleOutput = output = []) : (_sampleOutput = output)).length != _sampleOutputLength) output.resize(_sampleOutputLength);
_sampleValueGain = gain;
_sampleOutputMerge = outputMerge;
_sampleIndex = 0;
_sampleValue = 0;
public function getSamples(startPos:Float, length:Int, mono = true, channel = -1, volume = 1.0, ?output:Array<Float>, ?outputMerge = false):Array<Float> {
((!mono && (__sampleChannel = channel) == -1) ? (__sampleOutputLength = length * buffer.channels) : (__sampleOutputLength = length));
((output == null) ? (__sampleOutput = output = []) : (__sampleOutput = output)).resize(__sampleOutputLength);
((mono) ? (__sampleToValue = volume / (byteSize * buffer.channels)) : (__sampleToValue = 1.0 / byteSize));
__sampleOutputMerge = outputMerge;
__sampleIndex = 0;
final samplePos = Math.floor(switch (timeUnit) {
case SAMPLE: startPos;
case SECOND: startPos * data.sampleRate;
default: startPos * data.sampleRate / 1000;
});
_sampleChannel = mono ? data.channels - 1 : channel;
if (length > 0) _read(samplePos, samplePos + length, mono ? _getSamplesCallbackMono : (channel == -1 ? _getSamplesCallback : _getSamplesCallbackChannel));
__check();
__read(startPos, startPos + (length / __toBits * buffer.channels), mono ? __getSamplesCallbackMono : (channel == -1 ? __getSamplesCallback : __getSamplesCallbackChannel));
_sampleOutput = null;
__sampleOutput = null;
return output;
}
function _getSamplesCallbackMono(b:Int, c:Int):Void if (_sampleIndex < _sampleOutputLength) {
if (c == 0) _sampleValue = idiv(b, data.channels);
else _sampleValue += idiv(b, data.channels);
function __getSamplesCallbackMono(b:Int, c:Int):Void if (__sampleIndex < __sampleOutputLength) {
if (c == 0) {
if (__sampleOutputMerge) __sampleOutput[__sampleIndex] += b * __sampleToValue;
else __sampleOutput[__sampleIndex] = b * __sampleToValue;
}
else if (c == buffer.channels) {
__sampleOutput[__sampleIndex] += b * __sampleToValue;
__sampleIndex++;
}
else
__sampleOutput[__sampleIndex] += b * __sampleToValue;
}
if (c == _sampleChannel) {
if (_sampleOutputMerge) _sampleOutput[_sampleIndex] += _sampleValue / byteSize;
else _sampleOutput[_sampleIndex] = _sampleValue / byteSize;
_sampleIndex++;
function __getSamplesCallbackChannel(b:Int, c:Int):Void if (__sampleIndex < __sampleOutputLength) {
if (c == __sampleChannel) {
if (__sampleOutputMerge) __sampleOutput[__sampleIndex] += b * __sampleToValue;
else __sampleOutput[__sampleIndex] = b * __sampleToValue;
__sampleIndex++;
}
}
function _getSamplesCallbackChannel(b:Int, c:Int):Void if (_sampleIndex < _sampleOutputLength) {
if (c == _sampleChannel) {
if (_sampleOutputMerge) _sampleOutput[_sampleIndex] += b / byteSize;
else _sampleOutput[_sampleIndex] = b / byteSize;
_sampleIndex++;
}
}
function _getSamplesCallback(b:Int, c:Int):Void if (_sampleIndex < _sampleOutputLength) {
if (_sampleOutputMerge) _sampleOutput[_sampleIndex] += b / byteSize;
else _sampleOutput[_sampleIndex] = b / byteSize;
_sampleIndex++;
function __getSamplesCallback(b:Int, c:Int):Void if (__sampleIndex < __sampleOutputLength) {
if (__sampleOutputMerge) __sampleOutput[__sampleIndex] += b * __sampleToValue;
else __sampleOutput[__sampleIndex] = b * __sampleToValue;
__sampleIndex++;
}
/**
* Read an attached sound from startPos to endPos in milliseconds with a callback.
* @param startPos Start Position to get.
* @param endPos End Position to get.
* @param timeUnitTimeUnit to use for positions.
* @param callback Byte:Int->Channels:Int->Void Callback to get the byte of a sample.
* Read an attached FlxSound from startPos to endPos in milliseconds with a callback.
* @param startPos Start Position to get from sound in milliseconds.
* @param endPos End Position to get from sound in milliseconds.
* @param callback Int->Int->Void Byte->Channels->Void Callback to get the byte of a sample.
*/
public function read(startPos:Float, endPos:Float, ?timeUnit:TimeUnit, callback:ReadCallback) {
_check();
var conversion:Float = switch (timeUnit) {
case SAMPLE: 1;
case SECOND: data.sampleRate;
default: data.sampleRate / 1000;
}
if (startPos > endPos) _read(Math.floor(startPos * conversion), Math.floor(endPos * conversion), callback);
public function read(startPos:Float, endPos:Float, callback:AudioAnalyzerCallback) {
__check();
__read(startPos, endPos, callback);
}
function _read(startSample:Int, endSample:Int, callback:ReadCallback) {
// use data in ram if available
if (data.buffer.data != null) _readData(startSample * _sampleSize, endSample * _sampleSize, callback);
// use decoded datas that have been used in streaming sound to reduce jumping disk seeking
// if not use decoder and use seeking instead*
else if (sound.loaded) _readStream(startSample, endSample, callback);
// TODO
//else if ((!sound.loaded || (startSample = _readStream(startSample, endSample, callback)) < endSample) && _prepareDecoder())
// _readDecoder(startSample, endSample, callback);
inline function __read(startPos:Float, endPos:Float, callback:AudioAnalyzerCallback) {
if (buffer.data != null) __readData(startPos, endPos, callback);
#if lime_cffi
else if (__canReadStream() && (startPos += __readStream(startPos, endPos, callback)) >= endPos) {}
#if lime_vorbis
else if (__prepareDecoder()) __readDecoder(startPos, endPos, callback);
#end
#end
}
inline function _readData(startIndex:Int, endIndex:Int, callback:ReadCallback) {
if (endIndex > data.buffer.data.byteLength) endIndex = data.buffer.data.byteLength;
var buffer = data.buffer.data.buffer, byteRate = data.bitsPerSample >> 3, c = 0;
while (startIndex < endIndex) {
callback(getByte(buffer, startIndex, byteRate), c);
startIndex += byteRate;
if (++c == data.channels) c = 0;
inline function __readData(startPos:Float, endPos:Float, callback:AudioAnalyzerCallback) {
var pos = Math.floor(startPos * __toBits), end = Math.min(Math.floor(endPos * __toBits), buffer.data.buffer.length), c = 0;
pos -= pos % __sampleSize;
end -= end % __sampleSize;
while (pos < end) {
callback(getByte(buffer.data.buffer, pos, __wordSize), c);
if (++c > buffer.channels) c = 0;
pos += __wordSize;
}
}
function _readStream(startSample:Int, endSample:Int, callback:ReadCallback):Int @:privateAccess {
final backend = sound.source.__backend;
if (backend.filledBuffers == 0) return startSample;
#if lime_cffi
inline function __canReadStream():Bool
@:privateAccess return sound._source != null && sound._source.__backend != null && sound._source.__backend.playing;
backend.mutex.acquire();
inline function __readStream(startPos:Float, endPos:Float, callback:AudioAnalyzerCallback):Float @:privateAccess {
final backend = sound._source.__backend;
final max = backend.bufferViews.length;
var byteRate = data.bitsPerSample >> 3, i = max - backend.queuedBuffers, buffer:ArrayBuffer, bufferLen:Int, bufferSample:Int, pos:Int, c:Int;
while (i < max && startSample < endSample) {
if (startSample >= (bufferSample = backend.bufferCurs[i])) {
if ((pos = (startSample - bufferSample) * _sampleSize) < (bufferLen = backend.bufferLens[i])) {
buffer = backend.bufferViews[i].buffer;
c = 0;
while (startSample < endSample) {
callback(getByte(buffer, pos, byteRate), c);
if ((pos += byteRate) >= bufferLen) {
startSample++;
break;
}
else if (++c == data.channels) {
c = 0;
startSample++;
}
}
}
// TODO: Wrap it with try until i figured it out an effective way to do this...
// So... sometimes it just uses the decoder even if it looks good?? please help
var n = Math.floor((endPos - startPos) * __toBits);
var i = backend.bufferLengths.length - backend.requestBuffers - 1, time:Float;
while (++i < backend.bufferLengths.length) if (startPos >= (time = backend.bufferTimes[i] * 1000)) {
var pos = Math.floor((startPos - time) * __toBits), buf = backend.bufferDatas[i].buffer, size = backend.bufferLengths[i], c = 0;
while (pos >= size) {
if (++i >= backend.bufferLengths.length) break;
pos -= size;
buf = backend.bufferDatas[i].buffer;
size = backend.bufferLengths[i];
}
i++;
if (i >= backend.bufferLengths.length) break;
if ((pos -= pos % __sampleSize) < 0) pos = 0;
n -= pos % __sampleSize;
while (n > 0) {
callback(getByte(buf, pos, __wordSize), c);
if (++c > buffer.channels) c = 0;
if ((pos += __wordSize) >= size) {
if (++i >= backend.bufferLengths.length) break;
pos = 0;
buf = backend.bufferDatas[i].buffer;
size = backend.bufferLengths[i];
}
n -= __wordSize;
}
break;
}
backend.mutex.release();
return startSample;
return endPos - (n / __toBits);
}
// TODO: Fix this and _readDecoder in the future.
inline function _prepareDecoder():Bool {
return false;
/*
if (_decoder != null) return true;
if (data.decoder != null && (_decoder = data.decoder.clone()) != null) {
_bufferLen = (data.sampleRate >> 2) * _sampleSize;
#if cpp
if (_buffer != null) {
if (_buffer.length < _bufferLen) {
_buffer.getData().resize(_bufferLen);
_buffer.fill(_buffer.length, _bufferLen - _buffer.length, 0);
@:privateAccess _buffer.length = _bufferLen;
}
}
else
#end
_buffer = new ArrayBuffer(_bufferLen);
#if lime_vorbis
inline function __prepareDecoder():Bool @:privateAccess {
if (buffer.__srcVorbisFile == null) return __vorbis != null;
if (__vorbis != null) return true;
if ((__vorbis = buffer.__srcVorbisFile.clone()) != null) { // IM HOPING IT HAVE A GC CLOSURE.
__buffer = new ArrayBuffer(__bufferSize = (buffer.sampleRate >> 1) * __sampleSize); // 0.5 seconds of buffers.
return true;
}
return false;
*/
}
/*
function _readDecoder(startSample:Int, endSample:Int, callback:ReadCallback) {
var pos = (startSample - _bufferLastSample) * _sampleSize, n = endSample - startSample, c = 0;
inline function __readDecoder(startPos:Float, endPos:Float, callback:AudioAnalyzerCallback) {
var n = Math.floor((endPos - startPos) * __toBits);
if ((n -= n % __sampleSize) > 0) {
var pos = Math.floor((startPos - __bufferTime * 1000) * __toBits);
pos -= pos % __sampleSize;
var doDecode = _bufferLastSize == 0 || (pos < 0 && pos >= _bufferLastSize);
if (doDecode) {
_decoder.seek(startSample);
_bufferLastSize = pos = 0;
doDecode = true;
}
var result:Int;
while (n > 0) {
if (doDecode) {
_bufferLastSample = _decoder.tell();
result = _decoder.decode(_buffer, pos, _bufferLen - pos);
if (result == 0) break;
_bufferLastSize += result;
while (n > 0) {
callback(getByte(_buffer, pos, data.byteRate), c);
if (++c == data.channels) {
c = 0;
n--;
}
if ((pos += data.byteRate) >= _bufferLastSize) break;
var doRead = __bufferLastSize == 0 || (pos < 0 && pos >= __bufferSize);
if (doRead) {
if (startPos < 1) {
__vorbis.rawSeek(0);
__bufferTime = 0;
}
else
__vorbis.timeSeek(__bufferTime = startPos / 1000);
__bufferLastSize = pos = 0;
}
else {
while (n > 0) {
callback(getByte(_buffer, pos, data.byteRate), c);
if (++c == data.channels) {
c = 0;
n--;
var isBigEndian = lime.system.System.endianness == lime.system.Endian.BIG_ENDIAN, ranOut = false, c = 0, result;
while (true) {
if (doRead) {
result = __vorbis.read(__buffer, pos, __bufferSize - pos, isBigEndian, __wordSize, true);
if (result == Vorbis.HOLE) continue;
else if (result < 0) break;
else if (!(ranOut = result == 0)) {
__bufferLastTime = __vorbis.timeTell();
__bufferLastSize += result;
while (pos < __bufferLastSize) {
callback(getByte(__buffer, pos, __wordSize), c);
if (++c > buffer.channels) c = 0;
pos += __wordSize;
if ((n -= __wordSize) <= 0) break;
}
}
if ((pos += data.byteRate) >= _bufferLastSize) break;
}
doDecode = true;
_bufferLastSize = pos = 0;
else {
while (pos < __bufferLastSize) {
callback(getByte(__buffer, pos, __wordSize), c);
if (++c > buffer.channels) c = 0;
pos += __wordSize;
if ((n -= __wordSize) <= 0) break;
}
doRead = true;
ranOut = pos >= __bufferSize;
}
if (n <= 0) break;
else if (doRead && ranOut) {
__bufferLastSize = pos = 0;
__bufferTime = __bufferLastTime;
}
}
}
}
*/
static inline function idiv(num:Int, denom:Int):Int return #if (cpp && !cppia) cpp.NativeMath.idiv(num, denom) #else Std.int(num / denom) #end;
}
#end
#end
#end
}
+8 -21
View File
@@ -440,9 +440,11 @@ final class CoolUtil
* @param fadeIn
*/
@:noUsing public static function playMenuSong(fadeIn:Bool = false) {
if (FlxG.sound.music == null || !FlxG.sound.music.playing) {
if (FlxG.sound.music == null || !FlxG.sound.music.playing)
{
playMusic(Paths.music(Flags.DEFAULT_MENU_MUSIC), true, fadeIn ? 0 : 1, true, 102);
if (fadeIn) FlxG.sound.music.fadeIn(4, 0, 1);
if (fadeIn)
FlxG.sound.music.fadeIn(4, 0, 0.7);
}
}
@@ -878,7 +880,7 @@ final class CoolUtil
*/
public static inline function browsePath(path:String) {
var formattedPath:String = Path.normalize(path);
#if windows
formattedPath = formattedPath.replace("/", "\\");
Sys.command("explorer", [formattedPath]);
@@ -1025,20 +1027,6 @@ final class CoolUtil
return result;
}
/**
* Returns if the mouse is overlapping the sprite on a given camera, taking the camera's position, scroll and zoom into account.
*
* @param sprite Any `FlxObject`
* @param camera The camera you want to check overlap on. Uses the sprite's camera by default.
* @return Bool
*/
public static function mouseOverlaps(sprite:FlxObject, ?camera:FlxCamera) {
var camToCheck:FlxCamera = camera ?? sprite.camera;
var posthing:FlxPoint = FlxG.mouse.getWorldPosition(camToCheck);
return posthing != null && FlxMath.inBounds(posthing.x, sprite.x, sprite.x + sprite.width) && FlxMath.inBounds(posthing.y, sprite.y, sprite.y + sprite.height);
}
/**
* Sorts an array alphabetically.
* @param array Array to sort
@@ -1089,7 +1077,7 @@ final class CoolUtil
r.add(str);
return r.toString();
}
public static inline function bound(Value:Float, Min:Float, Max:Float):Float {
#if cpp
var _hx_tmp1:Float = Value;
@@ -1236,8 +1224,8 @@ final class CoolUtil
/**
* ! REQUIRES FULL PATH!!!
* @param path
* @return Bool
* @param path
* @return Bool
*/
public static function imageHasFrameData(path:String):String {
if (FileSystem.exists(Path.withExtension(path, "xml"))) return "xml";
@@ -1454,7 +1442,6 @@ final class CoolUtil
return toProperty.setValue(fromProperty.getValue());
}
}
class PropertyInfo {
+1 -33
View File
@@ -3,8 +3,6 @@ package funkin.backend.utils;
import haxe.macro.Expr;
final class MathUtil {
public static inline var EULER:Float = 2.718281828459;
/**
* Returns the maximum value in the arguments.
* @param args Array of values
@@ -153,36 +151,6 @@ final class MathUtil {
return Math.abs(a - b) > margin;
}
/**
* @param edge0 Float
* @param edge1 Float
* @param x Float
* @return Float
**/
public static function smoothStep(edge0:Float, edge1:Float, x:Float):Float {
var t = (x - edge0) / (edge1 - edge0);
var clamped = t < 0.0 ? 0.0 : (t > 1.0 ? 1.0 : t);
return clamped * clamped * (3.0 - 2.0 * clamped);
}
/**
* @param a Float
* @param b Float
* @param v Float
* @return Float
**/
public static function inverseLerp(a:Float, b:Float, v:Float):Float {
return (v - a) / (b - a);
}
/**
* @param v Float
* @return Float
**/
public static function fract(v:Float):Float {
return v - Math.floor(v);
}
/**
* Shortcut to `Math.max` but with infinite amount of arguments
*
@@ -231,4 +199,4 @@ final class MathUtil {
return macro $expr;
}
#end
}
}
+16 -1
View File
@@ -13,6 +13,12 @@ import flixel.util.FlxColor;
* Some functions might not have effect on some platforms.
*/
class NativeAPI {
@:dox(hide) public static function registerAudio() {
#if windows
Windows.registerAudio();
#end
}
@:dox(hide) public static function registerAsDPICompatible() {
#if windows
Windows.registerAsDPICompatible();
@@ -179,7 +185,16 @@ class NativeAPI {
public static function setConsoleColors(foregroundColor:ConsoleColor = NONE, ?backgroundColor:ConsoleColor = NONE) {
if(Main.noTerminalColor) return;
#if sys
#if (windows && !hl)
if(foregroundColor == NONE)
foregroundColor = LIGHTGRAY;
if(backgroundColor == NONE)
backgroundColor = BLACK;
var fg:Int = cast foregroundColor;
var bg:Int = cast backgroundColor;
Windows.setConsoleColors((bg * 16) + fg);
#elseif sys
Sys.print("\x1b[0m");
if(foregroundColor != NONE)
Sys.print("\x1b[" + Std.int(consoleColorToANSI(foregroundColor)) + "m");
+111 -97
View File
@@ -2,143 +2,157 @@ package funkin.backend.utils;
#if sys
import haxe.io.Input;
import haxe.zip.Entry;
import haxe.zip.InflateImpl;
import haxe.zip.Reader;
import sys.io.File;
import sys.io.FileInput;
import haxe.io.Bytes;
/**
* Class that extends Reader allowing you to load ZIP entries without blowing your RAM up!!
* ~~Half of the code is taken from haxe libraries btw~~ Reworked by ItsLJcool to actually work for zip files.
* Half of the code is taken from haxe libraries btw
*/
class SysZip {
class SysZip extends Reader {
var input:Input;
var fileInput:FileInput;
var filePath:String;
public var entries:List<SysZipEntry> = new List();
public var entries:List<SysZipEntry>;
/**
* Opens a zip from a specified path.
* @param path Path to the zip file. (With the extension)
* @param path Path to the zip file.
*/
public static function openFromFile(path:String) { return new SysZip(path); } // keeping for compatibility.
public static function openFromFile(path:String) {
return new SysZip(File.read(path, true));
}
/**
* Creates a new SysZip from a specified path.
* @param path Path to the zip file. (With the extension)
* Creates a new SysZip from a specified file input.
* @param input File input.
*/
public function new(path:String) {
this.filePath = path;
fileInput = File.read(path, true);
public function new(input:FileInput) {
super(input);
fileInput = input;
}
updateEntries(); // automatic but if you feel like you don't want it to be automatic, you can remove this.
/**
* Reads all the data present in a specified entry.
* NOTE: If the entry is compressed, the data won't be decompressed. For decompression, use `unzipEntry`.
* @param e Entry
*/
public function readEntryData(e:SysZipEntry) {
var bytes:haxe.io.Bytes = null;
var buf = null;
var tmp = null;
fileInput.seek(e.seekPos, SeekBegin);
if (e.crc32 == null) {
if (e.compressed) {
#if neko
// enter progressive mode : we use a different input which has
// a temporary buffer, this is necessary since we have to uncompress
// progressively, and after that we might have pending read data
// that needs to be processed
var bufSize = 65536;
if (buf == null) {
buf = new haxe.io.BufferInput(i, haxe.io.Bytes.alloc(bufSize));
tmp = haxe.io.Bytes.alloc(bufSize);
i = buf;
}
var out = new haxe.io.BytesBuffer();
var z = new neko.zip.Uncompress(-15);
z.setFlushMode(neko.zip.Flush.SYNC);
while (true) {
if (buf.available == 0)
buf.refill();
var p = bufSize - buf.available;
if (p != buf.pos) {
// because of lack of "srcLen" in zip api, we need to always be stuck to the buffer end
buf.buf.blit(p, buf.buf, buf.pos, buf.available);
buf.pos = p;
}
var r = z.execute(buf.buf, buf.pos, tmp, 0);
out.addBytes(tmp, 0, r.write);
buf.pos += r.read;
buf.available -= r.read;
if (r.done)
break;
}
bytes = out.getBytes();
#else
var bufSize = 65536;
if (tmp == null)
tmp = haxe.io.Bytes.alloc(bufSize);
var out = new haxe.io.BytesBuffer();
var z = new InflateImpl(i, false, false);
while (true) {
var n = z.readBytes(tmp, 0, bufSize);
out.addBytes(tmp, 0, n);
if (n < bufSize)
break;
}
bytes = out.getBytes();
#end
} else
bytes = i.read(e.dataSize);
e.crc32 = i.readInt32();
if (e.crc32 == 0x08074b50)
e.crc32 = i.readInt32();
e.dataSize = i.readInt32();
e.fileSize = i.readInt32();
// set data to uncompressed
e.dataSize = e.fileSize;
e.compressed = false;
} else
bytes = i.read(e.dataSize);
return bytes;
}
/**
* Unzips and returns all of the data present in an entry.
* @param f Entry to read from.
*/
public function unzipEntry(f:SysZipEntry):Bytes {
if (f.fileSize <= 0) return Bytes.alloc(0);
fileInput.seek(f.seekPos, SeekBegin);
var data = fileInput.read(f.compressedSize);
if (!f.compressed) return data;
public function unzipEntry(f:SysZipEntry) {
var data = readEntryData(f);
if (!f.compressed)
return data;
var c = new haxe.zip.Uncompress(-15);
var s = Bytes.alloc(f.fileSize);
var s = haxe.io.Bytes.alloc(f.fileSize);
var r = c.execute(data, 0, s, 0);
c.close();
if (!r.done || r.read != data.length || r.write != f.fileSize) throw 'Invalid compressed data for ${f.fileName} | ${f.compressedSize} -> ${f.fileSize}';
return s;
if (!r.done || r.read != data.length || r.write != f.fileSize)
throw "Invalid compressed data for " + f.fileName;
data = s;
return data;
}
/**
* Updates the `entries` list with the current contents of the zip file.
* This is done when the zip is read from SysZip the first time, but if you REALLY need to re-update the entries, you can call this again.
*
* Note: Calling this function will hold up the game as it has to read the ENTIRE zip, so if it's large like 1GiB or more, it might take a second or more.
*/
public function updateEntries() {
if (entries.length > 0) {
entries.clear();
entries = new List();
}
// --- locate End of Central Directory (EOCD) ---
var fileSize:Int = sys.FileSystem.stat(this.filePath).size; // probably need a better way to check the size of the file.
var scanSize:Int = (65535 < fileSize) ? 65535 : fileSize;
// It seems this usually ends up being 0 anyways, but for cases where it might not be?? I'd just make sure. but Someone do some digging I don't know if this required.
fileInput.seek(fileSize - scanSize, SeekBegin);
var buf = fileInput.read(scanSize);
var b = new haxe.io.BytesInput(buf);
// I LOVE USING MAGIC NUMBERS AND FORGETTING WHAT THEY DO 🔥🔥🔥🔥🔥🔥
b.position = (buf.length - 22) + 16; // offset to start of central directory
// --- read central directory ---
fileInput.seek(b.readInt32(), SeekBegin);
public override function read():List<Entry> {
if (entries != null)
return entries;
entries = new List();
while (true) {
if (fileInput.readInt32() != 0x02014b50) break; // central dir file header signature
var e = readEntryHeader();
if (e == null)
break;
fileInput.seek(6, SeekCur); // version/flags
var compression_method = fileInput.readUInt16();
fileInput.seek(8, SeekCur); // time/date + CRC32 (4, 4)
var compressed_size = fileInput.readInt32();
var uncompressed_size = fileInput.readInt32();
var nameLen = fileInput.readUInt16();
var extraLen = fileInput.readUInt16();
var commentLen = fileInput.readUInt16();
fileInput.seek(8, SeekCur); // skip disk number/start attrs
var localHeaderOffset = fileInput.readInt32();
var name = fileInput.read(nameLen).toString();
// skip central directory extra/comment
fileInput.seek(extraLen + commentLen, SeekCur);
// --- compute correct seekPos using local header ---
var curPos = fileInput.tell();
// I also forgor what the `+ 26` is for, so uh my b chat
fileInput.seek(localHeaderOffset + 26, SeekBegin);
var localNameLen = fileInput.readUInt16();
var localExtraLen = fileInput.readUInt16();
fileInput.seek(curPos, SeekBegin);
// I completely forgot that we don't really need to log the FOLDER of the content because we only care about where the contents are.
// the folders are labled as 0 bytes anyways so this will save on storing non-required data.
if (name.endsWith("/")) continue;
var zipEntry:SysZipEntry = {
fileName: name,
fileSize: uncompressed_size,
// I don't remember what the `+ 30` is for, but probably to offset something
seekPos: (localHeaderOffset + 30 + localNameLen + localExtraLen),
compressedSize: compressed_size,
compressed: (compression_method == 8),
};
var zipEntry:SysZipEntry = cast e;
zipEntry.seekPos = fileInput.tell();
entries.add(zipEntry);
fileInput.seek(e.dataSize, SeekCur);
}
return entries;
}
/**
* calling `dispose` doesn't actually kill the class, you can still access the entries.
* disposing of SysZip will free the compressed file from being used by the engine.
*/
public function dispose() {
if (fileInput != null) fileInput.close();
if (input != null)
input.close();
}
}
typedef SysZipEntry = {
var fileName:String;
var fileSize:Int;
> Entry,
var seekPos:Int;
var compressedSize:Int;
var compressed:Bool;
}
#end
@@ -27,10 +27,107 @@ import funkin.backend.utils.NativeAPI.MessageBoxIcon;
#include <wingdi.h>
#include <shellapi.h>
#include <uxtheme.h>
#define SAFE_RELEASE(punk) \\
if ((punk) != NULL) \\
{ (punk)->Release(); (punk) = NULL; }
static long lastDefId = 0;
class AudioFixClient : public IMMNotificationClient {
LONG _cRef;
IMMDeviceEnumerator *_pEnumerator;
public:
AudioFixClient() :
_cRef(1),
_pEnumerator(NULL)
{
HRESULT result = CoCreateInstance(__uuidof(MMDeviceEnumerator),
NULL, CLSCTX_INPROC_SERVER,
__uuidof(IMMDeviceEnumerator),
(void**)&_pEnumerator);
if (result == S_OK) {
_pEnumerator->RegisterEndpointNotificationCallback(this);
}
}
~AudioFixClient()
{
SAFE_RELEASE(_pEnumerator);
}
ULONG STDMETHODCALLTYPE AddRef()
{
return InterlockedIncrement(&_cRef);
}
ULONG STDMETHODCALLTYPE Release()
{
ULONG ulRef = InterlockedDecrement(&_cRef);
if (0 == ulRef)
{
delete this;
}
return ulRef;
}
HRESULT STDMETHODCALLTYPE QueryInterface(
REFIID riid, VOID **ppvInterface)
{
return S_OK;
}
HRESULT STDMETHODCALLTYPE OnDeviceAdded(LPCWSTR pwstrDeviceId)
{
return S_OK;
};
HRESULT STDMETHODCALLTYPE OnDeviceRemoved(LPCWSTR pwstrDeviceId)
{
return S_OK;
}
HRESULT STDMETHODCALLTYPE OnDeviceStateChanged(
LPCWSTR pwstrDeviceId,
DWORD dwNewState)
{
return S_OK;
}
HRESULT STDMETHODCALLTYPE OnPropertyValueChanged(
LPCWSTR pwstrDeviceId,
const PROPERTYKEY key)
{
return S_OK;
}
HRESULT STDMETHODCALLTYPE OnDefaultDeviceChanged(
EDataFlow flow, ERole role,
LPCWSTR pwstrDeviceId)
{
::funkin::backend::_hx_system::Main_obj::audioDisconnected = true;
return S_OK;
};
};
AudioFixClient *curAudioFix;
')
@:dox(hide)
final class Windows {
public static var __audioChangeCallback:Void->Void = function() {
trace("test");
};
@:functionCode('
if (!curAudioFix) curAudioFix = new AudioFixClient();
')
public static function registerAudio() {
funkin.backend.system.Main.audioDisconnected = false;
}
@:functionCode('
int darkMode = enable ? 1 : 0;
-17
View File
@@ -20,8 +20,6 @@ class EditorTreeMenu extends funkin.options.TreeMenu {
bg.antialiasing = true;
setBackgroundRotation(-5);
super.createPost();
if (Paths.assetsTree.hasCompressedLibrary) warnCompressLibrary();
}
public inline function setBackgroundRotation(rotation:Float) {
@@ -60,21 +58,6 @@ class EditorTreeMenu extends funkin.options.TreeMenu {
bg.colorTransform.greenMultiplier = FlxMath.lerp(1, color.greenFloat, 0.25);
bg.colorTransform.blueMultiplier = FlxMath.lerp(1, color.blueFloat, 0.25);
}
private function warnCompressLibrary() {
var warningMessage = "It seems you have libraries loaded that are compressed, and can not have files written to them.\n
This is just a friendly reminder that if you're loading a Mod and wish to edit files, you need to uncompress it to be able to use any editors!\n\nCompressed Libraries: ";
var compressedList = Paths.assetsTree.libraries.filter(l -> funkin.backend.assets.AssetsLibraryList.getCleanLibrary(l).isCompressed);
var modNameList = [for (l in compressedList) {
l = funkin.backend.assets.AssetsLibraryList.getCleanLibrary(l);
if (l is funkin.backend.assets.IModsAssetLibrary) cast(l, funkin.backend.assets.IModsAssetLibrary).modName;
}];
warningMessage += modNameList.join(", ");
var zipLibraryWarning = new funkin.editors.ui.UIWarningSubstate("Compressed Library Detected!", warningMessage, [{label: "Ok", color: 0x969533, onClick: (state) -> {} }], false);
openSubState(zipLibraryWarning);
}
}
class EditorTreeMenuScreen extends funkin.options.TreeMenuScreen {
-11
View File
@@ -2,9 +2,6 @@ package funkin.editors;
import haxe.io.Path;
import lime.ui.FileDialog;
#if lime_funkin
import lime.ui.FileDialogFilter;
#end
class SaveSubstate extends MusicBeatSubstate {
public var saveOptions:Map<String, Bool>;
@@ -29,13 +26,6 @@ class SaveSubstate extends MusicBeatSubstate {
public override function create() {
super.create();
#if lime_funkin
FileDialog.saveFile(FlxG.stage.window, "Save File", (fileName:String, activeFilter:FileDialogFilter) -> {
CoolUtil.safeSaveFile(fileName, data);
close();
}, [new FileDialogFilter("Specified File Extension", options.saveExt.getDefault(Path.extension(options.defaultSaveFile)))],
options.defaultSaveFile);
#else
var fileDialog = new FileDialog();
fileDialog.onCancel.add(function() close());
fileDialog.onSelect.add(function(str) {
@@ -43,7 +33,6 @@ class SaveSubstate extends MusicBeatSubstate {
close();
});
fileDialog.browse(SAVE, options.saveExt.getDefault(Path.extension(options.defaultSaveFile)), options.defaultSaveFile);
#end
}
public override function update(elapsed:Float) {
@@ -403,12 +403,12 @@ class CharacterAnimButton extends UIButton {
public function toggleGhost() {
if (valid && parent.ghosts.indexOf(anim) == -1) {
UIState.playEditorSound(Flags.DEFAULT_CHARACTER_GHOSTENABLE_SOUND);
FlxG.sound.play(Paths.sound(Flags.DEFAULT_CHARACTER_GHOSTENABLE_SOUND));
parent.ghosts.push(anim);
ghostIcon.animation.play("alive", true);
ghostIcon.color = 0xFFFFFFFF;
} else {
UIState.playEditorSound(Flags.DEFAULT_CHARACTER_GHOSTDISABLE_SOUND);
FlxG.sound.play(Paths.sound(Flags.DEFAULT_CHARACTER_GHOSTDISABLE_SOUND));
parent.ghosts.remove(anim);
ghostIcon.animation.play("dead", true);
ghostIcon.color = 0xFFADADAD;
@@ -119,7 +119,7 @@ class CharacterAnimsWindow extends UIButtonList<CharacterAnimButton> {
}
public function deleteAnimation(button:CharacterAnimButton, addToUndo:Bool = true) {
UIState.playEditorSound(Flags.DEFAULT_EDITOR_DELETE_SOUND);
FlxG.sound.play(Paths.sound(Flags.DEFAULT_EDITOR_DELETE_SOUND));
if (buttons.members.length <= 1) return;
if (character.getAnimName() == button.anim)
@:privateAccess CharacterEditor.instance._animation_down(null);
@@ -453,7 +453,7 @@ class CharacterEditor extends UIState {
function _file_save(_) {
#if sys
UIState.playEditorSound(Flags.DEFAULT_EDITOR_SAVE_SOUND);
FlxG.sound.play(Paths.sound('editors/save'));
CoolUtil.safeSaveFile(
'${Paths.getAssetsRoot()}/data/characters/${character.curCharacter}.xml',
buildCharacter()
@@ -465,7 +465,7 @@ class CharacterEditor extends UIState {
}
function _file_saveas(_) {
UIState.playEditorSound(Flags.DEFAULT_EDITOR_SAVE_SOUND);
FlxG.sound.play(Paths.sound('editors/save'));
openSubState(new SaveSubstate(buildCharacter(), {
defaultSaveFile: '${character.curCharacter}.xml'
}));
@@ -499,7 +499,7 @@ class CharacterEditor extends UIState {
}
function _undo(undo:CharacterEditorChange) {
UIState.playEditorSound(Flags.DEFAULT_EDITOR_UNDO_SOUND);
FlxG.sound.play(Paths.sound(Flags.DEFAULT_EDITOR_UNDO_SOUND));
switch (undo) {
case null: // do nothing
case CCharEditPosition(oldPos, newPos):
@@ -573,7 +573,7 @@ class CharacterEditor extends UIState {
}
function _redo(redo:CharacterEditorChange) {
UIState.playEditorSound(Flags.DEFAULT_EDITOR_REDO_SOUND);
FlxG.sound.play(Paths.sound(Flags.DEFAULT_EDITOR_REDO_SOUND));
switch (redo) {
case null: // do nothing
case CCharEditPosition(oldPos, newPos):
+66 -228
View File
@@ -77,10 +77,10 @@ class Charter extends UIState {
public var strumlineLockButton:CharterStrumlineButton;
public var hitsound:FlxSound;
public var hitsoundGlobalVolume:Float = 1.0;
public var metronome:FlxSound;
public var vocals:FlxSound;
public var voicesMuted:Bool = false;
public var quant:Int = 16;
public var quants:Array<Int> = [4, 8, 12, 16, 20, 24, 32, 48, 64, 192]; // different quants
@@ -96,7 +96,6 @@ class Charter extends UIState {
public var rightEventRowText:UIText;
public var leftEventsGroup:CharterEventGroup = new CharterEventGroup();
public var rightEventsGroup:CharterEventGroup = new CharterEventGroup();
public var cameraMovementChanges:Array<CameraChange> = [];
public var charterCamera:FlxCamera;
public var uiCamera:FlxCamera;
@@ -232,11 +231,6 @@ class Charter extends UIState {
label: translate("edit.delete"),
keybind: [DELETE],
onSelect: _edit_delete
},
{
label: translate("edit.deletestacked"),
keybind: [SHIFT,DELETE],
onSelect: _edit_deletestacked
}
]
},
@@ -314,11 +308,6 @@ class Charter extends UIState {
onSelect: _view_showeventBeatSeparator,
icon: Options.charterShowBeats ? 1 : 0
},
{
label: translate("view.showCameraHighlights"),
onSelect: _view_showeventCameraHighlights,
icon: Options.charterShowCameraHighlights ? 1 : 0
},
null,
{
label: translate("view.rainbowWaveforms"),
@@ -464,7 +453,7 @@ class Charter extends UIState {
rightEventsGroup.eventsRowText = rightEventRowText;
// thank you neo for pointing out im stupid -lunar
// this is future lunar i completely forgot what neo pointed out but hes awesome go follow him on twitter
// this is future lunar i completely forgot what neo pointed out but hes awesome go follow him on twitter
add(gridBackdropDummy = new CameraHoverDummy(gridBackdrops, FlxPoint.weak(1, 0)));
selectionBox = new UISliceSprite(0, 0, 2, 2, 'editors/ui/selection');
@@ -526,7 +515,7 @@ class Charter extends UIState {
strumlineLockButton = new CharterStrumlineButton("editors/charter/lock-strumline", translate("lock-unlock"));
strumlineLockButton.onClick = function () {
UIState.playEditorSound(!strumLines.draggable ? Flags.DEFAULT_CHARTER_STRUMUNLOCK_SOUND : Flags.DEFAULT_CHARTER_STRUMLOCK_SOUND);
FlxG.sound.play(Paths.sound(!strumLines.draggable ? Flags.DEFAULT_CHARTER_STRUMUNLOCK_SOUND : Flags.DEFAULT_CHARTER_STRUMLOCK_SOUND));
if (strumLines != null) {
strumLines.draggable = !strumLines.draggable;
strumlineLockButton.textTweenColor.color = strumLines.draggable ? 0xFF5C95CA : 0xFFE16565;
@@ -696,7 +685,6 @@ class Charter extends UIState {
CharterGridSeperatorBase.lastConductorSprY = Math.NEGATIVE_INFINITY;
updateWaveforms();
updateCameraChanges();
}
public function getWavesToGenerate():Array<{name:String, sound:FlxSound}> {
@@ -752,38 +740,6 @@ class Charter extends UIState {
}
}
public function updateCameraChanges() {
if (!Options.charterShowCameraHighlights) return;
cameraMovementChanges = [];
for (grp in [leftEventsGroup, rightEventsGroup]) {
grp.filterEvents();
grp.sortEvents();
for(e in grp.members) {
for(event in e.events) {
if (event.name == "Camera Movement") {
cameraMovementChanges.push({
strumLineID: event.params[0],
step: e.step,
endStep: __endStep
});
}
}
}
}
//need to sort again for both local and global events to be used
cameraMovementChanges.sort(function(e1, e2) {
return FlxSort.byValues(FlxSort.ASCENDING, e1.step, e2.step);
});
//update previous change
if (cameraMovementChanges.length > 0) {
for (i in 1...cameraMovementChanges.length) {
cameraMovementChanges[i-1].endStep = cameraMovementChanges[i].step;
}
}
}
public override function beatHit(curBeat:Int) {
super.beatHit(curBeat);
if (FlxG.sound.music.playing) {
@@ -801,7 +757,6 @@ class Charter extends UIState {
public var mousePos:FlxPoint = new FlxPoint();
public var selectionDragging:Bool = false;
public var isSelecting:Bool = false;
public var isAltCopyDrag:Bool = false;
public function updateSelectionLogic() {
function select(s:ICharterSelectable) {
@@ -847,7 +802,7 @@ class Charter extends UIState {
else
Chart.save(PlayState.SONG, __diff.toLowerCase(), __variant, {saveMetaInChart: true, saveLocalEvents: true, seperateGlobalEvents: true, prettyPrint: Options.editorCharterPrettyPrint});
UIState.playEditorSound(Flags.DEFAULT_EDITOR_SAVE_SOUND);
FlxG.sound.play(Paths.sound('editors/save'));
undos.save();
}
autoSaveNotif.cancelled = false;
@@ -966,24 +921,17 @@ class Charter extends UIState {
if (!(verticalChange == 0 && horizontalChange == 0)) {
notesGroup.sortNotes();
var changes:Array<CharterChange> = [];
if (isAltCopyDrag) {
changes.push(CCreateSelection(selection.copy()));
isAltCopyDrag = false;
}
changes.push(CSelectionDrag(undoDrags));
changes.push(updateEventsGroups(selection));
undos.addToUndo(CChangeBundle(changes));
} else if (isAltCopyDrag) {
undos.addToUndo(CCreateSelection(selection.copy()));
isAltCopyDrag = false;
undos.addToUndo(CChangeBundle([
CSelectionDrag(undoDrags),
updateEventsGroups(selection)
]));
}
gridActionType = NONE;
currentCursor = ARROW;
}
case NONE:
if (FlxG.mouse.justPressed)
if (FlxG.mouse.justPressed)
FlxG.mouse.getWorldPosition(charterCamera, dragStartPos);
else if (FlxG.mouse.justPressedRight) {
closeCurrentContextMenu();
@@ -1014,7 +962,7 @@ class Charter extends UIState {
notesGroup.add(note);
selection = [note];
undos.addToUndo(CCreateSelection([note]));
UIState.playEditorSound(Flags.DEFAULT_CHARTER_NOTEPLACE_SOUND);
FlxG.sound.play(Paths.sound(Flags.DEFAULT_CHARTER_NOTEPLACE_SOUND));
}
isSelecting = false;
}
@@ -1031,32 +979,7 @@ class Charter extends UIState {
}
if ((Math.abs(mousePos.x - dragStartPos.x) > (noteSusDrag ? 1 : 5) || Math.abs(mousePos.y - dragStartPos.y) > (noteSusDrag ? 1 : 5))) {
if (noteHovered) {
if (FlxG.keys.pressed.ALT && selection.length > 0) {
var newSelection:Array<ICharterSelectable> = [];
for (s in selection) {
if (s is CharterNote) {
var n:CharterNote = cast s;
var newNote = new CharterNote();
newNote.updatePos(n.step, n.id, n.susLength, n.type, n.strumLine);
notesGroup.add(newNote);
newSelection.push(newNote);
} else if (s is CharterEvent) {
var e:CharterEvent = cast s;
var newEvent = new CharterEvent(e.step, [for (event in e.events) Reflect.copy(event)], e.global);
newEvent.refreshEventIcons();
(e.global ? rightEventsGroup : leftEventsGroup).add(newEvent);
newSelection.push(newEvent);
}
}
for (s in selection) s.selected = false;
selection = newSelection;
for (s in selection) s.selected = true;
isAltCopyDrag = true;
UIState.playEditorSound(Flags.DEFAULT_EDITOR_COPY_SOUND);
}
gridActionType = NOTE_DRAG;
}
if (noteHovered) gridActionType = noteHovered ? NOTE_DRAG : INVALID_DRAG;
if (noteSusDrag) gridActionType = SUSTAIN_DRAG;
}
}
@@ -1168,7 +1091,7 @@ class Charter extends UIState {
if (selected == null) return selected;
if (selected is CharterNote) {
UIState.playEditorSound(Flags.DEFAULT_CHARTER_NOTEDELETE_SOUND);
FlxG.sound.play(Paths.sound(Flags.DEFAULT_CHARTER_NOTEDELETE_SOUND));
var note:CharterNote = cast selected;
note.strumLineID = strumLines.members.indexOf(note.strumLine);
note.strumLine = null; // For static undos :D
@@ -1648,20 +1571,20 @@ class Charter extends UIState {
else {undos = null; FlxG.switchState(new CharterSelection()); Charter.instance.__clearStatics();}
}
function _file_save_all(_) {saveEverything(); UIState.playEditorSound(Flags.DEFAULT_EDITOR_SAVE_SOUND);}
function _file_save(_) {saveChart(); UIState.playEditorSound(Flags.DEFAULT_EDITOR_SAVE_SOUND);}
function _file_saveas(_) {saveChartAs(); UIState.playEditorSound(Flags.DEFAULT_EDITOR_SAVE_SOUND);}
function _file_events_save(_) {saveEvents(); UIState.playEditorSound(Flags.DEFAULT_EDITOR_SAVE_SOUND);}
function _file_events_saveas(_) {saveEventsAs(); UIState.playEditorSound(Flags.DEFAULT_EDITOR_SAVE_SOUND);}
function _file_save_no_events(_) {saveChart(true, false); UIState.playEditorSound(Flags.DEFAULT_EDITOR_SAVE_SOUND);}
function _file_saveas_no_events(_) {saveChartAs(true, false); UIState.playEditorSound(Flags.DEFAULT_EDITOR_SAVE_SOUND);}
function _file_meta_save(_) {saveMeta(); UIState.playEditorSound(Flags.DEFAULT_EDITOR_SAVE_SOUND);}
function _file_meta_saveas(_) {saveMetaAs(); UIState.playEditorSound(Flags.DEFAULT_EDITOR_SAVE_SOUND);}
function _file_saveas_fnflegacy(_) {saveLegacyChartAs(); UIState.playEditorSound(Flags.DEFAULT_EDITOR_SAVE_SOUND);}
function _file_saveas_psych(_) {savePsychChartAs(); UIState.playEditorSound(Flags.DEFAULT_EDITOR_SAVE_SOUND);}
function _file_save_all(_) {saveEverything(); FlxG.sound.play(Paths.sound('editors/save'));}
function _file_save(_) {saveChart(); FlxG.sound.play(Paths.sound('editors/save'));}
function _file_saveas(_) {saveChartAs(); FlxG.sound.play(Paths.sound('editors/save'));}
function _file_events_save(_) {saveEvents(); FlxG.sound.play(Paths.sound('editors/save'));}
function _file_events_saveas(_) {saveEventsAs(); FlxG.sound.play(Paths.sound('editors/save'));}
function _file_save_no_events(_) {saveChart(true, false); FlxG.sound.play(Paths.sound('editors/save'));}
function _file_saveas_no_events(_) {saveChartAs(true, false); FlxG.sound.play(Paths.sound('editors/save'));}
function _file_meta_save(_) {saveMeta(); FlxG.sound.play(Paths.sound('editors/save'));}
function _file_meta_saveas(_) {saveMetaAs(); FlxG.sound.play(Paths.sound('editors/save'));}
function _file_saveas_fnflegacy(_) {saveLegacyChartAs(); FlxG.sound.play(Paths.sound('editors/save'));}
function _file_saveas_psych(_) {savePsychChartAs(); FlxG.sound.play(Paths.sound('editors/save'));}
function _edit_copy(_, playSFX=true) {
if (playSFX) UIState.playEditorSound(Flags.DEFAULT_EDITOR_COPY_SOUND);
if (playSFX) FlxG.sound.play(Paths.sound(Flags.DEFAULT_EDITOR_COPY_SOUND));
if(selection.length == 0) return;
var minStep:Float = selection[0].step;
@@ -1680,7 +1603,7 @@ class Charter extends UIState {
];
}
function _edit_paste(_) {
UIState.playEditorSound(Flags.DEFAULT_EDITOR_PASTE_SOUND);
FlxG.sound.play(Paths.sound(Flags.DEFAULT_EDITOR_PASTE_SOUND));
if (clipboard.length <= 0) return;
var minStep = curStep;
@@ -1708,7 +1631,7 @@ class Charter extends UIState {
}
function _edit_cut(_) {
UIState.playEditorSound(Flags.DEFAULT_EDITOR_CUT_SOUND);
FlxG.sound.play(Paths.sound(Flags.DEFAULT_EDITOR_CUT_SOUND));
if (selection == null || selection.length == 0) return;
_edit_copy(_, false);
@@ -1716,7 +1639,7 @@ class Charter extends UIState {
}
function _edit_delete(_) {
UIState.playEditorSound(Flags.DEFAULT_EDITOR_DELETE_SOUND);
FlxG.sound.play(Paths.sound(Flags.DEFAULT_EDITOR_DELETE_SOUND));
if (selection == null || selection.length == 0) return;
selection.loop((n:CharterNote) -> {
noteDeleteAnims.deleteNotes.push({note: n, time: noteDeleteAnims.deleteTime});
@@ -1724,27 +1647,8 @@ class Charter extends UIState {
selection = deleteSelection(selection, true);
}
function _edit_deletestacked(_) {
UIState.playEditorSound(Flags.DEFAULT_EDITOR_DELETE_SOUND);
if (notesGroup.members.length == 0) return;
var oldNote:CharterNote = null;
var selectionArray:Array<Dynamic> = ((selection.length != 0) ? selection : notesGroup.members.copy());
var toDelete:Selection = new Selection();
for (note in selectionArray) {
if (oldNote != null && oldNote.step == note.step && oldNote.strumLineID == note.strumLineID && oldNote.id == note.id) {
noteDeleteAnims.deleteNotes.push({note: oldNote, time: noteDeleteAnims.deleteTime});
toDelete.push(oldNote);
}
oldNote = note;
}
if (toDelete.length != 0) {
deleteSelection(toDelete);
if (selection.length != 0) for (i in toDelete) selection.remove(i); //crash prevention
}
}
function _undo(undo:CharterChange) {
UIState.playEditorSound(Flags.DEFAULT_EDITOR_UNDO_SOUND);
FlxG.sound.play(Paths.sound(Flags.DEFAULT_EDITOR_UNDO_SOUND));
switch(undo) {
case null: // do nothing
case CDeleteStrumLine(strumLineID, strumLine):
@@ -1793,11 +1697,7 @@ class Charter extends UIState {
case CEditSpecNotesType(notes, oldTypes, newTypes):
for(i=>note in notes) note.updatePos(note.step, note.id, note.susLength, oldTypes[i]);
case CChangeBundle(changes):
var i = changes.length - 1;
while (i >= 0) {
_undo(changes[i]);
i--;
}
for (change in changes) _undo(change);
}
}
@@ -1809,7 +1709,7 @@ class Charter extends UIState {
}
function _redo(redo:CharterChange) {
UIState.playEditorSound(Flags.DEFAULT_EDITOR_REDO_SOUND);
FlxG.sound.play(Paths.sound(Flags.DEFAULT_EDITOR_REDO_SOUND));
switch(redo) {
case null: // do nothing
case CDeleteStrumLine(strumLineID, strumLine):
@@ -1908,24 +1808,14 @@ class Charter extends UIState {
function _playback_metronome(t) {
t.icon = (Options.charterMetronomeEnabled = !Options.charterMetronomeEnabled) ? 1 : 0;
}
public function _slider_mutetoggle(t:UIContextMenuOption) {
if (t.slider == null) return;
t.button.slider.value = t.button.slider.value > 0 ? 0 : 1;
function _song_muteinst(t) {
FlxG.sound.music.volume = FlxG.sound.music.volume > 0 ? 0 : 1;
t.icon = 1 - Std.int(Math.ceil(FlxG.sound.music.volume));
}
function _song_instvolume(t) {
FlxG.sound.music.volume = t.slider.value;
t.icon = t.slider.value > 0.5 ? 7 : (t.slider.value > 0 ? 8 : 9);
}
function _song_voicesvolume(t) {
vocals.volume = t.slider.value;
for (strumLine in strumLines.members) strumLine.vocals.volume = t.slider.value * strumLine.vocalsVolume;
t.icon = t.slider.value > 0.5 ? 7 : (t.slider.value > 0 ? 8 : 9);
}
function _song_hitsoundvolume(t) {
hitsoundGlobalVolume = t.slider.value;
t.icon = t.slider.value > 0.5 ? 7 : (t.slider.value > 0 ? 8 : 9);
function _song_mutevoices(t) {
vocals.volume = (voicesMuted = !voicesMuted) ? 0 : 1;
for (strumLine in strumLines.members) strumLine.updateVoicesVolume();
t.icon = voicesMuted ? 1 : 0;
}
function _playback_back(_) {
if (FlxG.sound.music.playing) return;
@@ -1970,7 +1860,6 @@ class Charter extends UIState {
__event.refreshEventIcons();
(__event.global ? rightEventsGroup : leftEventsGroup).add(__event);
undos.addToUndo(CEditEvent(__event, [], __event.events));
updateCameraChanges();
}
public function getBookmarkList():Array<ChartBookmark> {
@@ -1979,7 +1868,7 @@ class Charter extends UIState {
if (PlayState.SONG.bookmarks != null)
bookmarks = PlayState.SONG.bookmarks;
} catch (e) {}
return bookmarks;
}
@@ -1989,9 +1878,9 @@ class Charter extends UIState {
var currentBookmarks:Array<ChartBookmark> = getBookmarkList();
var newBookmarks:Array<ChartBookmark> = getBookmarkList();
newBookmarks.push({time: daStep, name: name, color: color.toWebString()});
PlayState.SONG.bookmarks = newBookmarks;
updateBookmarks();
updateBookmarks();
undos.addToUndo(CEditBookmarks(currentBookmarks, newBookmarks));
}
@@ -2016,7 +1905,7 @@ class Charter extends UIState {
{
var bars:Array<FlxSprite> = bs[0];
var text:UIText = bs[1];
if (bars != null) {
for (spr in bars) {
if (spr == null) continue;
@@ -2070,7 +1959,7 @@ class Charter extends UIState {
0,
scrollBar.height
);
var bookmarkspr = new FlxSprite(scrollBar.x - 10, yPos).makeSolid(40, 4, bookmarkcolor);
uiGroup.add(bookmarkspr);
sprites.push(bookmarkspr);
@@ -2122,56 +2011,26 @@ class Charter extends UIState {
if (bookmarks.length > 0)
{
var bookmarkOptions:Array<UIContextMenuOption> = [];
var goToBookmark = TU.getRaw("charter.bookmarks.goTo");
for (b in bookmarks)
{
bookmarkOptions.push({
newChilds.push({
label: goToBookmark.format([b.name]),
onSelect: function(_) { Conductor.songPosition = Conductor.getTimeForStep(b.time); }
});
}
newChilds.push({
label: translate("bookmarks.bookmarkList"),
childs: bookmarkOptions
});
newChilds.push(null);
}
newChilds.push({
label: translate("song.inst"),
slider: {
min: 0,
max: 1,
value: 1,
onChange: _song_instvolume
},
onIconClick: _slider_mutetoggle,
icon: 7
label: translate("song.muteInst"),
onSelect: _song_muteinst
});
newChilds.push({
label: translate("song.voices"),
slider: {
min: 0,
max: 1,
value: 1,
onChange: _song_voicesvolume
},
onIconClick: _slider_mutetoggle,
icon: 7
});
newChilds.push({
label: translate("song.hitsounds"),
slider: {
min: 0,
max: 1,
value: 1,
onChange: _song_hitsoundvolume
},
onIconClick: _slider_mutetoggle,
icon: 7
label: translate("song.muteVoices"),
onSelect: _song_mutevoices
});
if (songTopButton != null) songTopButton.contextMenu = newChilds;
@@ -2196,10 +2055,6 @@ class Charter extends UIState {
function _view_showeventBeatSeparator(t) {
t.icon = (Options.charterShowBeats = !Options.charterShowBeats) ? 1 : 0;
}
function _view_showeventCameraHighlights(t) {
t.icon = (Options.charterShowCameraHighlights = !Options.charterShowCameraHighlights) ? 1 : 0;
updateCameraChanges();
}
function _view_switchWaveformRainbow(t) {
t.icon = (Options.charterRainbowWaveforms = !Options.charterRainbowWaveforms) ? 1 : 0;
@@ -2225,8 +2080,8 @@ class Charter extends UIState {
inline function _snap_decreasesnap(_) changequant(-1);
inline function _snap_resetsnap(_) setquant(16);
inline function changequant(change:Int) {UIState.playEditorSound(Flags.DEFAULT_CHARTER_SNAPPINGCHANGE_SOUND); quant = quants[FlxMath.wrap(quants.indexOf(quant) + change, 0, quants.length-1)]; buildSnapsUI();};
inline function setquant(newQuant:Int) {UIState.playEditorSound(Flags.DEFAULT_CHARTER_SNAPPINGCHANGE_SOUND); quant = newQuant; buildSnapsUI();}
inline function changequant(change:Int) {FlxG.sound.play(Paths.sound(Flags.DEFAULT_CHARTER_SNAPPINGCHANGE_SOUND)); quant = quants[FlxMath.wrap(quants.indexOf(quant) + change, 0, quants.length-1)]; buildSnapsUI();};
inline function setquant(newQuant:Int) {FlxG.sound.play(Paths.sound(Flags.DEFAULT_CHARTER_SNAPPINGCHANGE_SOUND)); quant = newQuant; buildSnapsUI();}
function buildSnapsUI():Array<UIContextMenuOption> {
var snapsTopButton:UITopMenuButton = topMenuSpr == null ? null : cast topMenuSpr.members[snapIndex];
@@ -2262,12 +2117,12 @@ class Charter extends UIState {
}
inline function _note_addsustain(t) {
UIState.playEditorSound(Flags.DEFAULT_CHARTER_SUSTAINADD_SOUND);
FlxG.sound.play(Paths.sound(Flags.DEFAULT_CHARTER_SUSTAINADD_SOUND));
changeNoteSustain(1);
}
inline function _note_subtractsustain(t) {
UIState.playEditorSound(Flags.DEFAULT_CHARTER_SUSTAINDELETE_SOUND);
FlxG.sound.play(Paths.sound(Flags.DEFAULT_CHARTER_SUSTAINDELETE_SOUND));
changeNoteSustain(-1);
}
@@ -2344,18 +2199,16 @@ class Charter extends UIState {
keybind: [CONTROL, SHIFT, A],
onSelect: _note_selectmeasure
},
null
null,
{
label: "(0) " + translate("noteTypes.default"),
keybind: [ZERO],
onSelect: (_) -> {changeNoteType(0);},
icon: this.noteType == 0 ? 1 : 0
}
];
var noteTypeOptions:Array<UIContextMenuOption> = [{
label: "(0) " + translate("noteTypes.default"),
keybind: [ZERO],
onSelect: (_) -> {changeNoteType(0);},
icon: this.noteType == 0 ? 1 : 0
}];
final noteKeys:Array<Array<Array<FlxKey>>> = [[[ZERO], [NUMPADZERO]], [[ONE], [NUMPADONE]], [[TWO], [NUMPADTWO]], [[THREE], [NUMPADTHREE]], [[FOUR], [NUMPADFOUR]], [[FIVE], [NUMPADFIVE]],
[[SIX], [NUMPADSIX]], [[SEVEN], [NUMPADSEVEN]], [[EIGHT], [NUMPADEIGHT]], [[NINE], [NUMPADNINE]]];
var noteKeys:Array<FlxKey> = [ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN, EIGHT, NINE];
for (i=>type in noteTypes) {
var realNoteID:Int = i+1; // Default Note not stored
var newChild:UIContextMenuOption = {
@@ -2364,13 +2217,9 @@ class Charter extends UIState {
onSelect: (_) -> {changeNoteType(realNoteID);},
icon: this.noteType == realNoteID ? 1 : 0
};
if (realNoteID <= 9) newChild.keybinds = noteKeys[realNoteID];
noteTypeOptions.push(newChild);
if (realNoteID <= 9) newChild.keybind = [noteKeys[realNoteID]];
newChilds.push(newChild);
}
newChilds.push({
label: translate("note.noteTypesList"),
childs: noteTypeOptions
});
newChilds.push({
label: translate("note.editNoteTypesList"),
color: 0xFF959829, icon: 4,
@@ -2378,7 +2227,6 @@ class Charter extends UIState {
onSelect: editNoteTypesList
});
if (noteTopButton != null) noteTopButton.contextMenu = newChilds;
if (topMenu != null && topMenu[noteIndex] != null) topMenu[noteIndex].childs = newChilds;
return newChilds;
}
@@ -2466,12 +2314,8 @@ class Charter extends UIState {
}
}
public inline function playHitsound(id:Int) {
if (strumLines.members[id] != null && strumLines.members[id].hitsoundVolume > 0 && hitsoundGlobalVolume > 0) {
hitsound.volume = hitsoundGlobalVolume * strumLines.members[id].hitsoundVolume;
hitsound.replay();
}
}
public inline function hitsoundsEnabled(id:Int)
return strumLines.members[id] != null && strumLines.members[id].hitsounds;
public inline function __fixSelection(selection:Selection):Selection {
var newSelection:Selection = new Selection();
@@ -2562,7 +2406,7 @@ class Charter extends UIState {
quantSelected: quant,
noteTypeSelected: noteType,
strumlinesDraggable: strumLines.draggable,
hitSounds: [for (strumLine in strumLines.members) strumLine.hitsoundVolume > 0],
hitSounds: [for (strumLine in strumLines.members) strumLine.hitsounds],
mutedVocals: [for (strumLine in strumLines.members) !(strumLine.vocals.volume > 0)],
waveforms: [for (strumLine in strumLines.members) strumLine.selectedWaveform]
}
@@ -2578,7 +2422,7 @@ class Charter extends UIState {
strumLines.draggable = playtestInfo.strumlinesDraggable;
for (i => strumLine in strumLines.members)
strumLine.hitsoundVolume = playtestInfo.hitSounds[i] ? 1 : 0;
strumLine.hitsounds = playtestInfo.hitSounds[i];
for (i => strumLine in strumLines.members)
strumLine.vocals.volume = playtestInfo.mutedVocals[i] ? 0 : 1;
for (i => strumLine in strumLines.members)
@@ -2670,9 +2514,3 @@ typedef PlaytestInfo = {
var mutedVocals:Array<Bool>;
var waveforms:Array<Int>;
}
typedef CameraChange = {
var strumLineID:Int;
var step:Float;
var endStep:Float;
}
@@ -82,7 +82,7 @@ class CharterAutoSaveUI extends UISliceSprite {
icon.animation.curAnim.curFrame = 1;
for (member in [this, progressBar, progressBarBack, autosavingText]) member.color = 0xFFA3EC95;
UIState.playEditorSound(Flags.DEFAULT_EDITOR_AUTOSAVE_SOUND);
FlxG.sound.play(Paths.sound(Flags.DEFAULT_EDITOR_AUTOSAVE_SOUND));
(new FlxTimer()).start(1, (_) -> {disappearAnimation();});
});
@@ -178,8 +178,6 @@ class CharterBackdrop extends FlxTypedGroup<FlxBasic> {
public var gridShader:CustomShader = new CustomShader("engine/charterGrid");
var __lastKeyCount:Int = 4;
public var cameraHighlight:CameraHighlight;
public function new() {
super();
@@ -189,10 +187,6 @@ class CharterBackdrop extends FlxTypedGroup<FlxBasic> {
add(gridBackDrop);
gridShader.hset("segments", 4);
cameraHighlight = new CameraHighlight(this);
cameraHighlight.makeSolid(1, 1, 0xFFFFFFFF);
add(cameraHighlight);
waveformSprite = new FlxSprite().makeSolid(1, 1, 0xFF000000);
waveformSprite.scale.set(160, 1);
waveformSprite.updateHitbox();
@@ -253,11 +247,10 @@ class CharterBackdrop extends FlxTypedGroup<FlxBasic> {
x = strumLine.x;
alpha = strumLine.strumLine.visible ? 0.9 : 0.4;
keyCount = strumLine.keyCount;
cameraHighlight.color = strumLine.highlightColor;
} else alpha = 0.9;
for (spr in [gridBackDrop, beatSeparator, topLimit, bottomLimit,
topSeparator, bottomSeparator, conductorFollowerSpr, waveformSprite, cameraHighlight]) {
topSeparator, bottomSeparator, conductorFollowerSpr, waveformSprite]) {
spr.x = x; if (spr != waveformSprite) spr.alpha = alpha;
spr.cameras = this.cameras;
}
@@ -279,9 +272,6 @@ class CharterBackdrop extends FlxTypedGroup<FlxBasic> {
spr.updateHitbox();
}
cameraHighlight.scale.x = (keyCount * 40)-2;
cameraHighlight.x += 1;
waveformSprite.visible = waveformSprite.shader != null;
if (waveformSprite.shader == null) return;
@@ -321,19 +311,16 @@ class CharterGridSeperatorBase extends FlxSprite {
private static var lastMaxMeasure:Float = -1;
public static var lastConductorSprY:Float = Math.NEGATIVE_INFINITY;
public static var lastCameraZoom:Float = -1;
private static var beatStepTimes:Array<Float> = [];
private static var measureStepTimes:Array<Float> = [];
private static var timeSignatureChangeGaps:Array<Float> = [];
private function recalculateBeats() {
@:privateAccess
var currentZoom = Charter.instance.__camZoom;
var conductorSprY = Charter.instance.gridBackdrops.conductorSprY;
if (conductorSprY == lastConductorSprY && currentZoom == lastCameraZoom) return; //only update if song pos or camera zoom has changed
if (conductorSprY == lastConductorSprY) return;
var zoomOffset = ((FlxG.height * (1/currentZoom)) * 0.5);
var zoomOffset = ((FlxG.height * (1/cameras[0].zoom)) * 0.5);
minStep = (conductorSprY - zoomOffset)/40;
maxStep = (conductorSprY + zoomOffset)/40;
@@ -372,7 +359,6 @@ class CharterGridSeperatorBase extends FlxSprite {
}
lastConductorSprY = conductorSprY;
lastCameraZoom = currentZoom;
}
private inline function calculateTimeSignatureGaps() {
@@ -460,77 +446,6 @@ class CharterGridSeperator extends CharterGridSeperatorBase {
}
}
class CameraHighlight extends FlxSprite {
private var grid:CharterBackdrop;
private var _currentCameraMovementIndex:Int = 0;
public function new(grid:CharterBackdrop) {
this.grid = grid;
super();
}
override public function draw() {
if (!Options.charterShowCameraHighlights || grid.strumLine == null) return;
@:privateAccess
var minStep = CharterGridSeperatorBase.minStep;
@:privateAccess
var maxStep = CharterGridSeperatorBase.maxStep;
@:privateAccess
var strumLineID = Charter.instance.strumLines.isDragging ? Charter.instance.strumLines.__pastStrumlines.indexOf(grid.strumLine) : Charter.instance.gridBackdrops.members.indexOf(grid);
//first default camera change
if ((Charter.instance.cameraMovementChanges[0] == null || Charter.instance.cameraMovementChanges[0].step != 0) && strumLineID == 0) {
var endStep = Charter.instance.cameraMovementChanges[0] != null ? Charter.instance.cameraMovementChanges[0].step : Charter.instance.__endStep;
if (endStep >= minStep) {
setupHighlight(0, endStep); super.draw();
setupLine(endStep); super.draw();
}
}
//update index if gone backwards
while(_currentCameraMovementIndex > 0) {
if (Charter.instance.cameraMovementChanges[_currentCameraMovementIndex] == null || Charter.instance.cameraMovementChanges[_currentCameraMovementIndex].endStep >= minStep) {
_currentCameraMovementIndex--;
} else {
break;
}
}
var seenFirstVisible = false;
for (i in _currentCameraMovementIndex...Charter.instance.cameraMovementChanges.length) {
var change = Charter.instance.cameraMovementChanges[i];
if (change.endStep >= minStep) {
if (!seenFirstVisible) {
_currentCameraMovementIndex = i; //remember the index for the next frame, so we dont need to loop through everything every time
seenFirstVisible = true;
}
if (change.strumLineID == strumLineID) {
setupHighlight(change.step, change.endStep); super.draw();
setupLine(change.step); super.draw();
setupLine(change.endStep); super.draw();
}
}
if (change.endStep > maxStep) break;
}
}
private inline function setupHighlight(step:Float, endStep:Float) {
y = step * 40;
alpha = 0.15;
scale.y = (endStep -step) * 40;
updateHitbox();
}
private inline function setupLine(step:Float) {
y = (step * 40)-1;
alpha = 0.8;
scale.y = 2;
updateHitbox();
}
}
class EventBackdrop extends FlxBackdrop {
public var eventBeatSeparator:CharterEventGridSeperator;
+2 -3
View File
@@ -161,9 +161,8 @@ class CharterNote extends UISprite implements ICharterSelectable {
}
if (__passed != (__passed = step < Conductor.curStepFloat + (Options.songOffsetAffectEditors ? (Conductor.songOffset / Conductor.stepCrochet) : 0))) {
if (__passed && FlxG.sound.music.playing) {
Charter.instance.playHitsound(strumLineID);
}
if (__passed && FlxG.sound.music.playing && Charter.instance.hitsoundsEnabled(strumLineID))
Charter.instance.hitsound.replay();
}
if (strumLine != null) {
@@ -21,7 +21,7 @@ class CharterPreviewStrumLine extends FlxTypedGroup<FlxSprite>
for (i in 0...keyCount){
var strum = new FlxSprite();
strum.frames = Paths.getFrames("game/notes/default");
strum.setGraphicSize(Std.int((strum.width * Flags.DEFAULT_NOTE_SCALE) * scale));
strum.setGraphicSize(Std.int((strum.width * 0.7) * scale));
strum.updateHitbox();
var animPrefix = strumAnimPrefix[i % 4];
@@ -33,7 +33,7 @@ class CharterPreviewStrumLine extends FlxTypedGroup<FlxSprite>
note = new FlxSprite();
note.frames = Paths.getFrames("game/notes/default");
note.setGraphicSize(Std.int((note.width * Flags.DEFAULT_NOTE_SCALE) * scale));
note.setGraphicSize(Std.int((note.width * 0.7) * scale));
note.updateHitbox();
note.animation.addByPrefix('purple', 'purple0');
note.animation.play('purple');
@@ -41,7 +41,7 @@ class CharterPreviewStrumLine extends FlxTypedGroup<FlxSprite>
add(note);
}
var noteTime:Float = FlxG.initialHeight;
var noteTime:Float = FlxG.height;
var scroll:Float = 1.0;
public function updatePos(x:Float, y:Float, scale:Float, spacing:Float, keyCount:Int, scrollSpeed:Float){
@@ -53,7 +53,7 @@ class CharterPreviewStrumLine extends FlxTypedGroup<FlxSprite>
strum.x = CoolUtil.fpsLerp(strum.x, x + (Note.swagWidth * scale * spacing * i), 0.2);
strum.y = CoolUtil.fpsLerp(strum.y, y + (Note.swagWidth*0.5) - (Note.swagWidth * scale * 0.5), 0.2);
strum.scale.x = strum.scale.y = CoolUtil.fpsLerp(strum.scale.x, Flags.DEFAULT_NOTE_SCALE * scale, 0.2);
strum.scale.x = strum.scale.y = CoolUtil.fpsLerp(strum.scale.x, 0.7 * scale, 0.2);
strum.updateHitbox();
}
@@ -61,7 +61,7 @@ class CharterPreviewStrumLine extends FlxTypedGroup<FlxSprite>
scroll = CoolUtil.fpsLerp(scroll, scrollSpeed, 0.2);
noteTime -= FlxG.elapsed * scroll * 1000 * 0.45;
if (noteTime <= 0.0)
noteTime = FlxG.initialHeight;
noteTime = FlxG.height;
note.x = members[0].x;
note.y = members[0].y + noteTime;
@@ -146,10 +146,11 @@ class CharterSelectionScreen extends EditorTreeMenuScreen {
var screen = parent.tree.last();
var idx = 0;
while (!(screen.members[idx] is Separator || idx >= screen.members.length)) idx++;
while (!(screen.members[idx] is Separator)) idx++;
screen.insert(idx, makeChartOption(name, curSong.variant != null && curSong.variant != "" ? curSong.variant : null, curSong.name));
// Add to Meta
var metaPath = '$songFolder/meta${curSong.variant != null && curSong.variant != "" ? "-" + curSong.variant : ""}.json';
var metaPath = '$songFolder/meta${curSong.variant != null && curSong.variant == "" ? "-" + curSong.variant : ""}.json';
CoolUtil.safeSaveFile(metaPath, Chart.makeMetaSaveable(curSong));
}
#end
@@ -105,7 +105,6 @@ class CharterStrumLineGroup extends FlxTypedGroup<CharterStrumline> {
draggingObj = null;
fixEvents();
refreshStrumlineIDs();
Charter.instance.updateCameraChanges();
}
public inline function fixEvents() {
@@ -1,7 +1,5 @@
package funkin.editors.charter;
import funkin.editors.ui.UIContextMenu.UIContextMenuOption;
import flixel.util.FlxColor;
import flixel.group.FlxSpriteGroup;
import flixel.sound.FlxSound;
import funkin.backend.chart.ChartData.ChartStrumLine;
@@ -12,11 +10,11 @@ import funkin.game.HealthIcon;
class CharterStrumline extends UISprite {
public var strumLine:ChartStrumLine;
public var hitsounds:Bool = true;
public var draggingSprite:UISprite;
public var healthIcons:FlxSpriteGroup;
public var button:CharterStrumlineOptions;
public var highlightColor:FlxColor;
public var draggable:Bool = false;
public var dragging:Bool = false;
@@ -24,9 +22,7 @@ class CharterStrumline extends UISprite {
public var curMenu:UIContextMenu = null;
public var vocals:FlxSound;
public var hasVocals:Bool = false;
public var vocalsVolume:Float = 1;
public var hitsoundVolume:Float = 1;
public var voicesMuted:Bool = false;
public var keyCount:Int = 4;
public var startingID(get, null):Int;
@@ -146,22 +142,15 @@ class CharterStrumline extends UISprite {
if (asset != null) {
vocals.reset();
vocals.loadEmbedded(asset);
hasVocals = true;
}
else {
vocals.destroy();
hasVocals = false;
}
vocals.group = FlxG.sound.defaultMusicGroup;
}
highlightColor = 0xFFFFFFFF;
if (icons[0] != null) {
var characterXML = Character.getXMLFromCharName(icons[0]);
if (characterXML != null && characterXML.x.exists("color")) highlightColor = FlxColor.fromString(characterXML.x.get("color"));
//make darker colors more visible for the highlight
highlightColor.brightness = Math.max(highlightColor.brightness, 0.65);
}
public function updateVoicesVolume() {
vocals.volume = (voicesMuted || (Charter.instance?.voicesMuted ?? false)) ? 0 : 1;
}
}
@@ -184,17 +173,18 @@ class CharterStrumlineOptions extends UITopMenuButton {
contextMenu = [
{
label: TU.translate("charter.strumLine.hitsounds"),
slider: {
min: 0,
max: 1,
value: strLine.hitsoundVolume,
onChange: function(t) {
strLine.hitsoundVolume = t.slider.value;
t.icon = t.slider.value > 0.5 ? 7 : (t.slider.value > 0 ? 8 : 9);
}
onSelect: function(_) {
strLine.hitsounds = !strLine.hitsounds;
},
onIconClick: Charter.instance._slider_mutetoggle,
icon: 7
icon: strLine.hitsounds ? 1 : 0
},
{
label: TU.translate("charter.strumLine.muteVocals"),
onSelect: function(_) {
strLine.voicesMuted = !strLine.voicesMuted;
strLine.updateVoicesVolume();
},
icon: strLine.voicesMuted ? 1 : 0
},
null,
{
@@ -215,44 +205,20 @@ class CharterStrumlineOptions extends UITopMenuButton {
}
];
if (strLine.hasVocals) {
contextMenu.insert(1, {
label: TU.translate("charter.strumLine.vocals"),
slider: {
min: 0,
max: 1,
value: strLine.vocalsVolume,
onChange: function(t) {
strLine.vocalsVolume = t.slider.value;
strLine.vocals.volume = Charter.instance.vocals.volume * strLine.vocalsVolume;
t.icon = t.slider.value > 0.5 ? 7 : (t.slider.value > 0 ? 8 : 9);
}
},
onIconClick: Charter.instance._slider_mutetoggle,
icon: 7
});
}
var waveformOptions:Array<UIContextMenuOption> = [
{
label: TU.translate("charter.strumLine.noWaveform"),
onSelect: function(_) {strLine.selectedWaveform = -1;},
icon: strLine.selectedWaveform == -1 ? 1 : 0
}
];
contextMenu.insert(0, {
label: TU.translate("charter.strumLine.noWaveform"),
onSelect: function(_) {strLine.selectedWaveform = -1;},
icon: strLine.selectedWaveform == -1 ? 1 : 0
});
for (i => name in Charter.waveformHandler.waveformList)
waveformOptions.push({
contextMenu.insert(1+i, {
label: name,
onSelect: function(_) {strLine.selectedWaveform = i;},
icon: strLine.selectedWaveform == i ? 6 : 5
});
contextMenu.insert(0, {
label: TU.translate("charter.strumLine.waveforms"),
childs: waveformOptions
});
contextMenu.insert(1, null);
contextMenu.insert(1+Charter.waveformHandler.waveformList.length, null);
var cam = Charter.instance.charterCamera;
var point = CoolUtil.worldToScreenPosition(this, cam);
@@ -33,8 +33,6 @@ class CharterStrumlineScreen extends UISubstateWindow {
public var strumLineCam:HudCamera;
public var previewStrumLine:CharterPreviewStrumLine;
public var previewBorder:Array<FlxSprite> = [];
private final borderGap:Int = 5;
private var onSave:ChartStrumLine -> Void = null;
@@ -178,30 +176,20 @@ class CharterStrumlineScreen extends UISubstateWindow {
addLabelOn(vocalsSuffixDropDown, TU.translate("charterStrumLine.vocalSuffix"));
keyCountStepper = new UINumericStepper(stagePositionDropdown.x, vocalsSuffixDropDown.y, strumLine.keyCount != null ? strumLine.keyCount : 4, 1, 0, 1, 1000, 84);
add(keyCountStepper);
addLabelOn(keyCountStepper, TU.translate("charterStrumLine.keyCount"));
// if (Flags.CHARTER_ADVANCED_SETTINGS) {
add(keyCountStepper);
addLabelOn(keyCountStepper, TU.translate("charterStrumLine.keyCount"));
// }
strumLineCam = new HudCamera();
strumLineCam.downscroll = Options.downscroll;
strumLineCam.bgColor = 0;
strumLineCam.alpha = 0;
FlxG.cameras.add(strumLineCam, false);
updateStrumlineCam(FlxG.width, FlxG.height);
previewStrumLine = new CharterPreviewStrumLine(0, 0, 0, 1, 4, 0);
previewStrumLine.camera = strumLineCam;
add(previewStrumLine);
FlxTween.tween(strumLineCam, {alpha: 1}, 0.25, {ease: FlxEase.cubeOut});
//preview border
previewBorder.push(new FlxSprite(-borderGap, -borderGap).makeSolid(FlxG.initialWidth + (borderGap*2), borderGap, 0x88FFFFFF));
previewBorder.push(new FlxSprite(-borderGap, 0).makeSolid(borderGap, FlxG.initialHeight, 0x88FFFFFF));
previewBorder.push(new FlxSprite(FlxG.initialWidth, 0).makeSolid(borderGap, FlxG.initialHeight, 0x88FFFFFF));
previewBorder.push(new FlxSprite(-borderGap, FlxG.initialHeight).makeSolid(FlxG.initialWidth + (borderGap*2), borderGap, 0x88FFFFFF));
for (border in previewBorder) {
border.camera = strumLineCam;
add(border);
}
}
function saveStrumline() {
@@ -234,7 +222,7 @@ class CharterStrumlineScreen extends UISubstateWindow {
previewStrumLine.visible = visibleCheckbox.checked;
var xOffset:Float = StrumLine.calculateStartingXPosFromInitialWidth(hudXStepper.value, hudScaleStepper.value, hudSpacingStepper.value, Std.int(keyCountStepper.value));
var xOffset:Float = StrumLine.calculateStartingXPos(hudXStepper.value, hudScaleStepper.value, hudSpacingStepper.value, Std.int(keyCountStepper.value));
previewStrumLine.updatePos(xOffset, hudYStepper.value, hudScaleStepper.value, hudSpacingStepper.value, Std.int(keyCountStepper.value), scrollSpeed);
super.update(elapsed);
@@ -245,26 +233,6 @@ class CharterStrumlineScreen extends UISubstateWindow {
FlxTween.cancelTweensOf(strumLineCam);
FlxG.cameras.remove(strumLineCam);
}
public override function onResize(width:Int, height:Int) {
super.onResize(width, height);
if (!UIState.resolutionAware) return;
if ((width < FlxG.initialWidth || height < FlxG.initialHeight) && !Options.bypassEditorsResize) {
width = FlxG.initialWidth; height = FlxG.initialHeight;
}
updateStrumlineCam(width, height);
}
private function updateStrumlineCam(windowWidth:Int, windowHeight:Int) {
strumLineCam.width = FlxG.initialWidth + (borderGap*2);
strumLineCam.height = FlxG.initialHeight + (borderGap*2);
strumLineCam.x = (windowWidth/2) - (strumLineCam.width/2);
strumLineCam.y = (windowHeight/2) - (strumLineCam.height/2);
strumLineCam.scroll.x = -borderGap;
strumLineCam.scroll.y = -borderGap;
}
}
class CharacterButton extends UIButton {
+4 -4
View File
@@ -508,7 +508,7 @@ class StageEditor extends UIState {
function _file_save(_) {
#if sys
UIState.playEditorSound(Flags.DEFAULT_EDITOR_SAVE_SOUND);
FlxG.sound.play(Paths.sound('editors/save'));
CoolUtil.safeSaveFile(
'${Paths.getAssetsRoot()}/data/stages/${__stage}.xml',
buildStage()
@@ -520,7 +520,7 @@ class StageEditor extends UIState {
}
function _file_saveas(_) {
UIState.playEditorSound(Flags.DEFAULT_EDITOR_SAVE_SOUND);
FlxG.sound.play(Paths.sound('editors/save'));
openSubState(new SaveSubstate(buildStage(), {
defaultSaveFile: '${__stage}.xml'
}));
@@ -730,7 +730,7 @@ class StageEditor extends UIState {
}
function _edit_undo(_) {
UIState.playEditorSound(Flags.DEFAULT_EDITOR_UNDO_SOUND);
FlxG.sound.play(Flags.DEFAULT_EDITOR_UNDO_SOUND);
var undo = undos.undo();
switch(undo) {
case null:
@@ -756,7 +756,7 @@ class StageEditor extends UIState {
}
function _edit_redo(_) {
UIState.playEditorSound(Flags.DEFAULT_EDITOR_REDO_SOUND);
FlxG.sound.play(Paths.sound(Flags.DEFAULT_EDITOR_REDO_SOUND));
var redo = undos.redo();
switch(redo) {
case null:
+1 -1
View File
@@ -30,7 +30,7 @@ class UIButton extends UISliceSprite {
super.onHovered();
if (FlxG.mouse.justPressed) {
hasBeenPressed = true;
UIState.playEditorSound(Flags.DEFAULT_EDITOR_BUTTONCLICK_SOUND);
FlxG.sound.play(Paths.sound(Flags.DEFAULT_EDITOR_BUTTONCLICK_SOUND));
}
if (FlxG.mouse.justReleased && callback != null && shouldPress && hasBeenPressed) {
callback();
+35 -229
View File
@@ -16,12 +16,6 @@ class UIContextMenu extends MusicBeatSubstate {
public var contextMenuOptions:Array<UIContextMenuOptionSpr> = [];
public var separators:Array<FlxSprite> = [];
public var childContextMenu:UIContextMenu = null;
public var parentContextMenu:UIContextMenu = null;
private var childContextMenuOptionIndex:Int = -1;
@:allow(funkin.editors.ui.UIContextMenu)
private var lastHoveredOptionIndex:Int = -1;
var scroll:Float = 0.0;
var flipped:Bool = false;
@@ -102,10 +96,6 @@ class UIContextMenu extends MusicBeatSubstate {
for(o in separators)
o.x -= bg.bWidth;
}
for(o in contextMenuOptions) {
o.postCreate();
}
}
public function select(option:UIContextMenuOption) {
@@ -115,12 +105,12 @@ class UIContextMenu extends MusicBeatSubstate {
if (callback != null)
callback(this, index, option);
if (option.closeOnSelect == null ? true : option.closeOnSelect)
closeWithParents();
close();
}
public override function update(elapsed:Float) {
if (__oobDeletion && FlxG.mouse.justPressed && !bg.hoveredByChild && !hoveringAnyChildren())
closeWithParents();
if (__oobDeletion && FlxG.mouse.justPressed && !bg.hoveredByChild)
close();
__oobDeletion = true;
@@ -131,14 +121,6 @@ class UIContextMenu extends MusicBeatSubstate {
contextCam.scroll.y = CoolUtil.fpsLerp(contextCam.scroll.y, scroll, 0.5);
contextCam.alpha = CoolUtil.fpsLerp(contextCam.alpha, 1, 0.25);
if (parentContextMenu != null) {
if (hoveringAnyParents() && parentContextMenu.lastHoveredOptionIndex != parentContextMenu.childContextMenuOptionIndex) {
closeWithChildren();
parentContextMenu.childContextMenuOptionIndex = -1;
}
}
}
public override function destroy() {
@@ -147,56 +129,6 @@ class UIContextMenu extends MusicBeatSubstate {
if (UIState.state.curContextMenu == this)
UIState.state.curContextMenu = null;
}
public function openChildContextMenu(optionSpr:UIContextMenuOptionSpr) {
var index = contextMenuOptions.indexOf(optionSpr);
if (index != childContextMenuOptionIndex) {
childContextMenuOptionIndex = index;
var child = new UIContextMenu(optionSpr.option.childs, null, optionSpr.x + optionSpr.bWidth + 4, optionSpr.y - 4);
persistentDraw = true;
persistentUpdate = true;
child.parentContextMenu = this;
childContextMenu = child;
openSubState(child);
}
}
public function closeWithParents() {
close();
if (parentContextMenu != null) {
parentContextMenu.closeWithParents();
}
}
public function closeWithChildren() {
if (childContextMenu != null) {
childContextMenu.closeWithChildren();
}
close();
}
public function hoveringAnyParents() {
if (parentContextMenu != null) {
return parentContextMenu.bg.hoveredByChild || parentContextMenu.hoveringAnyParents();
}
return false;
}
public function hoveringAnyChildren() {
if (childContextMenu != null) {
return childContextMenu.bg.hoveredByChild || childContextMenu.hoveringAnyChildren();
}
return false;
}
}
typedef UIContextMenuSliderOptionData = {
var min:Float;
var max:Float;
var value:Float;
var ?onChange:UIContextMenuOption->Void;
//default = 120, ignored if sameLine = false
var ?width:Float;
//disables stepper and text if false, default = false
var ?showValues:Bool;
//if true, the slider will be on the same line as the label text, otherwise it will be on the next line below the label
var ?sameLine:Bool;
}
typedef UIContextMenuCallback = UIContextMenu->Int->UIContextMenuOption->Void;
@@ -212,24 +144,13 @@ typedef UIContextMenuOption = {
var ?button:UIContextMenuOptionSpr;
var ?onCreate:UIContextMenuOptionSpr->Void;
var ?childs:Array<UIContextMenuOption>;
var ?slider:UIContextMenuSliderOptionData;
var ?onIconClick:UIContextMenuOption->Void;
}
enum abstract UIContextMenuOptionType(Int) from Int {
var DEFAULT = 0;
var SUBMENU = 1;
var SLIDER = 2;
}
class UIContextMenuOptionSpr extends UISliceSprite {
public var label:UIText;
public var labelKeybind:UIText;
public var icon:UIContextMenuOptionIcon;
public var icon:FlxSprite;
public var option:UIContextMenuOption;
public var optionType:UIContextMenuOptionType = DEFAULT;
public var slider:UISlider = null;
var parent:UIContextMenu;
@@ -239,96 +160,40 @@ class UIContextMenuOptionSpr extends UISliceSprite {
this.parent = parent;
this.color = option.color;
var w:Int = label.frameWidth + 22;
var h:Int = label.frameHeight;
if (option.childs != null) optionType = SUBMENU;
if (option.slider != null) optionType = SLIDER;
switch(optionType) {
case SUBMENU:
labelKeybind = new UIText(label.x + label.frameWidth + 10, 2, 0, ">");
case SLIDER:
labelKeybind = new UIText(label.x + label.frameWidth + 10, 2, 0, "");
//slider needs to be created after so that it can match the menu width (when not on the same line)
if (option.slider.sameLine != null && option.slider.sameLine) {
var sliderWidth = option.slider.width != null ? Std.int(option.slider.width) : 120;
w += 120 + slider.barWidth;
} else {
h *= 2;
}
default:
if (option.keybinds == null) {
if (option.keybind != null) {
option.keybinds = [option.keybind];
}
}
if (option.keybinds != null || option.keybindText != null) {
var text = if(option.keybindText == null) {
var textKeys:Array<String> = [];
for (o in option.keybinds[0]) {
if (Std.int(o) > 0) {
textKeys.push(o.toUIString());
}
}
textKeys.join("+");
} else {
option.keybindText;
}
labelKeybind = new UIText(label.x + label.frameWidth + 10, 2, 0, text);
labelKeybind.alpha = 0.75;
w = Std.int(labelKeybind.x + labelKeybind.frameWidth + 10);
}
if (option.icon != null && option.icon > 0) {
icon = new FlxSprite(0, 0).loadGraphic(Paths.image('editors/ui/context-icons'), true, 20, 20);
icon.animation.add('icon', [option.icon-1], 0, true);
icon.animation.play('icon');
}
super(x, y, w, h, 'editors/ui/menu-item');
if (option.keybinds == null) {
if (option.keybind != null) {
option.keybinds = [option.keybind];
}
}
if (option.keybinds != null || option.keybindText != null) {
var text = if(option.keybindText == null) {
var textKeys:Array<String> = [];
for (o in option.keybinds[0]) {
if (Std.int(o) > 0) {
textKeys.push(o.toUIString());
}
}
textKeys.join("+");
} else {
option.keybindText;
}
labelKeybind = new UIText(label.x + label.frameWidth + 10, 2, 0, text);
labelKeybind.alpha = 0.75;
}
super(x, y, labelKeybind != null ? Std.int(labelKeybind.x + labelKeybind.frameWidth + 10) : (label.frameWidth + 22), label.frameHeight, 'editors/ui/menu-item');
members.push(label);
updateIcon();
if (icon != null)
members.push(icon);
if (labelKeybind != null)
members.push(labelKeybind);
}
//Called after all options are created and the context menu width/height is final
public function postCreate() {
switch(optionType) {
case SLIDER:
var sliderWidth = bWidth-50;
if (option.slider.sameLine != null && option.slider.sameLine) {
option.slider.width != null ? Std.int(option.slider.width) : 120;
}
slider = new UISlider(0, 0, sliderWidth, option.slider.value,
[{start: option.slider.min, end: option.slider.max, size: option.slider.max-option.slider.min}], false);
slider.onChange = function(v) {
option.slider.value = v;
if (option.slider.onChange != null) option.slider.onChange(option);
updateIcon(); //check if icon has changed
@:privateAccess
labelKeybind.text = '${CoolUtil.quantize(slider.__barProgress * 100, 1)}%';
};
slider.value = option.slider.value;
if (option.slider.showValues == null || !option.slider.showValues) {
slider.startText.visible = false;
slider.endText.visible = false;
slider.valueStepper.visible = false;
slider.valueStepper.selectable = false;
}
members.push(slider);
case SUBMENU:
default:
}
members.push(labelKeybind);
}
public override function draw() {
@@ -340,71 +205,12 @@ class UIContextMenuOptionSpr extends UISliceSprite {
icon.follow(this, 0, 0);
if (labelKeybind != null)
labelKeybind.follow(this, bWidth - 10 - labelKeybind.frameWidth, 2);
if (slider != null) {
if (option.slider.sameLine != null && option.slider.sameLine) {
slider.follow(this, bWidth - 18 - slider.barWidth - (slider.endText.visible ? slider.endText.width : 0), 5);
} else {
slider.follow(this, 20, 5 + label.frameHeight);
}
}
super.draw();
}
public override function onHovered() {
super.onHovered();
parent.lastHoveredOptionIndex = parent.contextMenuOptions.indexOf(this);
switch(optionType) {
case SUBMENU:
parent.openChildContextMenu(this);
case SLIDER:
default:
if (FlxG.mouse.justReleased)
parent.select(option);
}
}
public function updateIcon() {
var currentIcon = option.icon != null ? option.icon : 0;
if (icon == null && currentIcon > 0) {
members.push(icon = new UIContextMenuOptionIcon(option));
}
if (icon != null) {
icon.updateIconState(currentIcon);
}
}
}
class UIContextMenuOptionIcon extends UISprite {
private var option:UIContextMenuOption;
private var _lastState:Int = 0;
override public function new(option:UIContextMenuOption) {
super();
this.option = option;
loadGraphic(Paths.image('editors/ui/context-icons'), true, 20, 20);
selectable = option.onIconClick != null;
cursor = option.onIconClick != null ? CLICK : ARROW;
}
public function updateIconState(state:Int) {
if (_lastState == state) return;
_lastState = state;
visible = state > 0;
if (state > 0) {
animation.add('icon', [state-1], 0, true);
animation.play('icon');
}
}
public override function onHovered() {
super.onHovered();
if (FlxG.mouse.justReleased && option.onIconClick != null) {
option.onIconClick(option);
}
if (FlxG.mouse.justReleased)
parent.select(option);
}
}
+1 -1
View File
@@ -87,7 +87,7 @@ class UIDropDown extends UISliceSprite {
}
public function openContextMenu() {
UIState.playEditorSound(Flags.DEFAULT_EDITOR_DROPDOWNAPPEAR_SOUND);
FlxG.sound.play(Paths.sound(Flags.DEFAULT_EDITOR_DROPDOWNAPPEAR_SOUND));
var screenPos = getScreenPosition(null, __lastDrawCameras[0] == null ? FlxG.camera : __lastDrawCameras[0]);
curMenu = UIState.state.openContextMenu([
for(k=>o in items) {
+1 -10
View File
@@ -2,9 +2,6 @@ package funkin.editors.ui;
import haxe.io.Bytes;
import lime.ui.FileDialog;
#if lime_funkin
import lime.ui.FileDialogFilter;
#end
class UIFileExplorer extends UISliceSprite {
public var uploadButton:UIButton;
@@ -27,16 +24,10 @@ class UIFileExplorer extends UISliceSprite {
if (onFile != null) this.onFile = onFile;
uploadButton = new UIButton(x + 8, y + 8, null, function () {
#if lime_funkin
FileDialog.openFile(FlxG.stage.window, "Open File", (fileNames:Array<String>, activeFilter:FileDialogFilter) -> {
loadFile(fileNames[0]);
}, this.fileType != null ? [new FileDialogFilter("Specified File Extension", this.fileType)] : null);
#else
uploadButton = new UIButton(x + 8, y+ 8, null, function () {
var fileDialog = new FileDialog();
fileDialog.onSelect.add(loadFile);
fileDialog.browse(OPEN, this.fileType);
#end
}, bWidth - 16, bHeight - 16);
members.push(uploadButton);
+3 -8
View File
@@ -106,7 +106,7 @@ class UIState extends MusicBeatState {
}
if (FlxG.mouse.justPressed) {
playEditorSound(Flags.DEFAULT_EDITOR_CLICK_SOUND);
FlxG.sound.play(Paths.sound(Flags.DEFAULT_EDITOR_CLICK_SOUND));
}
if (FlxG.mouse.justReleased)
@@ -147,7 +147,7 @@ class UIState extends MusicBeatState {
}
public function closeCurrentContextMenu() {
playEditorSound(Flags.DEFAULT_EDITOR_WINDOWCLOSE_SOUND);
FlxG.sound.play(Paths.sound(Flags.DEFAULT_EDITOR_WINDOWCLOSE_SOUND));
if(curContextMenu != null) {
curContextMenu.close();
curContextMenu = null;
@@ -155,7 +155,7 @@ class UIState extends MusicBeatState {
}
public function openContextMenu(options:Array<UIContextMenuOption>, ?callback:UIContextMenuCallback, ?x:Float, ?y:Float, ?w:Int) {
playEditorSound(Flags.DEFAULT_EDITOR_WINDOWAPPEAR_SOUND);
FlxG.sound.play(Paths.sound(Flags.DEFAULT_EDITOR_WINDOWAPPEAR_SOUND));
var state = FlxG.state;
while(state.subState != null && !(state._requestSubStateReset && state._requestedSubState == null))
state = state.subState;
@@ -191,9 +191,4 @@ class UIState extends MusicBeatState {
resolutionAware = true;
FlxG.scaleMode = uiScaleMode;
}
public static function playEditorSound(path:String) {
if (!Options.editorSFX) return;
FlxG.sound.play(Paths.sound(path));
}
}
+1 -1
View File
@@ -86,7 +86,7 @@ class UISubstateWindow extends MusicBeatSubstate {
}
public override function destroy() {
UIState.playEditorSound(Flags.DEFAULT_EDITOR_WINDOWCLOSE_SOUND);
FlxG.sound.play(Paths.sound(Flags.DEFAULT_EDITOR_WINDOWCLOSE_SOUND));
super.destroy();
for(e in camShaders)
e.removeShader(blurShader);
+26 -132
View File
@@ -60,7 +60,7 @@ class UITextBox extends UISliceSprite implements IUIFocusable {
var selected = selectable && focused;
if (autoAlpha) {
if (selectable) {
if(selectable) {
alpha = label.alpha = 1;
} else {
alpha = label.alpha = 0.4;
@@ -68,22 +68,19 @@ class UITextBox extends UISliceSprite implements IUIFocusable {
}
var off = multiline ? 4 : ((bHeight - label.height) / 2);
label.follow(this, label.autoSize ? (bWidth - label.textField.width) / 2 : 4, off);
label.follow(this, label.autoSize ? (bWidth-label.textField.width)/2 : 4, off);
framesOffset = (selected ? 18 : (hovered ? 9 : 0));
@:privateAccess {
if (selected) {
if (!__wasFocused) {
__wasFocused = true;
FlxG.stage.window.textInputEnabled = true;
}
__wasFocused = true;
caretSpr.alpha = (FlxG.game.ticks % 666) >= 333 ? 1 : 0;
var curPos = switch (position) {
var curPos = switch(position) {
case 0:
FlxPoint.get(0, 0);
default:
if (position >= label.text.length) {
label.textField.__getCharBoundaries(label.text.length - 1, cacheRect);
label.textField.__getCharBoundaries(label.text.length-1, cacheRect);
FlxPoint.get(cacheRect.x + cacheRect.width, cacheRect.y);
} else {
label.textField.__getCharBoundaries(position, cacheRect);
@@ -95,7 +92,6 @@ class UITextBox extends UISliceSprite implements IUIFocusable {
} else {
if (__wasFocused) {
__wasFocused = false;
FlxG.stage.window.textInputEnabled = false;
if (onChange != null)
onChange(label.text);
}
@@ -104,162 +100,60 @@ class UITextBox extends UISliceSprite implements IUIFocusable {
}
}
private static var seperators:Array<String> = [
" ", "\n", "\t", "\r", "-", "_", "=", "+", "/", "\\", "|", ",", ".", ";", ":", "!", "?", "@", "#", "$", "%", "^", "&", "*", "(", ")", "[", "]", "{",
"}",
];
public inline static function isSeperator(char:String):Bool
return seperators.contains(char);
public inline static function findWholeWord(text:String, pos:Int, ?isDelete:Bool = false):Null<Array<Int>> {
if (text.length == 0)
return null;
var start = pos;
var end = pos;
while (!isDelete && start > 0 && !isSeperator(text.charAt(start - 1)))
start--;
while (end < text.length && !isSeperator(text.charAt(end)))
end++;
if (end == pos && isSeperator(text.charAt(end - 1)))
start--;
return [start, end];
}
public function onKeyDown(e:KeyCode, modifier:KeyModifier) {
switch (e) {
switch(e) {
case RETURN:
focused = false;
if (onChange != null)
onChange(label.text);
return;
if (onChange != null) onChange(label.text);
case LEFT:
if (modifier.ctrlKey) {
if (position == 0)
return;
var wordBounds = findWholeWord(label.text, position);
if (wordBounds != null) {
position = position == wordBounds[0] ? wordBounds[0] - 1 : wordBounds[0];
} else {
position = 0;
}
return;
}
changeSelection(-1);
return;
case RIGHT:
if (modifier.ctrlKey) {
if (position == label.text.length)
return;
var wordBounds = findWholeWord(label.text, position);
if (wordBounds != null) {
position = position == wordBounds[1] ? wordBounds[1] + 1 : wordBounds[1];
} else {
position = label.text.length;
}
return;
}
changeSelection(1);
return;
case BACKSPACE:
UIState.playEditorSound(Flags.DEFAULT_EDITOR_TEXTREMOVE_SOUND);
if (modifier.ctrlKey) {
var wordBounds = findWholeWord(label.text, position);
if (wordBounds != null) {
label.text = label.text.substr(0, wordBounds[0]) + label.text.substr(wordBounds[1]);
position = wordBounds[0];
}
return;
}
FlxG.sound.play(Paths.sound(Flags.DEFAULT_EDITOR_TEXTREMOVE_SOUND));
if (position > 0) {
label.text = label.text.substr(0, position - 1) + label.text.substr(position);
label.text = label.text.substr(0, position-1) + label.text.substr(position);
changeSelection(-1);
}
return;
case DELETE:
UIState.playEditorSound(Flags.DEFAULT_EDITOR_TEXTREMOVE_SOUND);
if (modifier.ctrlKey) {
var wordBounds = findWholeWord(label.text, position, true);
if (wordBounds != null) {
label.text = label.text.substr(0, wordBounds[0]) + label.text.substr(wordBounds[1]);
position = wordBounds[0];
}
return;
}
if (position < label.text.length) {
label.text = label.text.substr(0, position) + label.text.substr(position + 1);
}
return;
case HOME:
position = 0;
return;
case END:
position = label.text.length;
return;
case V:
if (modifier.ctrlKey) {
UIState.playEditorSound(Flags.DEFAULT_EDITOR_PASTE_SOUND);
FlxG.sound.play(Paths.sound(Flags.DEFAULT_EDITOR_TEXTTYPE_SOUND));
// Hey lj here, fixed copying because before we checked if the modifier was left or right ctrl
// but somehow it gave a int outside of the KeyModifier's range :sob:
// apparently there is a boolean that just checks for you. yw :D
// Hey lj here, fixed copying because before we checked if the modifier was left or right ctrl
// but somehow it gave a int outside of the KeyModifier's range :sob:
// apparently there is a boolean that just checks for you. yw :D
// we pasting
var data:String = Clipboard.generalClipboard.getData(TEXT_FORMAT);
if (data != null)
onTextInput(data);
}
// if we are not holding ctrl, ignore
if (!modifier.ctrlKey) return;
// we pasting
var data:String = Clipboard.generalClipboard.getData(TEXT_FORMAT);
if (data != null) onTextInput(data);
case C:
if (modifier.ctrlKey) {
UIState.playEditorSound(Flags.DEFAULT_EDITOR_COPY_SOUND);
FlxG.sound.play(Paths.sound(Flags.DEFAULT_EDITOR_TEXTTYPE_SOUND));
// if we are not holding ctrl, ignore
if (!modifier.ctrlKey) return;
Clipboard.generalClipboard.setData(TEXT_FORMAT, label.text);
}
case X:
if (modifier.ctrlKey) {
UIState.playEditorSound(Flags.DEFAULT_EDITOR_CUT_SOUND);
Clipboard.generalClipboard.setData(TEXT_FORMAT, label.text);
position = 0;
label.text = "";
return;
}
// copying
Clipboard.generalClipboard.setData(TEXT_FORMAT, label.text);
default:
FlxG.sound.play(Paths.sound(Flags.DEFAULT_EDITOR_TEXTTYPE_SOUND));
}
if (modifier.ctrlKey || modifier.altKey || modifier.shiftKey) return;
UIState.playEditorSound(Flags.DEFAULT_EDITOR_TEXTTYPE_SOUND);
}
public function changeSelection(change:Int) {
position = Std.int(FlxMath.bound(position + change, 0, label.text.length));
position = FlxMath.wrap(position + change, 0, label.text.length);
}
public function onKeyUp(e:KeyCode, modifier:KeyModifier) {}
public function onTextInput(text:String):Void {
label.text = label.text.substr(0, position) + text + label.text.substr(position);
position += text.length;
}
// untested, but this should be a fix for if the text wont type
public function onTextEdit(text:String, start:Int, end:Int):Void {
label.text = label.text.substr(0, position) + text + label.text.substr(position);
position += text.length;
}
}
}
+1 -1
View File
@@ -15,7 +15,7 @@ class UIWindow extends UISliceSprite {
content = new FlxTypedGroup<FlxBasic>();
members.push(content);
UIState.playEditorSound(Flags.DEFAULT_EDITOR_WINDOWAPPEAR_SOUND);
FlxG.sound.play(Paths.sound(Flags.DEFAULT_EDITOR_WINDOWAPPEAR_SOUND));
}
public override function update(elapsed:Float) {
+6 -6
View File
@@ -165,7 +165,7 @@ class Character extends FunkinSprite implements IBeatReceiver implements IOffset
public function tryDance() {
var event = new CancellableEvent();
scripts.call("onTryDance", [event]);
script.call("onTryDance", [event]);
if (event.cancelled)
return;
@@ -197,7 +197,7 @@ class Character extends FunkinSprite implements IBeatReceiver implements IOffset
}
public override function measureHit(curMeasure:Int)
scripts.call("measureHit", [curMeasure]);
script.call("measureHit", [curMeasure]);
public override function stepHit(curStep:Int)
scripts.call("stepHit", [curStep]);
@@ -254,13 +254,13 @@ class Character extends FunkinSprite implements IBeatReceiver implements IOffset
public var ghostDraw:Bool = false;
public override function draw() {
var e = EventManager.get(DrawEvent).recycle();
scripts.call("draw", [e]);
script.call("draw", [e]);
preDraw();
super.draw();
postDraw();
scripts.call("postDraw", [e]);
script.call("postDraw", [e]);
}
public var singAnims = ["singLEFT", "singDOWN", "singUP", "singRIGHT"];
@@ -273,7 +273,7 @@ class Character extends FunkinSprite implements IBeatReceiver implements IOffset
public function playSingAnim(direction:Int, suffix:String = "", Context:PlayAnimContext = SING, ?Force:Null<Bool> = null, Reversed:Bool = false, Frame:Int = 0)
{
var event = EventManager.get(DirectionAnimEvent).recycle(getSingAnim(direction, suffix), direction, suffix, Context, Reversed, Frame, Force);
scripts.call("onPlaySingAnim", [event]);
script.call("onPlaySingAnim", [event]);
if (event.cancelled) return;
playSingAnimUnsafe(event.direction, hasAnimation(event.animName) ? event.suffix : "", event.context, event.force, event.reversed, event.frame);
@@ -281,7 +281,7 @@ class Character extends FunkinSprite implements IBeatReceiver implements IOffset
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);
scripts.call("playSingAnimUnsafe", [event]);
script.call("playSingAnimUnsafe", [event]);
if (event.cancelled) return;
playAnim(event.animName, event.force, event.context, event.reversed, event.frame);
+46 -81
View File
@@ -1,7 +1,6 @@
package funkin.game;
import flixel.math.FlxPoint;
import flixel.math.FlxAngle;
import flixel.math.FlxRect;
import funkin.backend.chart.ChartData;
import funkin.backend.scripting.events.note.NoteCreationEvent;
@@ -126,7 +125,8 @@ class Note extends FlxSprite
static var DEFAULT_FIELDS:Array<String> = ["time", "id", "type", "sLen"];
public function new(strumLine:StrumLine, noteData:ChartNote, sustain:Bool = false, sustainLength:Float = 0, sustainOffset:Float = 0, ?prev:Note) {
public function new(strumLine:StrumLine, noteData:ChartNote, sustain:Bool = false, sustainLength:Float = 0, sustainOffset:Float = 0, ?prev:Note)
{
super();
moves = false;
@@ -240,85 +240,60 @@ class Note extends FlxSprite
*/
public var strumRelativePos:Bool = true;
@:dox(hide) static var __lastAngle:Float = Math.NaN;
@:dox(hide) static var __lastAngleSin:Float = 0;
@:dox(hide) static var __lastAngleCos:Float = 0;
@:dox(hide) static var __lastStrumW:Float = Math.NaN;
@:dox(hide) static var __lastStrumH:Float = Math.NaN;
@:dox(hide) static var __lastStrumHalfW:Float = 0;
@:dox(hide) static var __lastStrumHalfH:Float = 0;
override function drawComplex(camera:FlxCamera) {
var downscrollCam = (camera is HudCamera ? ({var _:HudCamera=cast camera;_;}).downscroll : false);
if (updateFlipY) flipY = (isSustainNote && flipSustain) && (downscrollCam != (__strum != null && __strum.getScrollSpeed(this) < 0));
if (downscrollCam) {
frameOffset.y += __notePosFrameOffset.y * 2;
super.drawComplex(camera);
frameOffset.y -= __notePosFrameOffset.y * 2;
} else
super.drawComplex(camera);
}
static var __notePosFrameOffset:FlxPoint = new FlxPoint();
static var __posPoint:FlxPoint = new FlxPoint();
override function draw() {
@:privateAccess var oldDefaultCameras = FlxCamera._defaultCameras;
@:privateAccess if (__strumCameras != null) FlxCamera._defaultCameras = __strumCameras;
var negativeScroll = isSustainNote && strumRelativePos && lastScrollSpeed < 0;
if (negativeScroll) y -= height;
var negativeScroll = isSustainNote && nextSustain != null && lastScrollSpeed < 0;
if (negativeScroll) offset.y *= -1;
if (__strum != null && strumRelativePos) {
final originalX = x;
final originalY = y;
var pos = __posPoint.set(x, y);
if (__noteAngle != __lastAngle) {
__lastAngle = __noteAngle;
final result = FlxMath.fastSinCos((__noteAngle + 90) * FlxAngle.TO_RAD);
__lastAngleSin = result.sin;
__lastAngleCos = result.cos;
}
setPosition(__strum.x, __strum.y);
if (__strum.width != __lastStrumW || __strum.height != __lastStrumH) {
__lastStrumW = __strum.width;
__lastStrumH = __strum.height;
__lastStrumHalfW = __strum.width * 0.5;
__lastStrumHalfH = __strum.height * 0.5;
}
__notePosFrameOffset.set(pos.x / scale.x, pos.y / scale.y);
frameOffset.x -= __notePosFrameOffset.x;
frameOffset.y -= __notePosFrameOffset.y;
this.frameOffsetAngle = __noteAngle;
x = -origin.x + offset.x + (originalY * __lastAngleCos) + __strum.x + __lastStrumHalfW;
y = -origin.y + offset.y + (originalY * __lastAngleSin) + __strum.y + __lastStrumHalfH;
super.draw();
x = originalX;
y = originalY;
this.frameOffsetAngle = 0;
frameOffset.x += __notePosFrameOffset.x;
frameOffset.y += __notePosFrameOffset.y;
setPosition(pos.x, pos.y);
//pos.put();
} else {
__notePosFrameOffset.set(0, 0);
super.draw();
}
if (negativeScroll) y += height;
if (negativeScroll) offset.y *= -1;
@:privateAccess FlxCamera._defaultCameras = oldDefaultCameras;
}
var __lastDownscrollCam:Bool = false;
var __lastX:Float = 0;
@:noCompletion @:dox(hide) override function isOnScreen(?camera:FlxCamera):Bool {
var downscrollCam = (Std.isOfType(camera, HudCamera) ? cast(camera, HudCamera).downscroll : false);
if (downscrollCam == __lastDownscrollCam)
return super.isOnScreen(camera);
else
__lastX = x;
if (updateFlipY) flipY = (isSustainNote && flipSustain) && (downscrollCam != (__strum != null && __strum.getScrollSpeed(this) < 0));
if (downscrollCam && __strum != null) {
x = -x + 2 * (__strum.x - origin.x + offset.x) + __strum.width;
}
final isOnScreen = super.isOnScreen(camera);
return isOnScreen;
}
override function drawComplex(camera:FlxCamera):Void {
super.drawComplex(camera);
if (__lastDownscrollCam) {
__lastDownscrollCam = false;
x = __lastX;
}
}
public function isOnScreenOriginal(?camera:FlxCamera):Bool {
return super.isOnScreen(camera);
}
public var earlyPressWindow:Float = Flags.EARLY_HIT_WINDOW_RANGE;
public var latePressWindow:Float = Flags.LATE_HIT_WINDOW_RANGE;
// The * 0.5 is so that it's easier to hit them too late, instead of too early
public var earlyPressWindow:Float = 0.5;
public var latePressWindow:Float = 1;
public function updateSustain(strum:Strum) {
var scrollSpeed = strum.getScrollSpeed(this);
@@ -337,27 +312,17 @@ class Note extends FlxSprite
public function updateSustainClip() if (wasGoodHit && !noSustainClip) {
var t = CoolUtil.bound((Conductor.songPosition - strumTime) / height * 0.45 * lastScrollSpeed, 0, 1);
@:bypassAccessor {
if (clipRect == null) clipRect = FlxRect.get();
clipRect.set(0, frameHeight * t, frameWidth, frameHeight * (1 - t));
}
@:privateAccess {
if (frame != null && _frame != null)
_frame = frame.clipTo(clipRect, _frame);
}
var rect = clipRect == null ? FlxRect.get() : clipRect;
clipRect = rect.set(0, frameHeight * t, frameWidth, frameHeight * (1 - t));
}
@:noCompletion
override function set_clipRect(rect:FlxRect):FlxRect {
@:bypassAccessor clipRect = rect;
override function set_clipRect(rect:FlxRect):FlxRect
{
clipRect = rect;
@:privateAccess if (frame != null) {
if (rect != null && _frame != null)
_frame = frame.clipTo(rect, _frame);
else if (_frame != null)
_frame = frame.copyTo(_frame);
dirty = true;
}
if (frames != null)
frame = frames.frames[animation.frameIndex];
return rect;
}
+72 -143
View File
@@ -623,18 +623,6 @@ class PlayState extends MusicBeatState
curRating = event.rating;
}
private function onRatingChange(rating:Rating) {
if (!hits.exists(rating.name))
hits.set(rating.name, 0);
if (Options.ghostTapping) {
comboBreaks = false;
for (rating in ratingManager.ratingData)
comboBreaks = comboBreaks || rating.breaksCombo;
} else
comboBreaks = true;
}
private inline function set_health(v:Float)
return health = FlxMath.bound(v, 0, maxHealth);
private inline function set_maxHealth(v:Float) {
@@ -655,7 +643,7 @@ class PlayState extends MusicBeatState
public inline function callOnCharacters(func:String, ?parameters:Array<Dynamic>) {
if(strumLines != null) strumLines.forEachAlive(function (strLine:StrumLine) {
if (strLine.characters != null) for (character in strLine.characters)
if (character != null) character.scripts.call(func, parameters);
if (character != null) character.script.call(func, parameters);
});
}
@@ -702,14 +690,6 @@ class PlayState extends MusicBeatState
detailsText = isStoryMode ? ("Story Mode: " + storyWeek.name) : "Freeplay";
for (rating in [for (i in ratingManager.ratingData) i.name]) hits.set(rating, 0); // Ensure all keys exist as to prevent null errors.
if (Options.ghostTapping) {
comboBreaks = false;
for (rating in ratingManager.ratingData)
comboBreaks = comboBreaks || rating.breaksCombo;
} else
comboBreaks = true;
ratingManager.onRatingAdded.add(onRatingChange);
ratingManager.onRatingRemoved.add(onRatingChange);
// Checks if cutscene files exists
var cutscenePath = Paths.script('songs/${SONG.meta.name}/cutscene');
@@ -943,7 +923,7 @@ class PlayState extends MusicBeatState
// Make icons appear in the correct spot during cutscenes
healthBar.update(0);
if (updateIconPositions != null && Flags.ICONS_AUTOPOSITION)
if (updateIconPositions != null)
updateIconPositions();
__updateNote_event = EventManager.get(NoteUpdateEvent);
@@ -1102,7 +1082,7 @@ class PlayState extends MusicBeatState
if (notNull) PlayState.instance.gameAndCharsCall("onStageDestroy", [stage]);
scripts.call("destroy");
for (g in __cachedGraphics) g.decrementUseCount();
for (g in __cachedGraphics) g.useCount--;
@:privateAccess {
for (strumLine in strumLines.members) FlxG.sound.destroySound(strumLine.vocals);
if (FlxG.sound.music != inst) FlxG.sound.destroySound(inst);
@@ -1251,6 +1231,11 @@ class PlayState extends MusicBeatState
@:dox(hide)
override public function onFocus():Void
{
if (!paused && FlxG.autoPause) {
for (strumLine in strumLines.members) strumLine.vocals.resume();
inst.resume();
vocals.resume();
}
gameAndCharsCall("onFocus");
updateDiscordPresence();
super.onFocus();
@@ -1259,6 +1244,11 @@ class PlayState extends MusicBeatState
@:dox(hide)
override public function onFocusLost():Void
{
if (!paused && FlxG.autoPause) {
for (strumLine in strumLines.members) strumLine.vocals.pause();
inst.pause();
vocals.pause();
}
gameAndCharsCall("onFocusLost");
updateDiscordPresence();
super.onFocusLost();
@@ -1677,7 +1667,7 @@ class PlayState extends MusicBeatState
if (strLine.characters != null) // Alt anim Idle
for (character in strLine.characters) {
if (character == null) continue;
character.idleSuffix = event.params[1] ? strLine.defaultAnimSuffix : "";
character.idleSuffix = event.params[1] ? "-alt" : "";
}
}
case "Play Animation":
@@ -1686,8 +1676,6 @@ class PlayState extends MusicBeatState
if (char != null && char.hasAnim(event.params[1])) char.playAnim(event.params[1], event.params[2], event.params[3] == "NONE" ? null : event.params[3]);
case "Unknown": // nothing
}
gameAndCharsEvent("onPostEvent", e);
}
@:dox(hide)
@@ -1956,9 +1944,9 @@ class PlayState extends MusicBeatState
var event:NoteHitEvent;
if (strumLine != null && !strumLine.cpu)
event = EventManager.get(NoteHitEvent).recycle(rating.breaksCombo, !note.isSustainNote, !note.isSustainNote, null, null, null, note, strumLine.characters, true, note.noteType, note.animSuffix.getDefault(note.strumID < strumLine.members.length ? strumLine.members[note.strumID].animSuffix : strumLine.animSuffix), null, null, note.strumID, rating.score, note.isSustainNote ? null : rating.accuracy, rating.health, rating.name, Options.splashesEnabled && !note.isSustainNote && rating.splash, null, null, null, null, null, iconP1);
event = EventManager.get(NoteHitEvent).recycle(false, !note.isSustainNote, !note.isSustainNote, null, defaultDisplayRating, defaultDisplayCombo, note, strumLine.characters, true, note.noteType, note.animSuffix.getDefault(note.strumID < strumLine.members.length ? strumLine.members[note.strumID].animSuffix : strumLine.animSuffix), "game/score/", "", note.strumID, rating.score, note.isSustainNote ? null : rating.accuracy, 0.023, rating.name, Options.splashesEnabled && !note.isSustainNote && rating.splash, 0.5, true, 0.7, true, true, iconP1);
else
event = EventManager.get(NoteHitEvent).recycle(rating.breaksCombo, false, false, null, null, null, note, strumLine.characters, false, note.noteType, note.animSuffix.getDefault(note.strumID < strumLine.members.length ? strumLine.members[note.strumID].animSuffix : strumLine.animSuffix), null, null, note.strumID, 0, null, 0, rating.name, false, null, null, null, null, true, iconP2);
event = EventManager.get(NoteHitEvent).recycle(false, false, false, null, defaultDisplayRating, defaultDisplayCombo, note, strumLine.characters, false, note.noteType, note.animSuffix.getDefault(note.strumID < strumLine.members.length ? strumLine.members[note.strumID].animSuffix : strumLine.animSuffix), "game/score/", "", note.strumID, 0, null, 0, rating.name, false, 0.5, true, 0.7, true, true, iconP2);
event.deleteNote = !note.isSustainNote; // work around, to allow sustain notes to be deleted
event = scripts.event(strumLine != null && !strumLine.cpu ? "onPlayerHit" : "onDadHit", event);
strumLine.onHit.dispatch(event);
@@ -1974,17 +1962,13 @@ class PlayState extends MusicBeatState
totalAccuracyAmount += event.accuracy;
updateRating();
}
if (event.misses) {
combo = 0;
misses++;
} else if (event.countAsCombo)
combo++;
if (event.countAsCombo) combo++;
if (event.showRating || (event.showRating == null && event.player))
{
displayCombo(event);
displayRatingNumbers(event);
displayRating(event.rating, event);
if (event.displayRating)
displayRating(event.rating, event);
ratingNum += 1;
}
if (event.player) hits[rating.name] += 1;
@@ -2021,136 +2005,81 @@ class PlayState extends MusicBeatState
gameAndCharsEvent("onPostNoteHit", event);
}
public function displayRating(myRating:String, ?evt:NoteHitEvent):Void
{
var event:RatingsShowEvent = EventManager.get(RatingsShowEvent).recycle(comboGroup.recycleLoop(FlxSprite), null, null, null, null, 0.7, true, "game/score/", "", 550, FlxPoint.get(FlxG.random.int(0, 10), FlxG.random.int(140, 175)), 0.2, (Conductor.crochet * 0.001), true, false, false, true, null, FlxPoint.get(comboGroup.x + -40, comboGroup.y + -60), true, myRating, null);
gameAndCharsEvent("onRatingsShown", event);
public function displayRating(myRating:String, ?evt:NoteHitEvent = null):Void {
var hasEvent = evt != null;
var pre:String = hasEvent ? evt.ratingPrefix : "";
var suf:String = hasEvent ? evt.ratingSuffix : "";
if (event.cancelled || !event.displayRating) { // TODO: Find a better way for this?
event.ratingSprite.kill();
return;
var rating:FlxSprite = comboGroup.recycleLoop(FlxSprite);
CoolUtil.resetSprite(rating, comboGroup.x + -40, comboGroup.y + -60);
rating.loadAnimatedGraphic(Paths.image('${pre}${myRating}${suf}'));
rating.acceleration.y = 550;
rating.velocity.y -= FlxG.random.int(140, 175);
rating.velocity.x -= FlxG.random.int(0, 10);
if (hasEvent) {
rating.scale.set(evt.ratingScale, evt.ratingScale);
rating.antialiasing = evt.ratingAntialiasing;
}
var hasEvent:Bool = evt != null;
var pre:String = hasEvent && evt.ratingPrefix != null ? evt.ratingPrefix : event.ratingPrefix;
var suf:String = hasEvent && evt.ratingSuffix != null ? evt.ratingSuffix : event.ratingSuffix;
var ratingScale:Float = hasEvent && evt.ratingScale != null ? evt.ratingScale : event.ratingScale;
var rating:FlxSprite = event.ratingSprite.loadAnimatedGraphic(Paths.image('${pre}${event.rating}${suf}'));
if (event.resetSprite) {
CoolUtil.resetSprite(rating, event.position.x, event.position.y);
}
rating.acceleration.y = event.acceleration;
rating.velocity.y -= event.velocity.y;
rating.velocity.x -= event.velocity.x;
rating.scale.set(ratingScale, ratingScale);
rating.antialiasing = hasEvent && evt.ratingAntialiasing != null ? evt.ratingAntialiasing : event.ratingAntialiasing;
rating.updateHitbox();
if (event.playTween) {
event.tween = FlxTween.tween(rating, {alpha: 0}, event.tweenDuration, {
startDelay: event.startDelay,
onComplete: function(tween:FlxTween) {
rating.kill();
}
});
}
gameAndCharsEvent("onPostRatingsShown", event);
event.velocity.put();
event.position.put();
FlxTween.tween(rating, {alpha: 0}, 0.2, {
startDelay: Conductor.crochet * 0.001,
onComplete: function(tween:FlxTween) {
rating.kill();
}
});
}
public function displayCombo(?evt:NoteHitEvent):Void {
public function displayCombo(?evt:NoteHitEvent = null):Void {
if (minDigitDisplay >= 0 && (combo == 0 || combo >= minDigitDisplay)) {
var event:RatingsShowEvent = EventManager.get(RatingsShowEvent).recycle(null, null, comboGroup.recycleLoop(FlxSprite), null, null, 0.7, true, "game/score/", "", 600, FlxPoint.get(FlxG.random.int(0, 10), 150), 0.2, (Conductor.crochet * 0.001), false, false, evt != null && evt.displayCombo != null ? evt.displayCombo : defaultDisplayCombo, true, null, FlxPoint.get(comboGroup.x, comboGroup.y), true, null, null);
gameAndCharsEvent("onRatingsShown", event);
var hasEvent = evt != null;
var pre:String = hasEvent ? evt.ratingPrefix : "";
var suf:String = hasEvent ? evt.ratingSuffix : "";
if (event.cancelled || !event.displayCombo) { // TODO: Find a better way for this?
event.comboSprite.kill();
return;
}
if (evt.displayCombo) {
var comboSpr:FlxSprite = comboGroup.recycleLoop(FlxSprite).loadAnimatedGraphic(Paths.image('${pre}combo${suf}'));
CoolUtil.resetSprite(comboSpr, comboGroup.x, comboGroup.y);
comboSpr.acceleration.y = 600;
comboSpr.velocity.y -= 150;
comboSpr.velocity.x += FlxG.random.int(1, 10);
var hasEvent:Bool = evt != null;
if (hasEvent) {
comboSpr.scale.set(evt.ratingScale, evt.ratingScale);
comboSpr.antialiasing = evt.ratingAntialiasing;
}
comboSpr.updateHitbox();
var pre:String = hasEvent && evt.ratingPrefix != null ? evt.ratingPrefix : event.ratingPrefix;
var suf:String = hasEvent && evt.ratingSuffix != null ? evt.ratingSuffix : event.ratingSuffix;
var ratingScale:Float = hasEvent && evt.ratingScale != null ? evt.ratingScale : event.ratingScale;
var comboSpr:FlxSprite = event.comboSprite.loadAnimatedGraphic(Paths.image('${pre}combo${suf}'));
if (event.resetSprite) {
CoolUtil.resetSprite(comboSpr, event.position.x, event.position.y);
}
comboSpr.acceleration.y = event.acceleration;
comboSpr.velocity.y -= event.velocity.y;
comboSpr.velocity.x += event.velocity.x;
comboSpr.scale.set(ratingScale, ratingScale);
comboSpr.antialiasing = hasEvent && evt.ratingAntialiasing != null ? evt.ratingAntialiasing : event.ratingAntialiasing;
comboSpr.updateHitbox();
if (event.playTween) {
event.tween = FlxTween.tween(comboSpr, {alpha: 0}, event.tweenDuration, {
onComplete: function(tween:FlxTween) {
FlxTween.tween(comboSpr, {alpha: 0}, 0.2, {
onComplete: function(tween:FlxTween)
{
comboSpr.kill();
},
startDelay: event.startDelay
startDelay: Conductor.crochet * 0.001
});
}
gameAndCharsEvent("onPostRatingsShown", event);
event.velocity.put();
event.position.put();
}
}
public function displayRatingNumbers(?evt:NoteHitEvent):Void {
if (minDigitDisplay >= 0 && (combo == 0 || combo >= minDigitDisplay)) {
var separatedScore:String = Std.string(combo).addZeros(3);
for (i in 0...separatedScore.length)
{
var event:RatingsShowEvent = EventManager.get(RatingsShowEvent).recycle(null, comboGroup.recycleLoop(FlxSprite), null, 0.5, true, null, null, "game/score/", "", FlxG.random.int(200, 300), FlxPoint.get(FlxG.random.float(-5, 5), FlxG.random.int(140, 160)), 0.2, (Conductor.crochet * 0.002), false, true, false, true, 43, FlxPoint.get(comboGroup.x - 90, comboGroup.y + 80), true, null, null);
gameAndCharsEvent("onRatingsShown", event);
if (event.cancelled || !event.displayNumbers) { // TODO: Find a better way for this?
event.numberSprite.kill();
continue;
}
var hasEvent:Bool = evt != null;
var pre:String = hasEvent && evt.ratingPrefix != null ? evt.ratingPrefix : event.ratingPrefix;
var suf:String = hasEvent && evt.ratingSuffix != null ? evt.ratingSuffix : event.ratingSuffix;
var numScale:Float = hasEvent && evt.numScale != null ? evt.numScale : event.numScale;
var numScore:FlxSprite = event.numberSprite.loadAnimatedGraphic(Paths.image('${pre}num${separatedScore.charAt(i)}${suf}'));
event.position.x += event.numSpacing * i;
if (event.resetSprite) {
CoolUtil.resetSprite(numScore, event.position.x, event.position.y);
var numScore:FlxSprite = comboGroup.recycleLoop(FlxSprite).loadAnimatedGraphic(Paths.image('${pre}num${separatedScore.charAt(i)}${suf}'));
CoolUtil.resetSprite(numScore, comboGroup.x + (43 * i) - 90, comboGroup.y + 80);
if (hasEvent) {
numScore.antialiasing = evt.numAntialiasing;
numScore.scale.set(evt.numScale, evt.numScale);
}
numScore.antialiasing = hasEvent && evt.numAntialiasing != null ? evt.numAntialiasing : event.numAntialiasing;
numScore.scale.set(numScale, numScale);
numScore.updateHitbox();
numScore.acceleration.y = event.acceleration;
numScore.velocity.y -= event.velocity.y;
numScore.velocity.x = event.velocity.x;
numScore.acceleration.y = FlxG.random.int(200, 300);
numScore.velocity.y -= FlxG.random.int(140, 160);
numScore.velocity.x = FlxG.random.float(-5, 5);
if (event.playTween) {
event.tween = FlxTween.tween(numScore, {alpha: 0}, event.tweenDuration, {
onComplete: function(tween:FlxTween) {
numScore.kill();
},
startDelay: event.startDelay
});
}
gameAndCharsEvent("onPostRatingsShown", event);
event.velocity.put();
event.position.put();
FlxTween.tween(numScore, {alpha: 0}, 0.2, {
onComplete: function(tween:FlxTween)
{
numScore.kill();
},
startDelay: Conductor.crochet * 0.002
});
}
}
}
+34 -71
View File
@@ -1,8 +1,6 @@
package funkin.game;
import flixel.math.FlxPoint;
import flixel.math.FlxAngle;
import flixel.util.typeLimit.OneOfTwo;
import funkin.backend.system.Conductor;
class Strum extends FlxSprite {
@@ -53,27 +51,8 @@ class Strum extends FlxSprite {
public var updateNotesPosY:Bool = true;
public var extraCopyFields(default, set):Array<String> = [];
@:noCompletion public var __cachedCopyFields:Array<Array<OneOfTwo<String, Int>>> = null;
private function set_extraCopyFields(val:Array<String>) {
extraCopyFields = val == null ? [] : val;
__cachedCopyFields = null;
return extraCopyFields;
}
private inline function __initCachedCopyFields() {
if (__cachedCopyFields != null) return;
__cachedCopyFields = [for (field in extraCopyFields) CoolUtil.parsePropertyString(field)];
}
private inline function __applyCopyFields(daNote:Note) {
for (i in 0...extraCopyFields.length) {
final parsed = __cachedCopyFields[i];
final fromProp = CoolUtil.parseProperty(this, parsed);
final toProp = CoolUtil.parseProperty(daNote, parsed);
toProp.setValue(fromProp.getValue());
}
}
private inline function set_extraCopyFields(val:Array<String>)
return extraCopyFields = val == null ? [] : val;
/**
* Whenever the strum is pressed.
@@ -149,28 +128,13 @@ class Strum extends FlxSprite {
}
public override function draw() {
if (cameras.length == 1) {
if (lastDrawCameras.length != 1 || lastDrawCameras[0] != cameras[0]) {
lastDrawCameras = [cameras[0]];
}
} else {
lastDrawCameras = cameras.copy();
}
lastDrawCameras = cameras.copy();
super.draw();
}
@:noCompletion public static inline final PIX180:Float = 565.4866776461628; // 180 * Math.PI
@:noCompletion public static final N_WIDTHDIV2:Float = Note.swagWidth / 2; // DEPRECATED
static var __lastStrumW:Float = Math.NaN;
static var __lastStrumH:Float = Math.NaN;
static var __lastStrumHalfW:Float = 0;
static var __lastStrumHalfH:Float = 0;
static var __noteOffset:FlxPoint = FlxPoint.get();
static var __lastNoteAngle:Float = Math.NaN;
static var __lastAngleCos:Float = 0;
static var __lastAngleSin:Float = 0;
/**
* Updates the position of a note.
* @param daNote The note
@@ -188,10 +152,8 @@ class Strum extends FlxSprite {
}
updateNotePos(daNote);
if (extraCopyFields.length > 0) {
__initCachedCopyFields();
__applyCopyFields(daNote);
}
for (field in extraCopyFields)
CoolUtil.cloneProperty(daNote, field, this); // TODO: make this cached to reduce the reflection calls - Neo
}
private inline function updateNotePos(daNote:Note) {
@@ -200,40 +162,41 @@ class Strum extends FlxSprite {
if (shouldX || shouldY) {
if (daNote.strumRelativePos) {
if (shouldX) daNote.x = 0;
if (shouldX) daNote.x = (this.width - daNote.width) * 0.5;
if (shouldY) {
daNote.y = ((daNote.strumTime - Conductor.songPosition) * 0.45 * getScrollSpeed(daNote));
if (daNote.isSustainNote) daNote.y += daNote.height * 0.5;
daNote.y = (daNote.strumTime - Conductor.songPosition) * (0.45 * getScrollSpeed(daNote));
if (daNote.isSustainNote) daNote.y += height * 0.5;
}
} else {
if (width != __lastStrumW || height != __lastStrumH) {
__lastStrumW = width;
__lastStrumH = height;
__lastStrumHalfW = width * 0.5;
__lastStrumHalfH = height * 0.5;
}
var offset = FlxPoint.get(0, (Conductor.songPosition - daNote.strumTime) * (0.45 * getScrollSpeed(daNote)));
var realOffset = FlxPoint.get(0, 0);
if (daNote.__noteAngle != __lastNoteAngle) {
__lastNoteAngle = daNote.__noteAngle;
final result = FlxMath.fastSinCos((__lastNoteAngle + 90) * FlxAngle.TO_RAD);
__lastAngleCos = result.cos;
__lastAngleSin = result.sin;
}
if (daNote.isSustainNote) offset.y -= height * 0.5;
final speed = getScrollSpeed(daNote);
final distance = (daNote.strumTime - Conductor.songPosition) * 0.45 * speed;
__noteOffset.set(__lastAngleCos * distance, __lastAngleSin * distance);
__noteOffset.x += -daNote.origin.x + daNote.offset.x;
__noteOffset.y += -daNote.origin.y + daNote.offset.y;
if (daNote.isSustainNote) {
final m = (daNote.height * 0.5 * (speed < 0 ? -1 : 1));
__noteOffset.x += __lastAngleCos * m;
__noteOffset.y += __lastAngleSin * m;
if (Std.int(daNote.__noteAngle % 360) != 0) {
var noteAngle = FlxMath.fastSinCos(daNote.__noteAngle / PIX180);
var noteAngleCos = noteAngle.cos;
var noteAngleSin = noteAngle.sin;
var aOffset:FlxPoint = FlxPoint.get(
(daNote.origin.x / daNote.scale.x) - daNote.offset.x,
(daNote.origin.y / daNote.scale.y) - daNote.offset.y
);
realOffset.x = -aOffset.x + (noteAngleCos * (offset.x + aOffset.x)) + (noteAngleSin * (offset.y + aOffset.y));
realOffset.y = -aOffset.y + (noteAngleSin * (offset.x + aOffset.x)) + (noteAngleCos * (offset.y + aOffset.y));
aOffset.put();
} else {
realOffset.x = offset.x;
realOffset.y = offset.y;
}
__noteOffset.x += x + __lastStrumHalfW;
__noteOffset.y += y + __lastStrumHalfH;
if (shouldX) daNote.x = __noteOffset.x;
if (shouldY) daNote.y = __noteOffset.y;
realOffset.y *= -1;
if (shouldX) daNote.x = x + realOffset.x;
if (shouldY) daNote.y = y + realOffset.y;
offset.put();
realOffset.put();
}
}
}
+28 -49
View File
@@ -74,10 +74,6 @@ class StrumLine extends FlxTypedGroup<Strum> {
* Which animation suffix on characters that should be used when hitting notes.
*/
public var animSuffix(default, set):String = "";
/**
* The current animation suffix the strumline should use. Note that setting this will only take effect upon the alt. animation being reset.
*/
public var defaultAnimSuffix:String = Flags.DEFAULT_ALT_ANIM_SUFFIX;
/**
* TODO: Write documentation about this being a variable that can help when making multi key
*/
@@ -262,28 +258,21 @@ class StrumLine extends FlxTypedGroup<Strum> {
}
}
function __inputProcessJustPressed(note:Note) {
var strumID = note.strumID;
if (!__justPressed[strumID] || note.isSustainNote || note.wasGoodHit || !note.canBeHit) return;
var cur = __notePerStrum[strumID];
if (cur == null) {
__notePerStrum[strumID] = note;
return;
if (__justPressed[note.strumID] && !note.isSustainNote && !note.wasGoodHit && note.canBeHit) {
var cur = __notePerStrum[note.strumID];
var songPos = __updateNote_songPos;
var noteDist = Math.abs(note.strumTime - songPos);
var curDist = cur != null ? Math.abs(cur.strumTime - songPos) : 999999;
var notePenalty = note.avoid ? 1 : 0;
var curPenalty = (cur != null && cur.avoid) ? 1 : 0;
if (cur == null
|| notePenalty < curPenalty
|| (notePenalty == curPenalty && noteDist < curDist))
__notePerStrum[note.strumID] = note;
}
var songPos = __updateNote_songPos;
var noteDist = Math.abs(note.strumTime - songPos);
var noteShouldAvoid = note.avoid;
var curShouldAvoid = cur.avoid;
if (!noteShouldAvoid && curShouldAvoid) {
__notePerStrum[strumID] = note;
return;
}
var curDist = Math.abs(cur.strumTime - songPos);
if (noteShouldAvoid == curShouldAvoid && noteDist < curDist)
__notePerStrum[strumID] = note;
}
/**
@@ -295,18 +284,13 @@ class StrumLine extends FlxTypedGroup<Strum> {
if (cpu) return;
final membersLength = members.length;
if (__pressed.length != membersLength) {
__pressed.resize(membersLength);
__justPressed.resize(membersLength);
__justReleased.resize(membersLength);
if (__pressed.length != members.length) {
__pressed.resize(members.length);
__justPressed.resize(members.length);
__justReleased.resize(members.length);
}
if (__notePerStrum.length != membersLength)
__notePerStrum = cast new haxe.ds.Vector(membersLength); // [for(_ in 0...members.length) null];
for (i in 0...membersLength) {
for (i in 0...members.length) {
__pressed[i] = members[i].__getPressed(this);
__justPressed[i] = members[i].__getJustPressed(this);
__justReleased[i] = members[i].__getJustReleased(this);
@@ -320,20 +304,18 @@ class StrumLine extends FlxTypedGroup<Strum> {
__justPressed = CoolUtil.getDefault(event.justPressed, []);
__justReleased = CoolUtil.getDefault(event.justReleased, []);
__notePerStrum = cast new haxe.ds.Vector(members.length); // [for(_ in 0...members.length) null];
if (__pressed.contains(true)) {
if (__justPressed.contains(true)) {
notes.forEachAlive(__inputProcessJustPressed);
for (k => pr in __justPressed)
{
var note = __notePerStrum[k];
if (!ghostTapping) for (k => pr in __justPressed) if (pr && __notePerStrum[k] == null)
PlayState.instance.noteMiss(this, null, k, ID); // FUCK YOU
if (note != null) {
PlayState.instance.goodNoteHit(this, note);
__notePerStrum[k] = null;
} else if (pr && !ghostTapping)
PlayState.instance.noteMiss(this, null, k, ID);
}
for (e in __notePerStrum)
if (e != null)
PlayState.instance.goodNoteHit(this, e);
}
for (c in characters)
@@ -451,9 +433,6 @@ class StrumLine extends FlxTypedGroup<Strum> {
public static inline function calculateStartingXPos(hudXRatio:Float, scale:Float, spacing:Float, keyCount:Int) {
return (FlxG.width * hudXRatio) - ((Note.swagWidth * scale * ((keyCount/2)-0.5) * spacing) + Note.swagWidth * 0.5 * scale);
}
public static inline function calculateStartingXPosFromInitialWidth(hudXRatio:Float, scale:Float, spacing:Float, keyCount:Int) {
return (FlxG.initialWidth * hudXRatio) - ((Note.swagWidth * scale * ((keyCount/2)-0.5) * spacing) + Note.swagWidth * 0.5 * scale);
}
/**
* SETTERS & GETTERS
@@ -472,11 +451,11 @@ class StrumLine extends FlxTypedGroup<Strum> {
return animSuffix = str;
}
private inline function set_altAnim(b:Bool):Bool {
animSuffix = b ? defaultAnimSuffix : "";
animSuffix = b ? "-alt" : "";
return b;
}
private inline function get_altAnim():Bool {
return animSuffix == defaultAnimSuffix;
return animSuffix == "-alt";
}
#end
}
+24 -5
View File
@@ -60,12 +60,15 @@ class VideoCutscene extends Cutscene {
add(video = new FlxVideoSprite());
video.antialiasing = true;
#if (hxvlc < version("2.0.0"))
video.autoPause = false; // Imma handle it better inside this class, mainly because of the pause menu - Nex
#end
video.bitmap.onEndReached.add(close);
video.bitmap.onFormatSetup.add(function() if (video.bitmap != null && video.bitmap.bitmapData != null) {
final width = video.bitmap.bitmapData.width;
final height = video.bitmap.bitmapData.height;
final scale:Float = Math.min(FlxG.width / width, FlxG.height / height);
video.setGraphicSize(width * scale, height * scale);
video.setGraphicSize(Std.int(width * scale), Std.int(height * scale));
video.updateHitbox();
video.screenCenter();
});
@@ -100,9 +103,14 @@ class VideoCutscene extends Cutscene {
FlxTween.tween(loadingBackdrop, {alpha: 1}, 0.5, {ease: FlxEase.sineInOut});
Main.execAsync(function() {
if (video.load(localPath)) FlxTimer.wait(0.001, function() {
mutex.acquire(); onReady(); mutex.release();
});
if (localPath.startsWith("[ZIP]")) {
// ZIP PATH: EXPORT
// TODO: this but better and more ram friendly
localPath = './.temp/video-${curVideo++}.mp4';
File.saveBytes(localPath, Assets.getBytes(path));
}
if (video.load(localPath)) new FlxTimer().start(0.001, function(_) { mutex.acquire(); onReady(); mutex.release(); });
else { mutex.acquire(); close(); mutex.release(); }
});
@@ -174,7 +182,6 @@ class VideoCutscene extends Cutscene {
}
public inline function onReady() {
trace("VideoCutscene: Ready");
FlxTween.cancelTweensOf(loadingBackdrop);
FlxTween.tween(loadingBackdrop, {alpha: 0}, 0.7, {ease: FlxEase.sineInOut, onComplete: function(_) {
loadingBackdrop.destroy();
@@ -200,6 +207,18 @@ class VideoCutscene extends Cutscene {
}
}
#if (hxvlc < version("2.0.0"))
@:dox(hide) override public function onFocus() {
if(FlxG.autoPause && !paused) video.resume();
super.onFocus();
}
@:dox(hide) override public function onFocusLost() {
if(FlxG.autoPause && !paused) video.pause();
super.onFocusLost();
}
#end
public override function pauseCutscene() {
video.pause();
super.pauseCutscene();

Some files were not shown because too many files have changed in this diff Show More