diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml
index b9bcd698a2..90a119bed3 100644
--- a/.github/workflows/linux.yml
+++ b/.github/workflows/linux.yml
@@ -15,22 +15,30 @@ jobs:
runs-on: ubuntu-24.04
steps:
- name: Pulling the new commit
- uses: actions/checkout@v4
+ uses: actions/checkout@v6
with:
fetch-depth: 0
- name: Setting up Haxe
uses: krdlab/setup-haxe@v2
with:
haxe-version: ${{ env.HAXE_VERSION }}
- - 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/
+ # - 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: Installing Packages for Building Lime (TEMPORARY)
+ run: |
+ sudo apt-get update
+ sudo apt-get install -qq \
+ libegl1-mesa-dev libgl1-mesa-dev libibus-1.0-dev libdbus-1-dev libdecor-0-dev libudev-dev libgbm-dev libdrm-dev \
+ libpng-dev libturbojpeg-dev libvorbis-dev libopenal-dev libsdl2-dev libglu1-mesa-dev libmbedtls-dev libuv1-dev libsqlite3-dev \
+ libx11-dev libxext-dev libxfixes-dev libxi-dev libxinerama-dev libxkbcommon-dev libxcursor-dev libxrandr-dev libxss-dev libxt-dev libxtst-dev libxv-dev libxxf86vm-dev \
+ libpulse-dev libasound2-dev libpipewire-0.3-dev \
+ zlib1g-dev
- name: Installing LibVLC
run: |
sudo apt-get install libvlc-dev libvlccore-dev
@@ -39,47 +47,50 @@ jobs:
haxe -cp commandline -D analyzer-optimize --run Main setup -si
- name: Building the game
run: |
+ haxelib run lime setup -alias -y -nocffi
+ haxelib run lime rebuild hxcpp
+ haxelib run lime rebuild tools
+ haxelib run lime rebuild linux -nocolor -release
haxelib run lime build linux -DCOMPILE_EXPERIMENTAL
# - name: Tar files
# run: tar -zcvf CodenameEngine.tar.gz -C export/release/linux/bin .
- name: Uploading artifact (executable)
- uses: actions/upload-artifact@v4
+ uses: actions/upload-artifact@v7
with:
name: Codename Engine (Executable Only)
path: export/release/linux/bin/CodenameEngine
- name: Uploading artifact (entire build)
- uses: actions/upload-artifact@v4
+ uses: actions/upload-artifact@v7
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/haxe/
- 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/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:
@@ -89,20 +100,28 @@ 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@v4
+ uses: actions/checkout@v6
- name: Setting up Haxe
uses: krdlab/setup-haxe@v2
with:
haxe-version: ${{ env.HAXE_VERSION }}
- - 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/
+ # - 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: Installing Packages for Building Lime (TEMPORARY)
+ run: |
+ sudo apt-get update
+ sudo apt-get install -qq \
+ libegl1-mesa-dev libgl1-mesa-dev libibus-1.0-dev libdbus-1-dev libdecor-0-dev libudev-dev libgbm-dev libdrm-dev \
+ libpng-dev libturbojpeg-dev libvorbis-dev libopenal-dev libsdl2-dev libglu1-mesa-dev libmbedtls-dev libuv1-dev libsqlite3-dev \
+ libx11-dev libxext-dev libxfixes-dev libxi-dev libxinerama-dev libxkbcommon-dev libxcursor-dev libxrandr-dev libxss-dev libxt-dev libxtst-dev libxv-dev libxxf86vm-dev \
+ libpulse-dev libasound2-dev libpipewire-0.3-dev \
+ zlib1g-dev
- name: Installing LibVLC
run: |
sudo apt-get install libvlc-dev libvlccore-dev
@@ -111,39 +130,42 @@ jobs:
haxe -cp commandline -D analyzer-optimize --run Main setup -si
- name: Building the game
run: |
- haxelib run lime build linux -debug
+ haxelib run lime setup -alias -y -nocffi
+ haxelib run lime rebuild hxcpp
+ haxelib run lime rebuild tools
+ haxelib run lime rebuild linux -nocolor -release
+ haxelib run lime build linux -debug -DCOMPILE_EXPERIMENTAL
# - name: Tar files
# run: tar -zcvf CodenameEngine.tar.gz -C export/debug/linux/bin .
- name: Uploading artifact (entire build)
- uses: actions/upload-artifact@v4
+ uses: actions/upload-artifact@v7
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/haxe/
- 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/obj/
diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml
index ecac5f1aba..ba2bfcb48b 100644
--- a/.github/workflows/macos.yml
+++ b/.github/workflows/macos.yml
@@ -9,135 +9,363 @@ env:
HXCPP_COMPILE_CACHE: ${{ github.workspace }}/.hxcpp_cache
jobs:
- build:
- name: Mac OS Build
+ intel_build:
+ name: Mac OS x64 Build
permissions: write-all
- runs-on: macos-15
+ runs-on: macos-26
steps:
- name: Pulling the new commit
- uses: actions/checkout@v4
+ uses: actions/checkout@v6
with:
fetch-depth: 0
- name: Setting up Haxe
uses: krdlab/setup-haxe@v2
with:
haxe-version: ${{ env.HAXE_VERSION }}
- - 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/
+ # - 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: Installing/Updating libraries
run: |
+ brew trust aws/tap
+ arch -x86_64 /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install.sh)"
+ arch -x86_64 /usr/local/bin/brew install zlib
haxe -cp commandline -D analyzer-optimize --run Main setup -si
- name: Building the game
run: |
+ haxelib run lime setup -alias -y -nocffi
+ haxelib run lime rebuild hxcpp
+ haxelib run lime rebuild tools
+ haxelib run lime rebuild mac -64 -nocolor -release
arch -x86_64 haxelib run lime build mac -DCOMPILE_EXPERIMENTAL
- name: Tar files
run: tar -zcvf CodenameEngine.tar.gz -C export/release/macos/bin .
- - name: Uploading artifact (executable)
- uses: actions/upload-artifact@v4
+ - name: Uploading artifact (entire build)
+ uses: actions/upload-artifact@v7
with:
- name: Codename Engine (Executable Only)
- path: export/release/macos/bin/CodenameEngine.app/Contents/MacOS/CodenameEngine
+ name: Codename Engine-x64
+ 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/
+
+ arm_build:
+ name: Mac OS ARM64 Build
+ permissions: write-all
+ runs-on: macos-26
+ steps:
+ - name: Pulling the new commit
+ uses: actions/checkout@v6
+ with:
+ fetch-depth: 0
+ - name: Setting up Haxe
+ uses: krdlab/setup-haxe@v2
+ with:
+ haxe-version: ${{ env.HAXE_VERSION }}
+ # - 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: Installing/Updating libraries
+ run: |
+ brew trust aws/tap
+ brew install zlib
+ haxe -cp commandline -D analyzer-optimize --run Main setup -si
+ - name: Building the game
+ run: |
+ haxelib run lime setup -alias -y -nocffi
+ haxelib run lime rebuild hxcpp
+ haxelib run lime rebuild tools
+ haxelib run lime rebuild mac -nocolor -release
+ haxelib run lime build mac -DCOMPILE_EXPERIMENTAL
+ - name: Tar files
+ run: tar -zcvf CodenameEngine.tar.gz -C export/release/macos/bin .
- name: Uploading artifact (entire build)
- uses: actions/upload-artifact@v4
+ uses: actions/upload-artifact@v7
with:
- name: Codename Engine
+ name: Codename Engine-ARM64
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/haxe/
- 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/obj/
+
+ universal_build:
+ name: Mac OS Universal Build
+ permissions: write-all
+ runs-on: macos-26
+ needs:
+ - intel_build
+ - arm_build
+ steps:
+ - name: Download Mac OS x64 Full Build
+ uses: actions/download-artifact@v8
+ with:
+ name: Codename Engine-x64
+ path: intel
+ - name: Download Mac OS ARM64 Full Build
+ uses: actions/download-artifact@v8
+ with:
+ name: Codename Engine-ARM64
+ path: arm
+ - name: Tar files (x64)
+ run: tar -xvf intel/CodenameEngine.tar.gz -C intel
+ - name: Tar files (ARM64)
+ run: tar -xvf arm/CodenameEngine.tar.gz -C arm
+ - name: Removing ARM64 and x64 artifacts
+ uses: geekyeggo/delete-artifact@v6
+ with:
+ name: Codename Engine-*
+ - name: Creating Universal App Bundle
+ shell: bash
+ run: |
+ BIN="CodenameEngine.app/Contents/MacOS"
+ INTEL="intel/CodenameEngine.app/Contents/MacOS"
+ ARM="arm/CodenameEngine.app/Contents/MacOS"
+ cp -R intel/CodenameEngine.app CodenameEngine.app
+ lipo -create "$INTEL/CodenameEngine" "$ARM/CodenameEngine" -output "$BIN/CodenameEngine"
+ lipo -create "$INTEL/lime.ndll" "$ARM/lime.ndll" -output "$BIN/lime.ndll"
+ - name: Uploading Universal Mac OS bundle (executable)
+ uses: actions/upload-artifact@v7
+ with:
+ name: Codename Engine (Executable Only)
+ path: CodenameEngine.app/Contents/MacOS/CodenameEngine
+ - name: Uploading Universal Mac OS bundle
+ uses: actions/upload-artifact@v7
+ with:
+ name: Codename Engine
+ path: CodenameEngine.app
# 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
+ intel_debug_build:
+ name: Mac OS x64 Debug Build
permissions: write-all
- runs-on: macos-15
- needs: build # since its low priority, it'll run after, so actions will concentrate first on normal builds
+ runs-on: macos-26
+ needs: universal_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@v4
+ uses: actions/checkout@v6
- name: Setting up Haxe
uses: krdlab/setup-haxe@v2
with:
haxe-version: ${{ env.HAXE_VERSION }}
- - 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/
+ # - 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: Installing/Updating libraries
run: |
+ brew trust aws/tap
+ arch -x86_64 /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install.sh)"
+ arch -x86_64 /usr/local/bin/brew install zlib
haxe -cp commandline -D analyzer-optimize --run Main setup -si
- name: Building the game
run: |
- arch -x86_64 haxelib run lime build mac -debug
+ haxelib run lime setup -alias -y -nocffi
+ haxelib run lime rebuild hxcpp
+ haxelib run lime rebuild tools
+ haxelib run lime rebuild mac -64 -nocolor -release
+ arch -x86_64 haxelib run lime build mac -debug -DCOMPILE_EXPERIMENTAL
- name: Tar files
run: tar -zcvf CodenameEngine.tar.gz -C export/debug/macos/bin .
- name: Uploading artifact (entire build)
- uses: actions/upload-artifact@v4
+ uses: actions/upload-artifact@v7
with:
- name: Codename Engine Debug
+ name: Codename Engine-x64 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/
+
+ arm_debug_build:
+ name: Mac OS ARM64 Debug Build
+ permissions: write-all
+ runs-on: macos-26
+ needs: universal_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
+ - name: Setting up Haxe
+ uses: krdlab/setup-haxe@v2
+ with:
+ haxe-version: ${{ env.HAXE_VERSION }}
+ # - 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: Installing/Updating libraries
+ run: |
+ brew trust aws/tap
+ brew install zlib
+ haxe -cp commandline -D analyzer-optimize --run Main setup -si
+ - name: Building the game
+ run: |
+ haxelib run lime setup -alias -y -nocffi
+ haxelib run lime rebuild hxcpp
+ haxelib run lime rebuild tools
+ haxelib run lime rebuild mac -nocolor -release
+ haxelib run lime build mac -debug -DCOMPILE_EXPERIMENTAL
+ - name: Tar files
+ run: tar -zcvf CodenameEngine.tar.gz -C export/debug/macos/bin .
+ - name: Uploading artifact (entire build)
+ uses: actions/upload-artifact@v7
+ with:
+ name: Codename Engine-ARM64 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/haxe/
- 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/obj/
+
+ universal_debug_build:
+ name: Mac OS Universal Debug Build
+ permissions: write-all
+ runs-on: macos-26
+ needs:
+ - intel_debug_build
+ - arm_debug_build
+ steps:
+ - name: Download Mac OS x64 Full Build
+ uses: actions/download-artifact@v8
+ with:
+ name: Codename Engine-x64 Debug
+ path: intel
+ - name: Download Mac OS ARM64 Full Build
+ uses: actions/download-artifact@v8
+ with:
+ name: Codename Engine-ARM64 Debug
+ path: arm
+ - name: Tar files (x64)
+ run: tar -xvf intel/CodenameEngine.tar.gz -C intel
+ - name: Tar files (ARM64)
+ run: tar -xvf arm/CodenameEngine.tar.gz -C arm
+ - name: Removing ARM64 and x64 artifacts
+ uses: geekyeggo/delete-artifact@v6
+ with:
+ name: Codename Engine-*
+ - name: Creating Universal App Bundle
+ shell: bash
+ run: |
+ BIN="CodenameEngine.app/Contents/MacOS"
+ INTEL="intel/CodenameEngine.app/Contents/MacOS"
+ ARM="arm/CodenameEngine.app/Contents/MacOS"
+ cp -R intel/CodenameEngine.app CodenameEngine.app
+ lipo -create "$INTEL/CodenameEngine" "$ARM/CodenameEngine" -output "$BIN/CodenameEngine"
+ lipo -create "$INTEL/lime.ndll" "$ARM/lime.ndll" -output "$BIN/lime.ndll"
+ - name: Uploading Universal Mac OS bundle
+ uses: actions/upload-artifact@v7
+ with:
+ name: Codename Engine Debug
+ path: CodenameEngine.app
+
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index 1d44b18914..3e78c83f20 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -21,12 +21,12 @@ jobs:
steps:
- name: Checkout
- uses: actions/checkout@v4
+ uses: actions/checkout@v6
with:
fetch-depth: 0
- name: Download Windows Full Build
- uses: dawidd6/action-download-artifact@v6
+ uses: dawidd6/action-download-artifact@v8
with:
workflow: windows.yml
name: Codename Engine
@@ -34,7 +34,7 @@ jobs:
allow_forks: false
- name: Download Windows Executable
- uses: dawidd6/action-download-artifact@v6
+ uses: dawidd6/action-download-artifact@v8
with:
workflow: windows.yml
name: Codename Engine (Executable Only)
@@ -42,7 +42,7 @@ jobs:
allow_forks: false
- name: Download Mac OS Full Build
- uses: dawidd6/action-download-artifact@v6
+ uses: dawidd6/action-download-artifact@v8
with:
workflow: macos.yml
name: Codename Engine
@@ -50,7 +50,7 @@ jobs:
allow_forks: false
- name: Download Mac OS Executable
- uses: dawidd6/action-download-artifact@v6
+ uses: dawidd6/action-download-artifact@v8
with:
workflow: macos.yml
name: Codename Engine (Executable Only)
@@ -58,7 +58,7 @@ jobs:
allow_forks: false
- name: Download Linux Full Build
- uses: dawidd6/action-download-artifact@v6
+ uses: dawidd6/action-download-artifact@v8
with:
workflow: linux.yml
name: Codename Engine
@@ -66,7 +66,7 @@ jobs:
allow_forks: false
- name: Download Linux Executable
- uses: dawidd6/action-download-artifact@v6
+ uses: dawidd6/action-download-artifact@v8
with:
workflow: linux.yml
name: Codename Engine (Executable Only)
diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml
index 2f1584327d..ffaf51af8c 100644
--- a/.github/workflows/windows.yml
+++ b/.github/workflows/windows.yml
@@ -15,66 +15,70 @@ jobs:
runs-on: windows-latest
steps:
- name: Pulling the new commit
- uses: actions/checkout@v4
+ uses: actions/checkout@v6
with:
fetch-depth: 0
- name: Setting up Haxe
uses: krdlab/setup-haxe@v2
with:
haxe-version: ${{ env.HAXE_VERSION }}
- - 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/
+ # - 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: Installing/Updating libraries
run: |
haxe -cp commandline -D analyzer-optimize --run Main setup -si --no-vscheck
- name: Building the game
run: |
+ haxelib run lime setup -alias -y -nocffi
+ haxelib run lime rebuild hxcpp
+ haxelib run lime rebuild tools
+ haxelib run lime rebuild windows -nocolor -release
haxelib run lime build windows -DCOMPILE_EXPERIMENTAL
- name: Uploading artifact (executable)
- uses: actions/upload-artifact@v4
+ uses: actions/upload-artifact@v7
with:
name: Codename Engine (Executable Only)
- path: export/release/windows/bin/CodenameEngine.exe
+ path: |
+ export/release/windows/bin/CodenameEngine.exe
+ export/release/windows/bin/lime.ndll
- name: Uploading artifact (entire build)
- uses: actions/upload-artifact@v4
+ uses: actions/upload-artifact@v7
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/haxe/
- 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/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:
@@ -84,56 +88,59 @@ 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@v4
+ uses: actions/checkout@v6
- name: Setting up Haxe
uses: krdlab/setup-haxe@v2
with:
haxe-version: ${{ env.HAXE_VERSION }}
- - 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/
- name: Installing/Updating libraries
run: |
haxe -cp commandline -D analyzer-optimize --run Main setup -si --no-vscheck
- name: Building the game
run: |
+ haxelib run lime setup -alias -y -nocffi
+ haxelib run lime rebuild hxcpp
+ haxelib run lime rebuild tools
+ haxelib run lime rebuild windows -nocolor -release
haxelib run lime build windows -debug
- name: Uploading artifact (entire build)
- uses: actions/upload-artifact@v4
+ uses: actions/upload-artifact@v7
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/haxe/
- 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/obj/
diff --git a/README.md b/README.md
index 02f9929bac..c9ffb7bfcd 100644
--- a/README.md
+++ b/README.md
@@ -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 x64** and **Linux x64**.
+> Codename Engine as for now supports **Windows x64**, **Mac OS Universal** and **Linux x64**.
> More platforms will soon come, stay tuned!
> - [ ] **Web (HTML5) Support**
> - [ ] **Mobile Support**
diff --git a/assets/data/editors/layouts/stage/solidEditScreen.xml b/assets/data/editors/layouts/stage/solidEditScreen.xml
index a4f028fd3e..c79ec0f172 100644
--- a/assets/data/editors/layouts/stage/solidEditScreen.xml
+++ b/assets/data/editors/layouts/stage/solidEditScreen.xml
@@ -132,12 +132,17 @@
button.xml.set("name", nameTextBox.label.text);
button.xml.set("x", xStepper.value);
button.xml.set("y", yStepper.value);
- button.xml.set("zoom", zoomFactorStepper.value);
+ button.xml.set("zoomfactor", zoomFactorStepper.value);
button.xml.set("angle", angleStepper.value);
button.xml.set("alpha", alphaStepper.value);
+ button.xml.remove("blend");
+ if (sprite.blend != null)
+ button.xml.set("blend", blendMode.value);
saveXY(sprite.scrollFactor, "scroll", scrollXStepper, scrollYStepper);
saveXY(sprite.skew, "skew", skewXStepper, skewYStepper);
+ if (isSolid)
+ button.xml.remove("updateHitbox");
for (att in ["width", "height", "color"])
button.xml.remove(att);
XMLUtil.loadSpriteFromXML(sprite, button.xml, "", 0, false);
diff --git a/assets/data/editors/layouts/stage/spriteEditScreen.xml b/assets/data/editors/layouts/stage/spriteEditScreen.xml
index 12d768ad85..5b5652c818 100644
--- a/assets/data/editors/layouts/stage/spriteEditScreen.xml
+++ b/assets/data/editors/layouts/stage/spriteEditScreen.xml
@@ -32,14 +32,29 @@
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -221,11 +236,14 @@
button.xml.set("sprite", spriteTextBox.label.text);
button.xml.set("x", xStepper.value);
button.xml.set("y", yStepper.value);
- button.xml.set("zoom", zoomFactorStepper.value);
+ button.xml.set("zoomfactor", zoomFactorStepper.value);
button.xml.set("type", animTypeToString(animType.value));
button.xml.set("antialiasing", antialiasingCheckbox.checked);
button.xml.set("angle", angleStepper.value);
button.xml.set("alpha", alphaStepper.value);
+ button.xml.remove("blend");
+ if (sprite.blend != null)
+ button.xml.set("blend", blendMode.value);
getEx('anims').clear();
var allowedAnims:Array<String> = [for (b in animations.buttons.members) b.animData.name];
diff --git a/assets/data/stages/test.xml b/assets/data/stages/test.xml
deleted file mode 100644
index 697711bbc4..0000000000
--- a/assets/data/stages/test.xml
+++ /dev/null
@@ -1,12 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/assets/languages/en/Editors.xml b/assets/languages/en/Editors.xml
index 894aa13eea..447b0549a6 100644
--- a/assets/languages/en/Editors.xml
+++ b/assets/languages/en/Editors.xml
@@ -743,4 +743,9 @@
Created mod config!
Your mod config file has been created at {0}!
+
+
+ Outdated API Version! (v{0} < v{1})
+ Your mod's config runs on an outdated API Version! This WILL cause compatiblilty and code issues!\n\nIt is recommended that you make changes to your mod and its config file to fit the new version.\n\nMeanwhile, the game will try its best to make your mod compatible, but you should still update your mod.\n\n(PS: If this is not your mod please disable Developer mode, and notify the mod developers if needed.)
+
\ No newline at end of file
diff --git a/assets/languages/en/Options.xml b/assets/languages/en/Options.xml
index 39be41cb80..6bb0c86a23 100644
--- a/assets/languages/en/Options.xml
+++ b/assets/languages/en/Options.xml
@@ -67,6 +67,7 @@
Framerate
Pretty self explanatory, isn't it?
+ Unlimited
diff --git a/assets/languages/es/Editors.xml b/assets/languages/es/Editors.xml
index 8e4da69a0f..7b0c3853ac 100644
--- a/assets/languages/es/Editors.xml
+++ b/assets/languages/es/Editors.xml
@@ -639,4 +639,17 @@
Guardar y Volver al Menú
Cancelar
+
+
+ ¡Falta la configuración del mod!
+ ¡Tu mod actualmente no tiene un archivo de configuración!\n\n¿Quisieras generar uno automáticamente?\n\n(PD: Si este no es tu mod por favor deshabilita el Modo Desarrollador para que no aparezca esta ventana.)
+
+ ¡Configuración creada!
+ ¡Tu archivo de configuración se creó en {0}!
+
+
+
+ ¡Versión de API desactualizada! (v{0} < v{1})
+ ¡La configuración de tu mod usa una versión API antigua! ¡Esto CAUSARÁ problemas con código y compatibilidad!\n\nSe recomienda que cambies el código y configuración de tu mod para encajar con la versión nueva.\n\nMientras tanto, se hará lo mejor posible para compatibilizar el código actual, pero aún se recomienda actualizarlo.\n\n(PD: Si este no es tu mod por favor deshabilita el Modo Desarrollador, y notifique a los desarrolladores del mod si es necesario.)
+
\ No newline at end of file
diff --git a/assets/languages/es/Options.xml b/assets/languages/es/Options.xml
index 1f9bb0517d..ea1e235a7c 100644
--- a/assets/languages/es/Options.xml
+++ b/assets/languages/es/Options.xml
@@ -65,6 +65,7 @@
FPS
Yo creo que se explica sólo.
+ Ilimitado
@@ -76,6 +77,12 @@
Si está activado, Week 6 tendrá un efecto que alinea todos los píxeles en la pantalla.
+ CALIDAD
+ Modos de calidad del cual elegir, "BAJA" = Quitar efectos fuertes del juego para correr mejor, "ALTA" = Habilita todos los efectos, a costo de rendimiento, "PERSONALIZADA" = Deja cambiar todas las opciones a su gusto.
+ BAJA
+ ALTA
+ PERSONALIZADA
+
Si está desactivado, desactivará el antialiasing en todos los sprites, lo cual puede aumentar el rendimiento pero hará los visuales más pixelados.
Modo de Baja Memoria
diff --git a/assets/languages/es/config.ini b/assets/languages/es/config.ini
index 2a3e1384f9..2b144d515f 100644
--- a/assets/languages/es/config.ini
+++ b/assets/languages/es/config.ini
@@ -1,3 +1,3 @@
name="Español"
-credits="Terionic"
+credits="Terionic & Betopia"
version="1.0.0"
\ No newline at end of file
diff --git a/assets/languages/pt/Editors.xml b/assets/languages/pt/Editors.xml
index 2d0a05208d..e7ff96be96 100644
--- a/assets/languages/pt/Editors.xml
+++ b/assets/languages/pt/Editors.xml
@@ -695,4 +695,9 @@
Config. de mod criada!
As configurações do seu mod foram criadas em {0}!
+
+
+ Versão de API desatualizada! (v{0} < v{1})
+ A configuração do seu mod está a usar uma versão do API velha! Isto IRÁ causar problemas de compatibilidade e código!\n\nÉ recomendado que faça mudanças ao seu mod e o seu ficheiro de configuração para ajustar à versão mais recente.\n\nPor enquanto, o jogo irá tentar o seu melhor para fazer o seu mod compatível, mas deverá na mesma atualizá-lo.\n\n(PS: Se este mod não for seu, por favor desative o modo de desenvolvedor, e notifique os desenvolvedores deste mod se necessário.)
+
\ No newline at end of file
diff --git a/assets/shaders/engine/editorWaveforms.frag b/assets/shaders/engine/editorWaveforms.frag
index c65452dc7f..e3b3d21260 100644
--- a/assets/shaders/engine/editorWaveforms.frag
+++ b/assets/shaders/engine/editorWaveforms.frag
@@ -1,6 +1,9 @@
-#pragma header
+// this causes issues apparently?? need to check GLSLSourceAssembler
+// but it could just be unnecessary too anyway
+// if not, put this also in editorWaveformsRainbow.frag
+//#version 120
-// Used in charter by waveforms
+#pragma header
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);
@@ -18,6 +21,21 @@ 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);
diff --git a/building/alsoft.txt b/building/alsoft.txt
deleted file mode 100644
index 58d8c74cc1..0000000000
--- a/building/alsoft.txt
+++ /dev/null
@@ -1,17 +0,0 @@
-[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
\ No newline at end of file
diff --git a/building/libs.xml b/building/libs.xml
index 8888214090..57c0a61800 100644
--- a/building/libs.xml
+++ b/building/libs.xml
@@ -3,36 +3,34 @@
Preparing installation...
-
-
-
+
+
+
+
-
-
+
+
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
-
+
+
+
+
+
+
+
+
-
-
-
-
-
-
+
+
+
@@ -41,15 +39,16 @@
+
-
+
+
+
+
diff --git a/project.xml b/project.xml
index 3bcdd92c26..9c4eb7115c 100644
--- a/project.xml
+++ b/project.xml
@@ -1,84 +1,60 @@
-
-
-
+
+
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
-
-
+
+
-
+
+
-
-
+
+
-
-
-
+
+
-
+
-
+
-
+
@@ -87,36 +63,60 @@
-
+
-
+
-
+
-
+
-
+
-
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -124,34 +124,48 @@
-
-
+
-
-
+
+
-
+
+
-
-
-
-
-
+
+
-
+
-
-
+
+
+
-
+
-
-
-
-
-
+
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -167,81 +181,69 @@
-
-
-
-
+
-
-
-
-
-
-
+
-
+
+
+
+
+
+
-
-
-
-
-
-
-
+
+
-
-
+
+
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
-
+
+
-
+
+
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/source/flixel/sound/FlxSound.hx b/source/flixel/sound/FlxSound.hx
deleted file mode 100644
index 9891bc934a..0000000000
--- a/source/flixel/sound/FlxSound.hx
+++ /dev/null
@@ -1,960 +0,0 @@
-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;
-
- /**
- * The sound's "target" (for proximity and panning).
- * @since raltyMod
- */
- public var target:Null;
-
- /**
- * 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; // 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;
-
- 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, ?autoDestroy:Null, ?onComplete:NullVoid>):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):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):Null {
- 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)
- ]);
- }
-}
\ No newline at end of file
diff --git a/source/flixel/sound/FlxSoundGroup.hx b/source/flixel/sound/FlxSoundGroup.hx
deleted file mode 100644
index 9c2fcc1cbd..0000000000
--- a/source/flixel/sound/FlxSoundGroup.hx
+++ /dev/null
@@ -1,120 +0,0 @@
-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 = [];
-
- /**
- * 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;
- }
-}
diff --git a/source/flx3d/FlxView3D.hx b/source/flx3d/FlxView3D.hx
index b07b78e213..8ca07302be 100644
--- a/source/flx3d/FlxView3D.hx
+++ b/source/flx3d/FlxView3D.hx
@@ -23,32 +23,6 @@ class FlxView3D extends FlxSprite
private var _textureView:TextureView3D;
private var legacyRender:Bool = false;
- // With new rendering, it seems to only work if 2 or more views are being rendered.
- // This workaround creates a second instance if there is only one view.
- // "There is nothing more permanent than a temporary solution"
- // -idk who said this i just wanted to quote it
- private static var workaroundInstance:FlxView3D;
- private static var createdWorkaround:Bool = false;
-
- private inline function createWorkaround()
- {
- if (!createdWorkaround && !legacyRender)
- {
- createdWorkaround = true;
- workaroundInstance = new FlxView3D(0, 0, 1, 1);
- FlxG.state.add(workaroundInstance);
- }
- }
-
- private inline function destroyWorkaround()
- {
- if (workaroundInstance == this)
- {
- workaroundInstance = null;
- createdWorkaround = false;
- }
- }
-
/**
* The Away3D View
*/
@@ -90,8 +64,8 @@ class FlxView3D extends FlxSprite
view.visible = false;
- this.width = width == -1 ? FlxG.width : width;
- this.height = height == -1 ? FlxG.height : height;
+ this.width = width < 0 ? FlxG.width : width;
+ this.height = height < 0 ? FlxG.height : height;
if (legacyRender)
{
bmp = new BitmapData(Std.int(view.width), Std.int(view.height), true, 0x0);
@@ -100,8 +74,6 @@ class FlxView3D extends FlxSprite
view.backgroundAlpha = 0;
FlxG.stage.addChildAt(view, 0);
-
- createWorkaround();
}
/**
@@ -133,8 +105,6 @@ class FlxView3D extends FlxSprite
view.dispose();
view = null;
}
-
- destroyWorkaround();
}
@:noCompletion override function draw()
diff --git a/source/funkin/backend/FunkinText.hx b/source/funkin/backend/FunkinText.hx
index 09bd9dca97..76da9f66c6 100644
--- a/source/funkin/backend/FunkinText.hx
+++ b/source/funkin/backend/FunkinText.hx
@@ -3,6 +3,7 @@ package funkin.backend;
import animate.FlxAnimate;
import flixel.FlxCamera;
import flixel.FlxG;
+import flixel.math.FlxMath;
import flixel.math.FlxAngle;
import flixel.math.FlxMatrix;
import flixel.math.FlxPoint;
@@ -70,7 +71,7 @@ class FunkinText extends FlxText
override function drawComplex(camera:FlxCamera):Void
{
_frame.prepareMatrix(_matrix, ANGLE_0, checkFlipX(), checkFlipY());
- _matrix.translate(-origin.x, -origin.y);
+ _matrix.translate(-origin.x - _graphicOffset.x, -origin.y - _graphicOffset.y);
if (frameOffsetAngle != null && frameOffsetAngle != angle)
{
@@ -137,7 +138,7 @@ class FunkinText extends FlxText
override function getScreenBounds(?rect:FlxRect, ?camera:FlxCamera):FlxRect
{
- if (camera == null) camera = FlxG.camera;
+ if (camera == null) camera = getDefaultCamera();
rect = super.getScreenBounds(rect, camera);
if (zoomFactorEnabled && zoomFactor != 1) {
diff --git a/source/funkin/backend/assets/MultiFramesCollection.hx b/source/funkin/backend/assets/MultiFramesCollection.hx
index 2a03c64cdd..d4bed77bf2 100644
--- a/source/funkin/backend/assets/MultiFramesCollection.hx
+++ b/source/funkin/backend/assets/MultiFramesCollection.hx
@@ -47,7 +47,7 @@ class MultiFramesCollection extends FlxFramesCollection
public function addFrames(collection:FlxFramesCollection) {
if (collection == null || collection.frames == null) return;
- collection.parent.useCount++;
+ collection.parent.incrementUseCount();
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.useCount--;
+ collection.parent.decrementUseCount();
}
parentedFrames = null;
}
diff --git a/source/funkin/backend/assets/Paths.hx b/source/funkin/backend/assets/Paths.hx
index fd24dccfbe..ef24976975 100644
--- a/source/funkin/backend/assets/Paths.hx
+++ b/source/funkin/backend/assets/Paths.hx
@@ -238,6 +238,37 @@ class Paths
return false;
}
+ /*
+ * Loads frames from multiple specified image paths. Supports all atlas types.
+ * @param sheets An array of paths to the images
+ * @param unique (Additional) Whenever the image should be unique in the cache
+ * @param key (Additional) Key to the image in the cache
+ * @param ext (Additional) Extension of the images.
+ * @return FlxFramesCollection Frames
+ */
+ public static function getMultiFrames(sheets:Array, ?unique:Bool = true, ?key:String = null, ?ext:String = null, ?animateSettings:FlxAnimateSettings):FlxFramesCollection {
+ // TODO: cache properly
+ if (sheets.length == 1) return loadFrames(sheets[0], unique, key, false, false, ext, animateSettings);
+ if (key == null) key = 'combo/' + sheets.join(',');
+ var graphic = FlxG.bitmap.add("flixel/images/logo/default.png", unique, key);
+ var sprFrames:FlxAtlasFrames = new FlxAtlasFrames(graphic);
+ try {
+ for (x => path in sheets) {
+ var noExt = haxe.io.Path.withoutExtension(Paths.image(path, null, true, ext));
+ @:privateAccess
+ var newFrames = cast Paths.loadFrames(noExt, true, key + '_$path', false, false, ext, animateSettings);
+ if (newFrames == null) {
+ Logs.warn('There is no Bitmap asset for "$noExt". Skipping...');
+ continue;
+ }
+ sprFrames = FlxAnimateFrames.combineAtlas(sprFrames, newFrames);
+ }
+ } catch(e:Dynamic) {
+ Logs.error('Multisheet load error: ' + e.toString());
+ }
+ return sprFrames;
+ }
+
/**
* Loads frames from a specific image path. Supports Sparrow Atlases, Packer Atlases, and multiple spritesheets.
* @param path Path to the image
@@ -260,7 +291,6 @@ class Paths
if (frames != null)
return frames;
- trace("no frames yet for multiple atlases!!");
var cur = 1;
var finalFrames = new MultiFramesCollection(graphic);
while(Assets.exists('$noExt/$cur.${ext}')) {
@@ -269,7 +299,7 @@ class Paths
cur++;
}
return finalFrames;
- } else if (Assets.exists('$noExt/Animation.json')) {
+ } else if (!SkipAtlasCheck && Assets.exists('$noExt/Animation.json')) {
return Paths.getAnimateAtlasAlt(noExt, animateSettings);
} else if (Assets.exists('$noExt.xml')) {
return Paths.getSparrowAtlasAlt(noExt, ext);
diff --git a/source/funkin/backend/scripting/Script.hx b/source/funkin/backend/scripting/Script.hx
index a0dde86115..1df5063161 100644
--- a/source/funkin/backend/scripting/Script.hx
+++ b/source/funkin/backend/scripting/Script.hx
@@ -10,7 +10,8 @@ import lime.app.Application;
* Class used for scripting.
* Use `Script.create` to create a script.
*/
-class Script extends FlxBasic implements IFlxDestroyable {
+class Script extends FlxBasic implements IFlxDestroyable
+{
/**
* Use "static var thing = true;" in hscript to use those!!
* are reset every mod switch so once you're done with them make sure to make them null!!
@@ -20,7 +21,8 @@ class Script extends FlxBasic implements IFlxDestroyable {
/**
* Gets the default variables for a script.
*/
- public static function getDefaultVariables(?script:Script):Map {
+ public static function getDefaultVariables(?script:Script):Map
+ {
var vars = _defaultVariablesTemplate != null ? _defaultVariablesTemplate : (_defaultVariablesTemplate = buildDefaultVariables());
var copy = vars.copy();
copy.set("state", flixel.FlxG.state); // `state` changes on state switch, so it can't be cached
@@ -34,94 +36,117 @@ class Script extends FlxBasic implements IFlxDestroyable {
*/
private static var _defaultVariablesTemplate:Map = null;
- private static function buildDefaultVariables():Map {
+ private static function buildDefaultVariables():Map
+ {
return [
// Haxe related stuff
- "Std" => Std,
- "Math" => Math,
- "Reflect" => Reflect,
- "StringTools" => StringTools,
- "Json" => haxe.Json,
- "Xml" => Xml,
- "Type" => Type,
- "Date" => Date,
- "Lambda" => Lambda,
- #if sys "Sys" => Sys, #end
+ "Std" => Std,
+ "Math" => Math,
+ "Reflect" => Reflect,
+ "StringTools" => StringTools,
+ "Json" => haxe.Json,
+ "Xml" => Xml,
+ "Type" => Type,
+ "Date" => Date,
+ "Lambda" => Lambda,
+ #if sys "Sys" => Sys, #end
// OpenFL & Lime related stuff
- "BlendMode" => CoolUtil.getMacroAbstractClass("openfl.display.BlendMode"),
- "Assets" => openfl.utils.Assets,
- "Application" => lime.app.Application,
- "Main" => funkin.backend.system.Main,
+ "BlendMode" => CoolUtil.getMacroAbstractClass("openfl.display.BlendMode"),
+ "Assets" => openfl.utils.Assets,
+ "Application" => lime.app.Application,
+ "Main" => funkin.backend.system.Main,
// Flixel related stuff
- "FlxG" => flixel.FlxG,
- "FlxSprite" => flixel.FlxSprite,
- "FlxBasic" => flixel.FlxBasic,
- "FlxCamera" => flixel.FlxCamera,
- "FlxEase" => flixel.tweens.FlxEase,
- "FlxTween" => flixel.tweens.FlxTween,
- "FlxSound" => flixel.sound.FlxSound,
- "FlxAssets" => flixel.system.FlxAssets,
- "FlxMath" => flixel.math.FlxMath,
- "FlxGroup" => flixel.group.FlxGroup,
- "FlxTypedGroup" => flixel.group.FlxGroup.FlxTypedGroup,
- "FlxSpriteGroup" => flixel.group.FlxSpriteGroup,
- "FlxTypeText" => flixel.addons.text.FlxTypeText,
- "FlxText" => flixel.text.FlxText,
- "FlxTimer" => flixel.util.FlxTimer,
- "FlxPoint" => CoolUtil.getMacroAbstractClass("flixel.math.FlxPoint"),
- "FlxAxes" => CoolUtil.getMacroAbstractClass("flixel.util.FlxAxes"),
- "FlxColor" => CoolUtil.getMacroAbstractClass("flixel.util.FlxColor"),
+ "FlxG" => flixel.FlxG,
+ "FlxSprite" => flixel.FlxSprite,
+ "FlxBasic" => flixel.FlxBasic,
+ "FlxCamera" => flixel.FlxCamera,
+ "FlxEase" => flixel.tweens.FlxEase,
+ "FlxTween" => flixel.tweens.FlxTween,
+ "FlxSound" => flixel.sound.FlxSound,
+ "FlxAssets" => flixel.system.FlxAssets,
+ "FlxMath" => flixel.math.FlxMath,
+ "FlxGroup" => flixel.group.FlxGroup,
+ "FlxTypedGroup" => flixel.group.FlxGroup.FlxTypedGroup,
+ "FlxSpriteGroup" => flixel.group.FlxSpriteGroup,
+ "FlxTypeText" => flixel.addons.text.FlxTypeText,
+ "FlxText" => flixel.text.FlxText,
+ "FlxTimer" => flixel.util.FlxTimer,
+ "FlxPoint" => CoolUtil.getMacroAbstractClass("flixel.math.FlxPoint"),
+ "FlxAxes" => CoolUtil.getMacroAbstractClass("flixel.util.FlxAxes"),
+ "FlxColor" => CoolUtil.getMacroAbstractClass("flixel.util.FlxColor"),
+
+ #if (THREE_D_SUPPORT && foxlite)
+ // Foxlite stuff
+ "FoxScene" => foxlite.FoxScene, "FoxCamera" => foxlite.FoxCamera, "FoxFPSCamera" => foxlite.extras.FoxFPSCamera, "FoxRenderer" =>
+ foxlite.renderer.FoxRenderer, "FoxLoaderUtil" => foxlite.loaders.FoxLoaderUtil, "FoxModel" => foxlite.FoxModel, "FoxQuadMesh" =>
+ foxlite.mesh.FoxQuadMesh, "FoxCubeMesh" => foxlite.mesh.FoxCubeMesh, "FoxMaterial" => foxlite.material.FoxMaterial, "FoxShader" =>
+ foxlite.FoxShader, "FoxTexture" => foxlite.texture.FoxTexture, "FoxCache" => foxlite.FoxCache, "FoxRenderMetrics" =>
+ foxlite.flixel.FoxRenderMetrics, "FoxFunkinSprite" => foxlite.funkin.FoxFunkinSprite, "FoxFlxSprite" => foxlite.flixel.FoxFlxSprite,
+ "FoxPanoramaSky" => foxlite.sky.FoxPanoramaSky, "FoxStencilAction" => foxlite.stencil.FoxStencilAction, "FoxOBJLoader" =>
+ foxlite.loaders.FoxOBJLoader, "FoxMTLLoader" => foxlite.loaders.FoxMTLLoader, "FoxDirectionalLight" => foxlite.lights.FoxDirectionalLight,
+ "FoxLayer" => CoolUtil.getMacroAbstractClass("foxlite.FoxLayer"), "FoxEaseType" => CoolUtil.getMacroAbstractClass("foxlite.animation.FoxEaseType"),
+ "FoxInstanceUpdateMode" => CoolUtil.getMacroAbstractClass("foxlite.instancing.FoxInstanceUpdateMode"), "FoxAreaLightShape" =>
+ CoolUtil.getMacroAbstractClass("foxlite.lights.FoxAreaLightShape"), "FoxLightType" =>
+ CoolUtil.getMacroAbstractClass("foxlite.lights.FoxLightType"), "FoxBlendMode" => CoolUtil.getMacroAbstractClass("foxlite.material.FoxBlendMode"),
+ "FoxDepthCompareMode" => CoolUtil.getMacroAbstractClass("foxlite.material.FoxDepthCompareMode"), "FoxStencilCompareMode" =>
+ CoolUtil.getMacroAbstractClass("foxlite.stencil.FoxStencilCompareMode"), "FoxTriangleFace" =>
+ CoolUtil.getMacroAbstractClass("foxlite.material.FoxTriangleFace"), "FoxMeshBufferType" =>
+ CoolUtil.getMacroAbstractClass("foxlite.mesh.FoxMeshBufferType"), "FoxQuadFace" => CoolUtil.getMacroAbstractClass("foxlite.mesh.FoxQuadFace"),
+ "FoxStencilActionType" => CoolUtil.getMacroAbstractClass("foxlite.stencil.FoxStencilActionType"), "FoxCubemapSide" =>
+ CoolUtil.getMacroAbstractClass("foxlite.texture.FoxCubemapSide"), "FoxMipFilter" => CoolUtil.getMacroAbstractClass("foxlite.texture.FoxMipFilter"),
+ "FoxTextureFilter" => CoolUtil.getMacroAbstractClass("foxlite.texture.FoxTextureFilter"), "FoxWrapMode" =>
+ CoolUtil.getMacroAbstractClass("foxlite.texture.FoxWrapMode"),
+ #end
// Engine related stuff
- "engine" => {
+ "engine" => {
commit: Flags.COMMIT_NUMBER,
hash: Flags.COMMIT_HASH,
build: 2675, // 2675 being the last build num before it was removed
name: "Codename Engine"
},
- "ModState" => funkin.backend.scripting.ModState,
- "ModSubState" => funkin.backend.scripting.ModSubState,
- "PlayState" => funkin.game.PlayState,
- "GameOverSubstate" => funkin.game.GameOverSubstate,
- "HealthIcon" => funkin.game.HealthIcon,
- "HudCamera" => funkin.game.HudCamera,
- "Note" => funkin.game.Note,
- "Strum" => funkin.game.Strum,
- "StrumLine" => funkin.game.StrumLine,
- "Character" => funkin.game.Character,
- "Boyfriend" => funkin.game.Character, // for compatibility
- "PauseSubstate" => funkin.menus.PauseSubState,
- "FreeplayState" => funkin.menus.FreeplayState,
- "MainMenuState" => funkin.menus.MainMenuState,
- "PauseSubState" => funkin.menus.PauseSubState,
- "StoryMenuState" => funkin.menus.StoryMenuState,
- "TitleState" => funkin.menus.TitleState,
- "Options" => funkin.options.Options,
- "Paths" => funkin.backend.assets.Paths,
- "Conductor" => funkin.backend.system.Conductor,
- "FunkinShader" => funkin.backend.shaders.FunkinShader,
- "CustomShader" => funkin.backend.shaders.CustomShader,
- "FunkinText" => funkin.backend.FunkinText,
- "FlxAnimate" => animate.FlxAnimate,
- "FunkinSprite" => funkin.backend.FunkinSprite,
- "Alphabet" => funkin.menus.ui.Alphabet,
- "Flags" => funkin.backend.system.Flags,
-
- "CoolUtil" => funkin.backend.utils.CoolUtil,
- "IniUtil" => funkin.backend.utils.IniUtil,
- "XMLUtil" => funkin.backend.utils.XMLUtil,
- #if sys "ZipUtil" => funkin.backend.utils.ZipUtil, #end
- "MarkdownUtil" => funkin.backend.utils.MarkdownUtil,
- "EngineUtil" => funkin.backend.utils.EngineUtil,
- "ThreadUtil" => funkin.backend.utils.ThreadUtil,
- "MemoryUtil" => funkin.backend.utils.MemoryUtil,
- "BitmapUtil" => funkin.backend.utils.BitmapUtil,
+ "ModState" => funkin.backend.scripting.ModState,
+ "ModSubState" => funkin.backend.scripting.ModSubState,
+ "PlayState" => funkin.game.PlayState,
+ "GameOverSubstate" => funkin.game.GameOverSubstate,
+ "HealthIcon" => funkin.game.HealthIcon,
+ "HudCamera" => funkin.game.HudCamera,
+ "Note" => funkin.game.Note,
+ "Strum" => funkin.game.Strum,
+ "StrumLine" => funkin.game.StrumLine,
+ "Character" => funkin.game.Character,
+ "Boyfriend" => funkin.game.Character, // for compatibility
+ "PauseSubstate" => funkin.menus.PauseSubState,
+ "FreeplayState" => funkin.menus.FreeplayState,
+ "MainMenuState" => funkin.menus.MainMenuState,
+ "PauseSubState" => funkin.menus.PauseSubState,
+ "StoryMenuState" => funkin.menus.StoryMenuState,
+ "TitleState" => funkin.menus.TitleState,
+ "Options" => funkin.options.Options,
+ "Paths" => funkin.backend.assets.Paths,
+ "Conductor" => funkin.backend.system.Conductor,
+ "FunkinShader" => funkin.backend.shaders.FunkinShader,
+ "CustomShader" => funkin.backend.shaders.CustomShader, // deprecated
+ "FunkinText" => funkin.backend.FunkinText,
+ "FlxAnimate" => animate.FlxAnimate,
+ "FunkinSprite" => funkin.backend.FunkinSprite,
+ "Alphabet" => funkin.menus.ui.Alphabet,
+ "Flags" => funkin.backend.system.Flags,
+
+ "CoolUtil" => funkin.backend.utils.CoolUtil,
+ "IniUtil" => funkin.backend.utils.IniUtil,
+ "XMLUtil" => funkin.backend.utils.XMLUtil,
+ #if sys "ZipUtil" => funkin.backend.utils.ZipUtil, #end
+ "MarkdownUtil" => funkin.backend.utils.MarkdownUtil,
+ "EngineUtil" => funkin.backend.utils.EngineUtil,
+ "ThreadUtil" => funkin.backend.utils.ThreadUtil,
+ "MemoryUtil" => funkin.backend.utils.MemoryUtil,
+ "BitmapUtil" => funkin.backend.utils.BitmapUtil,
#if TRANSLATIONS_SUPPORT
- "TranslationUtil" => funkin.backend.utils.TranslationUtil,
- "translate" => funkin.backend.utils.TranslationUtil.get,
+ "TranslationUtil" => funkin.backend.utils.TranslationUtil, "translate" => funkin.backend.utils.TranslationUtil.get,
#end
];
}
@@ -131,43 +156,44 @@ class Script extends FlxBasic implements IFlxDestroyable {
* This gets set on `hscript.Interp.importRedirects`,
* if you wanna modify it, please edit `hscript.Interp.importRedirects` directly.
**/
- public static function getDefaultImportRedirects():Map {
+ public static function getDefaultImportRedirects():Map
+ {
var redirects:Map = [];
// Events
final events = "funkin.backend.scripting.events.";
- redirects[events + "CharacterNodeEvent"] = events + "character.CharacterNodeEvent";
- redirects[events + "CharacterXMLEvent"] = events + "character.CharacterXMLEvent";
- redirects[events + "DanceEvent"] = events + "character.DanceEvent";
- redirects[events + "DirectionAnimEvent"] = events + "character.DirectionAnimEvent";
- redirects[events + "DiscordPresenceUpdateEvent"] = events + "discord.DiscordPresenceUpdateEvent";
- redirects[events + "GameOverCreationEvent"] = events + "gameover.GameOverCreationEvent";
- redirects[events + "CamMoveEvent"] = events + "gameplay.CamMoveEvent";
- redirects[events + "CountdownEvent"] = events + "gameplay.CountdownEvent";
- redirects[events + "EventGameEvent"] = events + "gameplay.EventGameEvent";
- redirects[events + "GameOverEvent"] = events + "gameplay.GameOverEvent";
- redirects[events + "RatingUpdateEvent"] = events + "gameplay.RatingUpdateEvent";
- redirects[events + "HealthIconChangeEvent"] = events + "healthicon.HealthIconChangeEvent";
- redirects[events + "FreeplayAlphaUpdateEvent"] = events + "menu.freeplay.FreeplayAlphaUpdateEvent";
- redirects[events + "FreeplaySongSelectEvent"] = events + "menu.freeplay.FreeplaySongSelectEvent";
- redirects[events + "MenuChangeEvent"] = events + "menu.MenuChangeEvent";
- redirects[events + "PauseCreationEvent"] = events + "menu.pause.PauseCreationEvent";
- redirects[events + "WeekSelectEvent"] = events + "menu.storymenu.WeekSelectEvent";
- redirects[events + "InputSystemEvent"] = events + "note.InputSystemEvent";
- redirects[events + "NoteCreationEvent"] = events + "note.NoteCreationEvent";
- redirects[events + "NoteHitEvent"] = events + "note.NoteHitEvent";
- redirects[events + "NoteMissEvent"] = events + "note.NoteMissEvent";
- redirects[events + "NoteUpdateEvent"] = events + "note.NoteUpdateEvent";
- redirects[events + "SimpleNoteEvent"] = events + "note.SimpleNoteEvent";
- redirects[events + "StrumCreationEvent"] = events + "note.StrumCreationEvent";
- redirects[events + "SplashShowEvent"] = events + "splash.SplashShowEvent";
- redirects[events + "PlayAnimContext"] = events + "sprite.PlayAnimContext";
- redirects[events + "PlayAnimEvent"] = events + "sprite.PlayAnimEvent";
- redirects[events + "StageNodeEvent"] = events + "stage.StageNodeEvent";
- redirects[events + "StageXMLEvent"] = events + "stage.StageXMLEvent";
+ redirects[events + "CharacterNodeEvent"] = events + "character.CharacterNodeEvent";
+ redirects[events + "CharacterXMLEvent"] = events + "character.CharacterXMLEvent";
+ redirects[events + "DanceEvent"] = events + "character.DanceEvent";
+ redirects[events + "DirectionAnimEvent"] = events + "character.DirectionAnimEvent";
+ redirects[events + "DiscordPresenceUpdateEvent"] = events + "discord.DiscordPresenceUpdateEvent";
+ redirects[events + "GameOverCreationEvent"] = events + "gameover.GameOverCreationEvent";
+ redirects[events + "CamMoveEvent"] = events + "gameplay.CamMoveEvent";
+ redirects[events + "CountdownEvent"] = events + "gameplay.CountdownEvent";
+ redirects[events + "EventGameEvent"] = events + "gameplay.EventGameEvent";
+ redirects[events + "GameOverEvent"] = events + "gameplay.GameOverEvent";
+ redirects[events + "RatingUpdateEvent"] = events + "gameplay.RatingUpdateEvent";
+ redirects[events + "HealthIconChangeEvent"] = events + "healthicon.HealthIconChangeEvent";
+ redirects[events + "FreeplayAlphaUpdateEvent"] = events + "menu.freeplay.FreeplayAlphaUpdateEvent";
+ redirects[events + "FreeplaySongSelectEvent"] = events + "menu.freeplay.FreeplaySongSelectEvent";
+ redirects[events + "MenuChangeEvent"] = events + "menu.MenuChangeEvent";
+ redirects[events + "PauseCreationEvent"] = events + "menu.pause.PauseCreationEvent";
+ redirects[events + "WeekSelectEvent"] = events + "menu.storymenu.WeekSelectEvent";
+ redirects[events + "InputSystemEvent"] = events + "note.InputSystemEvent";
+ redirects[events + "NoteCreationEvent"] = events + "note.NoteCreationEvent";
+ redirects[events + "NoteHitEvent"] = events + "note.NoteHitEvent";
+ redirects[events + "NoteMissEvent"] = events + "note.NoteMissEvent";
+ redirects[events + "NoteUpdateEvent"] = events + "note.NoteUpdateEvent";
+ redirects[events + "SimpleNoteEvent"] = events + "note.SimpleNoteEvent";
+ redirects[events + "StrumCreationEvent"] = events + "note.StrumCreationEvent";
+ redirects[events + "SplashShowEvent"] = events + "splash.SplashShowEvent";
+ redirects[events + "PlayAnimContext"] = events + "sprite.PlayAnimContext";
+ redirects[events + "PlayAnimEvent"] = events + "sprite.PlayAnimEvent";
+ redirects[events + "StageNodeEvent"] = events + "stage.StageNodeEvent";
+ redirects[events + "StageXMLEvent"] = events + "stage.StageXMLEvent";
// Old State Names
- redirects["funkin.menus.BetaWarningState"] = "funkin.menus.WarningState";
+ redirects["funkin.menus.BetaWarningState"] = "funkin.menus.WarningState";
return redirects;
}
@@ -176,7 +202,8 @@ class Script extends FlxBasic implements IFlxDestroyable {
* Gets the default defines for a script.
* Includes all of the defines that the build was compiled with.
*/
- public static function getDefaultPreprocessors():Map {
+ public static function getDefaultPreprocessors():Map
+ {
var defines = funkin.backend.system.macros.DefinesMacro.defines;
defines.set("CODENAME_ENGINE", true);
defines.set("CODENAME_VER", Flags.VERSION);
@@ -184,14 +211,17 @@ class Script extends FlxBasic implements IFlxDestroyable {
defines.set("CODENAME_COMMIT", Flags.COMMIT_NUMBER);
return defines;
}
+
/**
* All available script extensions
*/
public static var scriptExtensions:Array = [
- "hx", "hscript", "hsc", "hxs",
+ "hx",
+ "hscript",
+ "hsc",
+ "hxs",
"pack", // combined file
- "lua" /** ACTUALLY NOT SUPPORTED, ONLY FOR THE MESSAGE **/
- ];
+ "lua" /** ACTUALLY NOT SUPPORTED, ONLY FOR THE MESSAGE **/];
/**
* Currently executing script.
@@ -232,9 +262,12 @@ class Script extends FlxBasic implements IFlxDestroyable {
* Creates a script from the specified asset path. The language is automatically determined.
* @param path Path in assets
*/
- public static function create(path:String):Script {
- if (Assets.exists(path)) {
- return switch(Path.extension(path).toLowerCase()) {
+ public static function create(path:String):Script
+ {
+ if (Assets.exists(path))
+ {
+ return switch (Path.extension(path).toLowerCase())
+ {
case "hx" | "hscript" | "hsc" | "hxs":
new HScript(path);
case "pack":
@@ -255,8 +288,10 @@ class Script extends FlxBasic implements IFlxDestroyable {
* @param code code
* @param path filename
*/
- public static function fromString(code:String, path:String):Script {
- return switch(Path.extension(path).toLowerCase()) {
+ public static function fromString(code:String, path:String):Script
+ {
+ return switch (Path.extension(path).toLowerCase())
+ {
case "hx" | "hscript" | "hsc" | "hxs":
new HScript(path).loadFromString(code);
case "lua":
@@ -271,7 +306,8 @@ class Script extends FlxBasic implements IFlxDestroyable {
* Creates a new instance of the script class.
* @param path
*/
- public function new(path:String) {
+ public function new(path:String)
+ {
super();
rawPath = path;
@@ -281,21 +317,24 @@ class Script extends FlxBasic implements IFlxDestroyable {
extension = Path.extension(path);
this.path = path;
onCreate(path);
- for(k=>e in getDefaultVariables(this)) {
+ for (k => e in getDefaultVariables(this))
+ {
set(k, e);
}
- set("disableScript", () -> {
+ set("disableScript", () ->
+ {
active = false;
});
set("__script__", this);
}
-
/**
* Loads the script
*/
- public function load() {
- if(didLoad) return;
+ public function load()
+ {
+ if (didLoad)
+ return;
var oldScript = curScript;
curScript = this;
@@ -309,38 +348,36 @@ class Script extends FlxBasic implements IFlxDestroyable {
* HSCRIPT ONLY FOR NOW
* Sets the "public" variables map for ScriptPack
*/
- public function setPublicMap(map:Map) {
-
+ public function setPublicMap(map:Map)
+ {
}
/**
* Hot-reloads the script, if possible
*/
- public function reload() {
-
+ public function reload()
+ {
}
/**
* Traces something as this script.
*/
- public function trace(v:Dynamic) {
+ public function trace(v:Dynamic)
+ {
var fileName = this.fileName;
- if(remappedNames.exists(fileName))
+ if (remappedNames.exists(fileName))
fileName = remappedNames.get(fileName);
- Logs.traceColored([
- Logs.logText(fileName + ': ', GREEN),
- Logs.logText(Std.string(v))
- ], TRACE);
+ Logs.traceColored([Logs.logText(fileName + ': ', GREEN), Logs.logText(Std.string(v))], TRACE);
}
-
/**
* Calls the function `func` defined in the script.
* @param func Name of the function
* @param parameters (Optional) Parameters of the function.
* @return Result (if void, then null)
*/
- public function call(func:String, ?parameters:Array):Dynamic {
+ public function call(func:String, ?parameters:Array):Dynamic
+ {
var oldScript = curScript;
curScript = this;
@@ -354,7 +391,8 @@ class Script extends FlxBasic implements IFlxDestroyable {
* Loads the code from a string, doesn't really work after the script has been loaded
* @param code The code.
*/
- public function loadFromString(code:String) {
+ public function loadFromString(code:String)
+ {
return this;
}
@@ -362,42 +400,45 @@ class Script extends FlxBasic implements IFlxDestroyable {
* Sets a script's parent object so that its properties can be accessed easily. Ex: Passing `PlayState.instance` will allow `boyfriend` to be typed instead of `PlayState.instance.boyfriend`.
* @param variable Parent variable.
*/
- public function setParent(variable:Dynamic) {}
+ public function setParent(variable:Dynamic)
+ {
+ }
/**
* Gets the variable `variable` from the script's variables.
* @param variable Name of the variable.
* @return Variable (or null if it doesn't exists)
*/
- public function get(variable:String):Dynamic {return null;}
+ public function get(variable:String):Dynamic
+ {
+ return null;
+ }
/**
* Sets the variable `variable` from the script's variables.
* @param variable Name of the variable.
* @return Variable (or null if it doesn't exists)
*/
- public function set(variable:String, value:Dynamic):Void {}
+ public function set(variable:String, value:Dynamic):Void
+ {
+ }
/**
* Shows an error from this script.
* @param text Text of the error (ex: Null Object Reference).
* @param additionalInfo Additional information you could provide.
*/
- public function error(text:String, ?additionalInfo:Dynamic):Void {
+ public function error(text:String, ?additionalInfo:Dynamic):Void
+ {
var fileName = this.fileName;
- if(remappedNames.exists(fileName))
+ if (remappedNames.exists(fileName))
fileName = remappedNames.get(fileName);
- Logs.traceColored([
- Logs.logText(fileName, RED),
- Logs.logText(text)
- ], ERROR);
+ Logs.traceColored([Logs.logText(fileName, RED), Logs.logText(text)], ERROR);
}
- override public function toString():String {
- return FlxStringUtil.getDebugString(didLoad ? [
- LabelValuePair.weak("path", path),
- LabelValuePair.weak("active", active),
- ] : [
+ override public function toString():String
+ {
+ return FlxStringUtil.getDebugString(didLoad ? [LabelValuePair.weak("path", path), LabelValuePair.weak("active", active),] : [
LabelValuePair.weak("path", path),
LabelValuePair.weak("active", active),
LabelValuePair.weak("loaded", didLoad),
@@ -407,17 +448,23 @@ class Script extends FlxBasic implements IFlxDestroyable {
/**
* PRIVATE HANDLERS - DO NOT TOUCH
*/
- private function onCall(func:String, parameters:Array):Dynamic {
+ private function onCall(func:String, parameters:Array):Dynamic
+ {
return null;
}
+
/**
* Called when the script is created.
* @param path Path to the script
*/
- public function onCreate(path:String) {}
+ public function onCreate(path:String)
+ {
+ }
/**
* Called when the script is loaded.
*/
- public function onLoad() {}
+ public function onLoad()
+ {
+ }
}
diff --git a/source/funkin/backend/shaders/BlendModeEffect.hx b/source/funkin/backend/shaders/BlendModeEffect.hx
deleted file mode 100644
index 2865829285..0000000000
--- a/source/funkin/backend/shaders/BlendModeEffect.hx
+++ /dev/null
@@ -1,36 +0,0 @@
-package funkin.backend.shaders;
-
-import flixel.util.FlxColor;
-import openfl.display.ShaderParameter;
-
-@:dox(hide)
-typedef BlendModeShader =
-{
- var uBlendColor:ShaderParameter;
-}
-
-@: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;
- }
-}
diff --git a/source/funkin/backend/shaders/CustomShader.hx b/source/funkin/backend/shaders/CustomShader.hx
index 62308e69ee..61066575c2 100644
--- a/source/funkin/backend/shaders/CustomShader.hx
+++ b/source/funkin/backend/shaders/CustomShader.hx
@@ -2,39 +2,15 @@ package funkin.backend.shaders;
import openfl.Assets;
-/**
- * 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`
- */
+@:deprecated("Use funkin.backend.shaders.FunkinShader.fromFile instead.")
class CustomShader extends FunkinShader {
- public var path:String = "";
+ @: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 return path = cast v;
- /**
- * 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);
+ public function new(name:String, ?glslVersion:String) {
+ super();
+ loadShaderFile(Paths.fragShader(name), Paths.vertShader(name), glslVersion);
}
}
\ No newline at end of file
diff --git a/source/funkin/backend/shaders/FunkinShader.hx b/source/funkin/backend/shaders/FunkinShader.hx
index 281616b03b..58fa9a4b8f 100644
--- a/source/funkin/backend/shaders/FunkinShader.hx
+++ b/source/funkin/backend/shaders/FunkinShader.hx
@@ -1,533 +1,65 @@
package funkin.backend.shaders;
-import flixel.graphics.FlxGraphic;
-import flixel.system.FlxAssets.FlxShader;
-import flixel.util.FlxSignal.FlxTypedSignal;
+import haxe.io.Path;
import haxe.Exception;
+
import hscript.IHScriptCustomBehaviour;
-import lime.utils.Float32Array;
+
import openfl.display.BitmapData;
-import openfl.display.ShaderInput;
+import openfl.display.Shader;
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 FlxShader implements IHScriptCustomBehaviour {
- private static var __instanceFields = Type.getInstanceFields(FunkinShader);
- private static var FRAGMENT_SHADER = 0;
- private static var VERTEX_SHADER = 1;
-
+class FunkinShader extends FlxRuntimeShader implements IHScriptCustomBehaviour {
public var onGLUpdate:FlxTypedSignalVoid> = new FlxTypedSignalVoid>();
- public var onProcessGLData:FlxTypedSignal<(String, String)->Void> = new FlxTypedSignal<(String, String)->Void>();
-
- 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();
- }
-
- 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;
- }
-
- 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);
- }
-
- return shader;
- }
-
- @: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();
+ public function new(?fragmentSource:String, ?vertexSource:String, ?version:String) {
+ super(fragmentSource, vertexSource, version ?? (fragmentSource != null || vertexSource != null ? Flags.DEFAULT_GLSL_VERSION : null));
}
- @: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
- }
+ public static function fromFile(fragmentPath:String, ?vertexPath:String, ?version:String):FunkinShader {
+ return new FunkinShader().loadShaderFile(fragmentPath, vertexPath, version);
}
- 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_]+)/;
+ 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);
}
- while (regex.matchSub(source, lastMatch))
- {
- type = regex.matched(1);
- name = regex.matched(2);
-
- if (StringTools.startsWith(name, "gl_"))
- {
- continue;
- }
+ fragmentPath = FlxRuntimeShader._getPath(fragmentPath, false);
+ vertexPath = FlxRuntimeShader._getPath(vertexPath, true);
+ _fromFile(fragmentPath, vertexPath, version ?? (fragmentPath != null || vertexPath != null ? Flags.DEFAULT_GLSL_VERSION : null));
- var isUniform = (storageType == "uniform");
- registerParameter(name, type, isUniform);
-
- position = regex.matchedPos();
- lastMatch = position.pos + position.len;
- }
+ return this;
}
- function registerParameter(name:String, type:String, isUniform:Bool):Void
- {
- if (StringTools.startsWith(type, "sampler"))
- {
- var input = new ShaderInput();
- 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;
- }
+ #if REGION /* IHScriptCustomBehaviour */
+ public function hget(name:String):Dynamic {
+ if (__glSourceDirty) __init();
- 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;
- }
+ if (__thisHasField(name) || __thisHasField('get_${name}')) return Reflect.getProperty(this, name);
+ else if (!Reflect.hasField(__data, name)) return null;
- var arrayLength = switch (parameterType)
- {
- case MATRIX2X2: 2;
- case MATRIX3X3: 3;
- case MATRIX4X4: 4;
- default: 1;
- }
+ final field:Dynamic = Reflect.field(__data, name);
- switch (parameterType)
- {
- case BOOL, BOOL2, BOOL3, BOOL4:
- var parameter = new ShaderParameter();
- 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();
- 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();
- 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) {}
- }
- }
- }
-
- public function hget(name:String):Dynamic {
- 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...
@@ -546,17 +78,19 @@ class FunkinShader extends FlxShader implements IHScriptCustomBehaviour {
}
public function hset(name:String, val:Dynamic):Dynamic {
- if (__instanceFields.contains(name) || __instanceFields.contains('set_${name}')) {
+ if (__glSourceDirty) __init();
+
+ if (__thisHasField(name) || __thisHasField('set_${name}')) {
Reflect.setProperty(this, name, val);
return val;
}
-
- if (!Reflect.hasField(data, name)) {
- Reflect.setField(data, name, val);
+ else if (!Reflect.hasField(__data, name)) {
+ // ??? huh
+ 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
@@ -598,67 +132,142 @@ class FunkinShader extends FlxShader implements IHScriptCustomBehaviour {
return val;
}
-}
+ #end
-class ShaderTemplates {
- public static final fragHeader:String = "varying float openfl_Alphav;
-varying vec4 openfl_ColorMultiplierv;
-varying vec4 openfl_ColorOffsetv;
-varying vec2 openfl_TextureCoordv;
+ override function __updateGL():Void {
+ onGLUpdate.dispatch();
+ super.__updateGL();
+ }
-uniform bool openfl_HasColorTransform;
-uniform vec2 openfl_TextureSize;
-uniform sampler2D bitmap;
+ override function __createAssembler():Void {
+ __glSourceAssembler = new FunkinShaderSourceAssembler(this);
+ }
-uniform bool hasTransform;
-uniform bool hasColorTransform;
+ 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;
-vec4 applyFlixelEffects(vec4 color) {
- if(!hasTransform) {
- return color;
+ 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);
}
- if(color.a == 0.0) {
- return vec4(0.0, 0.0, 0.0, 0.0);
+ // 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);
}
- if(!hasColorTransform) {
- return color * openfl_Alphav;
+ override function __appendIncludes(source:String, isVertex:Bool, ?includedKeys:Map):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);
+ });
}
- color.rgb = color.rgb / color.a;
- color = clamp(openfl_ColorOffsetv + (color * openfl_ColorMultiplierv), 0.0, 1.0);
+ override function __getIncludeSource(include:String, fromVertex:Bool):Null {
+ final path = Paths.getPath('shaders/' + include);
+ if (Assets.exists(path)) return Assets.getText(path);
- if(color.a > 0.0) {
- return vec4(color.rgb * color.a * openfl_Alphav, color.a * openfl_Alphav);
+ 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;
}
- 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);
-}
+ override function __appendPrefix(source:String, versionNumber:Int, versionProfile:String, extensions:Map, isVertex:Bool,
+ precisionHint:Null):String
+ {
+ var result = super.__appendPrefix(null, versionNumber, versionProfile, extensions, isVertex, precisionHint) + "\n";
-uniform vec4 _camSize;
+ result += funkinParent.shaderPrefix + "\n" + (isVertex ? funkinParent.vertexPrefix : funkinParent.fragmentPrefix) + "\n";
-float map(float value, float min1, float max1, float min2, float max2) {
- return min2 + (value - min1) * (max2 - min2) / (max1 - min1);
-}
+ if (source != null) {
+ if (!isVertex && versionNumber >= 300 && versionProfile != "compatibility" && !StringTools.contains(source, "out vec4")) {
+ result += "out vec4 openfl_FragColor;\n";
+ }
+ result += source;
+ }
-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));
+ return result;
+ }
+
+ private static inline function __getImportCompatibilityFinder():EReg {
+ return ~/#import\s+(?|"([^"]+)"|'([^']+)'|<(.*)>|([^\s]+))/g;
+ }
}
-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 REGION /* Backward Compatibility */
+class ShaderTemplates {
public static final vertHeader:String = "attribute float openfl_Alpha;
attribute vec4 openfl_ColorMultiplier;
attribute vec4 openfl_ColorOffset;
@@ -679,32 +288,97 @@ attribute vec4 colorMultiplier;
attribute vec4 colorOffset;
uniform bool hasColorTransform;";
- public static final vertBody:String = "openfl_Alphav = openfl_Alpha;
-openfl_TextureCoordv = openfl_TextureCoord;
+ public static final vertBody:String = "openfl_TextureCoordv = openfl_TextureCoord;
-if(openfl_HasColorTransform) {
- openfl_ColorMultiplierv = openfl_ColorMultiplier;
- openfl_ColorOffsetv = openfl_ColorOffset / 255.0;
+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;
+uniform bool premultiplyAlpha;
-openfl_Alphav = openfl_Alpha * alpha;
+vec4 apply_flixel_transform(vec4 color)
+{
+ if (!hasTransform) return color;
+ else if (color.a <= 0.0 || openfl_Alphav == 0.0) return vec4(0.0);
-if(hasColorTransform) {
- openfl_ColorOffsetv = colorOffset / 255.0;
- openfl_ColorMultiplierv = colorMultiplier;
+ // 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;
+
+ 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)
-gl_Position = openfl_Matrix * openfl_Position;";
+vec4 flixel_texture2D(sampler2D bitmap, vec2 coord)
+{
+ return apply_flixel_transform(texture2D(bitmap, coord));
+}
-// TODO: make this ignore comments
-public static final vertBackCompatVarList:Array = [
- ~/attribute float alpha/,
- ~/attribute vec4 colorMultiplier/,
- ~/attribute vec4 colorOffset/,
- ~/uniform bool hasColorTransform/
-];
+uniform vec4 _camSize;
+
+float map(float value, float min1, float max1, float min2, float max2) {
+ return min2 + (value - min1) * (max2 - min2) / (max1 - min1);
+}
-public static final vertHeaderBackCompat:String = "attribute float openfl_Alpha;
+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 = [
+ ~/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;
@@ -719,7 +393,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) {
@@ -728,19 +402,8 @@ 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;
@@ -753,16 +416,4 @@ 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;
- }
}
\ No newline at end of file
diff --git a/source/funkin/backend/shaders/FunkinShaderOLD._hx b/source/funkin/backend/shaders/FunkinShaderOLD._hx
new file mode 100644
index 0000000000..1853813dde
--- /dev/null
+++ b/source/funkin/backend/shaders/FunkinShaderOLD._hx
@@ -0,0 +1,471 @@
+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 {
+ 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
+ return Shader.processGLSLText(_processGLSLText(source, glVersion, isFragment, pragmas), glVersion, isFragment);
+
+ private static function _processGLSLText(source:String, glVersion:String, isFragment:Bool, ?pragmas:Map):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;
+ private static var __glVertexPragmasDefault:Map;
+ private static var __glFragmentExtensionsDefault:Array;
+ private static var __glVertexExtensionsDefault:Array;
+
+ public var onGLUpdate:FlxTypedSignalVoid> = new FlxTypedSignalVoid>();
+
+ 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, ?vertexExtensions:Array, 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 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):Array {
+ if (value == null) value = __glFragmentExtensionsDefault;
+ if (value != __glFragmentExtensions) __glSourceDirty = true;
+ return __glFragmentExtensions = value;
+ }
+
+ override function set_glVertexExtensions(value:Array):Array {
+ if (value == null) value = __glVertexExtensionsDefault;
+ if (value != __glVertexExtensions) __glSourceDirty = true;
+ return __glVertexExtensions = value;
+ }
+
+ override function set_glVersion(value:Null):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):Map {
+ if (value == null) value = __glFragmentPragmasDefault;
+ if (value != __glFragmentPragmas)
+ __glSourceDirty = true;
+
+ return __glFragmentPragmas = value;
+ }
+
+ override function set_glVertexPragmas(value:Map):Map {
+ 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 = [
+ ~/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;
+ var want:Class;
+ var name:String;
+
+ public function new(name:String, has:Class, 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)}.');
+ }
+}
\ No newline at end of file
diff --git a/source/funkin/backend/system/FakeCamera.hx b/source/funkin/backend/system/FakeCamera.hx
index 99d0eb1b88..31caaac51a 100644
--- a/source/funkin/backend/system/FakeCamera.hx
+++ b/source/funkin/backend/system/FakeCamera.hx
@@ -11,6 +11,11 @@ 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();
@@ -21,14 +26,14 @@ class FakeCamera extends FlxCamera {
visible = false;
}
- 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 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 clearDrawStack() {}
override function render() {}
- 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, indices:DrawData, uvtData:DrawData, ?colors:DrawData, ?position:FlxPoint, ?blend:BlendMode, repeat:Bool = false, smoothing:Bool = false, ?transform:ColorTransform, ?shader:FlxShader):Void {}
+ 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, indices:DrawData, uvtData:DrawData, ?colors:DrawData, ?position:FlxPoint, ?blend:BlendMode, repeat:Bool = false, smoothing:Bool = false, ?transform:ColorTransform, ?shader:FlxShader, ?wrapMode:Context3DWrapMode, ?depthCompareMode:Context3DCompareMode, ?culling:TriangleCulling):Void {}
public override function update(elapsed:Float) {}
@@ -41,10 +46,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) {
+ public dynamic function onDraw(?frame:FlxFrame, ?pixels:BitmapData, matrix:FlxMatrix, ?transform:ColorTransform, ?blend:BlendMode, ?smoothing:Bool = false, ?shader:FlxShader, ?wrapMode:Context3DWrapMode, ?depthCompareMode:Context3DCompareMode) {
}
- 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);
+ 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);
}
}
\ No newline at end of file
diff --git a/source/funkin/backend/system/Flags.hx b/source/funkin/backend/system/Flags.hx
index 216d02fdd9..9626a3ae73 100644
--- a/source/funkin/backend/system/Flags.hx
+++ b/source/funkin/backend/system/Flags.hx
@@ -107,6 +107,10 @@ class Flags {
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 = null;
+ @:lazy public static var USE_FLXTRAIL_FRAMES:Null = null;
+
public static var SUPPORTED_CHART_RUNTIME_FORMATS:Array = ["Legacy", "Psych Engine"];
public static var SUPPORTED_CHART_FORMATS:Array = ["BaseGame"];
@@ -285,7 +289,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";
- public static var DEFAULT_GLSL_VERSION:String = "120";
+ @:lazy public static var DEFAULT_GLSL_VERSION:String = null;
@:also(funkin.backend.utils.HttpUtil.userAgent)
public static var USER_AGENT:String = 'request';
// -- End of Codename's Default Flags --
@@ -319,6 +323,21 @@ class Flags {
if (WINDOW_TITLE_USE_MOD_NAME == null) WINDOW_TITLE_USE_MOD_NAME = !overridenFlags.exists('TITLE') && overridenFlags.exists('MOD_NAME');
if (USE_LEGACY_TIMING == null) USE_LEGACY_TIMING = 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;
+
if (USE_LEGACY_CENTER_CAM == null) USE_LEGACY_CENTER_CAM = MOD_API_VERSION < 3;
if (USE_LEGACY_FLXANIMATE_STAGE_MATRIX == null) USE_LEGACY_FLXANIMATE_STAGE_MATRIX = MOD_API_VERSION < 3;
}
diff --git a/source/funkin/backend/system/FunkinGame.hx b/source/funkin/backend/system/FunkinGame.hx
index b754c8d41d..08d2b5987d 100644
--- a/source/funkin/backend/system/FunkinGame.hx
+++ b/source/funkin/backend/system/FunkinGame.hx
@@ -28,12 +28,12 @@ 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();
- _total = ticks = getTicks();
+ ticks = getTicks();
skipNextTickUpdate = true;
}
- public override function onEnterFrame(t) {
- if (skipNextTickUpdate != (skipNextTickUpdate = false)) _total = ticks = getTicks();
- super.onEnterFrame(t);
+ override function __enterFrame(deltaTime:Float) {
+ if (skipNextTickUpdate != (skipNextTickUpdate = false)) ticks = getTicks();
+ super.__enterFrame(deltaTime);
}
}
diff --git a/source/funkin/backend/system/GraphicCacheSprite.hx b/source/funkin/backend/system/GraphicCacheSprite.hx
index 6ff61905b3..cba41b61bd 100644
--- a/source/funkin/backend/system/GraphicCacheSprite.hx
+++ b/source/funkin/backend/system/GraphicCacheSprite.hx
@@ -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.useCount++;
+ graphic.incrementUseCount();
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.useCount--;
+ g.decrementUseCount();
}
graphic = null;
super.destroy();
diff --git a/source/funkin/backend/system/Logs.hx b/source/funkin/backend/system/Logs.hx
index 1913e4abc6..3903caf61b 100644
--- a/source/funkin/backend/system/Logs.hx
+++ b/source/funkin/backend/system/Logs.hx
@@ -115,7 +115,7 @@ final class Logs {
Sys.print(t.text);
}
NativeAPI.setConsoleColors();
- Sys.print("\r\n");
+ Sys.println("\r");
__showing = false;
#elseif mobile
while(__showing) {
diff --git a/source/funkin/backend/system/Main.hx b/source/funkin/backend/system/Main.hx
index 31d270d9d2..ed033baf13 100644
--- a/source/funkin/backend/system/Main.hx
+++ b/source/funkin/backend/system/Main.hx
@@ -24,8 +24,8 @@ import openfl.utils.AssetLibrary;
import sys.FileSystem;
import sys.io.File;
#if android
-import android.content.Context;
-import android.os.Build;
+import extension.androidtools.content.Context;
+import extension.androidtools.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 = #if desktop false #else true #end;
+ public static var forceGPUOnlyBitmapsOff:Bool = false;
public static var noTerminalColor:Bool = false;
public static var verbose:Bool = false;
@@ -58,9 +58,11 @@ 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()
@@ -95,16 +97,10 @@ 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();
@@ -129,12 +125,11 @@ 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);
@@ -206,16 +201,6 @@ 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();
}
diff --git a/source/funkin/backend/system/MainState.hx b/source/funkin/backend/system/MainState.hx
index 14988dddc4..6e05b14f56 100644
--- a/source/funkin/backend/system/MainState.hx
+++ b/source/funkin/backend/system/MainState.hx
@@ -42,6 +42,7 @@ class MainState extends FlxState {
ControlsUtil.resetCustomControls();
FlxG.bitmap.reset();
FlxG.sound.destroy(true);
+ FlxG.sound.resetCache();
Paths.assetsTree.reset();
@@ -163,16 +164,16 @@ class MainState extends FlxState {
}
var startState:Class = Flags.DISABLE_WARNING_SCREEN ? TitleState : funkin.menus.WarningState;
-
+ var outdatedAPI:Bool = (Flags.MOD_API_VERSION ?? Flags.CURRENT_API_VERSION) < Flags.CURRENT_API_VERSION;
// 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) {
var lib:ModsFolderLibrary;
for (e in Paths.assetsTree.libraries) if ((lib = cast AssetsLibraryList.getCleanLibrary(e)) is ModsFolderLibrary
&& lib.modName == ModsFolder.currentModFolder)
{
- if (lib.exists(Paths.ini("config/modpack"), lime.utils.AssetType.TEXT)) break;
+ if (!outdatedAPI && lib.exists(Paths.ini("config/modpack"), lime.utils.AssetType.TEXT)) break;
- FlxG.switchState(new ModConfigWarning(lib, startState));
+ FlxG.switchState(new ModConfigWarning(lib, startState, outdatedAPI));
return;
}
}
diff --git a/source/funkin/backend/system/OptimizedBitmapData.hx b/source/funkin/backend/system/OptimizedBitmapData.hx
index f6cf1117a9..510ebf14e2 100644
--- a/source/funkin/backend/system/OptimizedBitmapData.hx
+++ b/source/funkin/backend/system/OptimizedBitmapData.hx
@@ -1,62 +1,14 @@
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
{
- #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
+ super.__fromImage(image);
+ toHardware();
}
}
\ No newline at end of file
diff --git a/source/funkin/backend/system/RotatingSpriteGroup.hx b/source/funkin/backend/system/RotatingSpriteGroup.hx
index ad3eea575e..520e77417f 100644
--- a/source/funkin/backend/system/RotatingSpriteGroup.hx
+++ b/source/funkin/backend/system/RotatingSpriteGroup.hx
@@ -8,7 +8,12 @@ class RotatingSpriteGroup extends FlxSpriteGroup {
if (maxSize <= 0)
return super.recycle(ObjectClass, ObjectFactory, Force, Revive);
if (group.members.length < maxSize)
- return group.recycleCreateObject(ObjectClass, ObjectFactory);
+ {
+ if (ObjectFactory != null) return add(ObjectFactory());
+ if (ObjectClass != null) return add(Type.createInstance(ObjectClass, []));
+
+ return null;
+ }
var spr = group.members.shift();
group.members.push(spr);
if (Revive)
diff --git a/source/funkin/backend/system/framerate/AssetTreeInfo.hx b/source/funkin/backend/system/framerate/AssetTreeInfo.hx
index f81218879e..b7647261c8 100644
--- a/source/funkin/backend/system/framerate/AssetTreeInfo.hx
+++ b/source/funkin/backend/system/framerate/AssetTreeInfo.hx
@@ -15,7 +15,7 @@ class AssetTreeInfo extends FramerateCategory {
super("Asset Libraries Tree Info");
}
- public override function __enterFrame(t:Int) {
+ public override function __enterFrame(t:Float) {
if (alpha <= 0.05) return;
if ((lastUpdateTime += FlxG.rawElapsed) < 1)
diff --git a/source/funkin/backend/system/framerate/CodenameBuildField.hx b/source/funkin/backend/system/framerate/CodenameBuildField.hx
index 656c0fff9c..57bf752a2a 100644
--- a/source/funkin/backend/system/framerate/CodenameBuildField.hx
+++ b/source/funkin/backend/system/framerate/CodenameBuildField.hx
@@ -6,14 +6,17 @@ import openfl.text.TextField;
class CodenameBuildField extends TextField {
public function new() {
super();
- defaultTextFormat = Framerate.textFormat;
autoSize = LEFT;
multiline = wordWrap = false;
reload();
}
public function reload() {
- #if COMPILE_EXPERIMENTAL
+ 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}';
diff --git a/source/funkin/backend/system/framerate/ConductorInfo.hx b/source/funkin/backend/system/framerate/ConductorInfo.hx
index d5fc9a1737..3fb213d3dd 100644
--- a/source/funkin/backend/system/framerate/ConductorInfo.hx
+++ b/source/funkin/backend/system/framerate/ConductorInfo.hx
@@ -7,7 +7,7 @@ class ConductorInfo extends FramerateCategory {
super("Conductor Info");
}
- public override function __enterFrame(t:Int) {
+ public override function __enterFrame(t:Float) {
if (alpha <= 0.05) return;
var buf = new StringBuf();
diff --git a/source/funkin/backend/system/framerate/FlixelInfo.hx b/source/funkin/backend/system/framerate/FlixelInfo.hx
index 0ce42c299a..675bd2facc 100644
--- a/source/funkin/backend/system/framerate/FlixelInfo.hx
+++ b/source/funkin/backend/system/framerate/FlixelInfo.hx
@@ -8,7 +8,7 @@ class FlixelInfo extends FramerateCategory {
super("Flixel Info");
}
- public override function __enterFrame(t:Int) {
+ public override function __enterFrame(t:Float) {
if (alpha <= 0.05) return;
@:privateAccess {
diff --git a/source/funkin/backend/system/framerate/Framerate.hx b/source/funkin/backend/system/framerate/Framerate.hx
index c33b416c68..0d04c14bfb 100644
--- a/source/funkin/backend/system/framerate/Framerate.hx
+++ b/source/funkin/backend/system/framerate/Framerate.hx
@@ -76,6 +76,7 @@ class Framerate extends Sprite {
}
public function reload() {
+ textFormat = new TextFormat(fontName, 12, -1);
for(c in categories)
c.reload();
#if SHOW_BUILD_ON_FPS
@@ -100,7 +101,7 @@ class Framerate extends Sprite {
var debugAlpha:Float = 0;
- public override function __enterFrame(t:Int) {
+ public override function __enterFrame(t:Float) {
alpha = CoolUtil.fpsLerp(alpha, debugMode > 0 ? 1 : 0, 0.5);
debugAlpha = CoolUtil.fpsLerp(debugAlpha, debugMode > 1 ? 1 : 0, 0.5);
diff --git a/source/funkin/backend/system/framerate/FramerateCategory.hx b/source/funkin/backend/system/framerate/FramerateCategory.hx
index 0bae85b2db..3bc28e3e56 100644
--- a/source/funkin/backend/system/framerate/FramerateCategory.hx
+++ b/source/funkin/backend/system/framerate/FramerateCategory.hx
@@ -40,9 +40,11 @@ class FramerateCategory extends Sprite {
this.text.y = this.title.y + this.title.height + 2;
}
- public function reload() {}
+ public function reload() {
+ for(label in [this.title, this.text]) label.defaultTextFormat = new TextFormat(Framerate.fontName, label == this.title ? 18 : 12, -1);
+ }
- public override function __enterFrame(t:Int) {
+ public override function __enterFrame(t:Float) {
if (alpha <= 0.05) return;
super.__enterFrame(t);
diff --git a/source/funkin/backend/system/framerate/FramerateCounter.hx b/source/funkin/backend/system/framerate/FramerateCounter.hx
index 191440c230..cebfa9f51f 100644
--- a/source/funkin/backend/system/framerate/FramerateCounter.hx
+++ b/source/funkin/backend/system/framerate/FramerateCounter.hx
@@ -46,7 +46,7 @@ class FramerateCounter extends Sprite {
lastUpdateTime = 0;
}
- public override function __enterFrame(t:Int) {
+ public override function __enterFrame(t:Float) {
if (alpha <= 0.001) return;
super.__enterFrame(t);
diff --git a/source/funkin/backend/system/framerate/MemoryCounter.hx b/source/funkin/backend/system/framerate/MemoryCounter.hx
index 39962e2399..0530d0bb7b 100644
--- a/source/funkin/backend/system/framerate/MemoryCounter.hx
+++ b/source/funkin/backend/system/framerate/MemoryCounter.hx
@@ -23,7 +23,7 @@ class MemoryCounter extends Sprite {
label.y = 0;
label.text = "MEM";
label.multiline = label.wordWrap = false;
- label.defaultTextFormat = new TextFormat(Framerate.fontName, 12, -1);
+ label.defaultTextFormat = Framerate.textFormat;
label.selectable = false;
addChild(label);
}
@@ -33,11 +33,13 @@ class MemoryCounter extends Sprite {
#end
}
- public function reload() {}
+ public function reload() {
+ for(label in [memoryText, memoryPeakText]) label.defaultTextFormat = Framerate.textFormat;
+ }
private var usingLegacy:Bool = false;
- public override function __enterFrame(t:Int) {
+ public override function __enterFrame(t:Float) {
if (alpha <= 0.05) return;
super.__enterFrame(t);
diff --git a/source/funkin/backend/system/framerate/StatsInfo.hx b/source/funkin/backend/system/framerate/StatsInfo.hx
index bc98c39223..134c2649fc 100644
--- a/source/funkin/backend/system/framerate/StatsInfo.hx
+++ b/source/funkin/backend/system/framerate/StatsInfo.hx
@@ -10,7 +10,7 @@ class StatsInfo extends FramerateCategory {
super("Asset Libraries Tree Info");
}
- public override function __enterFrame(t:Int) {
+ public override function __enterFrame(t:Float) {
if (alpha <= 0.05) return;
var buf = new StringBuf();
diff --git a/source/funkin/backend/system/framerate/SystemInfo.hx b/source/funkin/backend/system/framerate/SystemInfo.hx
index 4562b338a1..1cda22ff07 100644
--- a/source/funkin/backend/system/framerate/SystemInfo.hx
+++ b/source/funkin/backend/system/framerate/SystemInfo.hx
@@ -181,7 +181,7 @@ class SystemInfo extends FramerateCategory {
super("System Info");
}
- public override function __enterFrame(t:Int) {
+ public override function __enterFrame(t:Float) {
if (alpha <= 0.05) return;
var buf = new StringBuf();
diff --git a/source/funkin/backend/system/macros/Macros.hx b/source/funkin/backend/system/macros/Macros.hx
index 801eda0c17..d73961bb09 100644
--- a/source/funkin/backend/system/macros/Macros.hx
+++ b/source/funkin/backend/system/macros/Macros.hx
@@ -19,7 +19,19 @@ class Macros {
"flixel.addons.plugin", "flixel.addons.text", "flixel.addons.tile", "flixel.addons.transition",
"flixel.addons.util",
// OTHER LIBRARIES & STUFF
- #if THREE_D_SUPPORT "away3d", "flx3d", #end
+ #if THREE_D_SUPPORT
+ "away3d", "flx3d", // deprecated
+ #if foxlite
+ "foxlite",
+ "foxlite.animation", "foxlite.color", "foxlite.culling",
+ "foxlite.extra", "foxlite.flixel", "foxlite.funkin",
+ "foxlite.groups", "foxlite.instancing", "foxlite.lights",
+ "foxlite.loaders", "foxlite.materials", "foxlite.math",
+ "foxlite.mesh", "foxlite.polyfill", "foxlite.post",
+ "foxlite.renderer", "foxlite.skin", "foxlite.sky",
+ "foxlite.stencil", "foxlite.system", "foxlite.texture",
+ #end
+ #end
#if VIDEO_CUTSCENES "hxvlc.flixel", "hxvlc.openfl", #end
#if NAPE_ENABLED "nape", "flixel.addons.nape", #end
// BASE HAXE
@@ -58,6 +70,9 @@ 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")) {
@@ -73,5 +88,58 @@ class Macros {
return fields;
}
+
+ public static function buildLimeApplication():Array {
+ final fields:Array = 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 {
+ final fields:Array = 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 {
+ final fields:Array = 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)});
+
+ 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;
+ }
}
#end
\ No newline at end of file
diff --git a/source/funkin/backend/system/modules/ALSoftConfig.hx b/source/funkin/backend/system/modules/ALSoftConfig.hx
deleted file mode 100644
index 0b1b57a035..0000000000
--- a/source/funkin/backend/system/modules/ALSoftConfig.hx
+++ /dev/null
@@ -1,31 +0,0 @@
-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
-}
\ No newline at end of file
diff --git a/source/funkin/backend/system/modules/AudioSwitchFix.hx b/source/funkin/backend/system/modules/AudioSwitchFix.hx
deleted file mode 100644
index 3e89e373b4..0000000000
--- a/source/funkin/backend/system/modules/AudioSwitchFix.hx
+++ /dev/null
@@ -1,69 +0,0 @@
-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;
- }
-}
\ No newline at end of file
diff --git a/source/funkin/backend/utils/AudioAnalyzer.hx b/source/funkin/backend/utils/AudioAnalyzer.hx
index c6f02cd6b9..74f4c11b98 100644
--- a/source/funkin/backend/utils/AudioAnalyzer.hx
+++ b/source/funkin/backend/utils/AudioAnalyzer.hx
@@ -1,67 +1,94 @@
package funkin.backend.utils;
-import flixel.sound.FlxSound;
-import lime.media.AudioBuffer;
+#if lime_openal
+import sys.thread.Mutex;
+
import lime.utils.ArrayBufferView.ArrayBufferIO;
import lime.utils.ArrayBuffer;
-#if (lime_cffi && lime_vorbis)
-import lime.media.vorbis.Vorbis;
-import lime.media.vorbis.VorbisFile;
-#end
+import flixel.sound.FlxSound;
+import flixel.sound.FlxSoundData;
-#if (target.threaded)
-import sys.thread.Mutex;
-#end
+typedef ReadCallback = Int->Int->Void;
+typedef WindowFunction = Float->Float;
+
+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;
-typedef AudioAnalyzerCallback = Int->Int->Void;
+ 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;
+}
/**
- * An utility that analyze FlxSounds,
+ * An utility that analyze FlxSound,
* 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 inline ArrayBufferIO.getInt16(buffer, position);
+ if (wordSize == 2) return ArrayBufferIO.getInt16(buffer, position);
else if (wordSize == 3) {
- var b = inline ArrayBufferIO.getUint16(buffer, position) | (buffer.get(position + 2) << 16);
- if (b & 0x800000 != 0) return b - 0x1000000;
- else return b;
+ wordSize = ArrayBufferIO.getUint16(buffer, position) | (buffer.get(position + 2) << 16);
+ if (wordSize & 0x800000 != 0) return wordSize - 0x1000000;
+ else return wordSize;
}
- else if (wordSize == 4) return inline ArrayBufferIO.getInt32(buffer, position);
- else return inline ArrayBufferIO.getUint8(buffer, position) - 128;
+ else if (wordSize == 4) return ArrayBufferIO.getInt32(buffer, position);
+ else return ArrayBufferIO.getInt8(buffer, position);
}
/**
- * 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.
+ * 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.
*/
- public static function getLevelsFromFrequencies(frequencies:Array, sampleRate:Int, barCount:Int, ?levels:Array, ratio = 0.0, minDb = -63.0, maxDb = -10.0, minFreq = 20.0, maxFreq = 22000.0):Array {
- if (levels == null) levels = [];
- levels.resize(barCount);
+ public static function getSpectrumFromFrequencies(frequencies:Array, sampleRate:Int, barCount:Int, ?spectrum:Array, ratio = 0.0, minDb = -63.0, maxDb = -10.0, minFreq = 20.0, maxFreq = 20000.0):Array {
+ if (spectrum == null) spectrum = [];
+ if (spectrum.length != barCount) spectrum.resize(barCount);
- var logMin = Math.log(minFreq), logMax = Math.log(maxFreq);
- var logRange = logMax - logMin, dbRange = maxDb - minDb, n = frequencies.length;
+ var logMin = Math.log(minFreq), n = frequencies.length - 1;
+ var logRange = Math.log(maxFreq) - logMin, dbRangeRate = 1 / (maxDb - minDb), rate = frequencies.length * 2 / sampleRate;
inline function calculateScale(i:Int)
- return CoolUtil.bound(Math.exp(logMin + (logRange * i / (barCount + 1))) * n * 2 / sampleRate, 0, n - 1);
+ return FlxMath.bound(Math.exp(logMin + (logRange * i / (barCount + 1))) * rate, 0, n);
var s1 = calculateScale(0), s2;
var i1 = Math.floor(s1), i2;
@@ -81,138 +108,146 @@ final class AudioAnalyzer {
}
i1 = Math.floor(s1 = s2);
- 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;
+ 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;
}
- return levels;
+ return spectrum;
}
- static var __reverseIndices:Array> = [];
- static var __windows:Array> = [];
- static var __twiddleReals:Array> = [];
- static var __twiddleImags:Array> = [];
- static var __freqReals:Array> = [];
- static var __freqImags:Array> = [];
- static var __freqCalculating:Int = 0;
- #if (target.threaded)
- static var __mutex:Mutex = new Mutex();
- #end
+ /**
+ * 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, sampleRate:Int, barCount:Int, ?levels:Array, ratio = 0.0, minDb = -63.0, maxDb = -10.0, minFreq = 20.0, maxFreq = 22000.0):Array
+ return inline getSpectrumFromFrequencies(frequencies, sampleRate, barCount, levels, ratio, minDb, maxDb, minFreq, maxFreq);
+
+ static final _permutations:Map> = [];
+ static final _twiddleReals:Map> = [];
+ static final _twiddleImags:Map> = [];
+ static final _reals:Array> = [];
+ static final _imags:Array> = [];
+ static var _freqCalculating:Int = 0;
+ static final _mutex = new Mutex();
/**
* Gets frequencies from the samples.
- * @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.
+ * @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.
*/
- public static function getFrequenciesFromSamples(samples:Array, fftN = 2048, useWindowing = false, ?frequencies:Array):Array {
- var log = Math.floor(Math.log(fftN) / 0.6931471805599453);
- if (log == 0) throw "AudioAnalyzer.getFrequenciesFromSamples: Cannot insert a fftN of 1";
-
- var i = log - 1;
- fftN = 1 << log;
-
- #if (target.threaded) __mutex.acquire(); #end
- var reals:Array = __freqReals[__freqCalculating], imags:Array = __freqImags[__freqCalculating];
- if (reals == null) {
- __freqReals.push(reals = []);
- __freqImags.push(imags = []);
- }
- __freqCalculating++;
+ public static function getFrequenciesFromSamples(samples:Array, ?window:WindowFunction, ?frequencies:Array, ?fftN:Int):Array {
+ if (fftN == null) fftN = samples.length;
- var reverseIndices:Array = __reverseIndices[i];
- var windows:Array = __windows[i];
- var twiddleReals:Array = __twiddleReals[i];
- var twiddleImags:Array = __twiddleImags[i];
+ var bits = 0;
+ while ((fftN >>= 1) > 0) bits++;
+ if (bits == 0) throw "FunkinAudioAnalyzer.getFrequenciesFromSamples: Cannot insert a sample length or fftN of 1";
- if (reverseIndices == null) {
- __reverseIndices.resize(log);
- __windows.resize(log);
- __twiddleReals.resize(log);
- __twiddleImags.resize(log);
+ fftN = 1 << bits;
+ var fftN2 = fftN >> 1, n = fftN - 1;
- (reverseIndices = []).resize(fftN);
- (windows = []).resize(fftN);
- (twiddleReals = []).resize(fftN);
- (twiddleImags = []).resize(fftN);
+ var permutation:Array, twiddleReal:Array, twiddleImag:Array;
+ _mutex.acquire();
- var f;
+ var real:Array = _reals[_freqCalculating], imag:Array = _imags[_freqCalculating];
+ if (real == null) {
+ _reals.push(real = []);
+ _imags.push(imag = []);
+ }
+ _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 ang:Float;
for (i in 0...fftN) {
- 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);
+ permutation[i] = _bitReverse(i, bits);
+ if (i < fftN2) {
+ twiddleReal[i] = Math.cos((ang = -6.283185307179586 * i / n));
+ twiddleImag[i] = Math.sin(ang);
+ }
}
- __reverseIndices[i] = reverseIndices;
- __windows[i] = windows;
- __twiddleReals[i] = twiddleReals;
- __twiddleImags[i] = twiddleImags;
+ _permutations.set(bits, permutation);
+ _twiddleReals.set(bits, twiddleReal);
+ _twiddleImags.set(bits, twiddleImag);
}
- #if (target.threaded) __mutex.release(); #end
+ _mutex.release();
- if (fftN > reals.length) {
- reals.resize(fftN);
- imags.resize(fftN);
+ if (fftN > real.length) {
+ real.resize(fftN);
+ imag.resize(fftN);
}
if (frequencies == null) frequencies = [];
- frequencies.resize(1 << i);
+ if (frequencies.length != fftN2) frequencies.resize(fftN2);
- 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 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;
}
- 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++;
+ 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;
}
- i += size;
+ g += half << 1;
}
- half = size;
+ half <<= 1;
+ fftN2 >>= 1;
}
tr = 1.0 / fftN;
- 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;
+ 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;
- #if (target.threaded) __mutex.acquire(); #end
- __freqCalculating--;
- #if (target.threaded) __mutex.release(); #end
+ _mutex.acquire();
+ _freqCalculating--;
+ _mutex.release();
return frequencies;
}
- static function __bitReverse(x:Int, log:Int):Int {
- var y = 0, i = log;
+ static function _bitReverse(x:Int, bits:Int):Int {
+ var y = 0, i = bits;
while (i > 0) {
y = (y << 1) | (x & 1);
x >>= 1;
@@ -227,346 +262,357 @@ final class AudioAnalyzer {
public var sound:FlxSound;
/**
- * 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.
+ * The current data from sound.
*/
- public var useWindowingFFT:Bool;
+ public var data(default, null):FlxSoundData;
/**
- * The current buffer from sound.
+ * How much samples for the fourier transform to get.
+ * Has to be power of two, or it won't work.
*/
- public var buffer(default, null):AudioBuffer;
+ public var fftN:Int;
/**
* 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 __toBits:Float;
- var __wordSize:Int;
- var __sampleSize:Int;
-
- #if (lime_cffi && lime_vorbis)
- var __vorbis:VorbisFile;
- var __buffer:ArrayBuffer;
- var __bufferSize:Int;
- var __bufferLastSize:Int;
- var __bufferTime:Float;
- var __bufferLastTime:Float;
- #end
-
- // analyze
- var __min:Array = [];
- var __max:Array = [];
- 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;
-
- // frequencies
- var __freqSamples:Array;
- var __frequencies:Array;
-
- /**
- * 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 new(sound:FlxSound, fftN = 2048, useWindowingFFT = false) {
+ var _sampleSize:Int;
+ var _mins:Array = [];
+ var _maxs:Array = [];
+ //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;
+ var _freqSamples:Array;
+ var _frequencies:Array;
+
+ public function new(sound:FlxSound, fftN = 4096) {
this.sound = sound;
this.fftN = fftN;
- this.useWindowingFFT = useWindowingFFT;
- __check();
+ _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
+ 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;
+ }
- __toBits = buffer.sampleRate / 1000 * (__sampleSize = buffer.channels * (__wordSize = buffer.bitsPerSample >> 3));
- __min.resize(buffer.channels);
- __max.resize(buffer.channels);
+ /**
+ * 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.
+ */
+ public function getSpectrum(?pos:Float, ?timeUnit:TimeUnit, ?gain:Float, ?window:WindowFunction, barCount:Int, ?spectrum:Array, ?ratio:Float, ?minDb:Float, ?maxDb:Float, ?minFreq:Float, ?maxFreq:Float):Array {
+ return getSpectrumFromFrequencies(_frequencies = getFrequencies(pos, timeUnit, gain, window, _frequencies), data.sampleRate, barCount, spectrum, ratio, minDb, maxDb, minFreq, maxFreq);
}
/**
* 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.
+ * @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.
*/
+ @:deprecated("Use getSpectrum instead of getLevels.")
public function getLevels(?startPos:Float, ?volume:Float, barCount:Int, ?levels:Array, ?ratio:Float, ?minDb:Float, ?maxDb:Float, ?minFreq:Float, ?maxFreq:Float):Array
- return inline getLevelsFromFrequencies(__frequencies = getFrequencies(startPos, volume, __frequencies), buffer.sampleRate, barCount, levels, ratio, minDb, maxDb, minFreq, maxFreq);
+ return inline getSpectrum(startPos, MILLISECOND, volume, null, barCount, levels, ratio, minDb, maxDb, minFreq, maxFreq);
/**
- * 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.
+ * 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.
*/
- public function getFrequencies(?startPos:Float, ?volume:Float, ?frequencies:Array):Array
- return inline getFrequenciesFromSamples(__freqSamples = getSamples(startPos != null ? startPos : sound.time, fftN, true, -1, volume, __freqSamples), fftN, useWindowingFFT, frequencies);
+ public function getFrequencies(?pos:Float, ?timeUnit:TimeUnit, ?gain:Float, ?window:WindowFunction, ?frequencies:Array):Array {
+ 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);
+ }
/**
- * 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.
+ * 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.
*/
- public function analyze(startPos:Float, endPos:Float, ?outOrOutMin:Array, ?outMax:Array):Float {
- var hasOut = outOrOutMin != null;
- var hasTwoOut = hasOut && outMax != null;
-
- if (hasTwoOut) for (i in 0...buffer.channels) __min[i] = __max[i] = 0;
- __minByte = __maxByte = 0;
-
- __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;
+ public function analyze(startPos:Float, endPos:Float, ?timeUnit:TimeUnit, ?outOrOutMins:Array, ?outMaxs:Array):Float {
+ var hasOut = outOrOutMins != null;
+ var hasTwoOut = hasOut && outMaxs != 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);
+
+ 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;
}
+ if (_maxs[i] > max) max = _maxs[i];
+ if (_mins[i] > min) min = _mins[i];
}
- return (__maxByte + __minByte) / byteSize;
+ 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;
}
- 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)));
+ function _analyzeRead(b:Int, c:Int) ((b > _maxs[c]) ? (_maxs[c] = b) : (if (-b > _mins[c]) (_mins[c] = -b)));
/**
* Gets samples from startPos with given length 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.
+ * @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.
*/
- public function getSamples(startPos:Float, length:Int, mono = true, channel = -1, volume = 1.0, ?output:Array, ?outputMerge = false):Array {
- ((!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;
-
- __check();
- __read(startPos, startPos + (length / __toBits * buffer.channels), mono ? __getSamplesCallbackMono : (channel == -1 ? __getSamplesCallback : __getSamplesCallbackChannel));
-
- __sampleOutput = null;
+ public function getSamples(startPos:Float, ?timeUnit:TimeUnit, length:Int, mono = true, channel = -1, gain = 1.0, ?output:Array, ?outputMerge = false):Array {
+ _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;
+
+ 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));
+
+ _sampleOutput = null;
return output;
}
- 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++;
+ function _getSamplesCallbackMono(b:Int, c:Int):Void if (_sampleIndex < _sampleOutputLength) {
+ if (c == 0) _sampleValue = idiv(b, data.channels);
+ else _sampleValue += idiv(b, data.channels);
+
+ if (c == _sampleChannel) {
+ if (_sampleOutputMerge) _sampleOutput[_sampleIndex] += _sampleValue / byteSize;
+ else _sampleOutput[_sampleIndex] = _sampleValue / byteSize;
+ _sampleIndex++;
}
- else
- __sampleOutput[__sampleIndex] += b * __sampleToValue;
}
- 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 * __sampleToValue;
- else __sampleOutput[__sampleIndex] = b * __sampleToValue;
- __sampleIndex++;
+ function _getSamplesCallback(b:Int, c:Int):Void if (_sampleIndex < _sampleOutputLength) {
+ if (_sampleOutputMerge) _sampleOutput[_sampleIndex] += b / byteSize;
+ else _sampleOutput[_sampleIndex] = b / byteSize;
+ _sampleIndex++;
}
/**
- * 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.
+ * 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.
*/
- public function read(startPos:Float, endPos:Float, callback:AudioAnalyzerCallback) {
- __check();
- __read(startPos, endPos, callback);
+ 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);
}
- 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
- }
+ 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);
- 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;
+ // TODO
+ //else if ((!sound.loaded || (startSample = _readStream(startSample, endSample, callback)) < endSample) && _prepareDecoder())
+ // _readDecoder(startSample, endSample, callback);
+ }
- while (pos < end) {
- callback(getByte(buffer.data.buffer, pos, __wordSize), c);
- if (++c > buffer.channels) c = 0;
- pos += __wordSize;
+ 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;
}
}
- #if lime_cffi
- inline function __canReadStream():Bool
- @:privateAccess return sound._source != null && sound._source.__backend != null && sound._source.__backend.playing;
-
- inline function __readStream(startPos:Float, endPos:Float, callback:AudioAnalyzerCallback):Float @:privateAccess {
- final backend = sound._source.__backend;
-
- // 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];
- }
- 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];
+ function _readStream(startSample:Int, endSample:Int, callback:ReadCallback):Int @:privateAccess {
+ final backend = sound.source.__backend;
+ if (backend.filledBuffers == 0) return startSample;
+
+ backend.mutex.acquire();
+
+ 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++;
+ }
+ }
}
- n -= __wordSize;
}
-
- break;
+ i++;
}
- return endPos - (n / __toBits);
+ backend.mutex.release();
+
+ return startSample;
}
- #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.
+ // 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);
return true;
}
return false;
+ */
}
- 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 doRead = __bufferLastSize == 0 || (pos < 0 && pos >= __bufferSize);
- if (doRead) {
- if (startPos < 1) {
- __vorbis.rawSeek(0);
- __bufferTime = 0;
- }
- else
- __vorbis.timeSeek(__bufferTime = startPos / 1000);
+ /*
+ function _readDecoder(startSample:Int, endSample:Int, callback:ReadCallback) {
+ var pos = (startSample - _bufferLastSample) * _sampleSize, n = endSample - startSample, c = 0;
- __bufferLastSize = pos = 0;
- }
+ var doDecode = _bufferLastSize == 0 || (pos < 0 && pos >= _bufferLastSize);
+ if (doDecode) {
+ _decoder.seek(startSample);
+ _bufferLastSize = pos = 0;
+ doDecode = true;
+ }
- 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;
- }
+ 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;
}
- else {
- while (pos < __bufferLastSize) {
- callback(getByte(__buffer, pos, __wordSize), c);
- if (++c > buffer.channels) c = 0;
- pos += __wordSize;
- if ((n -= __wordSize) <= 0) break;
+ }
+ else {
+ while (n > 0) {
+ callback(getByte(_buffer, pos, data.byteRate), c);
+ if (++c == data.channels) {
+ c = 0;
+ n--;
}
- doRead = true;
- ranOut = pos >= __bufferSize;
- }
-
- if (n <= 0) break;
- else if (doRead && ranOut) {
- __bufferLastSize = pos = 0;
- __bufferTime = __bufferLastTime;
+ if ((pos += data.byteRate) >= _bufferLastSize) break;
}
+ doDecode = true;
+ _bufferLastSize = pos = 0;
}
}
}
- #end
- #end
-}
\ No newline at end of file
+ */
+
+ 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
\ No newline at end of file
diff --git a/source/funkin/backend/utils/CoolUtil.hx b/source/funkin/backend/utils/CoolUtil.hx
index 382c476046..e7adfd9f1d 100644
--- a/source/funkin/backend/utils/CoolUtil.hx
+++ b/source/funkin/backend/utils/CoolUtil.hx
@@ -987,15 +987,13 @@ final class CoolUtil
* Returns the screen position of an object, while taking the camera zoom into account.
*
* @param object Any `FlxObject`
- * @param camera The desired "screen" coordinate space. If `null`, `FlxG.camera` is used.
+ * @param camera The desired "screen" coordinate space. If `null`, a default camera is used.
* @param result Optional arg for the returning point
* @return The screen position of the object.
*/
public static function worldToScreenPosition(object:FlxObject, ?camera:FlxCamera, ?result:FlxPoint) {
- if (result == null)
- result = FlxPoint.get();
- if (camera == null)
- camera = FlxG.camera;
+ if (result == null) result = FlxPoint.get();
+ if (camera == null) camera = object.getDefaultCamera();
result.set(object.x, object.y);
result.x = (((result.x - camera.scroll.x * object.scrollFactor.x) * camera.zoom) - ((camera.width * 0.5) * (camera.zoom - camera.initialZoom)));
diff --git a/source/funkin/backend/utils/MathUtil.hx b/source/funkin/backend/utils/MathUtil.hx
index c71414edf3..5e6a3b4367 100644
--- a/source/funkin/backend/utils/MathUtil.hx
+++ b/source/funkin/backend/utils/MathUtil.hx
@@ -3,220 +3,232 @@ 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
- *
- * @return The maximum value
- **/
- public static function maxInt(...args:Int):Int {
- var max = args[0];
- for(i in 1...args.length) {
- var arg = args[i];
- if(arg > max)
- max = arg;
- }
- return max;
- }
-
- /**
- * Returns the minimum value in the arguments.
- * @param args Array of values
- *
- * @return The minimum value
- **/
- public static function minInt(...args:Int):Int {
- var min = args[0];
- for(i in 1...args.length) {
- var arg = args[i];
- if(arg < min)
- min = arg;
- }
- return min;
- }
-
- /**
- * Returns the maximum value in the arguments.
- *
- * NOTE: If you are using this in compile time, you should use `MathUtil.maxSmart` instead of this for better performance.
- *
- * @param args Array of values
- *
- * @return The maximum value
- **/
- public static function max(...args:Float):Float {
- var max = args[0];
- for(i in 1...args.length) {
- var arg = args[i];
- if(arg > max)
- max = arg;
- }
- return max;
- }
-
- /**
- * Returns the minimum value in the arguments.
- *
- * NOTE: If you are using this in compile time, you should use `MathUtil.minSmart` instead of this for better performance.
- *
- * @param args Array of values
- *
- * @return The minimum value
- **/
- public static function min(...args:Float):Float {
- var min = args[0];
- for(i in 1...args.length) {
- var arg = args[i];
- if(arg < min)
- min = arg;
- }
- return min;
- }
-
- /**
- * Checks if a is less than b with considering a margin of error.
- * * @param a Float
- * @param b Float
- * @param margin Float (Default: EPSILON)
- * * @return Bool
- **/
- public static function lessThan(a:Float, b:Float, margin:Float = 0.0000001):Bool {
- return a < b - margin;
- }
-
- /**
- * Checks if a is less than or equally b with considering a margin of error.
- * * @param a Float
- * @param b Float
- * @param margin Float (Default: EPSILON)
- * * @return Bool
- **/
- public static function lessThanEqual(a:Float, b:Float, margin:Float = 0.0000001):Bool {
- return a <= b - margin;
- }
-
- /**
- * Checks if a is greater than b with considering a margin of error.
- * * @param a Float
- * @param b Float
- * @param margin Float (Default: EPSILON)
- * * @return Bool
- **/
- public static function greaterThan(a:Float, b:Float, margin:Float = 0.0000001):Bool {
- return a > b + margin;
- }
-
- /**
- * Checks if a is greater than or equally b with considering a margin of error.
- * * @param a Float
- * @param b Float
- * @param margin Float (Default: EPSILON)
- * * @return Bool
- **/
- public static function greaterThanEqual(a:Float, b:Float, margin:Float = 0.0000001):Bool {
- return a >= b + margin;
- }
-
- /**
- * Checks if a is approximately equal to b.
- * * @param a Float
- * @param b Float
- * @param margin Float (Default: EPSILON)
- * * @return Bool
- **/
- public static function equal(a:Float, b:Float, margin:Float = 0.0000001):Bool {
- return Math.abs(a - b) <= margin;
- }
-
- /**
- * Checks if a are not approximately equal to b.
- * * @param a Float
- * @param b Float
- * @param margin Float (Default: EPSILON)
- * * @return Bool
- **/
- public static function notEqual(a:Float, b:Float, margin:Float = 0.0000001):Bool {
- 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
- *
- * Might not preserve the order of arguments, please test this.
- *
- * Dont use this in hscript, it doesnt work, it only works on compile time
- **/
- @:dox(hide) public static macro function maxSmart(..._args:Expr):Expr {
- return genericMinMaxSmart(_args.toArray(), "Math.max");
- }
-
- /**
- * Shortcut to `Math.min` but with infinite amount of arguments
- *
- * Might not preserve the order of arguments, please test this.
- *
- * Dont use this in hscript, it doesnt work, it only works on compile time
- **/
- @:dox(hide) public static macro function minSmart(..._args:Expr):Expr {
- return genericMinMaxSmart(_args.toArray(), "Math.min");
- }
-
- #if macro
- @:dox(hide) private static function genericMinMaxSmart(_args:Array, funcPath:String):Expr {
- var args = _args.copy();
- if (args.length == 0) return macro 0;
-
- var func = funcPath.split(".");
-
- function nested(lst:Array):Expr {
- if (lst.length == 1) {
- return macro ${lst[0]};
- } else if (lst.length == 2) {
- return macro $p{func}(${lst[0]}, ${lst[1]});
- } else {
- var mid = Std.int(lst.length / 2);
- return macro $p{func}(${nested(lst.slice(0, mid))}, ${nested(lst.slice(mid, lst.length))});
- }
- }
-
- var expr = nested(args);
-
- //var printer = new haxe.macro.Printer();
- //trace(printer.printExpr(expr));
-
- return macro $expr;
- }
- #end
+ public static inline var EULER:Float = 2.718281828459;
+
+ /**
+ * Returns the maximum value in the arguments.
+ * @param args Array of values
+ *
+ * @return The maximum value
+ **/
+ public static function maxInt(...args:Int):Int {
+ var max = args[0];
+ for(i in 1...args.length) {
+ var arg = args[i];
+ if(arg > max)
+ max = arg;
+ }
+ return max;
+ }
+
+ /**
+ * Returns the minimum value in the arguments.
+ * @param args Array of values
+ *
+ * @return The minimum value
+ **/
+ public static function minInt(...args:Int):Int {
+ var min = args[0];
+ for(i in 1...args.length) {
+ var arg = args[i];
+ if(arg < min)
+ min = arg;
+ }
+ return min;
+ }
+
+ /**
+ * Returns the maximum value in the arguments.
+ *
+ * NOTE: If you are using this in compile time, you should use `MathUtil.maxSmart` instead of this for better performance.
+ *
+ * @param args Array of values
+ *
+ * @return The maximum value
+ **/
+ public static function max(...args:Float):Float {
+ var max = args[0];
+ for(i in 1...args.length) {
+ var arg = args[i];
+ if(arg > max)
+ max = arg;
+ }
+ return max;
+ }
+
+ /**
+ * Returns the minimum value in the arguments.
+ *
+ * NOTE: If you are using this in compile time, you should use `MathUtil.minSmart` instead of this for better performance.
+ *
+ * @param args Array of values
+ *
+ * @return The minimum value
+ **/
+ public static function min(...args:Float):Float {
+ var min = args[0];
+ for(i in 1...args.length) {
+ var arg = args[i];
+ if(arg < min)
+ min = arg;
+ }
+ return min;
+ }
+
+ /**
+ * Checks if a is less than b with considering a margin of error.
+ *
+ * @param a Float
+ * @param b Float
+ * @param margin Float (Default: EPSILON)
+ *
+ * @return Bool
+ **/
+ public static function lessThan(a:Float, b:Float, margin:Float = 0.0000001):Bool {
+ return a < b - margin;
+ }
+
+ /**
+ * Checks if a is less than or equally b with considering a margin of error.
+ *
+ * @param a Float
+ * @param b Float
+ * @param margin Float (Default: EPSILON)
+ *
+ * @return Bool
+ **/
+ public static function lessThanEqual(a:Float, b:Float, margin:Float = 0.0000001):Bool {
+ return a <= b - margin;
+ }
+
+ /**
+ * Checks if a is greater than b with considering a margin of error.
+ *
+ * @param a Float
+ * @param b Float
+ * @param margin Float (Default: EPSILON)
+ *
+ * @return Bool
+ **/
+ public static function greaterThan(a:Float, b:Float, margin:Float = 0.0000001):Bool {
+ return a > b + margin;
+ }
+
+ /**
+ * Checks if a is greater than or equally b with considering a margin of error.
+ *
+ * @param a Float
+ * @param b Float
+ * @param margin Float (Default: EPSILON)
+ *
+ * @return Bool
+ **/
+ public static function greaterThanEqual(a:Float, b:Float, margin:Float = 0.0000001):Bool {
+ return a >= b + margin;
+ }
+
+ /**
+ * Checks if a is approximately equal to b.
+ *
+ * @param a Float
+ * @param b Float
+ * @param margin Float (Default: EPSILON)
+ *
+ * @return Bool
+ **/
+ public static function equal(a:Float, b:Float, margin:Float = 0.0000001):Bool {
+ return Math.abs(a - b) <= margin;
+ }
+
+ /**
+ * Checks if a are not approximately equal to b.
+ *
+ * @param a Float
+ * @param b Float
+ * @param margin Float (Default: EPSILON)
+ *
+ * @return Bool
+ **/
+ public static function notEqual(a:Float, b:Float, margin:Float = 0.0000001):Bool {
+ 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
+ *
+ * Might not preserve the order of arguments, please test this.
+ *
+ * Dont use this in hscript, it doesnt work, it only works on compile time
+ **/
+ @:dox(hide) public static macro function maxSmart(..._args:Expr):Expr {
+ return genericMinMaxSmart(_args.toArray(), "Math.max");
+ }
+
+ /**
+ * Shortcut to `Math.min` but with infinite amount of arguments
+ *
+ * Might not preserve the order of arguments, please test this.
+ *
+ * Dont use this in hscript, it doesnt work, it only works on compile time
+ **/
+ @:dox(hide) public static macro function minSmart(..._args:Expr):Expr {
+ return genericMinMaxSmart(_args.toArray(), "Math.min");
+ }
+
+ #if macro
+ @:dox(hide) private static function genericMinMaxSmart(_args:Array, funcPath:String):Expr {
+ var args = _args.copy();
+ if (args.length == 0) return macro 0;
+
+ var func = funcPath.split(".");
+
+ function nested(lst:Array):Expr {
+ if (lst.length == 1) {
+ return macro ${lst[0]};
+ } else if (lst.length == 2) {
+ return macro $p{func}(${lst[0]}, ${lst[1]});
+ } else {
+ var mid = Std.int(lst.length / 2);
+ return macro $p{func}(${nested(lst.slice(0, mid))}, ${nested(lst.slice(mid, lst.length))});
+ }
+ }
+
+ var expr = nested(args);
+
+ //var printer = new haxe.macro.Printer();
+ //trace(printer.printExpr(expr));
+
+ return macro $expr;
+ }
+ #end
}
diff --git a/source/funkin/backend/utils/NativeAPI.hx b/source/funkin/backend/utils/NativeAPI.hx
index d3b5e758e6..16e08c19b5 100644
--- a/source/funkin/backend/utils/NativeAPI.hx
+++ b/source/funkin/backend/utils/NativeAPI.hx
@@ -13,12 +13,6 @@ 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();
@@ -185,16 +179,7 @@ class NativeAPI {
public static function setConsoleColors(foregroundColor:ConsoleColor = NONE, ?backgroundColor:ConsoleColor = NONE) {
if(Main.noTerminalColor) return;
- #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
+ #if sys
Sys.print("\x1b[0m");
if(foregroundColor != NONE)
Sys.print("\x1b[" + Std.int(consoleColorToANSI(foregroundColor)) + "m");
diff --git a/source/funkin/backend/utils/XMLUtil.hx b/source/funkin/backend/utils/XMLUtil.hx
index ed30d6ba42..3dcbd2e94e 100644
--- a/source/funkin/backend/utils/XMLUtil.hx
+++ b/source/funkin/backend/utils/XMLUtil.hx
@@ -15,6 +15,8 @@ import funkin.backend.scripting.ScriptPack;
import flixel.util.typeLimit.OneOfTwo;
import funkin.backend.system.interfaces.IOffsetCompatible;
import haxe.xml.Access;
+import flixel.graphics.frames.FlxFramesCollection;
+import flixel.graphics.frames.FlxAtlasFrames;
import animate.FlxAnimateFrames;
using StringTools;
@@ -109,6 +111,41 @@ final class XMLUtil {
return OK;
}
+ /**
+ * Loads multiple sheets into 1 sprite,
+ * for every `characters/bf` there is.
+ * (Can also be spelled as ``, or ``)
+ * @param spr The sprite
+ * @param node The XML node
+ * @param parentFolder The parent folder
+ */
+ public static function appendSpriteSheetsFromXML(spr:FunkinSprite, node:Access, ?parentFolder:String = ''):FlxFramesCollection {
+ if (spr == null) return null;
+ var defaultPath = '$parentFolder${node.getAtt("sprite").getDefault(spr.name)}';
+ if (!node.hasNode.spritesheet && !node.hasNode.sheet) {
+ spr.loadSprite(Paths.image(defaultPath, null, true));
+ return spr.frames;
+ }
+ var seenSheets:Array = [defaultPath];
+ for (n in node.elements) {
+ if (n.name != 'spritesheet' && n.name != 'sheet') continue;
+ var path = n.x.get('path') ?? n.x.firstChild()?.nodeValue?.trim();
+ if (path == null) {
+ Logs.warn('Spritesheet node is missing text content or the path attribute. Skipping...');
+ continue;
+ }
+ if (seenSheets.contains(path)) {
+ Logs.warn('Spritesheet "${Paths.image(path)}" was already added. Skipping...');
+ continue;
+ }
+ if (!Paths.framesExists(path, true)) {
+ Logs.warn('Could not find a BitmapData asset with ID "${Paths.image(path)}". Skipping...');
+ continue;
+ }
+ seenSheets.push(path);
+ }
+ return spr.frames = Paths.getMultiFrames(seenSheets, false, null, null, spr.animateSettings);
+ }
/**
* Sets the properties of a sprite based on a XML node.
* @param spr The sprite
@@ -121,9 +158,9 @@ final class XMLUtil {
spr.name = node.getAtt("name");
spr.antialiasing = true;
- if (loadGraphic)
- spr.loadSprite(Paths.image('$parentFolder${node.getAtt("sprite").getDefault(spr.name)}', null, true));
-
+ if (loadGraphic) {
+ appendSpriteSheetsFromXML(spr, node, parentFolder);
+ }
spr.spriteAnimType = defaultAnimType;
if (node.has.type) {
spr.spriteAnimType = XMLAnimType.fromString(node.att.type, spr.spriteAnimType);
@@ -285,6 +322,7 @@ final class XMLUtil {
if (anim.has.forced) animData.forced = anim.att.forced == "true";
if (anim.has.indices) animData.indices = CoolUtil.parseNumberRange(anim.att.indices);
if (anim.has.label) animData.label = anim.att.label == "true";
+ if (anim.has.isAnimate) animData.isAnimate = anim.att.isAnimate == "true";
return animData;
}
@@ -311,7 +349,7 @@ final class XMLUtil {
if (animData.name != null) {
if (animData.fps <= 0 #if web || animData.fps == null #end) animData.fps = 24;
- if (sprite.frames is FlxAnimateFrames) {
+ if ((sprite.frames is FlxAnimateFrames) == (animData.isAnimate ?? true)) {
if(animData.anim == null)
return MISSING_PROPERTY;
@@ -485,6 +523,7 @@ typedef AnimData = {
var animType:XMLAnimType;
var label:Bool;
var ?forced:Bool;
+ var ?isAnimate:Bool;
}
typedef BeatAnim = {
diff --git a/source/funkin/backend/utils/native/Windows.hx b/source/funkin/backend/utils/native/Windows.hx
index 706eba21c2..026521597d 100644
--- a/source/funkin/backend/utils/native/Windows.hx
+++ b/source/funkin/backend/utils/native/Windows.hx
@@ -28,107 +28,10 @@ import funkin.backend.utils.NativeAPI.MessageBoxIcon;
#include
#include
#include
-
-#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;
diff --git a/source/funkin/editors/ModConfigWarning.hx b/source/funkin/editors/ModConfigWarning.hx
index a1dc4dd5c9..6e04663da8 100644
--- a/source/funkin/editors/ModConfigWarning.hx
+++ b/source/funkin/editors/ModConfigWarning.hx
@@ -8,6 +8,7 @@ class ModConfigWarning extends UIState {
var library:ModsFolderLibrary = null;
var goToState:Class;
+ var useAPIWarning:Bool = false;
public static var defaultModConfigText =
'[Common] # This section applies the \'MOD_\' prefix to the flags so you don\'t have to.
@@ -16,7 +17,9 @@ DESCRIPTION="YOUR MOD DESCRIPTION HERE"
AUTHOR="YOU/YOUR TEAM HERE"
VERSION="YOUR MOD\'S VERSION HERE"
-# DO NOT EDIT!! this is used to check for version compatibility!
+# This is used to check for version compatibility!
+# By default, the current API version is used.
+# Mods with a lower API version will need to get updated if needed!!!!!!!!!!!!!!
API_VERSION=${Flags.CURRENT_API_VERSION}
DOWNLOAD_LINK="YOUR MOD PAGE LINK HERE"
@@ -47,23 +50,36 @@ LOGO_TEXT=""
[StateRedirects.force] # Use this if you want to override redirects set by subsequent addons/mods
';
- public function new(library:ModsFolderLibrary, ?goToState:Class) {
+ public function new(library:ModsFolderLibrary, ?goToState:Class, ?useAPIWarning:Bool = false) {
super();
this.library = library;
this.goToState = goToState != null ? goToState : funkin.menus.TitleState;
+ this.useAPIWarning = useAPIWarning;
+ }
+
+ public function goBack() {
+ MusicBeatState.skipTransOut = MusicBeatState.skipTransIn = false;
+ FlxG.switchState(cast Type.createInstance(goToState, []));
}
override function createPost() {
super.createPost();
hadPopup = true;
- var substate = new UIWarningSubstate(TU.translate("modConfigWarning.warningTitle"), TU.translate("modConfigWarning.warningDesc"), [
+ var substate = useAPIWarning ? new UIWarningSubstate(TU.translate("modApiWarning.warningTitle", [Flags.MOD_API_VERSION != null ? Std.string(Flags.MOD_API_VERSION) : '???', Flags.CURRENT_API_VERSION]), TU.translate("modApiWarning.warningDesc"), [
+ {
+ label: TU.translate("editor.ok"),
+ color: 0x969533,
+ onClick: function (_) {
+ goBack();
+ }
+ }
+ ], false) : new UIWarningSubstate(TU.translate("modConfigWarning.warningTitle"), TU.translate("modConfigWarning.warningDesc"), [
{
label: TU.translate("editor.notNow"),
color: 0x969533,
onClick: function (_) {
- MusicBeatState.skipTransOut = MusicBeatState.skipTransIn = false;
- FlxG.switchState(cast Type.createInstance(goToState, []));
+ goBack();
}
},
{
@@ -75,8 +91,7 @@ LOGO_TEXT=""
{
label: TU.translate("editor.ok"),
onClick: function (_) {
- MusicBeatState.skipTransOut = MusicBeatState.skipTransIn = false;
- FlxG.switchState(cast Type.createInstance(goToState, []));
+ goBack();
}
},
], false));
diff --git a/source/funkin/editors/SaveSubstate.hx b/source/funkin/editors/SaveSubstate.hx
index 7202cf3738..be964248d2 100644
--- a/source/funkin/editors/SaveSubstate.hx
+++ b/source/funkin/editors/SaveSubstate.hx
@@ -2,6 +2,9 @@ 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;
@@ -26,6 +29,13 @@ 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) {
@@ -33,6 +43,7 @@ class SaveSubstate extends MusicBeatSubstate {
close();
});
fileDialog.browse(SAVE, options.saveExt.getDefault(Path.extension(options.defaultSaveFile)), options.defaultSaveFile);
+ #end
}
public override function update(elapsed:Float) {
diff --git a/source/funkin/editors/charter/Charter.hx b/source/funkin/editors/charter/Charter.hx
index a06118f425..a9d4bd216f 100644
--- a/source/funkin/editors/charter/Charter.hx
+++ b/source/funkin/editors/charter/Charter.hx
@@ -622,8 +622,25 @@ class Charter extends UIState {
noteTypes = PlayState.SONG.noteTypes;
FlxG.sound.setMusic(FlxG.sound.load(Paths.inst(__song, __diff, PlayState.SONG.meta.instSuffix)));
- if (Assets.exists(Paths.voices(__song, __diff, PlayState.SONG.meta.vocalsSuffix)))
+
+ // force full load the audio datas for waveform, maybe in the future dont do this and
+ // make it so it continously loads the only necessary waveform data in preview?
+
+ if (FlxG.sound.music.data?.buffer != null && FlxG.sound.music.data.buffer.data == null) {
+ FlxG.sound.music.data.buffer.load();
+ FlxG.sound.music.data.buffer.decoder?.dispose();
+ FlxG.sound.music.data.buffer.decoder = null;
+ }
+
+ if (Assets.exists(Paths.voices(__song, __diff, PlayState.SONG.meta.vocalsSuffix))) {
vocals = FlxG.sound.load(Paths.voices(__song, __diff, PlayState.SONG.meta.vocalsSuffix));
+
+ if (vocals.data?.buffer != null && vocals.data.buffer.data == null) {
+ vocals.data.buffer.load();
+ vocals.data.buffer.decoder?.dispose();
+ vocals.data.buffer.decoder = null;
+ }
+ }
else
vocals = new FlxSound();
@@ -1097,6 +1114,7 @@ class Charter extends UIState {
if (n.hovered || n.sustainDraggable) {
deletedNotes.push(n);
deleteSingleSelection(n, false);
+ UIState.playEditorSound(Flags.DEFAULT_CHARTER_NOTEDELETE_SOUND);
if (selection.contains(n)) selection.remove(n);
noteDeleteAnims.deleteNotes.push({
@@ -1171,7 +1189,6 @@ class Charter extends UIState {
if (selected == null) return selected;
if (selected is CharterNote) {
- UIState.playEditorSound(Flags.DEFAULT_CHARTER_NOTEDELETE_SOUND);
var note:CharterNote = cast selected;
note.strumLineID = strumLines.members.indexOf(note.strumLine);
note.strumLine = null; // For static undos :D
@@ -1226,6 +1243,7 @@ class Charter extends UIState {
else member++;
}
}
+ UIState.playEditorSound(Flags.DEFAULT_CHARTER_NOTEDELETE_SOUND);
notesGroup.sortNotes();
notesGroup.autoSort = true;
@@ -1323,7 +1341,7 @@ class Charter extends UIState {
FlxG.state.openSubState(new CharterStrumlineScreen(strumLines.members.length, null, (_) -> {
if (_ != null) {
createStrumline(strumLines.members.length, _);
-
+ strumlineAddButton.button.setColorTransform(1, 1, 1, strumlineAddButton.button.alpha);
strumlineAddButton.textTweenColor.color = 0xFF00FF00;
strumlineAddButton.pressAnimation(true);
}
@@ -1426,7 +1444,8 @@ class Charter extends UIState {
noteTypeText.x = noteTopButton.x + noteTopButton.bWidth + 6;
noteTypeText.y = Std.int((noteTopButton.bHeight - noteTypeText.height) / 2);
}
- noteTypeText.text = '($noteType) ' + (noteTypes[noteType-1] == null ? translate("noteTypes.default") : noteTypes[noteType-1]);
+ var targetNoteText = '($noteType) ' + (noteTypes[noteType-1] == null ? translate("noteTypes.default") : noteTypes[noteType-1]);
+ if (noteTypeText.text != targetNoteText) noteTypeText.text = targetNoteText;
super.update(elapsed);
@@ -1469,15 +1488,14 @@ class Charter extends UIState {
}
var curChange = Conductor.curChange;
- songPosInfo.text = [
- // no need to translate the time text since it has no text only numbers
- '${CoolUtil.timeToStr(Conductor.songPosition)} / ${CoolUtil.timeToStr(songLength)}',
- SONGPOSINFO_STEP.format([curStep]),
- SONGPOSINFO_BEAT.format([curBeat]),
- SONGPOSINFO_MEASURE.format([curMeasure]),
- SONGPOSINFO_BPM.format([(curChange != null && curChange.continuous && curChange.endSongTime > songPos) ? FlxMath.roundDecimal(Conductor.bpm, 3) : Conductor.bpm]),
- SONGPOSINFO_TIMESIGNATURE.format([Conductor.beatsPerMeasure, Conductor.denominator])
- ].join("\n");
+ var targetText = '${CoolUtil.timeToStr(Conductor.songPosition)} / ${CoolUtil.timeToStr(songLength)}'
+ +'\n'+SONGPOSINFO_STEP.format([curStep])
+ +'\n'+SONGPOSINFO_BEAT.format([curBeat])
+ +'\n'+SONGPOSINFO_MEASURE.format([curMeasure])
+ +'\n'+SONGPOSINFO_BPM.format([(curChange != null && curChange.continuous && curChange.endSongTime > songPos) ? FlxMath.roundDecimal(Conductor.bpm, 3) : Conductor.bpm])
+ +'\n'+SONGPOSINFO_TIMESIGNATURE.format([Conductor.beatsPerMeasure, Conductor.denominator]);
+
+ if (songPosInfo.text != targetText) songPosInfo.text = targetText;
if (charterCamera.zoom != (charterCamera.zoom = lerp(charterCamera.zoom, __camZoom, __firstFrame ? 1 : 0.125)))
updateDisplaySprites();
@@ -1485,6 +1503,15 @@ class Charter extends UIState {
if (strumLines != null)
strumlineLockButton.button.animation.play(strumLines.draggable ? "1" : "0", true);
+ if (strumLines.members.length <= 0) {
+ final glow = (Math.sin(FlxG.game.ticks * 0.004) + 1) * 0.5;
+ strumlineAddButton.button.setColorTransform(
+ 1 - (glow * 0.5), 1 - (glow * 0.5), 1 - (glow * 0.5),
+ strumlineAddButton.button.alpha,
+ Std.int(glow * 255), Std.int(glow * 255), Std.int(glow * 255), 0
+ );
+ }
+
WindowUtils.prefix = undos.unsaved ? Flags.UNDO_PREFIX : "";
SaveWarning.showWarning = undos.unsaved;
@@ -1723,6 +1750,7 @@ class Charter extends UIState {
if (selection == null || selection.length == 0) return;
selection.loop((n:CharterNote) -> {
noteDeleteAnims.deleteNotes.push({note: n, time: noteDeleteAnims.deleteTime});
+ @:privateAccess CharterNote.callScriptOnNote('onCharterNoteDelete', n);
});
selection = deleteSelection(selection, true);
}
@@ -1737,6 +1765,7 @@ class Charter extends UIState {
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);
+ @:privateAccess CharterNote.callScriptOnNote('onCharterNoteDelete', oldNote);
}
oldNote = note;
}
diff --git a/source/funkin/editors/charter/CharterBackdropGroup.hx b/source/funkin/editors/charter/CharterBackdropGroup.hx
index d021c66bcf..626618eb0d 100644
--- a/source/funkin/editors/charter/CharterBackdropGroup.hx
+++ b/source/funkin/editors/charter/CharterBackdropGroup.hx
@@ -139,21 +139,24 @@ class NotesDrawGroup extends FlxFastTypedGroup {
if (note != null && note.exists && note.visible) {
if (note.snappedToGrid) note.x = (note.strumLine != null ? note.strumLine.x : 0) + (note.id % (note.strumLine != null ? note.strumLine.keyCount : 4)) * 40;
note.drawMembers();
+ /*
}
}
i = 0; note = null;
while (i < length) {
note = members[i++];
- if (note != null && note.exists && note.visible)
+ if (note != null && note.exists && note.visible) {*/
note.drawSuper();
+ /* }
}
i = 0; note = null;
while (i < length) {
note = members[i++];
- if (note != null && note.exists && note.visible)
+ if (note != null && note.exists && note.visible) {*/
note.drawNoteTypeText();
+ }
}
FlxCamera._defaultCameras = oldDefaultCameras;
diff --git a/source/funkin/editors/charter/CharterDeleteAnim.hx b/source/funkin/editors/charter/CharterDeleteAnim.hx
index 2e23d38a76..f6ade916c2 100644
--- a/source/funkin/editors/charter/CharterDeleteAnim.hx
+++ b/source/funkin/editors/charter/CharterDeleteAnim.hx
@@ -54,12 +54,50 @@ class CharterDeleteAnim extends CharterNote {
garbageCircle.alpha = __garbageAlpha*.6;
}
+ static var __stupidScriptParamArray:Array = [];
+
+ static function callScriptOnNotes(event:String, note1:CharterNote, note2:CharterNote) {
+ if (Charter.instance != null) {
+ __stupidScriptParamArray[0] = note1;
+ __stupidScriptParamArray[1] = note2;
+ Charter.instance.stateScripts.call(event, __stupidScriptParamArray);
+ }
+ }
+
public override function draw() @:privateAccess {
+ // kill me
+ final __lastFrame:flixel.graphics.frames.FlxFrame = this.frame;
+ final __lastSX:Float = this.scale.x;
+ final __lastSY:Float = this.scale.y;
+ final __lastW:Float = this.width;
+ final __lastH:Float = this.height;
+ final __lastOX:Float = this.offset.x;
+ final __lastOY:Float = this.offset.y;
+ final __lastRX:Float = this.origin.x;
+ final __lastRY:Float = this.origin.y;
+ final __lastFX:Float = this.frameOffset.x;
+ final __lastFY:Float = this.frameOffset.y;
+ final __lastColor:flixel.util.FlxColor = this.color;
+
for (deleteData in deleteNotes) {
y = deleteData.note.y + (deleteData.time>deleteTime*.5 ? (deleteData.time/deleteTime)*FlxG.random.float(-1.1, 1.1) : 0); // lunar when no shake :((
x = deleteData.note.x + (deleteData.time>deleteTime*.5 ? (deleteData.time/deleteTime)*FlxG.random.float(-1.1, 1.1) : 0); // lunar when no shake :((
- angle = deleteData.note.angle; alpha = 1;
- animation.curAnim.curFrame = 3;
+ angle = 0;
+ setSize(deleteData.note.width, deleteData.note.height);
+ scale.set(deleteData.note.scale.x, deleteData.note.scale.y);
+ offset.set(deleteData.note.offset.x, deleteData.note.offset.y);
+ origin.set(deleteData.note.origin.x, deleteData.note.origin.y);
+ frameOffset.set(deleteData.note.frameOffset.x, deleteData.note.frameOffset.y);
+
+ if (!deleteData.note.noDefaultAnims) {
+ frame = __lastFrame;
+ angle = deleteData.note.angle;
+ animation.curAnim.curFrame = 3;
+ color = __lastColor;
+ } else {
+ frame = deleteData.note.frame;
+ }
+ alpha = 1;
sustainSpr.scale.set(10, (40 * deleteData.note.susLength) + (height/2));
sustainSpr.updateHitbox(); sustainSpr.follow(this, 15, 20);
@@ -77,9 +115,19 @@ class CharterDeleteAnim extends CharterNote {
member.alpha *= mult;
typeAlpha *= mult;
+ callScriptOnNotes('onCharterNoteDeleteUpdate', deleteData.note, this);
+
super.draw();
}
+ this.frame = __lastFrame;
+ this.scale.set(__lastSX, __lastSY);
+ this.setSize(__lastW, __lastH);
+ this.offset.set(__lastOX, __lastOY);
+ this.origin.set(__lastRX, __lastRY);
+ this.frameOffset.set(__lastFX, __lastFY);
+ this.color = __lastColor;
+
if (garbageIcon.alpha > 0) garbageIcon.draw();
if (garbageCircle.alpha > 0) garbageCircle.draw();
}
diff --git a/source/funkin/editors/charter/CharterEventAdd.hx b/source/funkin/editors/charter/CharterEventAdd.hx
index ee7ec48ec7..8396507d5a 100644
--- a/source/funkin/editors/charter/CharterEventAdd.hx
+++ b/source/funkin/editors/charter/CharterEventAdd.hx
@@ -63,19 +63,29 @@ class CharterEventAdd extends UISliceSprite {
curCharterEvent = null;
this.step = step;
this.y = (step * 40) - (bHeight / 2);
- text.text = TU.translate("charter.addEvent");
framesOffset = 0; bWidth = 37 + Math.ceil(text.width);
- x = (global != Options.charterSwapEventSides) ? Charter.instance.strumLines.members[Charter.instance.strumLines.members.length-1].x + (40*Charter.instance.strumLines.members[Charter.instance.strumLines.members.length-1].keyCount) : -(bWidth);
- sideText.text = TU.translate("charter.eventType-" + (global ? "global" : "local"));
+ updateStuff(global, false);
}
public function updateEdit(event:CharterEvent) {
if (FlxG.state.subState != null) return;
curCharterEvent = event;
this.y = event.y;
- text.text = TU.translate("charter.editEvent");
framesOffset = 9; bWidth = 27 + Math.ceil(text.width) + event.bWidth;
- x = (event.global != Options.charterSwapEventSides) ? Charter.instance.strumLines.members[Charter.instance.strumLines.members.length-1].x + (40*Charter.instance.strumLines.members[Charter.instance.strumLines.members.length-1].keyCount) : -(bWidth);
- sideText.text = TU.translate("charter.eventType-" + (event.global ? "global" : "local"));
+ updateStuff(event.global, true);
+ }
+
+ private function updateStuff(global:Bool = false, edit:Bool = false) {
+ final lastStrumline = Charter.instance.strumLines.members[Charter.instance.strumLines.members.length-1];
+ if (lastStrumline != null)
+ x = (global != Options.charterSwapEventSides) ? lastStrumline.x + (40*lastStrumline.keyCount) : -(bWidth);
+ else
+ x = (global != Options.charterSwapEventSides) ? 0 : -(bWidth);
+
+ final target = TU.translate("charter.eventType-" + (global ? "global" : "local"));
+ if (sideText.text != target) sideText.text = target;
+
+ final targetButtonText = TU.translate("charter." + (edit ? "edit" : "add") + "Event");
+ if (text.text != targetButtonText) text.text = targetButtonText;
}
}
\ No newline at end of file
diff --git a/source/funkin/editors/charter/CharterNote.hx b/source/funkin/editors/charter/CharterNote.hx
index 8bece2b4af..feb976b30f 100644
--- a/source/funkin/editors/charter/CharterNote.hx
+++ b/source/funkin/editors/charter/CharterNote.hx
@@ -20,6 +20,7 @@ class CharterNote extends UISprite implements ICharterSelectable {
0xFFF9393F
];
+ public var noDefaultAnims:Bool = false;
public var sustainSpr:UISprite;
public var tempSusLength:Float = 0;
public var sustainDraggable:Bool = false;
@@ -28,8 +29,17 @@ class CharterNote extends UISprite implements ICharterSelectable {
public var selected:Bool = false;
public var draggable:Bool = true;
+ public var extra:Map = [];
static var noteTypeTexts:Array = [];
+ static var __stupidScriptParamArray:Array = [];
+
+ static function callScriptOnNote(event:String, note:CharterNote) {
+ if (Charter.instance != null) {
+ __stupidScriptParamArray[0] = note;
+ Charter.instance.stateScripts.call(event, __stupidScriptParamArray);
+ }
+ }
public function new() {
super();
@@ -49,6 +59,8 @@ class CharterNote extends UISprite implements ICharterSelectable {
cursor = sustainSpr.cursor = CLICK;
moves = false;
+
+ callScriptOnNote('onCharterNoteCreation', this);
}
public override function updateButtonHandler() {
@@ -100,6 +112,11 @@ class CharterNote extends UISprite implements ICharterSelectable {
if (angleTween != null) angleTween.cancel();
+ if (noDefaultAnims) {
+ // angle = 0;
+ return callScriptOnNote('onCharterNoteUpdatePos', this);
+ }
+
var destAngle:Float = switch(animation.curAnim.curFrame = (id % 4)) {
case 0: 270;
case 1: 180;
@@ -112,10 +129,10 @@ class CharterNote extends UISprite implements ICharterSelectable {
if (!__doAnim) {
angle = destAngle;
- return;
+ return callScriptOnNote('onCharterNoteUpdatePos', this);
}
- if (angle == destAngle) return;
+ if (angle == destAngle) return callScriptOnNote('onCharterNoteUpdatePos', this);
if(angleTween != null)
angleTween.cancel();
@@ -125,20 +142,24 @@ class CharterNote extends UISprite implements ICharterSelectable {
angleTween = FlxTween.angle(this, angle, destAngle, (2/3)/__animSpeed, {ease: function(t) {
return ((Math.sin(t * Math.PI) * 0.35) * 3 * t * Math.sqrt(1 - t)) + t;
}});
+
+ callScriptOnNote('onCharterNoteUpdatePos', this);
}
public override function kill() {
- if (angleTween != null) {
- angleTween.cancel();
- angleTween = null;
- angle = switch(animation.curAnim.curFrame = (id % 4)) {
- case 0: 270;
- case 1: 180;
- case 2: 0;
- case 3: 90;
- default: 0; // how is that even possible
- };
- __doAnim = false;
+ if (!noDefaultAnims) {
+ if (angleTween != null) {
+ angleTween.cancel();
+ angleTween = null;
+ angle = switch(animation.curAnim.curFrame = (id % 4)) {
+ case 0: 270;
+ case 1: 180;
+ case 2: 0;
+ case 3: 90;
+ default: 0; // how is that even possible
+ };
+ __doAnim = false;
+ }
}
super.kill();
}
diff --git a/source/funkin/editors/charter/CharterNoteHoverer.hx b/source/funkin/editors/charter/CharterNoteHoverer.hx
index 3f7146ba86..51a2d299c9 100644
--- a/source/funkin/editors/charter/CharterNoteHoverer.hx
+++ b/source/funkin/editors/charter/CharterNoteHoverer.hx
@@ -20,13 +20,14 @@ class CharterNoteHoverer extends CharterNote {
if ((__mousePos.x > 0 && __mousePos.x < Charter.instance.strumLines.totalKeyCount * 40 && inBoundsY) && showHoverer) {
step = CoolUtil.bound(FlxG.keys.pressed.SHIFT ? ((__mousePos.y-20) / 40) : Charter.instance.quantStep(__mousePos.y/40), 0, Charter.instance.__endStep-1);
id = Math.floor(__mousePos.x / 40); y = step * 40; x = id * 40; visible = true; sustainSpr.visible = typeVisible = false;
- angle = switch(animation.curAnim.curFrame = ((id - Charter.instance.strumLines.getStrumlineFromID(id).startingID) % 4)) {
- case 0: -90;
- case 1: 180;
- case 2: 0;
- case 3: 90;
- default: 0; // how is that even possible
- };
+ if (!noDefaultAnims)
+ angle = switch(animation.curAnim.curFrame = ((id - Charter.instance.strumLines.getStrumlineFromID(id).startingID) % 4)) {
+ case 0: -90;
+ case 1: 180;
+ case 2: 0;
+ case 3: 90;
+ default: 0; // how is that even possible
+ };
} else
visible = false;
case NOTE_DRAG:
@@ -37,6 +38,20 @@ class CharterNoteHoverer extends CharterNote {
}
public override function draw() @:privateAccess {
+ // kill me
+ final __lastFrame:flixel.graphics.frames.FlxFrame = this.frame;
+ final __lastSX:Float = this.scale.x;
+ final __lastSY:Float = this.scale.y;
+ final __lastW:Float = this.width;
+ final __lastH:Float = this.height;
+ final __lastOX:Float = this.offset.x;
+ final __lastOY:Float = this.offset.y;
+ final __lastRX:Float = this.origin.x;
+ final __lastRY:Float = this.origin.y;
+ final __lastFX:Float = this.frameOffset.x;
+ final __lastFY:Float = this.frameOffset.y;
+ final __lastColor:flixel.util.FlxColor = this.color;
+
switch (Charter.instance.gridActionType) {
case NONE:
super.draw();
@@ -56,16 +71,28 @@ class CharterNoteHoverer extends CharterNote {
var newID:Int = CoolUtil.boundInt(draggingNote.fullID + horizontalChange, 0, Charter.instance.strumLines.totalKeyCount-1);
x = (id=newID) * 40; y = CoolUtil.bound(y, 0, (Charter.instance.__endStep*40) - height);
- angle = switch(animation.curAnim.curFrame = (draggingNote.id % 4)) {
- case 0: -90;
- case 1: 180;
- case 2: 0;
- case 3: 90;
- default: 0; // how is that even possible
- };
+ angle = 0;
+ setSize(draggingNote.width, draggingNote.height);
+ scale.set(draggingNote.scale.x, draggingNote.scale.y);
+ offset.set(draggingNote.offset.x, draggingNote.offset.y);
+ origin.set(draggingNote.origin.x, draggingNote.origin.y);
+ frameOffset.set(draggingNote.frameOffset.x, draggingNote.frameOffset.y);
+ color = draggingNote.color;
+ if (!draggingNote.noDefaultAnims) {
+ frame = __lastFrame;
+ angle = switch(animation.curAnim.curFrame = (draggingNote.id % 4)) {
+ case 0: -90;
+ case 1: 180;
+ case 2: 0;
+ case 3: 90;
+ default: 0; // how is that even possible
+ };
+ } else {
+ frame = draggingNote.frame;
+ }
sustainSpr.scale.set(10, (40 * draggingNote.susLength) + (height/2));
- sustainSpr.color = CharterNote.colors[animation.curAnim.curFrame];
+ sustainSpr.color = draggingNote.noDefaultAnims ? draggingNote.sustainSpr.color : CharterNote.colors[animation.curAnim.curFrame];
sustainSpr.updateHitbox(); sustainSpr.alpha = alpha; sustainSpr.follow(this, 15, 20);
sustainSpr.exists = draggingNote.susLength != 0;
@@ -80,6 +107,13 @@ class CharterNoteHoverer extends CharterNote {
}
default: // do nothing
}
+ this.frame = __lastFrame;
+ this.scale.set(__lastSX, __lastSY);
+ this.setSize(__lastW, __lastH);
+ this.offset.set(__lastOX, __lastOY);
+ this.origin.set(__lastRX, __lastRY);
+ this.frameOffset.set(__lastFX, __lastFY);
+ this.color = __lastColor;
}
public override function destroy() {
diff --git a/source/funkin/editors/charter/CharterStrumLineGroup.hx b/source/funkin/editors/charter/CharterStrumLineGroup.hx
index 2a0f42b617..a010f0e0c4 100644
--- a/source/funkin/editors/charter/CharterStrumLineGroup.hx
+++ b/source/funkin/editors/charter/CharterStrumLineGroup.hx
@@ -46,29 +46,39 @@ class CharterStrumLineGroup extends FlxTypedGroup {
refreshStrumlineIDs();
}
- for (i=>strum in members)
- if (strum != null && !strum.dragging) strum.x = CoolUtil.fpsLerp(strum.x, 40*strum.startingID, 0.225);
+ var minX:Float = (members.length > 0) ? (FlxG.width + cameras[0].scroll.x) : 0;
+ var maxX:Float = 0;
+ for (i=>strum in members) {
+ if (strum != null && !strum.dragging) strum.x = CoolUtil.fpsLerp(strum.x, 40 * strum.startingID, 0.225);
+ minX = Math.min(minX, strum.x);
+ maxX = Math.max(maxX, strum.x + (40 * strum.keyCount));
+ }
- if (Charter.instance.leftEventsBackdrop != null && members[0] != null) {
- Charter.instance.leftEventsBackdrop.x = members[0].button.x - Charter.instance.leftEventsBackdrop.width;
- Charter.instance.leftEventsBackdrop.alpha = members[0].strumLine.visible ? 0.9 : 0.4;
+ final firstStrumline = members[0];
+ final lastStrumline = members[members.length - 1];
+ final c = Charter.instance;
+ if (c.leftEventsBackdrop != null) {
+ c.leftEventsBackdrop.x = minX - c.leftEventsBackdrop.width;
+ c.leftEventsBackdrop.alpha = ((firstStrumline == null) || ((firstStrumline != null) && (firstStrumline.strumLine.visible))) ? 0.9 : 0.4;
- if (Charter.instance.leftEventRowText != null)
- Charter.instance.leftEventRowText.x = members[0].button.x - Charter.instance.leftEventRowText.width - 42;
+ if (c.leftEventRowText != null)
+ c.leftEventRowText.x = -c.leftEventRowText.width - 42;
}
- if (Charter.instance.rightEventsBackdrop != null && members[CoolUtil.maxInt(0, members.length-1)] != null) {
- Charter.instance.rightEventsBackdrop.x = members[members.length-1].x + (40*members[members.length-1].keyCount);
- Charter.instance.rightEventsBackdrop.alpha = members[CoolUtil.maxInt(0, members.length-1)].strumLine.visible ? 0.9 : 0.4;
+ if (c.strumlineLockButton != null)
+ c.strumlineLockButton.x = minX - 160;
+
+ if (c.strumlineAddButton != null)
+ c.strumlineAddButton.x = maxX;
- if (Charter.instance.rightEventRowText != null)
- Charter.instance.rightEventRowText.x = Charter.instance.rightEventsBackdrop.x + 42;
+ if (c.rightEventsBackdrop != null) {
+ c.rightEventsBackdrop.x = maxX;
+ c.rightEventsBackdrop.alpha = ((lastStrumline == null) || ((lastStrumline != null) && (lastStrumline.strumLine.visible))) ? 0.9 : 0.4;
+ c.rightEventsBackdrop.flipY = totalKeyCount % 2 == 0;
+
+ if (c.rightEventRowText != null)
+ c.rightEventRowText.x = c.rightEventsBackdrop.x + 42;
}
-
- if (Charter.instance.strumlineLockButton != null && members[0] != null)
- Charter.instance.strumlineLockButton.x = members[0].x - (160);
- if (Charter.instance.strumlineAddButton != null && members[CoolUtil.maxInt(0, members.length-1)] != null)
- Charter.instance.strumlineAddButton.x = members[members.length-1].x + (40*members[members.length-1].keyCount);
if ((FlxG.mouse.justReleased || !draggable) && isDragging)
finishDrag();
diff --git a/source/funkin/editors/charter/CharterStrumline.hx b/source/funkin/editors/charter/CharterStrumline.hx
index 40f2bf3699..dabcab6a64 100644
--- a/source/funkin/editors/charter/CharterStrumline.hx
+++ b/source/funkin/editors/charter/CharterStrumline.hx
@@ -154,6 +154,12 @@ class CharterStrumline extends UISprite {
}
vocals.group = FlxG.sound.defaultMusicGroup;
+ if (vocals.data?.buffer != null && vocals.data.buffer.data == null) {
+ vocals.data.buffer.load();
+ vocals.data.buffer.decoder?.dispose();
+ vocals.data.buffer.decoder = null;
+ }
+
highlightColor = 0xFFFFFFFF;
if (icons[0] != null) {
var characterXML = Character.getXMLFromCharName(icons[0]);
diff --git a/source/funkin/editors/stage/StageEditor.hx b/source/funkin/editors/stage/StageEditor.hx
index 98cb959c56..e781601277 100644
--- a/source/funkin/editors/stage/StageEditor.hx
+++ b/source/funkin/editors/stage/StageEditor.hx
@@ -222,7 +222,6 @@ class StageEditor extends UIState {
axisGizmo = new AxisGizmo();
axisGizmo.cameras = [gizmosCamera];
- add(axisGizmo);
uiCamera = new FlxCamera();
uiCamera.bgColor = 0;
@@ -272,6 +271,7 @@ class StageEditor extends UIState {
add(topMenuSpr);
add(uiGroup);
+ add(axisGizmo);
if(Framerate.isLoaded) {
Framerate.fpsCounter.alpha = 0.4;
@@ -647,6 +647,7 @@ class StageEditor extends UIState {
saveToXml(xml, "folder", stage.spritesParentFolder);
saveToXml(xml, "startCamPosX", stage.startCam.x, 0);
saveToXml(xml, "startCamPosY", stage.startCam.y, 0);
+ xml.attributeOrder = ["name", "folder", "zoom", "startCamPosX", "startCamPosY"];
for(prop in stage.extra.keys())
if(!Stage.DEFAULT_ATTRIBUTES.contains(prop) && !prop.startsWith("stageEditor."))
@@ -655,94 +656,19 @@ class StageEditor extends UIState {
var group:Xml = null;
var curGroup:String = null;
- for(sprite in getSprites()) {
- var button:StageElementButton = sprite.extra.get(exID("button"));
- var newNode:Xml = null;
+ for(button in stageSpritesWindow.buttons.members) {
+ button.cleanupXML();
var sprite:FunkinSprite = button.getSprite();
- if(button is StageSolidButton) {
- var button:StageSolidButton = cast button;
- var node:Access = cast sprite.extra.get(exID("node"));
- Logs.trace("SOLID / BOX isnt implemented yet!");
- } else if(button is StageSpriteButton) {
- var button:StageSpriteButton = cast button;
- var node:Access = cast sprite.extra.get(exID("node"));
- var spriteXML = Xml.createElement("sprite");
- saveToXml(spriteXML, "name", sprite.name);
- saveToXml(spriteXML, "x", sprite.x, 0);
- saveToXml(spriteXML, "y", sprite.y, 0);
- saveToXml(spriteXML, "sprite", sprite.extra.get(exID("imageFile")));
- savePointToXml(spriteXML, "scale", sprite.scale, 1);
- savePointToXml(spriteXML, "scroll", sprite.scrollFactor, 1);
- saveToXml(spriteXML, "skewx", sprite.skew.x, 0);
- saveToXml(spriteXML, "skewy", sprite.skew.y, 0);
- saveToXml(spriteXML, "alpha", sprite.alpha, 1);
- saveToXml(spriteXML, "angle", sprite.angle, 0);
- //saveToXml(spriteXML, "graphicSize", sprite.width, sprite.width);
- //saveToXml(spriteXML, "graphicSizex", sprite.height, sprite.height);
- //saveToXml(spriteXML, "graphicSizey", sprite.height, sprite.height);
- saveToXml(spriteXML, "zoomfactor", sprite.zoomFactor, 1);
- saveToXml(spriteXML, "updateHitbox", getBoolOfNode(node, "updateHitbox"), false);
- saveToXml(spriteXML, "antialiasing", sprite.antialiasing, true);
- //saveToXml(spriteXML, "width", sprite.width);
- //saveToXml(spriteXML, "height", sprite.height);
- saveToXml(spriteXML, "playOnCountdown", getBoolOfNode(node, "playOnCountdown"), false);
- saveToXml(spriteXML, "interval", node.getAtt("beatInterval"), 2);
- saveToXml(spriteXML, "interval", node.getAtt("interval"), 2);
- saveToXml(spriteXML, "beatOffset", node.getAtt("beatOffset"), 0);
- if(sprite.spriteAnimType != LOOP)
- spriteXML.set("type", sprite.spriteAnimType.toString());
- saveToXml(spriteXML, "color", sprite.color.toWebString(), "#FFFFFF");
- @:privateAccess saveToXml(spriteXML, "blend", sprite.blend.toString(), null);
- // TODO: save custom parameters
- //saveToXml(spriteXML, "flipX", sprite.flipX, false);
- if (node.hasNode.anim) for (animNode in node.nodes.anim)
- spriteXML.addChild(animNode.x);
- newNode = spriteXML;
- } else if(button is StageCharacterButton) {
- var button:StageCharacterButton = cast button;
- var char:Character = button.char;
- var node:Access = cast char.extra.get(exID("node"));
- var defaultPos = Stage.getDefaultPos(char.name.replace("NO_DELETE_", ""));
- var charXML:Xml = Xml.createElement(node.name);
- if(!char.name.startsWith("NO_DELETE_"))
- saveToXml(charXML, "name", char.name);
- saveToXml(charXML, "x", char.x, defaultPos.x);
- saveToXml(charXML, "y", char.y, defaultPos.y);
- saveToXml(charXML, "camxoffset", char.extra.get(exID("camX")), 0);
- saveToXml(charXML, "camyoffset", char.extra.get(exID("camY")), 0);
- saveToXml(charXML, "skewx", char.skew.x, 0);
- saveToXml(charXML, "skewy", char.skew.y, 0);
- saveToXml(charXML, "spacingx", char.extra.get(exID("spacingX")), 20);
- saveToXml(charXML, "spacingy", char.extra.get(exID("spacingY")), 0);
- saveToXml(charXML, "alpha", char.alpha / 0.75, 1);
- saveToXml(charXML, "angle", char.angle, 0);
- saveToXml(charXML, "zoomfactor", char.zoomFactor, 1);
- saveToXml(charXML, "flipX", char.isPlayer, defaultPos.flip);
- savePointToXml(charXML, "scroll", char.scrollFactor, defaultPos.scroll);
- savePointToXml(charXML, "scale", char.scale.scaleNew(button.charScale), 1);
- // TODO: save custom parameters
- newNode = charXML;
- } else if(button is StageUnknownButton) {
- var button:StageUnknownButton = cast button;
- newNode = button.xml.x;
- }
- else {
- Logs.trace("Unknown Stage Type : " + Type.getClassName(Type.getClass(button)));
- Logs.trace("> Sprite : " + Type.getClassName(Type.getClass(sprite)));
- }
+ var newNode:Xml = button.xml.x;
if(newNode != null && sprite != null) {
- var isLowMemory = sprite.extra.get(exID("lowMemory")) == true;
- var isHighMemory = sprite.extra.get(exID("highMemory")) == true;
- /* // Only if this compiled :sob:
- var groupName:String = null;
- if ((groupName = isLowMemory ? "low-memory" : isHighMemory ? "high-memory" : null) != null) {
- var a = group != null && groupName != curGroup && ((group = cast xml.addChild(group)) != null);
- (group = (group == null ? Xml.createElement(curGroup = groupName) : group)).addChild(newNode);
- }else xml.addChild(newNode);
- */
+ var groupName = (sprite == null ? null :
+ (
+ (sprite.extra.get(exID("lowMemory")) == true) ? "low-memory" :
+ (sprite.extra.get(exID("highMemory")) == true) ? "high-memory" : null
+ )
+ );
- var groupName = isLowMemory ? "low-memory" : isHighMemory ? "high-memory" : null;
if(group != null && groupName != curGroup) {
xml.addChild(group);
group = null;
@@ -758,6 +684,36 @@ class StageEditor extends UIState {
return Options.editorStagePrettyPrint ? xmlThingYea : xmlThingYea.replace("\n", "");
}
+ function storeSpriteTransform(sprite:FunkinSprite) {
+ sprite.setPosition(CoolUtil.quantize(sprite.x, 100), CoolUtil.quantize(sprite.y, 100));
+ sprite.scale.set(CoolUtil.quantize(sprite.scale.x, 100), CoolUtil.quantize(sprite.scale.y, 100));
+ sprite.skew.set(CoolUtil.quantize(sprite.skew.x, 100), CoolUtil.quantize(sprite.skew.y, 100));
+ sprite.angle = CoolUtil.quantize(sprite.angle, 100);
+
+ var button:StageElementButton = cast(sprite.extra.get(exID("button")), StageElementButton);
+ button.xml.att.x = Std.string(sprite.x);
+ button.xml.att.y = Std.string(sprite.y);
+ button.xml.att.skewx = Std.string(sprite.skew.x);
+ button.xml.att.skewy = Std.string(sprite.skew.y);
+ button.xml.att.angle = Std.string(sprite.angle);
+
+ for (attrib in ["graphicSize", "graphicSizex", "graphicSizey"])
+ button.xml.x.remove(attrib);
+ if (MathUtil.equal(sprite.scale.x, sprite.scale.y)) {
+ button.xml.att.scale = Std.string(sprite.scale.x);
+ } else {
+ button.xml.att.scalex = Std.string(sprite.scale.x);
+ button.xml.att.scaley = Std.string(sprite.scale.y);
+ }
+
+ if (button.xml.has.width)
+ button.xml.att.width = Std.string(sprite.width);
+ if (button.xml.has.height)
+ button.xml.att.height = Std.string(sprite.height);
+
+ button.updateInfo();
+ }
+
function _edit_undo(_) {
UIState.playEditorSound(Flags.DEFAULT_EDITOR_UNDO_SOUND);
var undo = undos.undo();
@@ -780,7 +736,7 @@ class StageEditor extends UIState {
sprite.scale.set(oldInfo.scaleX, oldInfo.scaleY);
sprite.skew.set(oldInfo.skewX, oldInfo.skewY);
sprite.angle = oldInfo.angle;
- cast(sprite.extra.get(exID("button")), StageElementButton).updateInfo();
+ storeSpriteTransform(sprite);
}
}
@@ -806,7 +762,7 @@ class StageEditor extends UIState {
sprite.scale.set(newInfo.scaleX, newInfo.scaleY);
sprite.skew.set(newInfo.skewX, newInfo.skewY);
sprite.angle = newInfo.angle;
- cast(sprite.extra.get(exID("button")), StageElementButton).updateInfo();
+ storeSpriteTransform(sprite);
}
}
@@ -1191,6 +1147,7 @@ class StageEditor extends UIState {
if (prevMode == NONE && mouseMode == NONE) return;
if (prevMode != NONE && mouseMode == NONE) {
+ storeSpriteTransform(sprite);
undos.addToUndo(CTransform(sprite, {
x: storedPos.x,
y: storedPos.y,
diff --git a/source/funkin/editors/stage/elements/StageCharacterButton.hx b/source/funkin/editors/stage/elements/StageCharacterButton.hx
index afd13840e8..142960734e 100644
--- a/source/funkin/editors/stage/elements/StageCharacterButton.hx
+++ b/source/funkin/editors/stage/elements/StageCharacterButton.hx
@@ -41,6 +41,50 @@ class StageCharacterButton extends StageElementButton {
return char;
}
+ override public function getDefaults():Map {
+ return [
+ "flip" => false,
+ "flipX" => false,
+ "camxoffset" => 0,
+ "camyoffset" => 0,
+ "spacingx" => 0,
+ "spacingy" => 0,
+ "scale" => 1,
+ "scroll" => 1,
+ "zoomfactor" => 1,
+ "alpha" => 1,
+ "angle" => 0,
+ "skew" => 0
+ ];
+ }
+
+ override public function getPointAttributes():Array {
+ return ["scale", "scroll", "skew"];
+ }
+
+ override public function getAttributeOrder():Array {
+ return [
+ "name",
+ "x",
+ "y",
+ "camxoffset",
+ "camyoffset",
+ "scale",
+ "scalex",
+ "scaley",
+ "scroll",
+ "scrollx",
+ "scrolly",
+ "zoomfactor",
+ "alpha",
+ "angle",
+ "skewx",
+ "skewy",
+ "flip",
+ "flipX"
+ ];
+ }
+
public override function onSelect() {
StageEditor.instance.selectSprite(char);
}
diff --git a/source/funkin/editors/stage/elements/StageElementButton.hx b/source/funkin/editors/stage/elements/StageElementButton.hx
index 4ba56ec882..a4eb4a367d 100644
--- a/source/funkin/editors/stage/elements/StageElementButton.hx
+++ b/source/funkin/editors/stage/elements/StageElementButton.hx
@@ -184,6 +184,18 @@ class StageElementButton extends UIButton {
return "UNKNOWN";
}
+ public function getDefaults():Map {
+ return [];
+ }
+
+ public function getPointAttributes():Array {
+ return [];
+ }
+
+ public function getAttributeOrder():Array {
+ return ["name"];
+ }
+
public function onSelect() {
// TODO: implement
}
@@ -217,4 +229,30 @@ class StageElementButton extends UIButton {
pos.put();
return text;
}
+
+ public function cleanupXML() {
+ var defaults = getDefaults();
+ var queuedPoints = [];
+ xml.x.attributeOrder = getAttributeOrder();
+ for (a in xml.x.attributes()) {
+ var attribIsPoint:Bool = isPoint(a);
+ var attrib = attribIsPoint ? a.substring(0, a.length - 1) : a;
+
+ var def:Dynamic = defaults[attrib];
+ def = def is Bool ? Std.string(def) : def;
+
+ var value:Dynamic = def is Float ? Std.parseFloat(xml.x.get(attrib)) : xml.x.get(attrib);
+ if (value == def || (def is Float && Math.abs(def - value) < 0.001)) {
+ if (!attribIsPoint || queuedPoints.contains(attrib))
+ xml.x.remove(a);
+ else
+ queuedPoints.push(attrib);
+ }
+ }
+ }
+ public function isPoint(attrib:String):Bool {
+ var lastChar = attrib.charCodeAt(attrib.length - 1);
+ var points = getPointAttributes();
+ return (lastChar == 'x'.code || lastChar == 'y'.code) && points.contains(attrib.substring(0, attrib.length - 1));
+ }
}
\ No newline at end of file
diff --git a/source/funkin/editors/stage/elements/StageSpriteButton.hx b/source/funkin/editors/stage/elements/StageSpriteButton.hx
index c84bb8f380..35ddaec06a 100644
--- a/source/funkin/editors/stage/elements/StageSpriteButton.hx
+++ b/source/funkin/editors/stage/elements/StageSpriteButton.hx
@@ -32,6 +32,64 @@ class StageSpriteButton extends StageElementButton {
return sprite;
}
+ override public function getDefaults():Map {
+ return [
+ "scale" => 1,
+ "scroll" => 1,
+ "zoomfactor" => 1,
+ "updateHitbox" => false,
+ "antialiasing" => true,
+ "alpha" => 1,
+ "angle" => 0,
+ "skew" => 0,
+ "type" => "loop",
+ "beatOffset" => 0,
+ "beatInterval" => 2,
+ "interval" => 2,
+ "flipX" => false,
+ "flipY" => false
+ ];
+ }
+
+ override public function getPointAttributes():Array {
+ return ["scale", "scroll", "skew"];
+ }
+
+ override public function getAttributeOrder():Array {
+ return [
+ "name",
+ "sprite",
+ "x",
+ "y",
+ "scale",
+ "scalex",
+ "scaley",
+ "updateHitbox",
+ "scroll",
+ "scrollx",
+ "scrolly",
+ "zoomfactor",
+ "antialiasing",
+ "alpha",
+ "blend",
+ "angle",
+ "skewx",
+ "skewy",
+ "color",
+ "flipX",
+ "flipY",
+ "width",
+ "height",
+ "graphicSize",
+ "graphicSizex",
+ "graphicSizey",
+ "beatInterval",
+ "interval",
+ "beatOffset",
+ "playOnCountdown"
+ ];
+ }
+
public override function onSelect() {
StageEditor.instance.selectSprite(sprite);
}
diff --git a/source/funkin/editors/ui/UIFileExplorer.hx b/source/funkin/editors/ui/UIFileExplorer.hx
index 4f75fd6efe..f402dcda81 100644
--- a/source/funkin/editors/ui/UIFileExplorer.hx
+++ b/source/funkin/editors/ui/UIFileExplorer.hx
@@ -2,6 +2,9 @@ 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;
@@ -24,10 +27,16 @@ class UIFileExplorer extends UISliceSprite {
if (onFile != null) this.onFile = onFile;
- uploadButton = new UIButton(x + 8, y+ 8, null, function () {
+ uploadButton = new UIButton(x + 8, y + 8, null, function () {
+ #if lime_funkin
+ FileDialog.openFile(FlxG.stage.window, "Open File", (fileNames:Array, activeFilter:FileDialogFilter) -> {
+ loadFile(fileNames[0]);
+ }, this.fileType != null ? [new FileDialogFilter("Specified File Extension", this.fileType)] : null);
+ #else
var fileDialog = new FileDialog();
fileDialog.onSelect.add(loadFile);
fileDialog.browse(OPEN, this.fileType);
+ #end
}, bWidth - 16, bHeight - 16);
members.push(uploadButton);
@@ -62,6 +71,7 @@ class UIFileExplorer extends UISliceSprite {
}
public function loadFile(path:String) {
+ if (path == null) return;
file = cast sys.io.File.getBytes(filePath = path);
deleteButton.visible = deleteButton.selectable = deleteIcon.visible = !(uploadButton.visible = uploadButton.selectable = false);
diff --git a/source/funkin/editors/ui/UIImageExplorer.hx b/source/funkin/editors/ui/UIImageExplorer.hx
index cf6cdab31d..5f5a2560b0 100644
--- a/source/funkin/editors/ui/UIImageExplorer.hx
+++ b/source/funkin/editors/ui/UIImageExplorer.hx
@@ -39,7 +39,7 @@ class UIImageExplorer extends UIFileExplorer {
return TU.translate("uiImageExplorer." + id, args);
public function new(x:Float, y:Float, image:String, ?w:Int, ?h:Int, ?onFile:(String, Bytes)->Void, ?directory:String = "images") {
- super(x, y, w, h, "png, jpg", function (filePath, file) {
+ super(x, y, w, h, "png;jpg", function (filePath, file) {
if (filePath != null && file != null) uploadImage(filePath, file);
if (onFile != null) onFile(filePath, file);
});
diff --git a/source/funkin/editors/ui/UINumericStepper.hx b/source/funkin/editors/ui/UINumericStepper.hx
index ff640414c9..73e12126f5 100644
--- a/source/funkin/editors/ui/UINumericStepper.hx
+++ b/source/funkin/editors/ui/UINumericStepper.hx
@@ -37,7 +37,9 @@ class UINumericStepper extends UITextBox {
} else if (max != null) {
v = Math.min(v, max);
}
- label.text = Std.string(FlxMath.roundDecimal(v, precision));
+ // charter leak fix
+ final targetText = Std.string(FlxMath.roundDecimal(v, precision));
+ if (label.text != targetText) label.text = targetText;
return value = v;
}
}
\ No newline at end of file
diff --git a/source/funkin/editors/ui/UITextBox.hx b/source/funkin/editors/ui/UITextBox.hx
index 0cdcd999fb..e080da51f5 100644
--- a/source/funkin/editors/ui/UITextBox.hx
+++ b/source/funkin/editors/ui/UITextBox.hx
@@ -72,7 +72,10 @@ class UITextBox extends UISliceSprite implements IUIFocusable {
framesOffset = (selected ? 18 : (hovered ? 9 : 0));
@:privateAccess {
if (selected) {
- __wasFocused = true;
+ if (!__wasFocused) {
+ FlxG.stage.window.textInputEnabled = true;
+ __wasFocused = true;
+ }
caretSpr.alpha = (FlxG.game.ticks % 666) >= 333 ? 1 : 0;
var curPos = switch (position) {
@@ -91,6 +94,7 @@ class UITextBox extends UISliceSprite implements IUIFocusable {
curPos.put();
} else {
if (__wasFocused) {
+ FlxG.stage.window.textInputEnabled = false;
__wasFocused = false;
if (onChange != null)
onChange(label.text);
diff --git a/source/funkin/game/Character.hx b/source/funkin/game/Character.hx
index be9f722789..2760dc1657 100644
--- a/source/funkin/game/Character.hx
+++ b/source/funkin/game/Character.hx
@@ -372,7 +372,7 @@ class Character extends FunkinSprite implements IBeatReceiver implements IOffset
xml = scripts.event("onCharacterXMLParsed", EventManager.get(CharacterXMLEvent).recycle(this, xml)).xml;
- sprite = curCharacter;
+ name = sprite = curCharacter;
spriteAnimType = BEAT;
this.xml = xml; // Modders wassup :D
@@ -414,7 +414,7 @@ class Character extends FunkinSprite implements IBeatReceiver implements IOffset
var hasInterval:Bool = xml.x.exists("interval");
if (hasInterval) beatInterval = Std.parseInt(xml.x.get("interval"));
- loadSprite(Paths.image('characters/$sprite'));
+ XMLUtil.appendSpriteSheetsFromXML(this, xml, 'characters/');
if (xml.x.exists("centercam")) centeredCamera = (xml.x.get("centercam") == "true");
else if (Flags.USE_LEGACY_CENTER_CAM) centeredCamera = true;
diff --git a/source/funkin/game/PlayState.hx b/source/funkin/game/PlayState.hx
index 135edeb766..9d2599c17e 100644
--- a/source/funkin/game/PlayState.hx
+++ b/source/funkin/game/PlayState.hx
@@ -1114,7 +1114,7 @@ class PlayState extends MusicBeatState
if (notNull) PlayState.instance.gameAndCharsCall("onStageDestroy", [stage]);
scripts.call("destroy");
- for (g in __cachedGraphics) g.useCount--;
+ for (g in __cachedGraphics) g.decrementUseCount();
@:privateAccess {
for (strumLine in strumLines.members) FlxG.sound.destroySound(strumLine.vocals);
if (FlxG.sound.music != inst) FlxG.sound.destroySound(inst);
@@ -1265,11 +1265,6 @@ 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();
@@ -1278,11 +1273,6 @@ 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();
diff --git a/source/funkin/game/cutscenes/VideoCutscene.hx b/source/funkin/game/cutscenes/VideoCutscene.hx
index 0e8fbe7449..9d035f8d60 100644
--- a/source/funkin/game/cutscenes/VideoCutscene.hx
+++ b/source/funkin/game/cutscenes/VideoCutscene.hx
@@ -25,7 +25,7 @@ class VideoCutscene extends Cutscene {
var localPath:String;
#if VIDEO_CUTSCENES
- var video:FlxVideoSprite;
+ final video:FlxVideoSprite = new FlxVideoSprite();
final mutex = new sys.thread.Mutex();
var cutsceneCamera:FlxCamera;
@@ -58,17 +58,14 @@ class VideoCutscene extends Cutscene {
parseSubtitles();
- add(video = new FlxVideoSprite());
+ add(video);
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(Std.int(width * scale), Std.int(height * scale));
+ video.setGraphicSize(width * scale, height * scale);
video.updateHitbox();
video.screenCenter();
});
@@ -103,7 +100,7 @@ class VideoCutscene extends Cutscene {
FlxTween.tween(loadingBackdrop, {alpha: 1}, 0.5, {ease: FlxEase.sineInOut});
Main.execAsync(function() {
- if (video.load(localPath)) new FlxTimer().start(0.001, function(_) {
+ if (video.load(localPath)) FlxTimer.wait(0.001, function() {
mutex.acquire(); onReady(); mutex.release();
});
else { mutex.acquire(); close(); mutex.release(); }
@@ -203,18 +200,6 @@ 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();
diff --git a/source/funkin/menus/FreeplayState.hx b/source/funkin/menus/FreeplayState.hx
index 27869494ec..7ee605baf3 100644
--- a/source/funkin/menus/FreeplayState.hx
+++ b/source/funkin/menus/FreeplayState.hx
@@ -189,37 +189,6 @@ class FreeplayState extends MusicBeatState
interpColor = new FlxInterpolateColor(bg.color);
}
- #if PRELOAD_ALL
- /**
- * How much time a song stays selected until it autoplays.
- */
- public var timeUntilAutoplay:Float = 1;
- /**
- * Whenever the song autoplays when hovered over.
- */
- public var disableAutoPlay:Bool = false;
- /**
- * Whenever the autoplayed song gets async loaded.
- */
- public var disableAsyncLoading:Bool = #if desktop false #else true #end;
- /**
- * Time elapsed since last autoplay. If this time exceeds `timeUntilAutoplay`, the currently selected song will play.
- */
- public var autoplayElapsed:Float = 0;
- /**
- * Whenever the currently selected song instrumental is playing.
- */
- public var songInstPlaying:Bool = true;
- /**
- * Path to the currently playing song instrumental.
- */
- public var curPlayingInst:String = null;
- /**
- * If it should play the song automatically.
- */
- public var autoplayShouldPlay:Bool = true;
- #end
-
private var TEXT_FREEPLAY_SCORE = TU.getRaw("freeplay.score");
override function update(elapsed:Float)
@@ -255,46 +224,6 @@ class FreeplayState extends MusicBeatState
interpColor.fpsLerpTo(curSong.color, 0.0625);
bg.color = interpColor.color;
- #if PRELOAD_ALL
- var dontPlaySongThisFrame = false;
- autoplayElapsed += elapsed;
- if (!disableAutoPlay && !songInstPlaying && (autoplayElapsed > timeUntilAutoplay)) {
- if (curPlayingInst != (curPlayingInst = Paths.inst(curSong.name, curDifficulties[curDifficulty], curSong.instSuffix))) {
- var streamed = false;
- /*if (Options.streamedMusic) {
- var sound = Assets.getMusic(curPlayingInst, true, false);
- streamed = sound != null;
-
- if (streamed && autoplayShouldPlay) {
- FlxG.sound.playMusic(sound, 0);
- Conductor.changeBPM(curSong.bpm, curSong.beatsPerMeasure, curSong.stepsPerBeat);
- }
- }*/
-
- if (!streamed) {
- var huh:Void->Void = function() {
- var soundPath = curPlayingInst;
- var sound = null;
- if (Assets.exists(soundPath, SOUND) || Assets.exists(soundPath, MUSIC))
- sound = Assets.getSound(soundPath);
- else
- FlxG.log.error('Could not find a Sound asset with an ID of \'$soundPath\'.');
-
- if (sound != null && autoplayShouldPlay) {
- FlxG.sound.playMusic(sound, 0);
- Conductor.changeBPM(curSong.bpm, curSong.beatsPerMeasure, curSong.stepsPerBeat);
- }
- }
- if (!disableAsyncLoading) Main.execAsync(huh);
- else huh();
- }
- }
- songInstPlaying = true;
- if (disableAsyncLoading/* && !Options.streamedMusic*/) dontPlaySongThisFrame = true;
- }
- #end
-
-
if (controls.BACK)
{
CoolUtil.playMenuSFX(CANCEL, 0.7);
@@ -306,7 +235,7 @@ class FreeplayState extends MusicBeatState
convertChart();
#end
- if (controls.ACCEPT #if PRELOAD_ALL && !dontPlaySongThisFrame #end)
+ if (controls.ACCEPT)
select();
}
@@ -338,10 +267,6 @@ class FreeplayState extends MusicBeatState
if (event.cancelled) return;
- #if PRELOAD_ALL
- autoplayShouldPlay = false;
- #end
-
Options.freeplayLastSong = curSong.name;
Options.freeplayLastDifficulty = curDifficulties[curDifficulty];
Options.freeplayLastVariation = curSong.variant;
@@ -377,13 +302,6 @@ class FreeplayState extends MusicBeatState
updateCurSong();
updateScore();
- #if PRELOAD_ALL
- if (curSong != prevSong) {
- autoplayElapsed = 0;
- songInstPlaying = false;
- }
- #end
-
var text = validDifficulties ? curDifficulties[curDifficulty].toUpperCase() + (curSong != songs[curSelected] ? ' (${curSong.variant.toUpperCase()})' : '') : '-';
diffText.text = curDifficulties.length > 1 ? '< $text >' : text;
}
@@ -466,11 +384,6 @@ class FreeplayState extends MusicBeatState
changeDiff(0, true);
- #if PRELOAD_ALL
- autoplayElapsed = 0;
- songInstPlaying = false;
- #end
-
coopText.visible = curSong.coopAllowed || curSong.opponentModeAllowed;
}
@@ -478,8 +391,8 @@ class FreeplayState extends MusicBeatState
var event = event("onUpdateOptionsAlpha", EventManager.get(FreeplayAlphaUpdateEvent).recycle(0.6, 0.45, 1, 1, 0.25));
if (event.cancelled) return;
- final idleAlpha = #if PRELOAD_ALL songInstPlaying ? event.idlePlayingAlpha : #end event.idleAlpha;
- final selectedAlpha = #if PRELOAD_ALL songInstPlaying ? event.selectedPlayingAlpha : #end event.selectedAlpha;
+ final idleAlpha = event.idleAlpha;
+ final selectedAlpha = event.selectedAlpha;
for (i in 0...iconArray.length)
iconArray[i].alpha = lerp(iconArray[i].alpha, idleAlpha, event.lerp);
diff --git a/source/funkin/options/Options.hx b/source/funkin/options/Options.hx
index 447b7d45cf..d384100b7f 100644
--- a/source/funkin/options/Options.hx
+++ b/source/funkin/options/Options.hx
@@ -39,13 +39,23 @@ class Options
public static var betaUpdates:Bool = false;
public static var splashesEnabled:Bool = true;
public static var legacyMemoryCounter:Bool = false;
- @:dox(hide) @:doNotSave public static var hitWindow:Float = 250; // DEPRECATED
+
+ // DEPRECATED
+ @:dox(hide) @:doNotSave public static var hitWindow:Float = 250;
+
+ /*
+ * The maximum LIMITED framerate the game can run at.
+ * CANNOT be changed through scripts.
+ * @since 1.1.0-rc2
+ */
+ @:doNotSave public static inline final maxFrameRate:Int = 240;
+
public static var songOffset:Float = 0;
public static var framerate:Int = 120;
public static var gpuOnlyBitmaps:Bool = #if (mac || web) false #else true #end; // causes issues on mac and web
public static var language = "en"; // default to english, Flags.DEFAULT_LANGUAGE should not modify this
- public static var streamedMusic:Bool = false;
- public static var streamedVocals:Bool = false;
+ public static var streamedMusic:Bool = true;
+ public static var streamedVocals:Bool = true;
public static var quality:Int = 1;
public static var allowConfigWarning:Bool = true;
#if MODCHARTING_FEATURES
@@ -231,10 +241,15 @@ class Options
applyKeybinds();
applyQuality();
+ flixel.sound.FlxSoundData.allowStreaming = streamedMusic;
FlxG.sound.defaultMusicGroup.volume = volumeMusic;
FlxG.autoPause = autoPause;
- if (FlxG.updateFramerate < framerate) FlxG.drawFramerate = FlxG.updateFramerate = framerate;
- else FlxG.updateFramerate = FlxG.drawFramerate = framerate;
+
+ var _framerate = framerate;
+ if (_framerate > maxFrameRate) _framerate = 0;
+
+ if (FlxG.updateFramerate < framerate) FlxG.drawFramerate = FlxG.updateFramerate = _framerate;
+ else FlxG.updateFramerate = FlxG.drawFramerate = _framerate;
}
public static function applyQuality() {
diff --git a/source/funkin/options/categories/AppearanceOptions.hx b/source/funkin/options/categories/AppearanceOptions.hx
index f6cf1783d0..dde819f66d 100644
--- a/source/funkin/options/categories/AppearanceOptions.hx
+++ b/source/funkin/options/categories/AppearanceOptions.hx
@@ -1,13 +1,19 @@
package funkin.options.categories;
class AppearanceOptions extends TreeMenuScreen {
+ // use for changing the text
+ var framerateOption:NumOption;
+
public function new() {
super('optionsTree.appearance-name', 'optionsTree.appearance-desc', 'AppearanceOptions.');
- add(new NumOption(getNameID('framerate'), getDescID('framerate'),
- 30, 240, 1,
+ add(framerateOption = new NumOption(getNameID('framerate'), getDescID('framerate'),
+ 30, Options.maxFrameRate + 1, 1,
'framerate', __changeFPS
));
+ if (framerateOption.currentValue > Options.maxFrameRate) {
+ __changeFPS(framerateOption.currentValue);
+ }
add(new Checkbox(getNameID('flashingMenu'), getDescID('flashingMenu'), 'flashingMenu'));
add(new Checkbox(getNameID('colorHealthBar'), getDescID('colorHealthBar'), 'colorHealthBar'));
add(new Checkbox(getNameID('week6PixelPerfect'), getDescID('week6PixelPerfect'), 'week6PixelPerfect'));
@@ -19,6 +25,10 @@ class AppearanceOptions extends TreeMenuScreen {
private function __changeFPS(value:Float) {
var framerate = Math.floor(value);
+ @:privateAccess if (framerate > Options.maxFrameRate) {
+ framerate = 0;
+ framerateOption.__number.text = TextOption.OPTION_VALUE_PREFIX + translate('framerate-unlimited');
+ }
if (FlxG.updateFramerate < framerate) FlxG.drawFramerate = FlxG.updateFramerate = framerate;
else FlxG.updateFramerate = FlxG.drawFramerate = framerate;
}
@@ -32,7 +42,7 @@ class AdvancedAppearanceOptions extends TreeMenuScreen {
add(new ArrayOption(getNameID('quality'), getDescID('quality'),
[0, 1, 2], [getID('quality-low'), getID('quality-high'), getID('quality-custom')],
- 'quality', __changeQuality, null
+ 'quality', __changeQuality
));
for (option in (qualityOptions = [
diff --git a/source/funkin/options/categories/GameplayOptions.hx b/source/funkin/options/categories/GameplayOptions.hx
index 4b69ccd3e7..868c3c4c95 100644
--- a/source/funkin/options/categories/GameplayOptions.hx
+++ b/source/funkin/options/categories/GameplayOptions.hx
@@ -67,14 +67,7 @@ class AdvancedGameplayOptions extends TreeMenuScreen {
public function new() {
super('optionsMenu.advanced', 'optionsTree.gameplay.advanced-desc', 'GameplayOptions.Advanced.');
- // Remove locked whenever this PR from FunkinCrew is merged.
- // https://github.com/FunkinCrew/lime/pull/57
- for (checkbox in [
- new Checkbox(getNameID('streamedMusic'), getDescID('streamedMusic'), 'streamedMusic'),
- new Checkbox(getNameID('streamedVocals'), getDescID('streamedVocals'), 'streamedVocals')
- ]) {
- checkbox.locked = true;
- add(checkbox);
- }
+ add(new Checkbox(getNameID('streamedMusic'), getDescID('streamedMusic'), 'streamedMusic'));
+ add(new Checkbox(getNameID('streamedVocals'), getDescID('streamedVocals'), 'streamedVocals'));
}
}
\ No newline at end of file
diff --git a/source/haxe/Timer.hx b/source/haxe/Timer.hx
deleted file mode 100644
index 5092af8cc9..0000000000
--- a/source/haxe/Timer.hx
+++ /dev/null
@@ -1,297 +0,0 @@
-package haxe;
-
-#if !lime_cffi
-// Original haxe.Timer class
-
-/*
- * Copyright (C)2005-2018 Haxe Foundation
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- * DEALINGS IN THE SOFTWARE.
- */
-/**
- The Timer class allows you to create asynchronous timers on platforms that
- support events.
-
- The intended usage is to create an instance of the Timer class with a given
- interval, set its run() method to a custom function to be invoked and
- eventually call stop() to stop the Timer.
-
- Note that a running Timer may or may not prevent the program to exit
- automatically when main() returns.
-
- It is also possible to extend this class and override its run() method in
- the child class.
-**/
-class Timer
-{
- #if (flash || js)
- private var id:Null;
- #elseif java
- private var timer:java.util.Timer;
- private var task:java.util.TimerTask;
- #elseif (haxe_ver >= "3.4.0")
- private var event:MainLoop.MainEvent;
- #end
-
- /**
- Creates a new timer that will run every `time_ms` milliseconds.
-
- After creating the Timer instance, it calls `this.run` repeatedly,
- with delays of `time_ms` milliseconds, until `this.stop` is called.
-
- The first invocation occurs after `time_ms` milliseconds, not
- immediately.
-
- The accuracy of this may be platform-dependent.
- **/
- public function new(time_ms:Int)
- {
- #if flash
- var me = this;
- id = untyped __global__["flash.utils.setInterval"](function()
- {
- me.run();
- }, time_ms);
- #elseif js
- var me = this;
- id = untyped setInterval(function() me.run(), time_ms);
- #elseif java
- timer = new java.util.Timer();
- timer.scheduleAtFixedRate(task = new TimerTask(this), haxe.Int64.ofInt(time_ms), haxe.Int64.ofInt(time_ms));
- #elseif (haxe_ver >= "3.4.0")
- var dt = time_ms / 1000;
- event = MainLoop.add(function()
- {
- @:privateAccess event.nextRun += dt;
- run();
- });
- event.delay(dt);
- #end
- }
-
- /**
- Stops `this` Timer.
-
- After calling this method, no additional invocations of `this.run`
- will occur.
-
- It is not possible to restart `this` Timer once stopped.
- **/
- public function stop()
- {
- #if (flash || js)
- if (id == null) return;
- #if flash
- untyped __global__["flash.utils.clearInterval"](id);
- #elseif js
- untyped clearInterval(id);
- #end
- id = null;
- #elseif java
- if (timer != null)
- {
- timer.cancel();
- timer = null;
- }
- task = null;
- #elseif (haxe_ver >= "3.4.0")
- if (event != null)
- {
- event.stop();
- event = null;
- }
- #end
- }
-
- /**
- This method is invoked repeatedly on `this` Timer.
-
- It can be overridden in a subclass, or rebound directly to a custom
- function:
- var timer = new haxe.Timer(1000); // 1000ms delay
- timer.run = function() { ... }
-
- Once bound, it can still be rebound to different functions until `this`
- Timer is stopped through a call to `this.stop`.
- **/
- public dynamic function run() {}
-
- /**
- Invokes `f` after `time_ms` milliseconds.
-
- This is a convenience function for creating a new Timer instance with
- `time_ms` as argument, binding its run() method to `f` and then stopping
- `this` Timer upon the first invocation.
-
- If `f` is null, the result is unspecified.
- **/
- public static function delay(f:Void->Void, time_ms:Int)
- {
- var t = new haxe.Timer(time_ms);
- t.run = function()
- {
- t.stop();
- f();
- };
- return t;
- }
-
- /**
- Measures the time it takes to execute `f`, in seconds with fractions.
-
- This is a convenience function for calculating the difference between
- Timer.stamp() before and after the invocation of `f`.
-
- The difference is passed as argument to Log.trace(), with "s" appended
- to denote the unit. The optional `pos` argument is passed through.
-
- If `f` is null, the result is unspecified.
- **/
- public static function measure(f:Void->T, ?pos:PosInfos):T
- {
- var t0 = stamp();
- var r = f();
- Log.trace((stamp() - t0) + "s", pos);
- return r;
- }
-
- /**
- Returns a timestamp, in seconds with fractions.
-
- The value itself might differ depending on platforms, only differences
- between two values make sense.
- **/
- public static inline function stamp():Float
- {
- #if flash
- return flash.Lib.getTimer() / 1000;
- #elseif (neko || php)
- return Sys.time();
- #elseif js
- return Date.now().getTime() / 1000;
- #elseif cpp
- return untyped __global__.__time_stamp();
- #elseif python
- return Sys.cpuTime();
- #elseif sys
- return Sys.time();
- #else
- return 0;
- #end
- }
-}
-
-#if java
-@:nativeGen
-private class TimerTask extends java.util.TimerTask
-{
- var timer:Timer;
-
- public function new(timer:Timer):Void
- {
- super();
- this.timer = timer;
- }
-
- @:overload override public function run():Void
- {
- timer.run();
- }
-}
-#end
-#else
-import lime.system.System;
-
-class Timer
-{
- private static var sRunningTimers:Array = [];
-
- private var mTime:Float;
- private var mFireAt:Float;
- private var mRunning:Bool;
-
- public function new(time:Float)
- {
- mTime = time;
- sRunningTimers.push(this);
- mFireAt = getMS() + mTime;
- mRunning = true;
- }
-
- public static function delay(f:Void->Void, time:Int)
- {
- var t = new Timer(time);
-
- t.run = function()
- {
- t.stop();
- f();
- };
-
- return t;
- }
-
- private static function getMS():Float
- {
- return System.getTimer();
- }
-
- public static function measure(f:Void->T, ?pos:PosInfos):T
- {
- var t0 = stamp();
- var r = f();
- Log.trace((stamp() - t0) + "s", pos);
- return r;
- }
-
- dynamic public function run() {}
-
- public static inline function stamp():Float
- {
- var timer = System.getTimer();
- return (timer > 0 ? timer / 1000 : 0);
- }
-
- public function stop():Void
- {
- /*if (mRunning)
- {
-
- for (i in 0...sRunningTimers.length)
- {
- if (sRunningTimers[i] == this)
- {
- sRunningTimers[i] = null;
- break;
- }
- }
- }*/
- mRunning = false;
- }
-
- @:noCompletion private function __check(inTime:Float)
- {
- if (inTime >= mFireAt)
- {
- mFireAt += mTime;
- run();
- }
- }
-}
-#end
diff --git a/source/hscript/Config.hx b/source/hscript/Config.hx
index 3acd554b16..949eca8d71 100644
--- a/source/hscript/Config.hx
+++ b/source/hscript/Config.hx
@@ -7,6 +7,9 @@ class Config {
"flixel",
"funkin",
+ #if foxlite
+ "foxlite",
+ #end
#if MODCHARTING_FEATURES
"modchart.engine",
"modchart.backend.standalone",
@@ -23,6 +26,9 @@ class Config {
"haxe.xml",
"haxe.CallStack",
"funkin",
+ #if foxlite
+ "foxlite"
+ #end
#end
];
diff --git a/source/lime/_internal/backend/html5/HTML5AudioSource.hx b/source/lime/_internal/backend/html5/HTML5AudioSource.hx
deleted file mode 100644
index 5d66fc04a5..0000000000
--- a/source/lime/_internal/backend/html5/HTML5AudioSource.hx
+++ /dev/null
@@ -1,283 +0,0 @@
-package lime._internal.backend.html5;
-
-import lime.math.Vector4;
-import lime.media.AudioSource;
-import lime.media.AudioManager;
-
-@:access(lime.media.AudioBuffer)
-class HTML5AudioSource
-{
- private var completed:Bool;
- private var gain:Float;
- private var id:Int;
- private var length:Null;
- private var loops:Int;
- private var parent:AudioSource;
- private var playing:Bool;
- private var position:Vector4;
-
- public function new(parent:AudioSource)
- {
- this.parent = parent;
-
- id = -1;
- gain = 1;
- position = new Vector4();
- }
-
- public function dispose():Void {
- stop();
- }
-
- public function init():Void {}
-
- public function play():Void
- {
- #if lime_howlerjs
- if (playing || parent.buffer == null || parent.buffer.__srcHowl == null)
- {
- return;
- }
-
- playing = true;
-
- var time = getCurrentTime();
-
- completed = false;
-
- var cacheVolume = untyped parent.buffer.__srcHowl._volume;
- untyped parent.buffer.__srcHowl._volume = parent.gain;
-
- id = parent.buffer.__srcHowl.play();
-
- untyped parent.buffer.__srcHowl._volume = cacheVolume;
- // setGain (parent.gain);
-
- setPosition(parent.position);
-
- parent.buffer.__srcHowl.on("end", howl_onEnd, id);
-
- // Calling setCurrentTime causes html5 audio to replay from this position on next frame
- #if force_html5_audio
- if (time == 0) setCurrentTime(time);
- #else
- setCurrentTime(time);
- #end
- #end
- }
-
- public function pause():Void
- {
- #if lime_howlerjs
- playing = false;
-
- if (parent.buffer != null && parent.buffer.__srcHowl != null)
- {
- parent.buffer.__srcHowl.pause(id);
- }
- #end
- }
-
- public function stop():Void
- {
- #if lime_howlerjs
- playing = false;
-
- if (parent.buffer != null && parent.buffer.__srcHowl != null)
- {
- parent.buffer.__srcHowl.stop(id);
- parent.buffer.__srcHowl.off("end", howl_onEnd, id);
- }
- #end
- }
-
- // Event Handlers
- private function howl_onEnd()
- {
- #if lime_howlerjs
- playing = false;
-
- if (loops > 0)
- {
- loops--;
- stop();
- if (loopTime != null && loopTime > 0) setCurrentTime(loopTime);
- play();
- parent.onLoop.dispatch();
- return;
- }
- else if (parent.buffer != null && parent.buffer.__srcHowl != null)
- {
- parent.buffer.__srcHowl.stop(id);
- parent.buffer.__srcHowl.off("end", howl_onEnd, id);
- }
-
- completed = true;
- parent.onComplete.dispatch();
- #end
- }
-
- // Get & Set Methods
- public function getCurrentTime():Float
- {
- if (id == -1)
- {
- return 0;
- }
-
- #if lime_howlerjs
- if (completed)
- {
- return getLength();
- }
- else if (parent.buffer != null && parent.buffer.__srcHowl != null)
- {
- var time = parent.buffer.__srcHowl.seek(id) * 1000.0 - parent.offset;
- if (time < 0) return 0;
- return time;
- }
- #end
-
- return 0;
- }
-
- public function getLatency():Float
- {
- var ctx = AudioManager.context.web;
- if (ctx != null)
- {
- var baseLatency:Float = untyped ctx.baseLatency != null ? untyped ctx.baseLatency : 0;
- var outputLatency:Float = untyped ctx.outputLatency != null ? untyped ctx.outputLatency : 0;
-
- return (baseLatency + outputLatency) * 1000;
- }
-
- return 0;
- }
-
- public function setCurrentTime(value:Float):Float
- {
- #if lime_howlerjs
- if (parent.buffer != null && parent.buffer.__srcHowl != null)
- {
- // if (playing) buffer.__srcHowl.play (id);
- var pos = (value + parent.offset) / 1000;
- if (pos < 0) pos = 0;
- parent.buffer.__srcHowl.seek(pos, id);
- }
- #end
-
- return value;
- }
-
- public function getGain():Float
- {
- return gain;
- }
-
- public function setGain(value:Float):Float
- {
- #if lime_howlerjs
- // set howler volume only if we have an active id.
- // Passing -1 might create issues in future play()'s.
-
- if (parent.buffer != null && parent.buffer.__srcHowl != null && id != -1)
- {
- parent.buffer.__srcHowl.volume(value, id);
- }
- #end
-
- return gain = value;
- }
-
- public function getLength():Null
- {
- if (length != 0)
- {
- return length;
- }
-
- #if lime_howlerjs
- if (parent.buffer != null && parent.buffer.__srcHowl != null)
- {
- return parent.buffer.__srcHowl.duration() * 1000.0;
- }
- #end
-
- return 0;
- }
-
- public function setLength(value:Null):Null
- {
- return length = value;
- }
-
- public function getLoops():Int
- {
- return loops;
- }
-
- public function setLoops(value:Int):Int
- {
- return loops = value;
- }
-
- public function getLoopTime():Float {
- return loopTime;
- }
-
- public function setLoopTime(value:Float):Float {
- return loopTime = value;
- }
-
- public function getPitch():Float
- {
- #if lime_howlerjs
- return parent.buffer.__srcHowl.rate();
- #else
- return 1;
- #end
- }
-
- public function setPitch(value:Float):Float
- {
- #if lime_howlerjs
- parent.buffer.__srcHowl.rate(value);
- #end
-
- return getPitch();
- }
-
-
- public function getPosition():Vector4
- {
- return position;
- }
-
- public function setPosition(value:Vector4):Vector4
- {
- position.x = value.x;
- position.y = value.y;
- position.z = value.z;
- position.w = value.w;
-
- #if lime_howlerjs
- if (parent.buffer != null && parent.buffer.__srcHowl != null && parent.buffer.__srcHowl.pos != null) parent.buffer.__srcHowl.pos(position.x, position.y, position.z, id);
- // There are more settings to the position of the sound on the "pannerAttr()" function of howler. Maybe somebody who understands sound should look into it?
- #end
-
- return position;
- }
-
- public function getPan():Float
- {
- return position.x;
- }
-
- public function setPan(value:Float):Float
- {
- position.setTo(value, 0, -Math.sqrt(1 - value * value));
- if (parent.buffer != null && parent.buffer.__srcHowl != null && parent.buffer.__srcHowl.stereo != null) parent.buffer.__srcHowl.stereo(value, id);
- return value;
- }
-}
diff --git a/source/lime/_internal/backend/native/NativeApplication.hx b/source/lime/_internal/backend/native/NativeApplication.hx
deleted file mode 100644
index ac8ab467ed..0000000000
--- a/source/lime/_internal/backend/native/NativeApplication.hx
+++ /dev/null
@@ -1,981 +0,0 @@
-package lime._internal.backend.native;
-
-import haxe.Timer;
-import lime._internal.backend.native.NativeCFFI;
-import lime.app.Application;
-import lime.graphics.opengl.GL;
-import lime.graphics.OpenGLRenderContext;
-import lime.graphics.RenderContext;
-import lime.math.Rectangle;
-import lime.media.AudioManager;
-import lime.system.Clipboard;
-import lime.system.Display;
-import lime.system.DisplayMode;
-import lime.system.JNI;
-import lime.system.Sensor;
-import lime.system.SensorType;
-import lime.system.System;
-import lime.ui.Gamepad;
-import lime.ui.Joystick;
-import lime.ui.JoystickHatPosition;
-import lime.ui.KeyCode;
-import lime.ui.KeyModifier;
-import lime.ui.Touch;
-import lime.ui.Window;
-
-#if !lime_debug
-@:fileXml('tags="haxe,release"')
-@:noDebug
-#end
-@:access(haxe.Timer)
-@:access(lime._internal.backend.native.NativeCFFI)
-@:access(lime._internal.backend.native.NativeOpenGLRenderContext)
-@:access(lime._internal.backend.native.NativeWindow)
-@:access(lime.app.Application)
-@:access(lime.graphics.opengl.GL)
-@:access(lime.graphics.OpenGLRenderContext)
-@:access(lime.graphics.Renderer)
-@:access(lime.system.Clipboard)
-@:access(lime.system.Sensor)
-@:access(lime.ui.Gamepad)
-@:access(lime.ui.Joystick)
-@:access(lime.ui.Window)
-class NativeApplication
-{
- private var applicationEventInfo = new ApplicationEventInfo(UPDATE);
- private var clipboardEventInfo = new ClipboardEventInfo();
- private var currentTouches = new Map();
- private var dropEventInfo = new DropEventInfo();
- private var gamepadEventInfo = new GamepadEventInfo();
- private var joystickEventInfo = new JoystickEventInfo();
- private var keyEventInfo = new KeyEventInfo();
- private var mouseEventInfo = new MouseEventInfo();
- private var renderEventInfo = new RenderEventInfo(RENDER);
- private var sensorEventInfo = new SensorEventInfo();
- private var textEventInfo = new TextEventInfo();
- private var touchEventInfo = new TouchEventInfo();
- private var unusedTouchesPool = new List();
- private var windowEventInfo = new WindowEventInfo();
-
- public var handle:Dynamic;
-
- private var pauseTimer:Int;
- private var parent:Application;
- private var toggleFullscreen:Bool;
-
- private static function __init__()
- {
- #if (lime_cffi && !macro)
- var init = NativeCFFI;
- #end
- }
-
- public function new(parent:Application):Void
- {
- this.parent = parent;
- pauseTimer = -1;
- toggleFullscreen = true;
-
- AudioManager.init();
-
- #if (ios || android || tvos)
- Sensor.registerSensor(SensorType.ACCELEROMETER, 0);
- #end
-
- #if (!macro && lime_cffi)
- handle = NativeCFFI.lime_application_create();
- #end
- }
-
- private function advanceTimer():Void
- {
- #if lime_cffi
- if (pauseTimer > -1)
- {
- var offset = System.getTimer() - pauseTimer;
- for(timer in Timer.sRunningTimers) {
- if(timer != null && timer.mRunning) timer.mFireAt += offset;
- }
- pauseTimer = -1;
- }
- #end
- }
-
- public function exec():Int
- {
- #if !macro
- #if lime_cffi
- NativeCFFI.lime_application_event_manager_register(handleApplicationEvent, applicationEventInfo);
- NativeCFFI.lime_clipboard_event_manager_register(handleClipboardEvent, clipboardEventInfo);
- NativeCFFI.lime_drop_event_manager_register(handleDropEvent, dropEventInfo);
- NativeCFFI.lime_gamepad_event_manager_register(handleGamepadEvent, gamepadEventInfo);
- NativeCFFI.lime_joystick_event_manager_register(handleJoystickEvent, joystickEventInfo);
- NativeCFFI.lime_key_event_manager_register(handleKeyEvent, keyEventInfo);
- NativeCFFI.lime_mouse_event_manager_register(handleMouseEvent, mouseEventInfo);
- NativeCFFI.lime_render_event_manager_register(handleRenderEvent, renderEventInfo);
- NativeCFFI.lime_text_event_manager_register(handleTextEvent, textEventInfo);
- NativeCFFI.lime_touch_event_manager_register(handleTouchEvent, touchEventInfo);
- NativeCFFI.lime_window_event_manager_register(handleWindowEvent, windowEventInfo);
- #if (ios || android || tvos)
- NativeCFFI.lime_sensor_event_manager_register(handleSensorEvent, sensorEventInfo);
- #end
- #end
-
- #if (nodejs && lime_cffi)
- NativeCFFI.lime_application_init(handle);
-
- var eventLoop = function()
- {
- var active = NativeCFFI.lime_application_update(handle);
-
- if (!active)
- {
- untyped process.exitCode = NativeCFFI.lime_application_quit(handle);
- parent.onExit.dispatch(untyped process.exitCode);
- }
- else
- {
- untyped setImmediate(eventLoop);
- }
- }
-
- untyped setImmediate(eventLoop);
- return 0;
- #elseif lime_cffi
- var result = NativeCFFI.lime_application_exec(handle);
-
- #if (!webassembly && !ios && !nodejs)
- parent.onExit.dispatch(result);
- #end
-
- return result;
- #end
- #end
-
- return 0;
- }
-
- public function exit():Void
- {
- AudioManager.shutdown();
-
- #if (!macro && lime_cffi)
- NativeCFFI.lime_application_quit(handle);
- #end
- }
-
- private function handleApplicationEvent():Void
- {
- switch (applicationEventInfo.type)
- {
- case UPDATE:
- updateTimer();
-
- parent.onUpdate.dispatch(applicationEventInfo.deltaTime);
-
- default:
- }
- }
-
- private function handleClipboardEvent():Void
- {
- Clipboard.__update();
- }
-
- private function handleDropEvent():Void
- {
- for (window in parent.windows)
- {
- window.onDropFile.dispatch(#if hl @:privateAccess String.fromUTF8(dropEventInfo.file) #else dropEventInfo.file #end);
- }
- }
-
- private function handleGamepadEvent():Void
- {
- switch (gamepadEventInfo.type)
- {
- case AXIS_MOVE:
- var gamepad = Gamepad.devices.get(gamepadEventInfo.id);
- if (gamepad != null) gamepad.onAxisMove.dispatch(gamepadEventInfo.axis, gamepadEventInfo.axisValue);
-
- case BUTTON_DOWN:
- var gamepad = Gamepad.devices.get(gamepadEventInfo.id);
- if (gamepad != null) gamepad.onButtonDown.dispatch(gamepadEventInfo.button);
-
- case BUTTON_UP:
- var gamepad = Gamepad.devices.get(gamepadEventInfo.id);
- if (gamepad != null) gamepad.onButtonUp.dispatch(gamepadEventInfo.button);
-
- case CONNECT:
- Gamepad.__connect(gamepadEventInfo.id);
-
- case DISCONNECT:
- Gamepad.__disconnect(gamepadEventInfo.id);
- }
- }
-
- private function handleJoystickEvent():Void
- {
- switch (joystickEventInfo.type)
- {
- case AXIS_MOVE:
- var joystick = Joystick.devices.get(joystickEventInfo.id);
- if (joystick != null) joystick.onAxisMove.dispatch(joystickEventInfo.index, joystickEventInfo.x);
-
- case HAT_MOVE:
- var joystick = Joystick.devices.get(joystickEventInfo.id);
- if (joystick != null) joystick.onHatMove.dispatch(joystickEventInfo.index, joystickEventInfo.eventValue);
-
- case TRACKBALL_MOVE: // I guess this was just removed ??
-
- case BUTTON_DOWN:
- var joystick = Joystick.devices.get(joystickEventInfo.id);
- if (joystick != null) joystick.onButtonDown.dispatch(joystickEventInfo.index);
-
- case BUTTON_UP:
- var joystick = Joystick.devices.get(joystickEventInfo.id);
- if (joystick != null) joystick.onButtonUp.dispatch(joystickEventInfo.index);
-
- case CONNECT:
- Joystick.__connect(joystickEventInfo.id);
-
- case DISCONNECT:
- Joystick.__disconnect(joystickEventInfo.id);
- }
- }
-
- private function handleKeyEvent():Void
- {
- var window = parent.__windowByID.get(keyEventInfo.windowID);
-
- if (window != null)
- {
- var type:KeyEventType = keyEventInfo.type;
- var int32:Float = keyEventInfo.keyCode;
- var keyCode:KeyCode = Std.int(int32);
- var modifier:KeyModifier = keyEventInfo.modifier;
-
- switch (type)
- {
- case KEY_DOWN:
- window.onKeyDown.dispatch(keyCode, modifier);
-
- case KEY_UP:
- window.onKeyUp.dispatch(keyCode, modifier);
- }
-
- #if (windows || linux)
- if (keyCode == RETURN)
- {
- if (type == KEY_DOWN)
- {
- if (toggleFullscreen && modifier.altKey && (!modifier.ctrlKey && !modifier.shiftKey && !modifier.metaKey))
- {
- toggleFullscreen = false;
-
- if (!window.onKeyDown.canceled)
- {
- window.fullscreen = !window.fullscreen;
- }
- }
- }
- else
- {
- toggleFullscreen = true;
- }
- }
-
- #if rpi
- if (keyCode == ESCAPE && modifier == KeyModifier.NONE && type == KEY_UP && !window.onKeyUp.canceled)
- {
- System.exit(0);
- }
- #end
- #elseif mac
- if (keyCode == F)
- {
- if (type == KEY_DOWN)
- {
- if (toggleFullscreen && (modifier.ctrlKey && modifier.metaKey) && (!modifier.altKey && !modifier.shiftKey))
- {
- toggleFullscreen = false;
-
- if (!window.onKeyDown.canceled)
- {
- window.fullscreen = !window.fullscreen;
- }
- }
- }
- else
- {
- toggleFullscreen = true;
- }
- }
- #elseif android
- if (keyCode == APP_CONTROL_BACK && modifier == KeyModifier.NONE && type == KEY_UP && !window.onKeyUp.canceled)
- {
- var mainActivity = JNI.createStaticField("org/haxe/extension/Extension", "mainActivity", "Landroid/app/Activity;");
- var moveTaskToBack = JNI.createMemberMethod("android/app/Activity", "moveTaskToBack", "(Z)Z");
-
- moveTaskToBack(mainActivity.get(), true);
- }
- #end
- }
- }
-
- private function handleMouseEvent():Void
- {
- var window = parent.__windowByID.get(mouseEventInfo.windowID);
-
- if (window != null)
- {
- switch (mouseEventInfo.type)
- {
- case MOUSE_DOWN:
- window.clickCount = mouseEventInfo.clickCount;
- window.onMouseDown.dispatch(mouseEventInfo.x, mouseEventInfo.y, mouseEventInfo.button);
- window.clickCount = 0;
-
- case MOUSE_UP:
- window.clickCount = mouseEventInfo.clickCount;
- window.onMouseUp.dispatch(mouseEventInfo.x, mouseEventInfo.y, mouseEventInfo.button);
- window.clickCount = 0;
-
- case MOUSE_MOVE:
- window.onMouseMove.dispatch(mouseEventInfo.x, mouseEventInfo.y);
- window.onMouseMoveRelative.dispatch(mouseEventInfo.movementX, mouseEventInfo.movementY);
-
- case MOUSE_WHEEL:
- window.onMouseWheel.dispatch(mouseEventInfo.x, mouseEventInfo.y, UNKNOWN);
-
- default:
- }
- }
- }
-
- private function handleRenderEvent():Void
- {
- // TODO: Allow windows to render independently
-
- for (window in parent.__windows)
- {
- if (window == null) continue;
-
- // parent.renderer = renderer;
-
- switch (renderEventInfo.type)
- {
- case RENDER:
- if (window.context != null)
- {
- window.__backend.render();
- window.onRender.dispatch(window.context);
-
- if (!window.onRender.canceled)
- {
- window.__backend.contextFlip();
- }
- }
-
- case RENDER_CONTEXT_LOST:
- if (window.__backend.useHardware && window.context != null)
- {
- switch (window.context.type)
- {
- case OPENGL, OPENGLES, WEBGL:
- #if (lime_cffi && (lime_opengl || lime_opengles) && !display)
- var gl = window.context.gl;
- (gl : NativeOpenGLRenderContext).__contextLost();
- if (GL.context == gl) GL.context = null;
- #end
-
- default:
- }
-
- window.context = null;
- window.onRenderContextLost.dispatch();
- }
-
- case RENDER_CONTEXT_RESTORED:
- if (window.__backend.useHardware)
- {
- // GL.context = new OpenGLRenderContext ();
- // window.context.gl = GL.context;
-
- window.onRenderContextRestored.dispatch(window.context);
- }
- }
- }
- }
-
- private function handleSensorEvent():Void
- {
- var sensor = Sensor.sensorByID.get(sensorEventInfo.id);
-
- if (sensor != null)
- {
- sensor.onUpdate.dispatch(sensorEventInfo.x, sensorEventInfo.y, sensorEventInfo.z);
- }
- }
-
- private function handleTextEvent():Void
- {
- var window = parent.__windowByID.get(textEventInfo.windowID);
-
- if (window != null)
- {
- switch (textEventInfo.type)
- {
- case TEXT_INPUT:
- window.onTextInput.dispatch(#if hl @:privateAccess String.fromUTF8(textEventInfo.text) #else textEventInfo.text #end);
-
- case TEXT_EDIT:
- window.onTextEdit.dispatch(#if hl @:privateAccess String.fromUTF8(textEventInfo.text) #else textEventInfo.text #end, textEventInfo.start,
- textEventInfo.length);
-
- default:
- }
- }
- }
-
- private function handleTouchEvent():Void
- {
- switch (touchEventInfo.type)
- {
- case TOUCH_START:
- var touch = unusedTouchesPool.pop();
-
- if (touch == null)
- {
- touch = new Touch(touchEventInfo.x, touchEventInfo.y, touchEventInfo.id, touchEventInfo.dx, touchEventInfo.dy, touchEventInfo.pressure,
- touchEventInfo.device);
- }
- else
- {
- touch.x = touchEventInfo.x;
- touch.y = touchEventInfo.y;
- touch.id = touchEventInfo.id;
- touch.dx = touchEventInfo.dx;
- touch.dy = touchEventInfo.dy;
- touch.pressure = touchEventInfo.pressure;
- touch.device = touchEventInfo.device;
- }
-
- currentTouches.set(touch.id, touch);
-
- Touch.onStart.dispatch(touch);
-
- case TOUCH_END:
- var touch = currentTouches.get(touchEventInfo.id);
-
- if (touch != null)
- {
- touch.x = touchEventInfo.x;
- touch.y = touchEventInfo.y;
- touch.dx = touchEventInfo.dx;
- touch.dy = touchEventInfo.dy;
- touch.pressure = touchEventInfo.pressure;
-
- Touch.onEnd.dispatch(touch);
-
- currentTouches.remove(touchEventInfo.id);
- unusedTouchesPool.add(touch);
- }
-
- case TOUCH_MOVE:
- var touch = currentTouches.get(touchEventInfo.id);
-
- if (touch != null)
- {
- touch.x = touchEventInfo.x;
- touch.y = touchEventInfo.y;
- touch.dx = touchEventInfo.dx;
- touch.dy = touchEventInfo.dy;
- touch.pressure = touchEventInfo.pressure;
-
- Touch.onMove.dispatch(touch);
- }
-
- default:
- }
- }
-
- private function handleWindowEvent():Void
- {
- var window = parent.__windowByID.get(windowEventInfo.windowID);
-
- if (window != null)
- {
- switch (windowEventInfo.type)
- {
- case WINDOW_ACTIVATE:
- advanceTimer();
- window.onActivate.dispatch();
- AudioManager.resume();
-
- case WINDOW_CLOSE:
- window.close();
-
- case WINDOW_DEACTIVATE:
- window.onDeactivate.dispatch();
- AudioManager.suspend();
- pauseTimer = System.getTimer();
-
- case WINDOW_ENTER:
- window.onEnter.dispatch();
-
- case WINDOW_EXPOSE:
- window.onExpose.dispatch();
-
- case WINDOW_FOCUS_IN:
- window.onFocusIn.dispatch();
-
- case WINDOW_FOCUS_OUT:
- window.onFocusOut.dispatch();
-
- case WINDOW_LEAVE:
- window.onLeave.dispatch();
-
- case WINDOW_MAXIMIZE:
- window.__maximized = true;
- window.__fullscreen = false;
- window.__minimized = false;
- window.onMaximize.dispatch();
-
- case WINDOW_MINIMIZE:
- window.__minimized = true;
- window.__maximized = false;
- window.__fullscreen = false;
- window.onMinimize.dispatch();
-
- case WINDOW_MOVE:
- window.__x = windowEventInfo.x;
- window.__y = windowEventInfo.y;
- window.onMove.dispatch(windowEventInfo.x, windowEventInfo.y);
-
- case WINDOW_RESIZE:
- window.__width = windowEventInfo.width;
- window.__height = windowEventInfo.height;
- window.onResize.dispatch(windowEventInfo.width, windowEventInfo.height);
-
- case WINDOW_RESTORE:
- window.__fullscreen = false;
- window.__minimized = false;
- window.onRestore.dispatch();
-
- case WINDOW_SHOW:
- window.onShow.dispatch();
-
- case WINDOW_HIDE:
- window.onHide.dispatch();
- }
- }
- }
-
- private function updateTimer():Void
- {
- #if lime_cffi
- if (Timer.sRunningTimers.length > 0)
- {
- var currentTime = System.getTimer();
- var foundNull = false;
- var timer;
-
- for (i in 0...Timer.sRunningTimers.length)
- {
- timer = Timer.sRunningTimers[i];
-
- if (timer != null && timer.mRunning)
- {
- if (currentTime >= timer.mFireAt)
- {
- timer.mFireAt += timer.mTime;
- timer.run();
- }
- }
- else
- {
- foundNull = true;
- }
- }
-
- if (foundNull)
- {
- Timer.sRunningTimers = Timer.sRunningTimers.filter(function(val)
- {
- return val != null && val.mRunning;
- });
- }
- }
-
- #if (haxe_ver >= 4.2)
- #if target.threaded
- sys.thread.Thread.current().events.progress();
- #else
- // Duplicate code required because Haxe 3 can't handle
- // #if (haxe_ver >= 4.2 && target.threaded)
- @:privateAccess haxe.EntryPoint.processEvents();
- #end
- #else
- @:privateAccess haxe.EntryPoint.processEvents();
- #end
- #end
- }
-}
-
-@:keep /*private*/ class ApplicationEventInfo
-{
- public var deltaTime:Int;
- public var type:ApplicationEventType;
-
- public function new(type:ApplicationEventType = null, deltaTime:Int = 0)
- {
- this.type = type;
- this.deltaTime = deltaTime;
- }
-
- public function clone():ApplicationEventInfo
- {
- return new ApplicationEventInfo(type, deltaTime);
- }
-}
-
-#if (haxe_ver >= 4.0) private enum #else @:enum private #end abstract ApplicationEventType(Int)
-{
- var UPDATE = 0;
- var EXIT = 1;
-}
-
-@:keep /*private*/ class ClipboardEventInfo
-{
- public var type:ClipboardEventType;
-
- public function new(type:ClipboardEventType = null)
- {
- this.type = type;
- }
-
- public function clone():ClipboardEventInfo
- {
- return new ClipboardEventInfo(type);
- }
-}
-
-#if (haxe_ver >= 4.0) private enum #else @:enum private #end abstract ClipboardEventType(Int)
-{
- var UPDATE = 0;
-}
-
-@:keep /*private*/ class DropEventInfo
-{
- public var file:#if hl hl.Bytes #else String #end;
- public var type:DropEventType;
-
- public function new(type:DropEventType = null, file = null)
- {
- this.type = type;
- this.file = file;
- }
-
- public function clone():DropEventInfo
- {
- return new DropEventInfo(type, file);
- }
-}
-
-#if (haxe_ver >= 4.0) private enum #else @:enum private #end abstract DropEventType(Int)
-{
- var DROP_FILE = 0;
-}
-
-@:keep /*private*/ class GamepadEventInfo
-{
- public var axis:Int;
- public var button:Int;
- public var id:Int;
- public var type:GamepadEventType;
- public var axisValue:Float;
-
- public function new(type:GamepadEventType = null, id:Int = 0, button:Int = 0, axis:Int = 0, value:Float = 0)
- {
- this.type = type;
- this.id = id;
- this.button = button;
- this.axis = axis;
- this.axisValue = value;
- }
-
- public function clone():GamepadEventInfo
- {
- return new GamepadEventInfo(type, id, button, axis, axisValue);
- }
-}
-
-#if (haxe_ver >= 4.0) private enum #else @:enum private #end abstract GamepadEventType(Int)
-{
- var AXIS_MOVE = 0;
- var BUTTON_DOWN = 1;
- var BUTTON_UP = 2;
- var CONNECT = 3;
- var DISCONNECT = 4;
-}
-
-@:keep /*private*/ class JoystickEventInfo
-{
- public var id:Int;
- public var index:Int;
- public var type:JoystickEventType;
- public var eventValue:Int;
- public var x:Float;
- public var y:Float;
-
- public function new(type:JoystickEventType = null, id:Int = 0, index:Int = 0, value:Int = 0, x:Float = 0, y:Float = 0)
- {
- this.type = type;
- this.id = id;
- this.index = index;
- this.eventValue = value;
- this.x = x;
- this.y = y;
- }
-
- public function clone():JoystickEventInfo
- {
- return new JoystickEventInfo(type, id, index, eventValue, x, y);
- }
-}
-
-#if (haxe_ver >= 4.0) private enum #else @:enum private #end abstract JoystickEventType(Int)
-{
- var AXIS_MOVE = 0;
- var HAT_MOVE = 1;
- var TRACKBALL_MOVE = 2;
- var BUTTON_DOWN = 3;
- var BUTTON_UP = 4;
- var CONNECT = 5;
- var DISCONNECT = 6;
-}
-
-@:keep /*private*/ class KeyEventInfo
-{
- public var keyCode: Float;
- public var modifier:Int;
- public var type:KeyEventType;
- public var windowID:Int;
-
- public function new(type:KeyEventType = null, windowID:Int = 0, keyCode: Float = 0, modifier:Int = 0)
- {
- this.type = type;
- this.windowID = windowID;
- this.keyCode = keyCode;
- this.modifier = modifier;
- }
-
- public function clone():KeyEventInfo
- {
- return new KeyEventInfo(type, windowID, keyCode, modifier);
- }
-}
-
-#if (haxe_ver >= 4.0) private enum #else @:enum private #end abstract KeyEventType(Int)
-{
- var KEY_DOWN = 0;
- var KEY_UP = 1;
-}
-
-@:keep /*private*/ class MouseEventInfo
-{
- public var button:Int;
- public var movementX:Float;
- public var movementY:Float;
- public var type:MouseEventType;
- public var windowID:Int;
- public var x:Float;
- public var y:Float;
- public var clickCount:Int;
-
- public function new(type:MouseEventType = null, windowID:Int = 0, x:Float = 0, y:Float = 0, button:Int = 0, movementX:Float = 0, movementY:Float = 0, clickCount:Int = 0)
- {
- this.type = type;
- this.windowID = 0;
- this.x = x;
- this.y = y;
- this.button = button;
- this.movementX = movementX;
- this.movementY = movementY;
- this.clickCount = clickCount;
- }
-
- public function clone():MouseEventInfo
- {
- return new MouseEventInfo(type, windowID, x, y, button, movementX, movementY, clickCount);
- }
-}
-
-#if (haxe_ver >= 4.0) private enum #else @:enum private #end abstract MouseEventType(Int)
-{
- var MOUSE_DOWN = 0;
- var MOUSE_UP = 1;
- var MOUSE_MOVE = 2;
- var MOUSE_WHEEL = 3;
-}
-
-@:keep /*private*/ class RenderEventInfo
-{
- public var type:RenderEventType;
-
- public function new(type:RenderEventType = null)
- {
- this.type = type;
- }
-
- public function clone():RenderEventInfo
- {
- return new RenderEventInfo(type);
- }
-}
-
-#if (haxe_ver >= 4.0) private enum #else @:enum private #end abstract RenderEventType(Int)
-{
- var RENDER = 0;
- var RENDER_CONTEXT_LOST = 1;
- var RENDER_CONTEXT_RESTORED = 2;
-}
-
-@:keep /*private*/ class SensorEventInfo
-{
- public var id:Int;
- public var x:Float;
- public var y:Float;
- public var z:Float;
- public var type:SensorEventType;
-
- public function new(type:SensorEventType = null, id:Int = 0, x:Float = 0, y:Float = 0, z:Float = 0)
- {
- this.type = type;
- this.id = id;
- this.x = x;
- this.y = y;
- this.z = z;
- }
-
- public function clone():SensorEventInfo
- {
- return new SensorEventInfo(type, id, x, y, z);
- }
-}
-
-#if (haxe_ver >= 4.0) private enum #else @:enum private #end abstract SensorEventType(Int)
-{
- var ACCELEROMETER = 0;
-}
-
-@:keep /*private*/ class TextEventInfo
-{
- public var id:Int;
- public var length:Int;
- public var start:Int;
- public var text:#if hl hl.Bytes #else String #end;
- public var type:TextEventType;
- public var windowID:Int;
-
- public function new(type:TextEventType = null, windowID:Int = 0, text = null, start:Int = 0, length:Int = 0)
- {
- this.type = type;
- this.windowID = windowID;
- this.text = text;
- this.start = start;
- this.length = length;
- }
-
- public function clone():TextEventInfo
- {
- return new TextEventInfo(type, windowID, text, start, length);
- }
-}
-
-#if (haxe_ver >= 4.0) private enum #else @:enum private #end abstract TextEventType(Int)
-{
- var TEXT_INPUT = 0;
- var TEXT_EDIT = 1;
-}
-
-@:keep /*private*/ class TouchEventInfo
-{
- public var device:Int;
- public var dx:Float;
- public var dy:Float;
- public var id:Int;
- public var pressure:Float;
- public var type:TouchEventType;
- public var x:Float;
- public var y:Float;
-
- public function new(type:TouchEventType = null, x:Float = 0, y:Float = 0, id:Int = 0, dx:Float = 0, dy:Float = 0, pressure:Float = 0, device:Int = 0)
- {
- this.type = type;
- this.x = x;
- this.y = y;
- this.id = id;
- this.dx = dx;
- this.dy = dy;
- this.pressure = pressure;
- this.device = device;
- }
-
- public function clone():TouchEventInfo
- {
- return new TouchEventInfo(type, x, y, id, dx, dy, pressure, device);
- }
-}
-
-#if (haxe_ver >= 4.0) private enum #else @:enum private #end abstract TouchEventType(Int)
-{
- var TOUCH_START = 0;
- var TOUCH_END = 1;
- var TOUCH_MOVE = 2;
-}
-
-@:keep /*private*/ class WindowEventInfo
-{
- public var height:Int;
- public var type:WindowEventType;
- public var width:Int;
- public var windowID:Int;
- public var x:Int;
- public var y:Int;
-
- public function new(type:WindowEventType = null, windowID:Int = 0, width:Int = 0, height:Int = 0, x:Int = 0, y:Int = 0)
- {
- this.type = type;
- this.windowID = windowID;
- this.width = width;
- this.height = height;
- this.x = x;
- this.y = y;
- }
-
- public function clone():WindowEventInfo
- {
- return new WindowEventInfo(type, windowID, width, height, x, y);
- }
-}
-
-#if (haxe_ver >= 4.0) private enum #else @:enum private #end abstract WindowEventType(Int)
-{
- var WINDOW_ACTIVATE = 0;
- var WINDOW_CLOSE = 1;
- var WINDOW_DEACTIVATE = 2;
- var WINDOW_ENTER = 3;
- var WINDOW_EXPOSE = 4;
- var WINDOW_FOCUS_IN = 5;
- var WINDOW_FOCUS_OUT = 6;
- var WINDOW_LEAVE = 7;
- var WINDOW_MAXIMIZE = 8;
- var WINDOW_MINIMIZE = 9;
- var WINDOW_MOVE = 10;
- var WINDOW_RESIZE = 11;
- var WINDOW_RESTORE = 12;
- var WINDOW_SHOW = 13;
- var WINDOW_HIDE = 14;
-}
diff --git a/source/lime/_internal/backend/native/NativeAudioSource.hx b/source/lime/_internal/backend/native/NativeAudioSource.hx
deleted file mode 100644
index 9f09b24471..0000000000
--- a/source/lime/_internal/backend/native/NativeAudioSource.hx
+++ /dev/null
@@ -1,804 +0,0 @@
-package lime._internal.backend.native;
-
-import sys.thread.Thread;
-import sys.thread.Mutex;
-
-import haxe.Timer;
-import haxe.Int64;
-
-import lime.media.openal.AL;
-import lime.media.openal.ALBuffer;
-import lime.media.openal.ALSource;
-
-#if lime_vorbis
-import lime.media.vorbis.Vorbis;
-import lime.media.vorbis.VorbisFile;
-import lime.media.vorbis.VorbisInfo;
-#end
-
-import lime.math.Vector2;
-import lime.math.Vector4;
-import lime.media.AudioBuffer;
-import lime.media.AudioSource;
-import lime.system.Endian;
-import lime.system.System;
-import lime.utils.ArrayBuffer;
-import lime.utils.ArrayBufferView.TypedArrayType;
-import lime.utils.ArrayBufferView;
-
-#if !lime_debug
-@:fileXml('tags="haxe,release"')
-@:noDebug
-#end
-@:access(haxe.Timer)
-@:access(lime.media.AudioBuffer)
-@:access(lime.utils.ArrayBufferView)
-class NativeAudioSource {
- // Can hold up to 3 hours 44100 sampleRate audio, if you are into that, theorically.
-
- public static var STREAM_BUFFER_SAMPLES:Int = 0x2000; // how much buffers will be generating every frequency (doesnt have to be pow of 2?).
- public static var STREAM_MIN_BUFFERS:Int = 2; // how much buffers can a stream hold on minimum or starting.
- public static var STREAM_MAX_BUFFERS:Int = 8; // how much limit of a buffers can be used for streamed audios, must be higher than minimum.
- public static var STREAM_MAX_FLUSH_BUFFERS:Int = 3; // how much buffers can it play.
- public static var STREAM_PROCESS_BUFFERS:Int = 2; // how much buffers can be processed in a frequency tick.
- public static var POOL_MAX_BUFFERS:Int = 32; // how much buffers for the pool to hold.
-
- public static var moreFormatsSupported:Null;
- public static var loopPointsSupported:Null;
- public static var stereoAnglesExtensionSupported:Null;
- public static var latencyExtensionSupported:Null;
-
- private static var bufferDataPool:Array = [];
- private static var isBigEndian:Bool = System.endianness == Endian.BIG_ENDIAN;
-
- public static function getALFormat(bitsPerSample:Int, channels:Int):Int {
- if (moreFormatsSupported == null) moreFormatsSupported = AL.isExtensionPresent("AL_EXT_MCFORMATS");
-
- // There was a code to also supports for X-Fi Renderer but, that kind of device is
- // rare nowadays and none of sounds should have more than 24 bitsPerSample.
- // https://github.com/kcat/openal-soft/issues/934
-
- if (channels > 2 && moreFormatsSupported) {
- if (channels == 3) return bitsPerSample == 32 ? 0x1209 : (bitsPerSample == 16 ? 0x1208 : 0x1207);
- else if (channels == 4) return bitsPerSample == 32 ? 0x1206 : (bitsPerSample == 16 ? 0x1205 : 0x1204);
- else if (channels == 6) return bitsPerSample == 32 ? 0x120C : (bitsPerSample == 16 ? 0x120B : 0x120A);
- else if (channels == 7) return bitsPerSample == 32 ? 0x120F : (bitsPerSample == 16 ? 0x120E : 0x120D);
- else if (channels == 8) return bitsPerSample == 32 ? 0x1212 : (bitsPerSample == 16 ? 0x1211 : 0x1210);
- else return AL.FORMAT_MONO8;
- }
- else if (bitsPerSample == 32 && moreFormatsSupported) return channels == 2 ? 0x1203 : 0x1202;
- else if (channels == 2) return bitsPerSample == 16 ? AL.FORMAT_STEREO16 : AL.FORMAT_STEREO8;
- else return bitsPerSample == 16 ? AL.FORMAT_MONO16 : AL.FORMAT_MONO8;
- }
-
- private static function resetTimer(timer:Timer, time:Float, callback:Void->Void):Timer {
- if (timer == null) (timer = new Timer(time)).run = callback;
- else {
- timer.mTime = time;
- timer.mFireAt = Timer.getMS() + time;
- timer.mRunning = true;
- timer.run = callback;
-
- if (!Timer.sRunningTimers.contains(timer)) Timer.sRunningTimers.push(timer);
- }
- return timer;
- }
-
- inline private static function getFloat(x:Int64):Float return x.high * 4294967296. + (x.low >> 0);
-
- // Backward Compatibility Variables
- var handle(get, set):ALSource; inline function get_handle() return source; inline function set_handle(v) return source = v;
- var timer(get, set):Timer; inline function get_timer() return completeTimer; inline function set_timer(v) return completeTimer = v;
- var length(get, set):Null; inline function get_length() return endTime; inline function set_length(v) return endTime = v;
- var toLoop(get, set):Int; inline function get_toLoop() return streamLoops; inline function set_toLoop(v) return streamLoops = v;
- var bufferSizes(get, set):Array; inline function get_bufferSizes() return bufferLengths; inline function set_bufferSizes(v) return bufferLengths = v;
-
- var parent:AudioSource;
- var disposed:Bool;
- var streamed:Bool;
- var playing:Bool;
- var completed:Bool;
- var lastTime:Float;
-
- var position:Vector4;
- var angles:Vector2;
- var anglesArray:Array;
- var endTime:Null;
- var loopTime:Float;
- var loops:Int;
-
- var channels:Int;
- var sampleRate:Int;
- var wordSize:Int; // is bitsPerSample >> 3, ex: 8 bits is 1, 16 bits is 2, 32 bits is 4, etc.
- var samples:Int;
- var dataLength:Int;
- var duration:Float;
-
- var completeTimer:Timer;
- var source:ALSource;
- var buffer:ALBuffer;
- var standaloneBuffer:Bool;
- var format:Int; // AL.FORMAT_...
- var arrayType:TypedArrayType;
- var loopPoints:Array; // In Samples
-
- static var streamSources:Array = [];
- static var queuedStreamSources:Array = [];
-
- static var streamMutex:Mutex = new Mutex();
- static var streamTimer:Timer;
-
- #if !ALLOW_MULTITHREADING
- static var wasEmpty:Bool = false;
- static var threadRunning:Bool = false;
- static var streamThread:Thread;
- #end
-
- var streamRemove:Bool;
-
- var bufferLength:Int; // Size in bytes for current streamed audio buffers.
- var requestBuffers:Int;
- var queuedBuffers:Int;
- var streamLoops:Int;
- var streamEnded:Bool;
-
- // ORDERING IS CURRENT TO NEXT, STARTS FROM THE LENGTH OF THE ARRAYS
- var bufferDatas:Array;
- var bufferTimes:Array;
- var bufferLengths:Array;
-
- var buffers:Array;
- var nextBuffer:Int = 0;
-
- public function new(parent:AudioSource) {
- this.parent = parent;
-
- if (loopPointsSupported == null) loopPointsSupported = AL.isExtensionPresent("AL_SOFT_loop_points");
- if (stereoAnglesExtensionSupported == null) stereoAnglesExtensionSupported = AL.isExtensionPresent("AL_EXT_STEREO_ANGLES");
- if (latencyExtensionSupported == null) latencyExtensionSupported = AL.isExtensionPresent("AL_SOFT_source_latency");
- }
-
- public function dispose() {
- streamMutex.acquire();
- removeStream();
-
- stop();
- disposed = true;
-
- position = null;
- angles = null;
- anglesArray = null;
-
- if (source != null) {
- if (streamed) AL.sourceUnqueueBuffers(source, AL.getSourcei(source, AL.BUFFERS_QUEUED));
- else AL.sourcei(source, AL.BUFFER, AL.NONE);
-
- AL.deleteSource(source);
- source = null;
- }
-
- if (standaloneBuffer && buffer != null) {
- AL.bufferData(buffer, 0, null, 0, 0);
- AL.deleteBuffer(buffer);
- buffer = null;
- }
- loopPoints = null;
-
- if (buffers != null) {
- for (buffer in buffers) AL.bufferData(buffer, 0, null, 0, 0);
- AL.deleteBuffers(buffers);
- buffers = null;
- }
-
- if (bufferDatas != null) {
- for (data in bufferDatas) if (bufferDataPool.length < POOL_MAX_BUFFERS) bufferDataPool.push(data);
- bufferDatas = null;
- }
-
- completeTimer = null;
-
- bufferTimes = null;
- bufferLengths = null;
-
- streamMutex.release();
- }
-
- public function init() {
- if (source != null || (disposed = parent == null || (source = AL.createSource()) == null)) return;
- AL.sourcef(source, AL.MAX_GAIN, 32);
- AL.distanceModel(AL.NONE);
-
- if (position == null) position = new Vector4();
- if (angles == null) angles = new Vector2(Math.PI / 6, -Math.PI / 6); // https://github.com/kcat/openal-soft/issues/1032
- if (loopPoints == null) loopPoints = [0, 0];
- if (stereoAnglesExtensionSupported && anglesArray == null) anglesArray = [0, 0];
-
- resetBuffer();
- }
-
- public function resetBuffer() {
- if (parent.buffer == null) return;
-
- streamMutex.acquire();
- removeStream();
-
- stop();
-
- if (streamed) AL.sourceUnqueueBuffers(source, AL.getSourcei(source, AL.BUFFERS_QUEUED));
- else AL.sourcei(source, AL.BUFFER, AL.NONE);
-
- streamMutex.release();
-
- final audioBuffer = parent.buffer;
- channels = audioBuffer.channels;
- sampleRate = audioBuffer.sampleRate;
- wordSize = audioBuffer.bitsPerSample >> 3;
- format = getALFormat(audioBuffer.bitsPerSample, channels);
- arrayType = wordSize == 4 ? TypedArrayType.Uint32 : (wordSize == 2 ? TypedArrayType.Uint16 : TypedArrayType.Int8);
- standaloneBuffer = false;
- loopTime = 0;
- endTime = null;
-
- if (buffer != null) {
- if (standaloneBuffer) {
- AL.bufferData(buffer, 0, null, 0, 0);
- AL.deleteBuffer(buffer);
- }
- buffer = null;
- }
-
- if (audioBuffer.data != null) {
- streamed = false;
- samples = Std.int((dataLength = audioBuffer.data.byteLength) / wordSize / channels);
- }
- #if lime_vorbis
- else if (audioBuffer.__srcVorbisFile != null) {
- streamed = true;
- dataLength = Std.int(getFloat(samples = Int64.toInt(audioBuffer.__srcVorbisFile.pcmTotal())) * channels * wordSize);
- }
- #end
- else return;
-
- duration = getFloat(samples) / sampleRate * 1000;
-
- loopPoints[0] = 0;
- loopPoints[1] = samples - 1;
-
- if (streamed) {
- final length = STREAM_BUFFER_SAMPLES * channels;
- bufferLength = length * wordSize;
-
- if (buffers == null) buffers = AL.genBuffers(STREAM_MAX_FLUSH_BUFFERS);
- if (bufferDatas == null) {
- bufferDatas = [];
- bufferTimes = [];
- bufferLengths = [];
- }
-
- for (i in 0...STREAM_MAX_BUFFERS) {
- bufferTimes[i] = 0.0;
- bufferLengths[i] = 0;
-
- var data = bufferDataPool.pop();
- if (data == null) data = new ArrayBufferView(length, arrayType);
- else {
- data.type = arrayType;
- data.bytesPerElement = wordSize;
- data.length = length;
- if (data.byteLength != bufferLength) {
- #if cpp
- data.buffer.getData().resize(bufferLength);
- data.buffer.fill(data.byteLength, bufferLength - data.byteLength, 0);
- @:privateAccess data.buffer.length = bufferLength;
- #else
- data.buffer = new ArrayBuffer(bufferLength);
- #end
- }
- data.byteLength = bufferLength;
- }
- bufferDatas[i] = data;
- }
- }
- else {
- if (buffers != null) {
- for (buffer in buffers) AL.bufferData(buffer, 0, null, 0, 0);
- AL.deleteBuffers(buffers);
- buffers = null;
-
- for (data in bufferDatas) if (bufferDataPool.length < POOL_MAX_BUFFERS) bufferDataPool.push(data);
- bufferDatas.resize(0);
- }
-
- if (audioBuffer.__srcBuffer != null) {
- if (AL.getBufferi(audioBuffer.__srcBuffer, AL.SIZE) != dataLength) {
- AL.bufferData(audioBuffer.__srcBuffer, 0, null, 0, 0);
- AL.deleteBuffer(audioBuffer.__srcBuffer);
- if ((buffer = audioBuffer.__srcBuffer = AL.createBuffer()) != null)
- AL.bufferData(buffer, format, audioBuffer.data, dataLength, sampleRate);
- }
- else
- buffer = audioBuffer.__srcBuffer;
- }
- else if ((buffer = audioBuffer.__srcBuffer = AL.createBuffer()) != null)
- AL.bufferData(buffer, format, audioBuffer.data, dataLength, sampleRate);
-
- AL.sourcei(source, AL.BUFFER, buffer);
- }
-
- updateLoopPoints();
- }
-
- function updateLoopPoints() {
- if (loops <= 0) return AL.sourcei(source, AL.LOOPING, AL.FALSE);
-
- var time = getCurrentTime() + parent.offset, length = getRealLength();
- final fixed = time >= length;
- if (fixed) time = loopTime;
-
- if (!streamed) {
- var internalLoop = AL.TRUE;
- if (length < duration - 1 && loopTime > 1) {
- if (!loopPointsSupported) internalLoop = AL.FALSE;
- else {
- AL.sourceStop(source);
- AL.sourcei(source, AL.BUFFER, AL.NONE);
- if (!standaloneBuffer) {
- if (standaloneBuffer = (buffer = AL.createBuffer()) != null)
- AL.bufferData(buffer, format, parent.buffer.data, dataLength, sampleRate);
- else {
- buffer = parent.buffer.__srcBuffer;
- internalLoop = AL.FALSE;
- }
- }
- if (internalLoop == AL.TRUE) AL.bufferiv(buffer, 0x2015/*AL.LOOP_POINTS_SOFT*/, loopPoints);
- AL.sourcei(source, AL.BUFFER, buffer);
- }
- }
-
- AL.sourcei(source, AL.LOOPING, internalLoop);
- if (playing) setCurrentTime(time - parent.offset);
- else updateCompleteTimer();
- }
- else if (playing && (fixed || streamLoops > 0)) {
- AL.sourcei(source, AL.LOOPING, AL.FALSE);
- AL.sourceStop(source);
- snapBuffersToTime(time, streamLoops > 0);
- AL.sourcePlay(source);
- }
- }
-
- // https://github.com/xiph/vorbis/blob/master/CHANGES#L39 bug in libvorbis <= 1.3.4
- inline function streamSeek(samples:Int64)
- if (samples <= 1) parent.buffer.__srcVorbisFile.rawSeek(0); else parent.buffer.__srcVorbisFile.pcmSeek(samples);
-
- inline function streamTell():Int64
- return parent.buffer.__srcVorbisFile.pcmTell();
-
- inline function streamRead(buffer:ArrayBuffer, position:Int, length:Int, wordSize:Int):Int
- return parent.buffer.__srcVorbisFile.read(buffer, position, length, isBigEndian, wordSize, true);
-
- function readToBufferData(data:ArrayBufferView, currentPCM:Int64):Int {
- var length = (Int64.ofInt(loopPoints[1]) - currentPCM) * channels * wordSize;
- var n = length < bufferLength ? length.low : bufferLength, total = 0, result = 0, wasEOF = false;
-
- try while (total < bufferLength) {
- result = n > 0 ? streamRead(data.buffer, total, n, wordSize) : 0;
-
- if (result == Vorbis.HOLE) continue;
- else if (result <= Vorbis.EREAD) break;
- else if (result == 0) {
- if (streamEnded = wasEOF == (wasEOF = true) || loops <= streamLoops) break;
-
- streamSeek(Int64.ofInt(loopPoints[0]));
- streamLoops++;
- if ((length = Int64.ofInt(loopPoints[1] - loopPoints[0]) * channels * wordSize) < (n = bufferLength - total)) n = length.low;
- }
- else {
- total += result;
- n -= result;
- wasEOF = false;
- }
- }
- catch (e:haxe.Exception) {
- trace('NativeAudioSource readToBufferData Bug! error: ${e.details()}, streamEnded: $streamEnded, total: $total, n: $n');
- return result;
- }
-
- if (result < 0) {
- trace('NativeAudioSource readToBufferData Bug! reading result is $result, streamEnded: $streamEnded, total: $total, n: $n');
- return result;
- }
- return total;
- }
-
- function fillBuffers(n:Int) {
- final max = STREAM_MAX_BUFFERS - 1;
- var i:Int, j:Int, data:ArrayBufferView, pcm:Int64, decoded:Int;
- while (n-- > 0 && !streamEnded && (decoded = readToBufferData(data = bufferDatas[i = max - requestBuffers], pcm = streamTell())) > 0) {
- j = i;
- while (i < max) {
- bufferDatas[i] = bufferDatas[++j];
- bufferTimes[i] = bufferTimes[j];
- bufferLengths[i] = bufferLengths[j];
- i = j;
- }
- bufferDatas[max] = data;
- bufferTimes[max] = getFloat(pcm) / sampleRate;
- bufferLengths[max] = decoded;
- requestBuffers++;
- }
- }
-
- inline function flushBuffers() {
- var i = STREAM_MAX_BUFFERS - (requestBuffers - queuedBuffers);
- while (queuedBuffers < STREAM_MAX_FLUSH_BUFFERS && queuedBuffers < requestBuffers) {
- AL.bufferData(buffers[nextBuffer], format, bufferDatas[i], bufferLengths[i], sampleRate);
- AL.sourceQueueBuffer(source, buffers[nextBuffer]);
- if (++nextBuffer == STREAM_MAX_FLUSH_BUFFERS) nextBuffer = 0;
- queuedBuffers++;
- i++;
- }
- }
-
- inline function skipBuffers(n:Int) {
- queuedBuffers -= (n = AL.sourceUnqueueBuffers(source, n).length);
- requestBuffers -= n;
- }
-
- function snapBuffersToTime(time:Float, force:Bool) {
- if (source == null || parent.buffer == null || parent.buffer.__srcVorbisFile == null) return;
-
- streamMutex.acquire();
-
- final sec = time / 1000;
- if (!force) {
- var bufferTime:Float;
- for (i in (STREAM_MAX_BUFFERS - requestBuffers)...(STREAM_MAX_BUFFERS - STREAM_MIN_BUFFERS))
- if (sec >= (bufferTime = bufferTimes[i]) && sec < bufferTime + (bufferLengths[i] / wordSize / channels / sampleRate))
- {
- skipBuffers(i - STREAM_MAX_BUFFERS + requestBuffers);
- AL.sourcei(source, AL.SAMPLE_OFFSET, Math.floor((sec - bufferTime) * sampleRate));
- return streamMutex.release();
- }
- }
-
- AL.sourceUnqueueBuffers(source, AL.getSourcei(source, AL.BUFFERS_QUEUED));
-
- streamEnded = false;
- streamSeek(Int64.fromFloat(sec * sampleRate));
-
- requestBuffers = queuedBuffers = streamLoops = nextBuffer = 0;
- fillBuffers(STREAM_MIN_BUFFERS);
- flushBuffers();
- streamMutex.release();
- }
-
- static function streamBuffersUpdate() {
- streamMutex.acquire();
-
- var i:Int = streamSources.length, source:NativeAudioSource, process:Int, v:Int;
- while (i-- > 0) {
- if ((source = streamSources[i]).streamRemove) continue;
- else if (source.parent.buffer == null) {
- source.stopStream();
- continue;
- }
-
- process = source.requestBuffers < STREAM_MIN_BUFFERS ? STREAM_MIN_BUFFERS - source.requestBuffers : 0;
- process = STREAM_PROCESS_BUFFERS > process ? STREAM_PROCESS_BUFFERS : process;
- if ((process = (v = STREAM_MAX_BUFFERS - source.requestBuffers) > process ? process : v) > 0) source.fillBuffers(process);
- }
-
- streamMutex.release();
- }
-
- #if !ALLOW_MULTITHREADING
- static function streamThreadRun() {
- while (Thread.readMessage(true)) streamBuffersUpdate();
- threadRunning = false;
- }
- #end
-
- static function streamUpdate() {
- if (!streamMutex.tryAcquire()) return;
-
- var i = queuedStreamSources.length, source:NativeAudioSource;
- while (i-- > 0) streamSources.push(queuedStreamSources[i]);
- queuedStreamSources.resize(0);
-
- i = streamSources.length;
- while (i-- > 0) {
- if ((source = streamSources[i]).streamRemove || source.source == null) source.removeStream();
- else {
- source.skipBuffers(AL.getSourcei(source.source, AL.BUFFERS_PROCESSED));
- source.flushBuffers();
- if (AL.getSourcei(source.source, AL.SOURCE_STATE) == AL.STOPPED) {
- AL.sourcePlay(source.source);
- source.updateCompleteTimer();
- }
- if (source.streamEnded && source.requestBuffers == source.queuedBuffers) source.removeStream();
- }
- }
-
- #if ALLOW_MULTITHREADING
- if (streamSources.length != 0) funkin.backend.utils.ThreadUtil.execAsync(streamBuffersUpdate);
- #else
- if (streamSources.length == 0) {
- if (wasEmpty) {
- wasEmpty = false;
- streamTimer.stop();
- if (threadRunning) streamThread.sendMessage(1);
- }
- else {
- wasEmpty = true;
- streamTimer = resetTimer(streamTimer, 1000, streamUpdate);
- }
- }
- else {
- wasEmpty = false;
- if (threadRunning || (threadRunning = (streamThread = Thread.create(streamThreadRun)) != null))
- streamThread.sendMessage(1);
- }
- #end
-
- streamMutex.release();
- }
-
- function removeStream() {
- streamRemove = false;
- queuedStreamSources.remove(this);
- streamSources.remove(this);
- }
-
- function stopStream() {
- streamRemove = true;
- queuedStreamSources.remove(this);
- }
-
- function resetStream() {
- streamRemove = false;
- if (!queuedStreamSources.contains(this) && !streamSources.contains(this)) {
- queuedStreamSources.push(this);
- if (streamTimer == null || !streamTimer.mRunning) streamTimer = resetTimer(streamTimer, 0, streamUpdate);
- }
- }
-
- function timer_onRun() {
- final pitch = getPitch();
- var timeRemaining = (getLength() - getCurrentTime()) / pitch;
- if (timeRemaining > 50 && AL.getSourcei(source, AL.SOURCE_STATE) == AL.PLAYING && (!streamed || !streamEnded && streamLoops <= 0)) {
- completeTimer = resetTimer(completeTimer, timeRemaining, timer_onRun);
- return;
- }
-
- completeTimer.stop();
-
- if (loops == 0) return complete();
-
- if (streamLoops > 0) {
- loops -= streamLoops;
- streamLoops = 0;
- completeTimer = resetTimer(completeTimer, (getLength() + parent.offset - loopTime) / pitch, timer_onRun);
- }
- else if (!loopPointsSupported || AL.getSourcei(source, AL.LOOPING) == AL.FALSE) {
- loops--;
- playing = true;
- setCurrentTime(loopTime - parent.offset);
- }
-
- if (loops <= 0) {
- loops = 0;
- AL.sourcei(source, AL.LOOPING, AL.FALSE);
- }
-
- parent.onLoop.dispatch();
- }
-
- function updateCompleteTimer() {
- if (playing) {
- var timeRemaining = (getLength() - getCurrentTime()) / getPitch();
- if (timeRemaining > 50) completeTimer = resetTimer(completeTimer, timeRemaining, timer_onRun);
- else {
- if (completeTimer != null) completeTimer.stop();
- if (loops > 0) play();
- else complete();
- }
- }
- else if (completeTimer != null)
- completeTimer.stop();
- }
-
- public function play() {
- if (playing || disposed) return;
- final time = completed ? 0 : getCurrentTime();
- playing = true;
- setCurrentTime(time);
- }
-
- public function pause() {
- if (!disposed) AL.sourcePause(source);
- lastTime = getCurrentTime();
- playing = false;
- stopStream();
- if (completeTimer != null) completeTimer.stop();
- }
-
- public function stop() {
- if (!disposed) AL.sourceStop(source);
- lastTime = 0;
- streamLoops = 0;
- playing = false;
- stopStream();
- if (completeTimer != null) completeTimer.stop();
- }
-
- public function complete() {
- if (!completed) parent.onComplete.dispatch();
- stop();
- completed = true;
- }
-
- public function getCurrentTime():Float {
- if (disposed) return 0.0;
- else if (completed) return getLength();
- else if (!playing) return lastTime - parent.offset;
-
- var time = AL.getSourcef(source, AL.SEC_OFFSET);
- if (streamed) {
- if (playing && streamEnded && AL.getSourcei(source, AL.SOURCE_STATE) == AL.STOPPED) {
- complete();
- return getLength();
- }
- else if (bufferTimes != null)
- time += bufferTimes[STREAM_MAX_BUFFERS - requestBuffers];
- }
- time *= 1000;
-
- var length = getRealLength();
- return if (loops <= 0 || time <= length) time - parent.offset;
- else ((time - loopTime) % (length - loopTime)) + loopTime - parent.offset;
- }
-
- public function setCurrentTime(value:Float):Float {
- if (disposed) return 0.0;
-
- final length = getRealLength();
- value = Math.isFinite(value) ? Math.max(Math.min(value + parent.offset, length), parent.offset) : parent.offset;
-
- if (streamed) AL.sourceStop(source);
- else AL.sourcef(source, AL.SEC_OFFSET, value / 1000);
-
- final timeRemaining = (length - value) / getPitch();
- if (playing) {
- if (timeRemaining < 8 && value > 8) complete();
- else {
- completed = false;
- if (streamed) {
- snapBuffersToTime(value, false);
- if (!streamEnded) resetStream();
- }
- if (AL.getSourcei(source, AL.SOURCE_STATE) != AL.PLAYING) AL.sourcePlay(source);
- completeTimer = resetTimer(completeTimer, timeRemaining, timer_onRun);
- }
- }
- else {
- completed = timeRemaining < 8;
- lastTime = value;
- if (completeTimer != null) completeTimer.stop();
- }
-
- return value;
- }
-
- public function getPitch():Float {
- return if (disposed) 1;
- else AL.getSourcef(source, AL.PITCH);
- }
-
- public function setPitch(value:Float):Float {
- if (disposed || (value = Math.max(value, 0)) == AL.getSourcef(source, AL.PITCH)) return value;
- AL.sourcef(source, AL.PITCH, value);
- updateCompleteTimer();
- return value;
- }
-
- public function getGain():Float {
- return if (disposed) 1;
- else AL.getSourcef(source, AL.GAIN);
- }
-
- public function setGain(value:Float):Float {
- value = Math.max(value, 0);
- if (!disposed) AL.sourcef(source, AL.GAIN, value);
- return value;
- }
-
- public function getLoops():Int return loops;
-
- public function setLoops(value:Int):Int {
- if (loops == (loops = value < 0 ? 0 : value)) return loops;
- updateLoopPoints();
- return loops;
- }
-
- public function getLoopTime():Float return loopTime - parent.offset;
-
- public function setLoopTime(value:Float):Float {
- if (loopTime == (loopTime = Math.max(Math.min(value + parent.offset, duration), 0))) return loopTime - parent.offset;
- if ((loopPoints[0] = Std.int(value / 1000 * sampleRate)) >= samples) loopPoints[0] = samples - 1;
- updateLoopPoints();
- return loopTime - parent.offset;
- }
-
- public function getRealLength():Float return if (endTime == null) duration; else endTime;
- public function getLength():Float return if (disposed) 0; else (inline getRealLength()) - parent.offset;
-
- public function setLength(value:Null):Null {
- if (endTime == (endTime = (value == null ? value : Math.max(Math.min(value + parent.offset, duration), 0)))) return endTime - parent.offset;
- if ((loopPoints[1] = Std.int(getRealLength() / 1000 * sampleRate)) >= samples) loopPoints[1] = samples - 1;
- updateLoopPoints();
- return endTime - parent.offset;
- }
-
- public function getLatency():Float {
- //#if (lime >= "8.4.0")
- //if (latencyExtensionSupported) {
- // final offsets = AL.getSourcedvSOFT(source, AL.SEC_OFFSET_LATENCY_SOFT, 2);
- // if (offsets != null) return offsets[1] * 1000;
- //}
- //#end
- return 0;
- }
-
- public function getAngles():Vector2 {
- if (angles == null) angles = new Vector2(Math.PI / 6, -Math.PI / 6);
- return angles;
- }
-
- public function setAngles(left:Float, right:Float):Vector2 {
- if (angles == null) angles = new Vector2(left, right);
- else angles.setTo(left, right);
-
- if (!disposed && stereoAnglesExtensionSupported) {
- anglesArray[0] = angles.x;
- anglesArray[1] = angles.y;
- AL.sourcei(source, 0x1214/*AL.SOURCE_SPATIALIZE_SOFT*/, AL.FALSE);
- AL.sourcefv(source, 0x1030/*AL.STEREO_ANGLES*/, anglesArray);
- AL.source3f(source, AL.POSITION, 0, 0, 0);
- }
- return angles;
- }
-
- public function getPosition():Vector4 {
- if (position == null) position = new Vector4();
- return position;
- }
-
- public function setPosition(value:Vector4):Vector4 {
- position.x = value.x;
- position.y = value.y;
- position.z = value.z;
- position.w = value.w;
-
- // OpenAL Soft Positions doesn't seem to do anything but panning?
- if (!disposed) {
- if (stereoAnglesExtensionSupported) AL.sourcei(source, 0x1214/*AL.SOURCE_SPATIALIZE_SOFT*/, Math.abs(position.x) > 1e-04 ? AL.TRUE : AL.FALSE);
- AL.sourcei(source, AL.MAX_DISTANCE, 1);
- AL.source3f(source, AL.POSITION, position.x, position.y, position.z);
- }
- return position;
- }
-
- public function getPan():Float return getPosition().x;
-
- public function setPan(value:Float):Float {
- getPosition().setTo(value, 0, -Math.sqrt(1 - value * value));
- if (!disposed) {
- if (parent.buffer.channels > 1)
- setAngles(Math.PI * (Math.min(-value * 2 + 1, 1)) / 6, -Math.PI * Math.min(value * 2 + 1, 1) / 6);
- else
- setPosition(position);
- }
- return value;
- }
-}
\ No newline at end of file
diff --git a/source/lime/_internal/backend/native/NativeWindow.hx b/source/lime/_internal/backend/native/NativeWindow.hx
deleted file mode 100644
index 397d94ac90..0000000000
--- a/source/lime/_internal/backend/native/NativeWindow.hx
+++ /dev/null
@@ -1,766 +0,0 @@
-package lime._internal.backend.native;
-
-import haxe.io.Bytes;
-import lime._internal.backend.native.NativeCFFI;
-import lime.app.Application;
-import lime.graphics.cairo.Cairo;
-import lime.graphics.cairo.CairoFormat;
-import lime.graphics.cairo.CairoImageSurface;
-import lime.graphics.cairo.CairoSurface;
-import lime.graphics.opengl.GL;
-import lime.graphics.CairoRenderContext;
-import lime.graphics.Image;
-import lime.graphics.ImageBuffer;
-import lime.graphics.OpenGLRenderContext;
-import lime.graphics.RenderContext;
-import lime.math.Rectangle;
-import lime.math.Vector2;
-import lime.system.Display;
-import lime.system.DisplayMode;
-import lime.system.JNI;
-import lime.system.System;
-import lime.ui.MouseCursor;
-import lime.ui.Window;
-import lime.utils.UInt8Array;
-
-#if !lime_debug
-@:fileXml('tags="haxe,release"')
-@:noDebug
-#end
-@:access(lime._internal.backend.native.NativeCFFI)
-@:access(lime._internal.backend.native.NativeOpenGLRenderContext)
-@:access(lime.app.Application)
-@:access(lime.graphics.cairo.Cairo)
-@:access(lime.graphics.opengl.GL)
-@:access(lime.graphics.OpenGLRenderContext)
-@:access(lime.graphics.RenderContext)
-@:access(lime.system.DisplayMode)
-@:access(lime.ui.Window)
-class NativeWindow
-{
- public var handle:Dynamic;
-
- private var closing:Bool;
- private var cursor:MouseCursor;
- private var displayMode:DisplayMode;
- private var frameRate:Float;
- private var mouseLock:Bool;
- private var parent:Window;
- private var useHardware:Bool;
- #if lime_cairo
- private var cacheLock:Dynamic;
- private var cairo:Cairo;
- private var primarySurface:CairoSurface;
- #end
-
- public function new(parent:Window)
- {
- this.parent = parent;
-
- cursor = DEFAULT;
- displayMode = new DisplayMode(0, 0, 0, 0);
-
- var attributes = parent.__attributes;
- var contextAttributes = Reflect.hasField(attributes, "context") ? attributes.context : {};
- var title = Reflect.hasField(attributes, "title") ? attributes.title : "Lime Application";
- var flags = 0;
-
- if (!Reflect.hasField(contextAttributes, "antialiasing")) contextAttributes.antialiasing = 0;
- if (!Reflect.hasField(contextAttributes, "background")) contextAttributes.background = 0;
- if (!Reflect.hasField(contextAttributes, "colorDepth")) contextAttributes.colorDepth = 24;
- if (!Reflect.hasField(contextAttributes, "depth")) contextAttributes.depth = true;
- if (!Reflect.hasField(contextAttributes, "hardware")) contextAttributes.hardware = true;
- if (!Reflect.hasField(contextAttributes, "stencil")) contextAttributes.stencil = true;
- if (!Reflect.hasField(contextAttributes, "vsync")) contextAttributes.vsync = false;
-
- #if (cairo || (!lime_opengl && !lime_opengles))
- contextAttributes.type = CAIRO;
- #end
- if (Reflect.hasField(contextAttributes, "type") && contextAttributes.type == CAIRO) contextAttributes.hardware = false;
-
- if (Reflect.hasField(attributes, "allowHighDPI") && attributes.allowHighDPI) flags |= cast WindowFlags.WINDOW_FLAG_ALLOW_HIGHDPI;
- if (Reflect.hasField(attributes, "alwaysOnTop") && attributes.alwaysOnTop) flags |= cast WindowFlags.WINDOW_FLAG_ALWAYS_ON_TOP;
- if (Reflect.hasField(attributes, "borderless") && attributes.borderless) flags |= cast WindowFlags.WINDOW_FLAG_BORDERLESS;
- if (Reflect.hasField(attributes, "fullscreen") && attributes.fullscreen) flags |= cast WindowFlags.WINDOW_FLAG_FULLSCREEN;
- if (Reflect.hasField(attributes, "hidden") && attributes.hidden) flags |= cast WindowFlags.WINDOW_FLAG_HIDDEN;
- if (Reflect.hasField(attributes, "maximized") && attributes.maximized) flags |= cast WindowFlags.WINDOW_FLAG_MAXIMIZED;
- if (Reflect.hasField(attributes, "minimized") && attributes.minimized) flags |= cast WindowFlags.WINDOW_FLAG_MINIMIZED;
- if (Reflect.hasField(attributes, "resizable") && attributes.resizable) flags |= cast WindowFlags.WINDOW_FLAG_RESIZABLE;
-
- if (contextAttributes.antialiasing >= 4)
- {
- flags |= cast WindowFlags.WINDOW_FLAG_HW_AA_HIRES;
- }
- else if (contextAttributes.antialiasing >= 2)
- {
- flags |= cast WindowFlags.WINDOW_FLAG_HW_AA;
- }
-
- if (contextAttributes.colorDepth == 32) flags |= cast WindowFlags.WINDOW_FLAG_COLOR_DEPTH_32_BIT;
- if (contextAttributes.depth) flags |= cast WindowFlags.WINDOW_FLAG_DEPTH_BUFFER;
- if (contextAttributes.hardware) flags |= cast WindowFlags.WINDOW_FLAG_HARDWARE;
- if (contextAttributes.stencil) flags |= cast WindowFlags.WINDOW_FLAG_STENCIL_BUFFER;
- if (contextAttributes.vsync) flags |= cast WindowFlags.WINDOW_FLAG_VSYNC;
-
- var width = Reflect.hasField(attributes, "width") ? attributes.width : #if desktop 800 #else 0 #end;
- var height = Reflect.hasField(attributes, "height") ? attributes.height : #if desktop 600 #else 0 #end;
-
- #if (!macro && lime_cffi)
- handle = NativeCFFI.lime_window_create(parent.application.__backend.handle, width, height, flags, title);
-
- #if (DARK_MODE_WINDOW && !macro)
- funkin.backend.utils.NativeAPI.setDarkMode(title, true);
- #end
-
- if (handle != null)
- {
- parent.__width = NativeCFFI.lime_window_get_width(handle);
- parent.__height = NativeCFFI.lime_window_get_height(handle);
- parent.__x = NativeCFFI.lime_window_get_x(handle);
- parent.__y = NativeCFFI.lime_window_get_y(handle);
- parent.__hidden = (Reflect.hasField(attributes, "hidden") && attributes.hidden);
- parent.id = NativeCFFI.lime_window_get_id(handle);
- }
-
- parent.__scale = NativeCFFI.lime_window_get_scale(handle);
-
- var context = new RenderContext();
- context.window = parent;
-
- #if hl
- var contextType = @:privateAccess String.fromUTF8(NativeCFFI.lime_window_get_context_type(handle));
- #else
- var contextType:String = NativeCFFI.lime_window_get_context_type(handle);
- #end
-
- switch (contextType)
- {
- case "opengl":
- var gl = new NativeOpenGLRenderContext();
-
- useHardware = true;
-
- #if lime_opengl
- context.gl = gl;
- #end
-
- context.gles2 = gl;
- context.webgl = gl;
- context.type = gl.type;
- context.version = Std.string(gl.version);
-
- if (gl.type == OPENGLES && gl.version >= 3)
- {
- context.gles3 = gl;
- context.webgl2 = gl;
- }
-
- if (GL.context == null)
- {
- GL.context = gl;
- }
-
- default:
- useHardware = false;
-
- #if lime_cairo
- context.cairo = cairo;
- context.type = CAIRO;
- context.version = "";
-
- parent.context = context;
- render();
- #end
- context.type = CAIRO;
- }
-
- contextAttributes.type = context.type;
- context.attributes = contextAttributes;
- parent.context = context;
-
- setFrameRate(Reflect.hasField(attributes, "frameRate") ? attributes.frameRate : 60);
- #end
- }
-
- public function alert(message:String, title:String):Void
- {
- if (handle != null)
- {
- #if (!macro && lime_cffi)
- NativeCFFI.lime_window_alert(handle, message, title);
- #end
- }
- }
-
- public function close():Void
- {
- if (!closing)
- {
- closing = true;
- parent.onClose.dispatch();
-
- if (!parent.onClose.canceled)
- {
- if (handle != null)
- {
- #if (!macro && lime_cffi)
- NativeCFFI.lime_window_close(handle);
- #end
- handle = null;
- }
- }
- else
- {
- closing = false;
- }
- }
- }
-
- public function contextFlip():Void
- {
- #if (!macro && lime_cffi)
- if (!useHardware)
- {
- #if lime_cairo
- if (cairo != null)
- {
- primarySurface.flush();
- }
- #end
- NativeCFFI.lime_window_context_unlock(handle);
- }
-
- NativeCFFI.lime_window_context_flip(handle);
- #end
- }
-
- public function focus():Void
- {
- if (handle != null)
- {
- #if (!macro && lime_cffi)
- NativeCFFI.lime_window_focus(handle);
- #end
- }
- }
-
- public function getCursor():MouseCursor
- {
- return cursor;
- }
-
- public function getDisplay():Display
- {
- if (handle != null)
- {
- #if (!macro && lime_cffi)
- var index = NativeCFFI.lime_window_get_display(handle);
-
- if (index > -1)
- {
- return System.getDisplay(index);
- }
- #end
- }
-
- return null;
- }
-
- public function getDisplayMode():DisplayMode
- {
- if (handle != null)
- {
- #if (!macro && lime_cffi)
- #if hl
- NativeCFFI.lime_window_get_display_mode(handle, displayMode);
- #else
- var data:Dynamic = NativeCFFI.lime_window_get_display_mode(handle);
- displayMode.width = data.width;
- displayMode.height = data.height;
- displayMode.pixelFormat = data.pixelFormat;
- displayMode.refreshRate = data.refreshRate;
- #end
- #end
- }
-
- return displayMode;
- }
-
- public function getFrameRate():Float
- {
- return frameRate;
- }
-
- public function getMouseLock():Bool
- {
- if (handle != null)
- {
- #if (!macro && lime_cffi)
- mouseLock = NativeCFFI.lime_window_get_mouse_lock(handle);
- #end
- }
-
- return mouseLock;
- }
-
- public function getTextInputEnabled():Bool
- {
- if (handle != null)
- {
- #if (!macro && lime_cffi)
- return NativeCFFI.lime_window_get_text_input_enabled(handle);
- #end
- }
-
- return false;
- }
-
- public function move(x:Int, y:Int):Void
- {
- if (handle != null)
- {
- #if (!macro && lime_cffi)
- NativeCFFI.lime_window_move(handle, x, y);
- #end
- }
- }
-
- public function readPixels(rect:Rectangle):Image
- {
- var imageBuffer:ImageBuffer = null;
-
- switch (parent.context.type)
- {
- case OPENGL, OPENGLES, WEBGL:
- var gl = parent.context.webgl;
- var windowWidth = Std.int(parent.__width * parent.__scale);
- var windowHeight = Std.int(parent.__height * parent.__scale);
-
- var x, y, width, height;
-
- if (rect != null)
- {
- x = Std.int(rect.x);
- y = Std.int((windowHeight - rect.y) - rect.height);
- width = Std.int(rect.width);
- height = Std.int(rect.height);
- }
- else
- {
- x = 0;
- y = 0;
- width = windowWidth;
- height = windowHeight;
- }
-
- var data = new UInt8Array(width * height * 4);
-
- gl.readPixels(x, y, width, height, gl.RGBA, gl.UNSIGNED_BYTE, data);
-
- #if !js // TODO
-
- var rowLength = width * 4;
- var srcPosition = (height - 1) * rowLength;
- var destPosition = 0;
-
- var temp = Bytes.alloc(rowLength);
- var buffer = data.buffer;
- var rows = Std.int(height / 2);
-
- while (rows-- > 0)
- {
- temp.blit(0, buffer, destPosition, rowLength);
- buffer.blit(destPosition, buffer, srcPosition, rowLength);
- buffer.blit(srcPosition, temp, 0, rowLength);
-
- destPosition += rowLength;
- srcPosition -= rowLength;
- }
- #end
-
- imageBuffer = new ImageBuffer(data, width, height, 32, RGBA32);
-
- default:
- #if (!macro && lime_cffi)
- #if !cs
- imageBuffer = NativeCFFI.lime_window_read_pixels(handle, rect, new ImageBuffer(new UInt8Array(Bytes.alloc(0))));
- #else
- var data:Dynamic = NativeCFFI.lime_window_read_pixels(handle, rect, null);
- if (data != null)
- {
- imageBuffer = new ImageBuffer(new UInt8Array(@:privateAccess new Bytes(data.data.length, data.data.b)), data.width, data.height,
- data.bitsPerPixel);
- }
- #end
- #end
-
- if (imageBuffer != null)
- {
- imageBuffer.format = RGBA32;
- }
- }
-
- if (imageBuffer != null)
- {
- return new Image(imageBuffer);
- }
-
- return null;
- }
-
- public function render():Void
- {
- #if (!macro && lime_cffi)
- NativeCFFI.lime_window_context_make_current(handle);
-
- if (!useHardware)
- {
- #if lime_cairo
- var lock:Dynamic = NativeCFFI.lime_window_context_lock(handle);
-
- if (lock != null
- && (cacheLock == null || cacheLock.pixels != lock.pixels || cacheLock.width != lock.width || cacheLock.height != lock.height))
- {
- primarySurface = CairoImageSurface.create(lock.pixels, CairoFormat.ARGB32, lock.width, lock.height, lock.pitch);
-
- if (cairo != null)
- {
- cairo.recreate(primarySurface);
- }
- else
- {
- cairo = new Cairo(primarySurface);
- }
-
- parent.context.cairo = cairo;
- }
-
- cacheLock = lock;
- #else
- parent.context = null;
- #end
- }
- #end
- }
-
- public function resize(width:Int, height:Int):Void
- {
- if (handle != null)
- {
- #if (!macro && lime_cffi)
- NativeCFFI.lime_window_resize(handle, width, height);
- #end
- }
- }
-
- #if (lime >= "8.1.0")
- public function setMinSize(width:Int, height:Int):Void
- {
- if (handle != null)
- {
- #if (!macro && lime_cffi)
- NativeCFFI.lime_window_set_minimum_size(handle, width, height);
- #end
- }
- }
-
- public function setMaxSize(width:Int, height:Int):Void
- {
- if (handle != null)
- {
- #if (!macro && lime_cffi)
- NativeCFFI.lime_window_set_maximum_size(handle, width, height);
- #end
- }
- }
- #end
-
- public function setBorderless(value:Bool):Bool
- {
- if (handle != null)
- {
- #if (!macro && lime_cffi)
- NativeCFFI.lime_window_set_borderless(handle, value);
- #end
- }
-
- return value;
- }
-
- public function setCursor(value:MouseCursor):MouseCursor
- {
- if (cursor != value)
- {
- if (value == null)
- {
- #if (!macro && lime_cffi)
- NativeCFFI.lime_window_set_cursor(handle, 0);
- #end
- }
- else
- {
- var type:MouseCursorType = switch (value)
- {
- case ARROW: ARROW;
- case CROSSHAIR: CROSSHAIR;
- case MOVE: MOVE;
- case POINTER: POINTER;
- case RESIZE_NESW: RESIZE_NESW;
- case RESIZE_NS: RESIZE_NS;
- case RESIZE_NWSE: RESIZE_NWSE;
- case RESIZE_WE: RESIZE_WE;
- case TEXT: TEXT;
- case WAIT: WAIT;
- case WAIT_ARROW: WAIT_ARROW;
- default: DEFAULT;
- }
-
- #if (!macro && lime_cffi)
- NativeCFFI.lime_window_set_cursor(handle, type);
- #end
- }
-
- cursor = value;
- }
-
- return cursor;
- }
-
- public function setDisplayMode(value:DisplayMode):DisplayMode
- {
- if (handle != null)
- {
- #if (!macro && lime_cffi)
- #if hl
- NativeCFFI.lime_window_set_display_mode(handle, value, displayMode);
- #else
- var data:Dynamic = NativeCFFI.lime_window_set_display_mode(handle, value);
- displayMode.width = data.width;
- displayMode.height = data.height;
- displayMode.pixelFormat = data.pixelFormat;
- displayMode.refreshRate = data.refreshRate;
- #end
- #end
- }
-
- return displayMode;
- }
-
- public function setMouseLock(value:Bool):Bool
- {
- if (mouseLock != value)
- {
- #if (!macro && lime_cffi)
- NativeCFFI.lime_window_set_mouse_lock(handle, value);
- #end
-
- mouseLock = value;
- }
-
- return mouseLock;
- }
-
- public function setTextInputEnabled(value:Bool):Bool
- {
- if (handle != null)
- {
- #if (!macro && lime_cffi)
- NativeCFFI.lime_window_set_text_input_enabled(handle, value);
- #end
- }
-
- return value;
- }
-
- public function setTextInputRect(value:Rectangle):Rectangle
- {
- if (handle != null)
- {
- #if (!macro && lime_cffi)
- NativeCFFI.lime_window_set_text_input_rect(handle, value);
- #end
- }
-
- return value;
- }
-
- public function setFrameRate(value:Float):Float
- {
- // TODO: Support multiple independent frame rates per window
-
- if (handle != null)
- {
- #if (!macro && lime_cffi)
- NativeCFFI.lime_application_set_frame_rate(parent.application.__backend.handle, value);
- #end
- }
-
- return frameRate = value;
- }
-
- public function setFullscreen(value:Bool):Bool
- {
- if (handle != null)
- {
- #if (!macro && lime_cffi)
- value = NativeCFFI.lime_window_set_fullscreen(handle, value);
-
- parent.__width = NativeCFFI.lime_window_get_width(handle);
- parent.__height = NativeCFFI.lime_window_get_height(handle);
- parent.__x = NativeCFFI.lime_window_get_x(handle);
- parent.__y = NativeCFFI.lime_window_get_y(handle);
- #end
-
- if (value)
- {
- parent.onFullscreen.dispatch();
- }
- }
-
- return value;
- }
-
- public function setIcon(image:Image):Void
- {
- if (handle != null)
- {
- #if (!macro && lime_cffi)
- NativeCFFI.lime_window_set_icon(handle, image.buffer);
- #end
- }
- }
-
- public function setMaximized(value:Bool):Bool
- {
- if (handle != null)
- {
- #if (!macro && lime_cffi)
- return NativeCFFI.lime_window_set_maximized(handle, value);
- #end
- }
-
- return value;
- }
-
- public function setMinimized(value:Bool):Bool
- {
- if (handle != null)
- {
- #if (!macro && lime_cffi)
- return NativeCFFI.lime_window_set_minimized(handle, value);
- #end
- }
-
- return value;
- }
-
- public function setResizable(value:Bool):Bool
- {
- if (handle != null)
- {
- #if (!macro && lime_cffi)
- NativeCFFI.lime_window_set_resizable(handle, value);
-
- // TODO: remove need for workaround
-
- NativeCFFI.lime_window_set_borderless(handle, !parent.__borderless);
- NativeCFFI.lime_window_set_borderless(handle, parent.__borderless);
- #end
- }
-
- return value;
- }
-
- public function setTitle(value:String):String
- {
- if (handle != null)
- {
- #if (!macro && lime_cffi)
- return NativeCFFI.lime_window_set_title(handle, value);
- #end
- }
-
- return value;
- }
-
- #if (lime >= "8.1.0")
- public function setVisible(value:Bool):Bool
- {
- if (handle != null)
- {
- #if (!macro && lime_cffi)
- NativeCFFI.lime_window_set_visible(handle, value);
- #end
- }
-
- return value;
- }
-
- public function getOpacity():Float
- {
- if (handle != null)
- {
- #if (!macro && lime_cffi)
- return NativeCFFI.lime_window_get_opacity(handle);
- #end
- }
-
- return 1.0;
- }
-
- public function setOpacity(value:Float):Void
- {
- if (handle != null)
- {
- #if (!macro && lime_cffi)
- NativeCFFI.lime_window_set_opacity(handle, value);
- #end
- }
- }
- #end
-
- public function warpMouse(x:Int, y:Int):Void
- {
- #if (!macro && lime_cffi)
- NativeCFFI.lime_window_warp_mouse(handle, x, y);
- #end
- }
-}
-
-enum abstract MouseCursorType(Int) from Int to Int
-{
- var HIDDEN = 0;
- var ARROW = 1;
- var CROSSHAIR = 2;
- var DEFAULT = 3;
- var MOVE = 4;
- var POINTER = 5;
- var RESIZE_NESW = 6;
- var RESIZE_NS = 7;
- var RESIZE_NWSE = 8;
- var RESIZE_WE = 9;
- var TEXT = 10;
- var WAIT = 11;
- var WAIT_ARROW = 12;
-}
-
-enum abstract WindowFlags(Int)
-{
- var WINDOW_FLAG_FULLSCREEN = 0x00000001;
- var WINDOW_FLAG_BORDERLESS = 0x00000002;
- var WINDOW_FLAG_RESIZABLE = 0x00000004;
- var WINDOW_FLAG_HARDWARE = 0x00000008;
- var WINDOW_FLAG_VSYNC = 0x00000010;
- var WINDOW_FLAG_HW_AA = 0x00000020;
- var WINDOW_FLAG_HW_AA_HIRES = 0x00000060;
- var WINDOW_FLAG_ALLOW_SHADERS = 0x00000080;
- var WINDOW_FLAG_REQUIRE_SHADERS = 0x00000100;
- var WINDOW_FLAG_DEPTH_BUFFER = 0x00000200;
- var WINDOW_FLAG_STENCIL_BUFFER = 0x00000400;
- var WINDOW_FLAG_ALLOW_HIGHDPI = 0x00000800;
- var WINDOW_FLAG_HIDDEN = 0x00001000;
- var WINDOW_FLAG_MINIMIZED = 0x00002000;
- var WINDOW_FLAG_MAXIMIZED = 0x00004000;
- var WINDOW_FLAG_ALWAYS_ON_TOP = 0x00008000;
- var WINDOW_FLAG_COLOR_DEPTH_32_BIT = 0x00010000;
-}
\ No newline at end of file
diff --git a/source/lime/media/AudioBuffer.hx b/source/lime/media/AudioBuffer.hx
deleted file mode 100644
index 30c5448baa..0000000000
--- a/source/lime/media/AudioBuffer.hx
+++ /dev/null
@@ -1,518 +0,0 @@
-package lime.media;
-
-import haxe.io.Bytes;
-import haxe.io.Path;
-import lime._internal.backend.native.NativeCFFI;
-import lime._internal.format.Base64;
-import lime.app.Future;
-import lime.app.Promise;
-import lime.media.openal.AL;
-import lime.media.openal.ALBuffer;
-#if lime_vorbis
-import lime.media.vorbis.Vorbis;
-import lime.media.vorbis.VorbisFile;
-#end
-import lime.net.HTTPRequest;
-import lime.utils.Log;
-import lime.utils.UInt8Array;
-#if lime_howlerjs
-import lime.media.howlerjs.Howl;
-#end
-#if (js && html5)
-import js.html.Audio;
-#elseif flash
-import flash.media.Sound;
-import flash.net.URLRequest;
-#end
-
-@:access(lime._internal.backend.native.NativeCFFI)
-@:access(lime.utils.Assets)
-#if hl
-@:keep
-#end
-#if !lime_debug
-@:fileXml('tags="haxe,release"')
-@:noDebug
-#end
-
-/**
- The `AudioBuffer` class represents a buffer of audio data that can be played back using an `AudioSource`.
- It supports a variety of audio formats and platforms, providing a consistent API for loading and managing audio data.
-
- Depending on the platform, the audio backend may differ, but the class provides a unified interface for accessing
- audio data, whether it's stored in memory, loaded from a file, or streamed.
-
- @see lime.media.AudioSource
-**/
-class AudioBuffer
-{
- /**
- The number of bits per sample in the audio data.
- **/
- public var bitsPerSample:Int;
-
- /**
- The number of audio channels (e.g., 1 for mono, 2 for stereo).
- **/
- public var channels:Int;
-
- /**
- The raw audio data stored as a `UInt8Array`.
- **/
- public var data:UInt8Array;
-
- /**
- The sample rate of the audio data, in Hz.
- **/
- public var sampleRate:Int;
-
- /**
- The source of the audio data. This can be an `Audio`, `Sound`, `Howl`, or other platform-specific object.
- **/
- public var src(get, set):Dynamic;
-
- @:noCompletion private var __srcAudio:#if (js && html5) Audio #else Dynamic #end;
- @:noCompletion private var __srcBuffer:#if lime_cffi ALBuffer #else Dynamic #end;
- @:noCompletion private var __srcCustom:Dynamic;
- @:noCompletion private var __srcHowl:#if lime_howlerjs Howl #else Dynamic #end;
- @:noCompletion private var __srcSound:#if flash Sound #else Dynamic #end;
- @:noCompletion private var __srcVorbisFile:#if lime_vorbis VorbisFile #else Dynamic #end;
-
- #if commonjs
- private static function __init__()
- {
- var p = untyped AudioBuffer.prototype;
- untyped Object.defineProperties(p,
- {
- "src": {get: p.get_src, set: p.set_src}
- });
- }
- #end
-
- /**
- Creates a new, empty `AudioBuffer` instance.
- **/
- public function new() {}
-
- /**
- Disposes of the resources used by this `AudioBuffer`, such as unloading any associated audio data.
- **/
- public function dispose():Void
- {
- #if (js && html5 && lime_howlerjs)
- if (__srcHowl != null) __srcHowl.unload();
- __srcHowl = null;
- #end
- #if lime_cffi
- if (__srcBuffer != null) {
- AL.bufferData(__srcBuffer, 0, null, 0, 0);
- AL.deleteBuffer(__srcBuffer);
- }
- __srcBuffer = null;
- #end
- #if lime_vorbis
- if (__srcVorbisFile != null) __srcVorbisFile.clear();
- __srcVorbisFile = null;
- #end
- }
-
- /**
- Creates an `AudioBuffer` from a Base64-encoded string.
-
- @param base64String The Base64-encoded audio data.
- @return An `AudioBuffer` instance with the decoded audio data.
- **/
- public static function fromBase64(base64String:String):AudioBuffer
- {
- if (base64String == null) return null;
-
- #if (js && html5 && lime_howlerjs)
- // if base64String doesn't contain codec data, add it.
- if (base64String.indexOf(",") == -1)
- {
- base64String = "data:" + __getCodec(Base64.decode(base64String)) + ";base64," + base64String;
- }
-
- var audioBuffer = new AudioBuffer();
- audioBuffer.src = new Howl({src: [base64String], preload: false});
- return audioBuffer;
- #elseif (lime_cffi && !macro)
- #if !cs
- // if base64String contains codec data, strip it then decode it.
- var base64StringSplit = base64String.split(",");
- var base64StringNoEncoding = base64StringSplit[base64StringSplit.length - 1];
- var bytes:Bytes = Base64.decode(base64StringNoEncoding);
- var audioBuffer = new AudioBuffer();
- audioBuffer.data = new UInt8Array(Bytes.alloc(0));
-
- return NativeCFFI.lime_audio_load_bytes(bytes, audioBuffer);
- #else
- // if base64String contains codec data, strip it then decode it.
- var base64StringSplit = base64String.split(",");
- var base64StringNoEncoding = base64StringSplit[base64StringSplit.length - 1];
- var bytes:Bytes = Base64.decode(base64StringNoEncoding);
- var data:Dynamic = NativeCFFI.lime_audio_load_bytes(bytes, null);
-
- if (data != null)
- {
- var audioBuffer = new AudioBuffer();
- audioBuffer.bitsPerSample = data.bitsPerSample;
- audioBuffer.channels = data.channels;
- audioBuffer.data = new UInt8Array(@:privateAccess new Bytes(data.data.length, data.data.b));
- audioBuffer.sampleRate = data.sampleRate;
- return audioBuffer;
- }
- #end
- #end
-
- return null;
- }
-
- /**
- Creates an `AudioBuffer` from a `Bytes` object.
-
- @param bytes The `Bytes` object containing the audio data.
- @return An `AudioBuffer` instance with the decoded audio data.
- **/
- public static function fromBytes(bytes:Bytes):AudioBuffer
- {
- if (bytes == null) return null;
-
- #if (js && html5 && lime_howlerjs)
- var audioBuffer = new AudioBuffer();
- audioBuffer.src = new Howl({src: ["data:" + __getCodec(bytes) + ";base64," + Base64.encode(bytes)], preload: false});
-
- return audioBuffer;
- #elseif (lime_cffi && !macro)
- #if lime_vorbis
- var vorbisFile = VorbisFile.fromBytes(bytes);
- if (vorbisFile != null) return fromVorbisFile(vorbisFile);
- #end
- #if !cs
- var audioBuffer = new AudioBuffer();
- audioBuffer.data = new UInt8Array(Bytes.alloc(0));
-
- return NativeCFFI.lime_audio_load_bytes(bytes, audioBuffer);
- #else
- var data:Dynamic = NativeCFFI.lime_audio_load_bytes(bytes, null);
-
- if (data != null)
- {
- var audioBuffer = new AudioBuffer();
- audioBuffer.bitsPerSample = data.bitsPerSample;
- audioBuffer.channels = data.channels;
- audioBuffer.data = new UInt8Array(@:privateAccess new Bytes(data.data.length, data.data.b));
- audioBuffer.sampleRate = data.sampleRate;
- return audioBuffer;
- }
- #end
- #end
-
- return null;
- }
-
- /**
- Creates an `AudioBuffer` from a file.
-
- @param path The file path to the audio data.
- @return An `AudioBuffer` instance with the audio data loaded from the file.
- **/
- public static function fromFile(path:String #if (js && html5 && lime_howlerjs), ?howlHtml5 = false #end):AudioBuffer
- {
- if (path == null) return null;
-
- #if (js && html5 && lime_howlerjs)
- var audioBuffer = new AudioBuffer();
-
- #if force_html5_audio
- audioBuffer.__srcHowl = new Howl({src: [path], html5: true, preload: false});
- #else
- audioBuffer.__srcHowl = new Howl({src: [path], html5: howlHtml5, preload: false});
- #end
-
- return audioBuffer;
- #elseif flash
- switch (Path.extension(path))
- {
- case "ogg", "wav":
- return null;
- default:
- }
-
- var audioBuffer = new AudioBuffer();
- audioBuffer.__srcSound = new Sound(new URLRequest(path));
- return audioBuffer;
- #elseif (lime_cffi && !macro)
- #if !cs
- var audioBuffer = new AudioBuffer();
- audioBuffer.data = new UInt8Array(Bytes.alloc(0));
-
- //audioBuffer = NativeCFFI.lime_audio_load_file(path, audioBuffer);
- //if (audioBuffer != null) audioBuffer.initBuffer();
- //return audioBuffer;
- return NativeCFFI.lime_audio_load_file(path, audioBuffer);
- #else
- var data:Dynamic = NativeCFFI.lime_audio_load_file(path, null);
-
- if (data != null)
- {
- var audioBuffer = new AudioBuffer();
- audioBuffer.bitsPerSample = data.bitsPerSample;
- audioBuffer.channels = data.channels;
- audioBuffer.data = new UInt8Array(@:privateAccess new Bytes(data.data.length, data.data.b));
- audioBuffer.sampleRate = data.sampleRate;
- //audioBuffer.initBuffer();
- return audioBuffer;
- }
-
- return null;
- #end
- #else
- return null;
- #end
- }
-
- /**
- Creates an `AudioBuffer` from an array of file paths.
-
- @param paths An array of file paths to search for audio data.
- @return An `AudioBuffer` instance with the audio data loaded from the first valid file found.
- **/
- public static function fromFiles(paths:Array #if (js && html5 && lime_howlerjs), ?howlHtml5 = false #end):AudioBuffer
- {
- #if (js && html5 && lime_howlerjs)
- var audioBuffer = new AudioBuffer();
-
- #if force_html5_audio
- audioBuffer.__srcHowl = new Howl({src: paths, html5: true, preload: false});
- #else
- audioBuffer.__srcHowl = new Howl({src: paths, html5: howlHtml5, preload: false});
- #end
-
- return audioBuffer;
- #else
- var buffer = null;
-
- for (path in paths)
- {
- buffer = AudioBuffer.fromFile(path);
- if (buffer != null) break;
- }
-
- return buffer;
- #end
- }
-
- /**
- Creates an `AudioBuffer` from a `VorbisFile`.
-
- @param vorbisFile The `VorbisFile` object containing the audio data.
- @return An `AudioBuffer` instance with the decoded audio data.
- **/
- #if lime_vorbis
-
- public static function fromVorbisFile(vorbisFile:VorbisFile):AudioBuffer
- {
- if (vorbisFile == null) return null;
-
- var info = vorbisFile.info();
-
- var audioBuffer = new AudioBuffer();
- audioBuffer.channels = info.channels;
- audioBuffer.sampleRate = info.rate;
- audioBuffer.bitsPerSample = 16;
-
- final pcmTotal = vorbisFile.pcmTotal(-1);
- if (!vorbisFile.seekable() || pcmTotal < (audioBuffer.sampleRate << 2)) {
- vorbisFile.rawSeek(0);
-
- final isBigEndian = lime.system.System.endianness == lime.system.Endian.BIG_ENDIAN;
- final bytes = Bytes.alloc(Std.int((pcmTotal.high * 4294967296. + (pcmTotal.low >> 0)) * info.channels * (audioBuffer.bitsPerSample >> 3)));
- var total = 0, result = 0;
- do {
- result = vorbisFile.read(bytes, total, 0x1000, isBigEndian, 2, true);
- total += result;
- } while (result > 0 || result == Vorbis.HOLE);
-
- audioBuffer.data = new UInt8Array(bytes);
- vorbisFile.clear();
- }
- else
- audioBuffer.__srcVorbisFile = vorbisFile;
-
- return audioBuffer;
- }
- #else
- public static function fromVorbisFile(vorbisFile:Dynamic):AudioBuffer
- {
- return null;
- }
- #end
-
- /**
- Asynchronously loads an `AudioBuffer` from a file.
-
- @param path The file path to the audio data.
- @return A `Future` that resolves to the loaded `AudioBuffer`.
- **/
- public static function loadFromFile(path:String):Future
- {
- #if (flash || (js && html5))
- var promise = new Promise();
-
- var audioBuffer = AudioBuffer.fromFile(path);
-
- if (audioBuffer != null)
- {
- #if flash
- audioBuffer.__srcSound.addEventListener(flash.events.Event.COMPLETE, function(event)
- {
- promise.complete(audioBuffer);
- });
-
- audioBuffer.__srcSound.addEventListener(flash.events.ProgressEvent.PROGRESS, function(event)
- {
- promise.progress(Std.int(event.bytesLoaded), Std.int(event.bytesTotal));
- });
-
- audioBuffer.__srcSound.addEventListener(flash.events.IOErrorEvent.IO_ERROR, promise.error);
- #elseif (js && html5 && lime_howlerjs)
- if (audioBuffer != null)
- {
- audioBuffer.__srcHowl.on("load", function()
- {
- promise.complete(audioBuffer);
- });
-
- audioBuffer.__srcHowl.on("loaderror", function(id, msg)
- {
- promise.error(msg);
- });
-
- audioBuffer.__srcHowl.load();
- }
- #else
- promise.complete(audioBuffer);
- #end
- }
- else
- {
- promise.error(null);
- }
-
- return promise.future;
- #else
- // TODO: Streaming
-
- var request = new HTTPRequest();
- return request.load(path).then(function(buffer)
- {
- if (buffer != null)
- {
- return Future.withValue(buffer);
- }
- else
- {
- return cast Future.withError("");
- }
- });
- #end
- }
-
- /**
- Asynchronously loads an `AudioBuffer` from multiple files.
-
- @param paths An array of file paths to search for audio data.
- @return A `Future` that resolves to the loaded `AudioBuffer`.
- **/
- public static function loadFromFiles(paths:Array):Future
- {
- #if (js && html5 && lime_howlerjs)
- var promise = new Promise();
-
- var audioBuffer = AudioBuffer.fromFiles(paths);
-
- if (audioBuffer != null)
- {
- audioBuffer.__srcHowl.on("load", function()
- {
- promise.complete(audioBuffer);
- });
-
- audioBuffer.__srcHowl.on("loaderror", function()
- {
- promise.error(null);
- });
-
- audioBuffer.__srcHowl.load();
- }
- else
- {
- promise.error(null);
- }
-
- return promise.future;
- #else
- return new Future(fromFiles.bind(paths), true);
- #end
- }
-
- private static function __getCodec(bytes:Bytes):String
- {
- var signature = bytes.getString(0, 4);
-
- switch (signature)
- {
- case "OggS":
- return "audio/ogg";
- case "fLaC":
- return "audio/flac";
- case "RIFF" if (bytes.getString(8, 4) == "WAVE"):
- return "audio/wav";
- default:
- switch ([bytes.get(0), bytes.get(1), bytes.get(2)])
- {
- case [73, 68, 51] | [255, 251, _] | [255, 250, _] | [255, 243, _]: return "audio/mp3";
- default:
- }
- }
-
- Log.error("Unsupported sound format");
- return null;
- }
-
- // Get & Set Methods
- @:noCompletion private function get_src():Dynamic
- {
- #if (js && html5)
- #if lime_howlerjs
- return __srcHowl;
- #else
- return __srcAudio;
- #end
- #elseif flash
- return __srcSound;
- #elseif lime_vorbis
- return __srcVorbisFile;
- #else
- return __srcCustom;
- #end
- }
-
- @:noCompletion private function set_src(value:Dynamic):Dynamic
- {
- #if (js && html5)
- #if lime_howlerjs
- return __srcHowl = value;
- #else
- return __srcAudio = value;
- #end
- #elseif flash
- return __srcSound = value;
- #elseif lime_vorbis
- return __srcVorbisFile = value;
- #else
- return __srcCustom = value;
- #end
- }
-}
diff --git a/source/lime/media/AudioSource.hx b/source/lime/media/AudioSource.hx
deleted file mode 100644
index 992bfdb5bf..0000000000
--- a/source/lime/media/AudioSource.hx
+++ /dev/null
@@ -1,257 +0,0 @@
-package lime.media;
-
-import lime.app.Event;
-import lime.math.Vector4;
-
-#if !lime_debug
-@:fileXml('tags="haxe,release"')
-@:noDebug
-#end
-/**
- The `AudioSource` class provides a way to control audio playback in a Lime application.
- It allows for playing, pausing, and stopping audio, as well as controlling various
- audio properties such as gain, pitch, and looping.
-
- Depending on the platform, the audio backend may vary, but the API remains consistent.
-
- @see lime.media.AudioBuffer
-**/
-class AudioSource
-{
- private static var activeSources:Array = [];
-
- /**
- An event that is dispatched when the audio playback is complete.
- **/
- public var onComplete = new EventVoid>();
-
- /**
- An event that is dispatched when the audio playback looped.
- **/
- public var onLoop = new EventVoid>();
-
- /**
- The `AudioBuffer` associated with this `AudioSource`.
- **/
- public var buffer:AudioBuffer;
-
- /**
- An property if this 'AudioSource' is playing.
- **/
- public var playing(get, null):Bool;
-
- /**
- The current playback position of the audio, in milliseconds.
- **/
- public var currentTime(get, set):Float;
-
- /**
- The gain (volume) of the audio. A value of `1.0` represents the default volume.
- **/
- public var gain(get, set):Float;
-
- /**
- The length of the audio, in milliseconds.
- **/
- public var length(get, set):Null;
-
- /**
- The number of times the audio will loop. A value of `0` means the audio will not loop.
- **/
- public var loops(get, set):Int;
-
- /**
- In which audio playback time the audio will loop.
- **/
- public var loopTime(get, set):Float;
-
- /**
- The pitch of the audio. A value of `1.0` represents the default pitch.
- **/
- public var pitch(get, set):Float;
-
- /**
- The offset within the audio buffer to start playback, in samples.
- **/
- public var offset:Float;
-
- /**
- The 3D position of the audio source, represented as a `Vector4`.
- **/
- public var position(get, set):Vector4;
-
- /**
- The stereo pan of the audio source.
- **/
- public var pan(get, set):Float;
-
- /**
- The latency of the audio source.
- **/
- public var latency(get, never):Float;
-
- @:noCompletion private var __backend:AudioSourceBackend;
-
- /**
- Creates a new `AudioSource` instance.
- @param buffer The `AudioBuffer` to associate with this `AudioSource`.
- @param offset The starting offset within the audio buffer, in samples.
- @param length The length of the audio to play, in milliseconds. If `null`, the full buffer is used.
- @param loops The number of times to loop the audio. `0` means no looping.
- **/
- public function new(buffer:AudioBuffer = null, offset:Float = 0, length:Null = null, loops:Int = 0)
- {
- this.buffer = buffer;
- this.offset = offset;
-
- __backend = new AudioSourceBackend(this);
-
- if (length != null && length != 0)
- {
- this.length = length;
- }
-
- if (buffer != null)
- {
- init();
- }
-
- this.loops = loops;
- }
-
- /**
- Releases any resources used by this `AudioSource`.
- **/
- inline public function dispose():Void
- {
- __backend.dispose();
- activeSources.remove(this);
- }
-
- @:noCompletion inline private function init():Void
- {
- __backend.init();
- if (!activeSources.contains(this)) activeSources.push(this);
- }
-
- /**
- Starts or resumes audio playback.
- **/
- inline public function play():Void
- {
- __backend.play();
- }
-
- /**
- Pauses audio playback.
- **/
- inline public function pause():Void
- {
- __backend.pause();
- }
-
- /**
- Stops audio playback and resets the playback position to the beginning.
- **/
- inline public function stop():Void
- {
- __backend.stop();
- }
-
- // Get & Set Methods
- @:noCompletion inline private function get_playing():Bool
- {
- @:privateAccess return __backend.playing;
- }
-
- @:noCompletion inline private function get_currentTime():Float
- {
- return __backend.getCurrentTime();
- }
-
- @:noCompletion inline private function set_currentTime(value:Float):Float
- {
- return __backend.setCurrentTime(value);
- }
-
- @:noCompletion inline private function get_gain():Float
- {
- return __backend.getGain();
- }
-
- @:noCompletion inline private function set_gain(value:Float):Float
- {
- return __backend.setGain(value);
- }
-
- @:noCompletion inline private function get_length():Null
- {
- return __backend.getLength();
- }
-
- @:noCompletion inline private function set_length(value:Null):Null
- {
- return __backend.setLength(value);
- }
-
- @:noCompletion inline private function get_loops():Int
- {
- return __backend.getLoops();
- }
-
- @:noCompletion inline private function set_loops(value:Int):Int
- {
- return __backend.setLoops(value);
- }
-
- @:noCompletion inline private function get_loopTime():Float
- {
- return __backend.getLoopTime();
- }
-
- @:noCompletion inline private function set_loopTime(value:Float):Float
- {
- return __backend.setLoopTime(value);
- }
-
- @:noCompletion inline private function get_pitch():Float
- {
- return __backend.getPitch();
- }
-
- @:noCompletion inline private function set_pitch(value:Float):Float
- {
- return __backend.setPitch(value);
- }
-
- @:noCompletion inline private function get_position():Vector4
- {
- return __backend.getPosition();
- }
-
- @:noCompletion inline private function set_position(value:Vector4):Vector4
- {
- return __backend.setPosition(value);
- }
-
- @:noCompletion inline private function get_pan():Float
- {
- return __backend.getPan();
- }
-
- @:noCompletion inline private function set_pan(value:Float):Float
- {
- return __backend.setPan(value);
- }
-
- @:noCompletion inline private function get_latency():Float
- {
- return __backend.getLatency();
- }
-}
-
-#if (js && html5)
-@:noCompletion private typedef AudioSourceBackend = lime._internal.backend.html5.HTML5AudioSource;
-#else
-@:noCompletion private typedef AudioSourceBackend = lime._internal.backend.native.NativeAudioSource;
-#end
diff --git a/source/lime/media/vorbis/VorbisFile.hx b/source/lime/media/vorbis/VorbisFile.hx
deleted file mode 100644
index bd29a7a7f5..0000000000
--- a/source/lime/media/vorbis/VorbisFile.hx
+++ /dev/null
@@ -1,356 +0,0 @@
-package lime.media.vorbis;
-
-#if (!lime_doc_gen || lime_vorbis)
-import haxe.Int64;
-import haxe.io.Bytes;
-import lime._internal.backend.native.NativeCFFI;
-
-#if hl
-@:keep
-#end
-@:access(lime._internal.backend.native.NativeCFFI)
-class VorbisFile
-{
- public var bitstream(default, null):Int;
-
- @:noCompletion private var handle:Dynamic;
-
- @:noCompletion var _filePath:String;
- @:noCompletion var _bytes:Bytes;
-
- @:noCompletion private function new(handle:Dynamic)
- {
- this.handle = handle;
- }
-
- public function bitrate(bitstream:Int = -1):Int
- {
- #if (lime_cffi && lime_vorbis && !macro)
- return NativeCFFI.lime_vorbis_file_bitrate(handle, bitstream);
- #else
- return 0;
- #end
- }
-
- public function bitrateInstant():Int
- {
- #if (lime_cffi && lime_vorbis && !macro)
- return NativeCFFI.lime_vorbis_file_bitrate_instant(handle);
- #else
- return 0;
- #end
- }
-
- public function clear():Void
- {
- #if (lime_cffi && lime_vorbis && !macro)
- NativeCFFI.lime_vorbis_file_clear(handle);
- #end
- handle = null;
- _filePath = null;
- _bytes = null;
- }
-
- public function clone():VorbisFile
- {
- if (_filePath != null) return fromFile(_filePath);
- if (_bytes != null) return fromBytes(_bytes);
- return null;
- }
-
- public function comment(bitstream:Int = -1):VorbisComment
- {
- #if (lime_cffi && lime_vorbis && !macro)
- var data = NativeCFFI.lime_vorbis_file_comment(handle, bitstream);
-
- if (data != null)
- {
- var comment = new VorbisComment();
- comment.userComments = data.userComments;
- comment.vendor = data.vendor;
- return comment;
- }
- #end
-
- return null;
- }
-
- public function crosslap(other:VorbisFile):Int
- {
- #if (lime_cffi && lime_vorbis && !macro)
- return NativeCFFI.lime_vorbis_file_crosslap(handle, other.handle);
- #else
- return 0;
- #end
- }
-
- public static function fromBytes(bytes:Bytes):VorbisFile
- {
- #if (lime_cffi && lime_vorbis && !macro)
- var handle = NativeCFFI.lime_vorbis_file_from_bytes(bytes);
-
- if (handle != null)
- {
- var vorbisFile = new VorbisFile(handle);
- vorbisFile._bytes = bytes;
- return vorbisFile;
- }
- #end
-
- return null;
- }
-
- public static function fromFile(path:String):VorbisFile
- {
- #if (lime_cffi && lime_vorbis && !macro)
- var handle = NativeCFFI.lime_vorbis_file_from_file(path);
-
- if (handle != null)
- {
- var vorbisFile = new VorbisFile(handle);
- vorbisFile._filePath = path;
- return vorbisFile;
- }
- #end
-
- return null;
- }
-
- public function info(bitstream:Int = -1):VorbisInfo
- {
- #if (lime_cffi && lime_vorbis && !macro)
- var data = NativeCFFI.lime_vorbis_file_info(handle, bitstream);
-
- if (data != null)
- {
- var info = new VorbisInfo();
- info.bitrateLower = data.bitrateLower;
- info.bitrateNominal = data.bitrateNominal;
- info.bitrateUpper = data.bitrateUpper;
- info.channels = data.channels;
- info.rate = data.rate;
- info.version = data.version;
- return info;
- }
- #end
-
- return null;
- }
-
- public function pcmSeek(pos:Int64):Int
- {
- #if (lime_cffi && lime_vorbis && !macro)
- return NativeCFFI.lime_vorbis_file_pcm_seek(handle, pos.low, pos.high);
- #else
- return 0;
- #end
- }
-
- public function pcmSeekLap(pos:Int64):Int
- {
- #if (lime_cffi && lime_vorbis && !macro)
- return NativeCFFI.lime_vorbis_file_pcm_seek_lap(handle, pos.low, pos.high);
- #else
- return 0;
- #end
- }
-
- public function pcmSeekPage(pos:Int64):Int
- {
- #if (lime_cffi && lime_vorbis && !macro)
- return NativeCFFI.lime_vorbis_file_pcm_seek_page(handle, pos.low, pos.high);
- #else
- return 0;
- #end
- }
-
- public function pcmSeekPageLap(pos:Int64):Int
- {
- #if (lime_cffi && lime_vorbis && !macro)
- return NativeCFFI.lime_vorbis_file_pcm_seek_page_lap(handle, pos.low, pos.high);
- #else
- return 0;
- #end
- }
-
- public function pcmTell():Int64
- {
- #if (lime_cffi && lime_vorbis && !macro)
- var data = NativeCFFI.lime_vorbis_file_pcm_tell(handle);
-
- if (data != null)
- {
- return Int64.make(data.high, data.low);
- }
- #end
-
- return Int64.ofInt(0);
- }
-
- public function pcmTotal(bitstream:Int = -1):Int64
- {
- #if (lime_cffi && lime_vorbis && !macro)
- var data = NativeCFFI.lime_vorbis_file_pcm_total(handle, bitstream);
-
- if (data != null)
- {
- return Int64.make(data.high, data.low);
- }
- #end
-
- return Int64.ofInt(0);
- }
-
- public function rawSeek(pos:Int64):Int
- {
- #if (lime_cffi && lime_vorbis && !macro)
- return NativeCFFI.lime_vorbis_file_raw_seek(handle, pos.low, pos.high);
- #else
- return 0;
- #end
- }
-
- public function rawSeekLap(pos:Int64):Int
- {
- #if (lime_cffi && lime_vorbis && !macro)
- return NativeCFFI.lime_vorbis_file_raw_seek_lap(handle, pos.low, pos.high);
- #else
- return 0;
- #end
- }
-
- public function rawTell():Int64
- {
- #if (lime_cffi && lime_vorbis && !macro)
- var data = NativeCFFI.lime_vorbis_file_raw_tell(handle);
-
- if (data != null)
- {
- return Int64.make(data.high, data.low);
- }
- #end
-
- return Int64.ofInt(0);
- }
-
- public function rawTotal(bitstream:Int = -1):Int64
- {
- #if (lime_cffi && lime_vorbis && !macro)
- var data = NativeCFFI.lime_vorbis_file_raw_total(handle, bitstream);
-
- if (data != null)
- {
- return Int64.make(data.high, data.low);
- }
- #end
-
- return Int64.ofInt(0);
- }
-
- public function read(buffer:Bytes, position:Int, length:Int = 4096, bigEndianPacking:Bool = false, wordSize:Int = 2, signed:Bool = true):Int
- {
- #if (lime_cffi && lime_vorbis && !macro)
- var data = NativeCFFI.lime_vorbis_file_read(handle, buffer, position, length, bigEndianPacking, wordSize, signed);
- if (data == null) return 0;
- bitstream = data.bitstream;
- return data.returnValue;
- #else
- return 0;
- #end
- }
-
- // public function readFilter (buffer:Bytes, length:Int = 4096, endianness:Endian = LITTLE_ENDIAN, wordSize:Int = 2, signed:Bool = true, bitstream:Int = 0, filter, filter_param
- public function readFloat(pcmChannels:Bytes, samples:Int):Int
- {
- #if (lime_cffi && lime_vorbis && !macro)
- var data = NativeCFFI.lime_vorbis_file_read_float(handle, pcmChannels, samples);
- if (data == null) return 0;
- bitstream = data.bitstream;
- return data.returnValue;
- #else
- return 0;
- #end
- }
-
- public function seekable():Bool
- {
- #if (lime_cffi && lime_vorbis && !macro)
- return NativeCFFI.lime_vorbis_file_seekable(handle);
- #else
- return false;
- #end
- }
-
- public function serialNumber(bitstream:Int = -1):Int
- {
- #if (lime_cffi && lime_vorbis && !macro)
- return NativeCFFI.lime_vorbis_file_serial_number(handle, bitstream);
- #else
- return 0;
- #end
- }
-
- public function streams():Int
- {
- #if (lime_cffi && lime_vorbis && !macro)
- return NativeCFFI.lime_vorbis_file_streams(handle);
- #else
- return 0;
- #end
- }
-
- public function timeSeek(s:Float):Int
- {
- #if (lime_cffi && lime_vorbis && !macro)
- return NativeCFFI.lime_vorbis_file_time_seek(handle, s);
- #else
- return 0;
- #end
- }
-
- public function timeSeekLap(s:Float):Int
- {
- #if (lime_cffi && lime_vorbis && !macro)
- return NativeCFFI.lime_vorbis_file_time_seek_lap(handle, s);
- #else
- return 0;
- #end
- }
-
- public function timeSeekPage(s:Float):Int
- {
- #if (lime_cffi && lime_vorbis && !macro)
- return NativeCFFI.lime_vorbis_file_time_seek_page(handle, s);
- #else
- return 0;
- #end
- }
-
- public function timeSeekPageLap(s:Float):Int
- {
- #if (lime_cffi && lime_vorbis && !macro)
- return NativeCFFI.lime_vorbis_file_time_seek_page_lap(handle, s);
- #else
- return 0;
- #end
- }
-
- public function timeTell():Float
- {
- #if (lime_cffi && lime_vorbis && !macro)
- return NativeCFFI.lime_vorbis_file_time_tell(handle);
- #else
- return 0;
- #end
- }
-
- public function timeTotal(bitstream:Int = -1):Float
- {
- #if (lime_cffi && lime_vorbis && !macro)
- return NativeCFFI.lime_vorbis_file_time_total(handle, bitstream);
- #else
- return 0;
- #end
- }
-}
-#end
diff --git a/source/lime/system/System.hx b/source/lime/system/System.hx
deleted file mode 100644
index 2184f5fe62..0000000000
--- a/source/lime/system/System.hx
+++ /dev/null
@@ -1,905 +0,0 @@
-package lime.system;
-
-import haxe.Constraints;
-import lime._internal.backend.native.NativeCFFI;
-import lime.app.Application;
-import lime.graphics.RenderContextAttributes;
-import lime.math.Rectangle;
-import lime.ui.WindowAttributes;
-import lime.utils.ArrayBuffer;
-import lime.utils.UInt8Array;
-import lime.utils.UInt16Array;
-#if flash
-import openfl.net.URLRequest;
-import openfl.system.Capabilities;
-import openfl.Lib;
-#end
-#if air
-import openfl.desktop.NativeApplication;
-#end
-#if ((js && html5) || electron)
-import js.html.Element;
-import js.Browser;
-#end
-#if sys
-import sys.io.Process;
-#end
-
-/**
- Access operating system level settings and operations.
-**/
-#if !lime_debug
-@:fileXml('tags="haxe,release"')
-@:noDebug
-#end
-@:access(lime._internal.backend.native.NativeCFFI)
-@:access(lime.system.Display)
-@:access(lime.system.DisplayMode)
-#if (cpp && windows && !HXCPP_MINGW && !lime_disable_gpu_hint)
-@:cppFileCode('
-#if defined(HX_WINDOWS)
-extern "C" {
- _declspec(dllexport) unsigned long NvOptimusEnablement = 0x00000001;
- _declspec(dllexport) int AmdPowerXpressRequestHighPerformance = 1;
-}
-#endif
-')
-#end
-class System
-{
- /**
- Determines if the screen saver is allowed to start or not.
- **/
- public static var allowScreenTimeout(get, set):Bool;
-
- /**
- The path to the directory where the application is installed, along with
- its supporting files. In many cases, this directory is read-only, and
- attempts to write to files, create new files, or delete files in this
- directory are likely fail.
- **/
- public static var applicationDirectory(get, never):String;
-
- /**
- The application's dedicated storage directory, which unique to each
- application and user. Useful for storing settings on a user-specific
- and application-specific basis.
-
- This directory may or may not be removed when the application is
- uninstalled, and it depends on the platform and installer technology
- that is used.
- **/
- public static var applicationStorageDirectory(get, never):String;
-
- /**
- The path to the directory containing the user's desktop.
- **/
- public static var desktopDirectory(get, never):String;
-
- public static var deviceModel(get, never):String;
- public static var deviceVendor(get, never):String;
- public static var disableCFFI:Bool;
-
- /**
- The path to the directory containing the user's documents.
- **/
- public static var documentsDirectory(get, never):String;
-
- /**
- The platform's default endianness for bytes.
- **/
- public static var endianness(get, never):Endian;
-
- /**
- The path to the directory where fonts are installed.
- **/
- public static var fontsDirectory(get, never):String;
-
- /**
- The number of available video displays.
- **/
- public static var numDisplays(get, never):Int;
-
- public static var platformLabel(get, never):String;
- public static var platformName(get, never):String;
- public static var platformVersion(get, never):String;
-
- /**
- The path to the user's home directory.
- **/
- public static var userDirectory(get, never):String;
-
- @:noCompletion private static var __applicationDirectory:String;
- @:noCompletion private static var __applicationEntryPoint:Map;
- @:noCompletion private static var __applicationStorageDirectory:String;
- @:noCompletion private static var __desktopDirectory:String;
- @:noCompletion private static var __deviceModel:String;
- @:noCompletion private static var __deviceVendor:String;
- @:noCompletion private static var __directories = new Map();
- @:noCompletion private static var __documentsDirectory:String;
- @:noCompletion private static var __endianness:Endian;
- @:noCompletion private static var __fontsDirectory:String;
- @:noCompletion private static var __platformLabel:String;
- @:noCompletion private static var __platformName:String;
- @:noCompletion private static var __platformVersion:String;
- @:noCompletion private static var __userDirectory:String;
-
- #if (js && html5)
- @:keep @:expose("lime.embed")
- public static function embed(projectName:String, element:Dynamic, width:Null = null, height:Null = null, config:Dynamic = null):Void
- {
- if (__applicationEntryPoint == null) return;
-
- if (__applicationEntryPoint.exists(projectName))
- {
- var htmlElement:Element = null;
-
- if ((element is String))
- {
- htmlElement = cast Browser.document.getElementById(element);
- }
- else if (element == null)
- {
- htmlElement = cast Browser.document.createElement("div");
- }
- else
- {
- htmlElement = cast element;
- }
-
- if (htmlElement == null)
- {
- Browser.window.console.log("[lime.embed] ERROR: Cannot find target element: " + element);
- return;
- }
-
- if (width == null)
- {
- width = 0;
- }
-
- if (height == null)
- {
- height = 0;
- }
-
- if (config == null) config = {};
-
- if (Reflect.hasField(config, "background") && (config.background is String))
- {
- var background = StringTools.replace(Std.string(config.background), "#", "");
-
- if (background.indexOf("0x") > -1)
- {
- config.background = Std.parseInt(background);
- }
- else
- {
- config.background = Std.parseInt("0x" + background);
- }
- }
-
- config.element = htmlElement;
- config.width = width;
- config.height = height;
-
- __applicationEntryPoint[projectName](config);
- }
- }
- #end
-
- #if (!lime_doc_gen || sys)
- /**
- Attempts to exit the application. Dispatches `onExit`, and will not
- exit if the event is canceled.
- **/
- public static function exit(code:Int):Void
- {
- var currentApp = Application.current;
- #if ((sys || (js && html5) || air) && !macro)
- if (currentApp != null)
- {
- currentApp.onExit.dispatch(code);
-
- if (currentApp.onExit.canceled)
- {
- return;
- }
- }
- #end
-
- #if sys
- Sys.exit(code);
- #elseif (js && html5)
- if (currentApp != null && currentApp.window != null)
- {
- currentApp.window.close();
- }
- #elseif air
- NativeApplication.nativeApplication.exit(code);
- #end
- }
- #end
-
- /**
- Returns information about the video display with the specified ID.
- **/
- public static function getDisplay(id:Int):Display
- {
- #if (lime_cffi && !macro)
- var displayInfo:Dynamic = NativeCFFI.lime_system_get_display(id);
-
- if (displayInfo != null)
- {
- var display = new Display();
- display.id = id;
- #if hl
- display.name = @:privateAccess String.fromUTF8(displayInfo.name);
- #else
- display.name = displayInfo.name;
- #end
- display.bounds = new Rectangle(displayInfo.bounds.x, displayInfo.bounds.y, displayInfo.bounds.width, displayInfo.bounds.height);
-
- #if ios
- var tablet = NativeCFFI.lime_system_get_ios_tablet();
- var scale = Application.current.window.scale;
- if (!tablet && scale > 2.46)
- {
- display.dpi = 401; // workaround for iPhone Plus
- }
- else
- {
- display.dpi = (tablet ? 132 : 163) * scale;
- }
- #elseif android
- var getDisplayDPI = JNI.createStaticMethod("org/haxe/lime/GameActivity", "getDisplayXDPI", "()D");
- display.dpi = Math.round(getDisplayDPI());
- #else
- display.dpi = displayInfo.dpi;
- #end
-
- display.supportedModes = [];
-
- var displayMode;
-
- #if hl
- var supportedModes:hl.NativeArray = displayInfo.supportedModes;
- #else
- var supportedModes:Array = displayInfo.supportedModes;
- #end
- for (mode in supportedModes)
- {
- displayMode = new DisplayMode(mode.width, mode.height, mode.refreshRate, mode.pixelFormat);
- display.supportedModes.push(displayMode);
- }
-
- var mode = displayInfo.currentMode;
- var currentMode = new DisplayMode(mode.width, mode.height, mode.refreshRate, mode.pixelFormat);
-
- for (mode in display.supportedModes)
- {
- if (currentMode.pixelFormat == mode.pixelFormat
- && currentMode.width == mode.width
- && currentMode.height == mode.height
- && currentMode.refreshRate == mode.refreshRate)
- {
- currentMode = mode;
- break;
- }
- }
-
- display.currentMode = currentMode;
-
- return display;
- }
- #elseif (flash || html5)
- if (id == 0)
- {
- var display = new Display();
- display.id = 0;
- display.name = "Generic Display";
-
- #if flash
- display.dpi = Capabilities.screenDPI;
- display.currentMode = new DisplayMode(Std.int(Capabilities.screenResolutionX), Std.int(Capabilities.screenResolutionY), 60, ARGB32);
- #elseif (js && html5)
- // var div = Browser.document.createElement ("div");
- // div.style.width = "1in";
- // Browser.document.body.appendChild (div);
- // var ppi = Browser.document.defaultView.getComputedStyle (div, null).getPropertyValue ("width");
- // Browser.document.body.removeChild (div);
- // display.dpi = Std.parseFloat (ppi);
- display.dpi = 96 * Browser.window.devicePixelRatio;
- display.currentMode = new DisplayMode(Browser.window.screen.width, Browser.window.screen.height, 60, ARGB32);
- #end
-
- display.supportedModes = [display.currentMode];
- display.bounds = new Rectangle(0, 0, display.currentMode.width, display.currentMode.height);
- return display;
- }
- #end
-
- return null;
- }
-
- /**
- The number of milliseconds since the application was initialized.
- **/
- public static function getTimer():Int
- {
- #if flash
- return flash.Lib.getTimer();
- #elseif ((js && !nodejs) || electron)
- return Std.int(Browser.window.performance.now());
- #elseif (lime_cffi && !macro)
- return cast NativeCFFI.lime_system_get_timer();
- #elseif cpp
- return Std.int(untyped __global__.__time_stamp() * 1000);
- #elseif sys
- return Std.int(Sys.time() * 1000);
- #else
- return 0;
- #end
- }
-
- #if (!lime_doc_gen || lime_cffi)
- public static inline function load(library:String, method:String, args:Int = 0, lazy:Bool = false):Dynamic
- {
- #if !macro
- return CFFI.load(library, method, args, lazy);
- #else
- return null;
- #end
- }
- #end
-
- /**
- Opens a file with the suste, default application.
-
- In a web browser, opens a URL with target `_blank`.
- **/
- public static function openFile(path:String):Void
- {
- if (path != null)
- {
- #if (sys && windows)
- Sys.command("start", ["", path]);
- #elseif mac
- Sys.command("/usr/bin/open", [path]);
- #elseif linux
- // generally `xdg-open` should work in every distro
- var cmd = Sys.command("xdg-open", [path, "&"]);
- // run old command JUST IN CASE it fails, which it shouldn't
- if (cmd != 0) cmd = Sys.command("/usr/bin/xdg-open", [path, "&"]);
- #elseif (js && html5)
- Browser.window.open(path, "_blank");
- #elseif flash
- Lib.getURL(new URLRequest(path), "_blank");
- #elseif android
- var openFile = JNI.createStaticMethod("org/haxe/lime/GameActivity", "openFile", "(Ljava/lang/String;)V");
- openFile(path);
- #elseif (lime_cffi && !macro)
- NativeCFFI.lime_system_open_file(path);
- #end
- }
- }
-
- /**
- Opens a URL with the specified target web browser window.
- **/
- public static function openURL(url:String, target:String = "_blank"):Void
- {
- if (url != null)
- {
- #if desktop
- openFile(url);
- #elseif (js && html5)
- Browser.window.open(url, target);
- #elseif flash
- Lib.getURL(new URLRequest(url), target);
- #elseif android
- var openURL = JNI.createStaticMethod("org/haxe/lime/GameActivity", "openURL", "(Ljava/lang/String;Ljava/lang/String;)V");
- openURL(url, target);
- #elseif (lime_cffi && !macro)
- NativeCFFI.lime_system_open_url(url, target);
- #end
- }
- }
-
- @:noCompletion private static function __copyMissingFields(target:Dynamic, source:Dynamic):Void
- {
- if (source == null || target == null) return;
-
- for (field in Reflect.fields(source))
- {
- if (!Reflect.hasField(target, field))
- {
- Reflect.setField(target, field, Reflect.field(source, field));
- }
- }
- }
-
- @:noCompletion private static function __getDirectory(type:SystemDirectory):String
- {
- #if (lime_cffi && !macro)
- if (__directories.exists(type))
- {
- return __directories.get(type);
- }
- else
- {
- var path:String;
-
- if (type == APPLICATION_STORAGE)
- {
- var company = "MyCompany";
- var file = "MyApplication";
-
- if (Application.current != null)
- {
- if (Application.current.meta.exists("company"))
- {
- company = Application.current.meta.get("company");
- }
-
- if (Application.current.meta.exists("file"))
- {
- file = Application.current.meta.get("file");
- }
- }
-
- #if hl
- path = @:privateAccess String.fromUTF8(NativeCFFI.lime_system_get_directory(type, company, file));
- #else
- path = NativeCFFI.lime_system_get_directory(type, company, file);
- #end
- }
- else
- {
- #if hl
- path = @:privateAccess String.fromUTF8(NativeCFFI.lime_system_get_directory(type, null, null));
- #else
- path = NativeCFFI.lime_system_get_directory(type, null, null);
- #end
- }
-
- #if windows
- var seperator = "\\";
- #else
- var seperator = "/";
- #end
-
- if (path != null && path.length > 0 && !StringTools.endsWith(path, seperator))
- {
- path += seperator;
- }
-
- __directories.set(type, path);
- return path;
- }
- #elseif flash
- if (type != FONTS && Capabilities.playerType == "Desktop")
- {
- var propertyName = switch (type)
- {
- case APPLICATION: "applicationDirectory";
- case APPLICATION_STORAGE: "applicationStorageDirectory";
- case DESKTOP: "desktopDirectory";
- case DOCUMENTS: "documentsDirectory";
- default: "userDirectory";
- }
-
- return Reflect.getProperty(Type.resolveClass("flash.filesystem.File"), propertyName).nativePath;
- }
- #end
-
- return null;
- }
-
- #if sys
- private static function __parseArguments(attributes:WindowAttributes):Void
- {
- // TODO: Handle default arguments, like --window-fps=60
-
- var arguments = Sys.args();
- var stripQuotes = ~/^['"](.*)['"]$/;
- var equals, argValue, parameters = null;
- var windowParamPrefix = "--window-";
-
- if (arguments != null)
- {
- for (argument in arguments)
- {
- equals = argument.indexOf("=");
-
- if (equals > 0)
- {
- argValue = argument.substr(equals + 1);
-
- if (stripQuotes.match(argValue))
- {
- argValue = stripQuotes.matched(1);
- }
-
- if (parameters == null) parameters = new Map();
- parameters.set(argument.substr(0, equals), argValue);
- }
- }
- }
-
- if (parameters != null)
- {
- if (attributes.parameters == null) attributes.parameters = {};
- if (attributes.context == null) attributes.context = {};
-
- for (parameter in parameters.keys())
- {
- argValue = parameters.get(parameter);
-
- if (#if lime_disable_window_override false && #end StringTools.startsWith(parameter, windowParamPrefix))
- {
- switch (parameter.substr(windowParamPrefix.length))
- {
- case "allow-high-dpi":
- attributes.allowHighDPI = __parseBool(argValue);
- case "always-on-top":
- attributes.alwaysOnTop = __parseBool(argValue);
- case "antialiasing":
- attributes.context.antialiasing = Std.parseInt(argValue);
- case "background":
- attributes.context.background = (argValue == "" || argValue == "null") ? null : Std.parseInt(argValue);
- case "borderless":
- attributes.borderless = __parseBool(argValue);
- case "colorDepth":
- attributes.context.colorDepth = Std.parseInt(argValue);
- case "depth", "depth-buffer":
- attributes.context.depth = __parseBool(argValue);
- // case "display": windowConfig.display = Std.parseInt (argValue);
- case "fullscreen":
- attributes.fullscreen = __parseBool(argValue);
- case "hardware":
- attributes.context.hardware = __parseBool(argValue);
- case "height":
- attributes.height = Std.parseInt(argValue);
- case "hidden":
- attributes.hidden = __parseBool(argValue);
- case "maximized":
- attributes.maximized = __parseBool(argValue);
- case "minimized":
- attributes.minimized = __parseBool(argValue);
- case "render-type", "renderer":
- attributes.context.type = argValue;
- case "render-version", "renderer-version":
- attributes.context.version = argValue;
- case "resizable":
- attributes.resizable = __parseBool(argValue);
- case "stencil", "stencil-buffer":
- attributes.context.stencil = __parseBool(argValue);
- // case "title": windowConfig.title = argValue;
- case "vsync":
- attributes.context.vsync = __parseBool(argValue);
- case "width":
- attributes.width = Std.parseInt(argValue);
- case "x":
- attributes.x = Std.parseInt(argValue);
- case "y":
- attributes.y = Std.parseInt(argValue);
- default:
- }
- }
- else if (!Reflect.hasField(attributes.parameters, parameter))
- {
- Reflect.setField(attributes.parameters, parameter, argValue);
- }
- }
- }
- }
- #end
-
- @:noCompletion private static inline function __parseBool(value:String):Bool
- {
- return (value == "true");
- }
-
- @:noCompletion private static function __registerEntryPoint(projectName:String, entryPoint:Function):Void
- {
- // executes first!!
- #if (sys && !macro)
- funkin.backend.system.Main.preInit();
- #end
-
- if (__applicationEntryPoint == null)
- {
- __applicationEntryPoint = new Map();
- }
-
- __applicationEntryPoint[projectName] = entryPoint;
- }
-
- @:noCompletion private static function __runProcess(command:String, args:Array = null):String
- {
- #if sys
- try
- {
- if (args == null) args = [];
-
- var process = new Process(command, args);
- var value = StringTools.trim(process.stdout.readLine().toString());
- process.close();
- return value;
- }
- catch (e:Dynamic) {}
- #end
- return null;
- }
-
- // Get & Set Methods
- private static function get_allowScreenTimeout():Bool
- {
- #if (lime_cffi && !macro)
- return NativeCFFI.lime_system_get_allow_screen_timeout();
- #else
- return true;
- #end
- }
-
- private static function set_allowScreenTimeout(value:Bool):Bool
- {
- #if (lime_cffi && !macro)
- return NativeCFFI.lime_system_set_allow_screen_timeout(value);
- #else
- return true;
- #end
- }
-
- private static function get_applicationDirectory():String
- {
- if (__applicationDirectory == null)
- {
- __applicationDirectory = __getDirectory(APPLICATION);
- }
-
- return __applicationDirectory;
- }
-
- private static function get_applicationStorageDirectory():String
- {
- if (__applicationStorageDirectory == null)
- {
- __applicationStorageDirectory = __getDirectory(APPLICATION_STORAGE);
- }
-
- return __applicationStorageDirectory;
- }
-
- private static function get_deviceModel():String
- {
- if (__deviceModel == null)
- {
- #if (lime_cffi && !macro && (windows || ios || tvos))
- #if hl
- __deviceModel = @:privateAccess String.fromUTF8(NativeCFFI.lime_system_get_device_model());
- #else
- __deviceModel = NativeCFFI.lime_system_get_device_model();
- #end
- #elseif android
- var manufacturer:String = JNI.createStaticField("android/os/Build", "MANUFACTURER", "Ljava/lang/String;").get();
- var model:String = JNI.createStaticField("android/os/Build", "MODEL", "Ljava/lang/String;").get();
- if (manufacturer != null && model != null)
- {
- if (StringTools.startsWith(model.toLowerCase(), manufacturer.toLowerCase()))
- {
- model = StringTools.trim(model.substr(manufacturer.length));
- while (StringTools.startsWith(model, "-"))
- {
- model = StringTools.trim(model.substr(1));
- }
- }
- __deviceModel = model;
- }
- #elseif mac
- __deviceModel = __runProcess("sysctl", ["-n", "hw.model"]);
- #elseif linux
- __deviceModel = __runProcess("cat", ["/sys/devices/virtual/dmi/id/sys_vendor"]);
- #end
- }
-
- return __deviceModel;
- }
-
- private static function get_deviceVendor():String
- {
- if (__deviceVendor == null)
- {
- #if (lime_cffi && !macro && windows && !html5)
- #if hl
- __deviceVendor = @:privateAccess String.fromUTF8(NativeCFFI.lime_system_get_device_vendor());
- #else
- __deviceVendor = NativeCFFI.lime_system_get_device_vendor();
- #end
- #elseif android
- var vendor:String = JNI.createStaticField("android/os/Build", "MANUFACTURER", "Ljava/lang/String;").get();
- if (vendor != null)
- {
- __deviceVendor = vendor.charAt(0).toUpperCase() + vendor.substr(1);
- }
- #elseif (ios || mac || tvos)
- __deviceVendor = "Apple";
- #elseif linux
- __deviceVendor = __runProcess("cat", ["/sys/devices/virtual/dmi/id/product_name"]);
- #end
- }
-
- return __deviceVendor;
- }
-
- private static function get_desktopDirectory():String
- {
- if (__desktopDirectory == null)
- {
- __desktopDirectory = __getDirectory(DESKTOP);
- }
-
- return __desktopDirectory;
- }
-
- private static function get_documentsDirectory():String
- {
- if (__documentsDirectory == null)
- {
- __documentsDirectory = __getDirectory(DOCUMENTS);
- }
-
- return __documentsDirectory;
- }
-
- private static function get_endianness():Endian
- {
- if (__endianness == null)
- {
- #if (ps3 || wiiu || flash)
- __endianness = BIG_ENDIAN;
- #else
- var arrayBuffer = new ArrayBuffer(2);
- var uint8Array = new UInt8Array(arrayBuffer);
- var uint16array = new UInt16Array(arrayBuffer);
- uint8Array[0] = 0xAA;
- uint8Array[1] = 0xBB;
- if (uint16array[0] == 0xAABB) __endianness = BIG_ENDIAN;
- else
- __endianness = LITTLE_ENDIAN;
- #end
- }
-
- return __endianness;
- }
-
- private static function get_fontsDirectory():String
- {
- if (__fontsDirectory == null)
- {
- __fontsDirectory = __getDirectory(FONTS);
- }
-
- return __fontsDirectory;
- }
-
- private static function get_numDisplays():Int
- {
- #if (lime_cffi && !macro)
- return NativeCFFI.lime_system_get_num_displays();
- #else
- return 1;
- #end
- }
-
- private static function get_platformLabel():String
- {
- if (__platformLabel == null)
- {
- #if (lime_cffi && !macro && windows && !html5)
- #if hl
- var label:String = @:privateAccess String.fromUTF8(NativeCFFI.lime_system_get_platform_label());
- #else
- var label:String = NativeCFFI.lime_system_get_platform_label();
- #end
- if (label != null) __platformLabel = StringTools.trim(label);
- #elseif linux
- __platformLabel = __runProcess("lsb_release", ["-ds"]);
- #else
- var name = System.platformName;
- var version = System.platformVersion;
- if (name != null && version != null) __platformLabel = name + " " + version;
- else if (name != null) __platformLabel = name;
- #end
- }
-
- return __platformLabel;
- }
-
- private static function get_platformName():String
- {
- if (__platformName == null)
- {
- #if windows
- __platformName = "Windows";
- #elseif mac
- __platformName = "macOS";
- #elseif linux
- __platformName = __runProcess("lsb_release", ["-is"]);
- #elseif ios
- __platformName = "iOS";
- #elseif android
- __platformName = "Android";
- #elseif air
- __platformName = "AIR";
- #elseif flash
- __platformName = "Flash Player";
- #elseif tvos
- __platformName = "tvOS";
- #elseif tizen
- __platformName = "Tizen";
- #elseif blackberry
- __platformName = "BlackBerry";
- #elseif firefox
- __platformName = "Firefox";
- #elseif webos
- __platformName = "webOS";
- #elseif nodejs
- __platformName = "Node.js";
- #elseif js
- __platformName = "HTML5";
- #end
- }
-
- return __platformName;
- }
-
- private static function get_platformVersion():String
- {
- if (__platformVersion == null)
- {
- #if (lime_cffi && !macro && windows && !html5)
- #if hl
- __platformVersion = @:privateAccess String.fromUTF8(NativeCFFI.lime_system_get_platform_version());
- #else
- __platformVersion = NativeCFFI.lime_system_get_platform_version();
- #end
- #elseif android
- var release = JNI.createStaticField("android/os/Build$VERSION", "RELEASE", "Ljava/lang/String;").get();
- var api = JNI.createStaticField("android/os/Build$VERSION", "SDK_INT", "I").get();
- if (release != null && api != null) __platformVersion = release + " (API " + api + ")";
- #elseif (lime_cffi && !macro && (ios || tvos))
- __platformVersion = NativeCFFI.lime_system_get_platform_version();
- #elseif mac
- __platformVersion = __runProcess("sw_vers", ["-productVersion"]);
- #elseif linux
- __platformVersion = __runProcess("lsb_release", ["-rs"]);
- #elseif flash
- __platformVersion = Capabilities.version;
- #end
- }
-
- return __platformVersion;
- }
-
- private static function get_userDirectory():String
- {
- if (__userDirectory == null)
- {
- __userDirectory = __getDirectory(USER);
- }
-
- return __userDirectory;
- }
-}
-
-#if (haxe_ver >= 4.0) enum #else @:enum #end abstract SystemDirectory(Int) from Int to Int from UInt to UInt
-{
- var APPLICATION = 0;
- var APPLICATION_STORAGE = 1;
- var DESKTOP = 2;
- var DOCUMENTS = 3;
- var FONTS = 4;
- var USER = 5;
-}
diff --git a/source/lime/ui/FileDialog.hx b/source/lime/ui/FileDialog.hx
deleted file mode 100644
index 418d711936..0000000000
--- a/source/lime/ui/FileDialog.hx
+++ /dev/null
@@ -1,411 +0,0 @@
-package lime.ui;
-
-import haxe.io.Bytes;
-import haxe.io.Path;
-import lime._internal.backend.native.NativeCFFI;
-import lime.app.Event;
-import lime.graphics.Image;
-import lime.system.BackgroundWorker;
-import lime.utils.ArrayBuffer;
-import lime.utils.Resource;
-#if hl
-import hl.Bytes as HLBytes;
-import hl.NativeArray;
-#end
-#if sys
-import sys.io.File;
-#end
-#if (js && html5)
-import js.html.Blob;
-#end
-
-/**
- Simple file dialog used for asking user where to save a file, or select files to open.
-
- Example usage:
- ```haxe
- var fileDialog = new FileDialog();
-
- fileDialog.onCancel.add( () -> trace("Canceled.") );
-
- fileDialog.onSave.add( path -> trace("File saved in " + path) );
-
- fileDialog.onOpen.add( res -> trace("Size of the file = " + (res:haxe.io.Bytes).length) );
-
- if ( fileDialog.open("jpg", null, "Load file") )
- trace("File dialog opened, waiting for selection...");
- else
- trace("This dialog is unsupported.");
- ```
-
- Availability note: most file dialog operations are only available on desktop targets, though
- `save()` is also available in HTML5.
-**/
-#if !lime_debug
-@:fileXml('tags="haxe,release"')
-@:noDebug
-#end
-@:access(lime._internal.backend.native.NativeCFFI)
-@:access(lime.graphics.Image)
-class FileDialog
-{
- /**
- Triggers when the user clicks "Cancel" during any operation, or when a function is unsupported
- (such as `open()` on HTML5).
- **/
- public var onCancel = new EventVoid>();
-
- /**
- Triggers when `open()` is successful. The `lime.utils.Resource` contains the file's data, and can
- be implicitly cast to `haxe.io.Bytes`.
- **/
- public var onOpen = new EventVoid>();
-
- /**
- Triggers when `open()` is successful. The `lime.utils.Resource` contains the file's data, and can
- be implicitly cast to `haxe.io.Bytes`, the String is the path to the file.
- **/
- public var onOpenFile = new Event<(Resource, String)->Void>(); // Added by @NeeEoo
-
- /**
- Triggers when `save()` is successful. The `String` is the path to the saved file.
- **/
- public var onSave = new EventVoid>();
-
- /**
- Triggers when `browse()` is successful and `type` is anything other than
- `FileDialogType.OPEN_MULTIPLE`. The `String` is the path to the selected file.
- **/
- public var onSelect = new EventVoid>();
-
- /**
- Triggers when `browse()` is successful and `type` is `FileDialogType.OPEN_MULTIPLE`. The
- `Array` contains all selected file paths.
- **/
- public var onSelectMultiple = new Event->Void>();
-
- public function new() {}
-
- /**
- Opens a file selection dialog. If successful, either `onSelect` or `onSelectMultiple` will trigger
- with the result(s).
-
- This function only works on desktop targets, and will return `false` otherwise.
- @param type Type of the file dialog: `OPEN`, `SAVE`, `OPEN_DIRECTORY` or `OPEN_MULTIPLE`.
- @param filter A filter to use when browsing. Asterisks are treated as wildcards. For example,
- `"*.jpg"` will match any file ending in `.jpg`.
- @param defaultPath The directory in which to start browsing and/or the default filename to
- suggest. Defaults to `Sys.getCwd()`, with no default filename.
- @param title The title to give the dialog window.
- @return Whether `browse()` is supported on this target.
- **/
- public function browse(type:FileDialogType = null, filter:String = null, defaultPath:String = null, title:String = null):Bool
- {
- if (type == null) type = FileDialogType.OPEN;
-
- #if desktop
- var worker = new BackgroundWorker();
-
- worker.doWork.add(function(_)
- {
- switch (type)
- {
- case OPEN:
- #if linux
- if (title == null) title = "Open File";
- #end
-
- var path = null;
- #if (!macro && lime_cffi)
- #if hl
- var bytes = NativeCFFI.lime_file_dialog_open_file(title, filter, defaultPath);
- if (bytes != null)
- {
- path = @:privateAccess String.fromUTF8(cast bytes);
- }
- #else
- path = NativeCFFI.lime_file_dialog_open_file(title, filter, defaultPath);
- #end
- #end
-
- worker.sendComplete(path);
-
- case OPEN_MULTIPLE:
- #if linux
- if (title == null) title = "Open Files";
- #end
-
- var paths = null;
- #if (!macro && lime_cffi)
- #if hl
- var bytes:NativeArray = cast NativeCFFI.lime_file_dialog_open_files(title, filter, defaultPath);
- if (bytes != null)
- {
- paths = [];
- for (i in 0...bytes.length)
- {
- paths[i] = @:privateAccess String.fromUTF8(bytes[i]);
- }
- }
- #else
- paths = NativeCFFI.lime_file_dialog_open_files(title, filter, defaultPath);
- #end
- #end
-
- worker.sendComplete(paths);
-
- case OPEN_DIRECTORY:
- #if linux
- if (title == null) title = "Open Directory";
- #end
-
- var path = null;
- #if (!macro && lime_cffi)
- #if hl
- var bytes = NativeCFFI.lime_file_dialog_open_directory(title, filter, defaultPath);
- if (bytes != null)
- {
- path = @:privateAccess String.fromUTF8(cast bytes);
- }
- #else
- path = NativeCFFI.lime_file_dialog_open_directory(title, filter, defaultPath);
- #end
- #end
-
- worker.sendComplete(path);
-
- case SAVE:
- #if linux
- if (title == null) title = "Save File";
- #end
-
- var path = null;
- #if (!macro && lime_cffi)
- #if hl
- var bytes = NativeCFFI.lime_file_dialog_save_file(title, filter, defaultPath);
- if (bytes != null)
- {
- path = @:privateAccess String.fromUTF8(cast bytes);
- }
- #else
- path = NativeCFFI.lime_file_dialog_save_file(title, filter, defaultPath);
- #end
- #end
-
- worker.sendComplete(path);
- }
- });
-
- worker.onComplete.add(function(result)
- {
- switch (type)
- {
- case OPEN, OPEN_DIRECTORY, SAVE:
- var path:String = cast result;
-
- if (path != null)
- {
- // Makes sure the filename ends with extension
- if (type == SAVE && filter != null && path.indexOf(".") == -1)
- {
- path += "." + filter;
- }
-
- onSelect.dispatch(path);
- }
- else
- {
- onCancel.dispatch();
- }
-
- case OPEN_MULTIPLE:
- var paths:Array = cast result;
-
- if (paths != null && paths.length > 0)
- {
- onSelectMultiple.dispatch(paths);
- }
- else
- {
- onCancel.dispatch();
- }
- }
- });
-
- worker.run();
-
- return true;
- #else
- onCancel.dispatch();
- return false;
- #end
- }
-
- /**
- Shows an open file dialog. If successful, `onOpen` will trigger with the file contents.
-
- This function only works on desktop targets, and will return `false` otherwise.
- @param filter A filter to use when browsing. Asterisks are treated as wildcards. For example,
- `"*.jpg"` will match any file ending in `.jpg`.
- @param defaultPath The directory in which to start browsing and/or the default filename to
- suggest. Defaults to `Sys.getCwd()`, with no default filename.
- @param title The title to give the dialog window.
- @return Whether `open()` is supported on this target.
- **/
- public function open(filter:String = null, defaultPath:String = null, title:String = null):Bool
- {
- #if (desktop && sys)
- var worker = new BackgroundWorker();
-
- worker.doWork.add(function(_)
- {
- #if linux
- if (title == null) title = "Open File";
- #end
-
- var path = null;
- #if (!macro && lime_cffi)
- #if hl
- var bytes = NativeCFFI.lime_file_dialog_open_file(title, filter, defaultPath);
- if (bytes != null) path = @:privateAccess String.fromUTF8(cast bytes);
- #else
- path = NativeCFFI.lime_file_dialog_open_file(title, filter, defaultPath);
- #end
- #end
-
- worker.sendComplete(path);
- });
-
- worker.onComplete.add(function(path:String)
- {
- if (path != null)
- {
- try
- {
- var data = File.getBytes(path);
- onOpen.dispatch(data);
- onOpenFile.dispatch(data, path); // Added by @NeeEoo
- return;
- }
- catch (e:Dynamic) {}
- }
-
- onCancel.dispatch();
- });
-
- worker.run();
-
- return true;
- #else
- onCancel.dispatch();
- return false;
- #end
- }
-
- /**
- Shows an open file dialog. If successful, `onSave` will trigger with the selected path.
-
- This function only works on desktop and HMTL5 targets, and will return `false` otherwise.
- @param data The file contents, in `haxe.io.Bytes` format. (Implicit casting possible.)
- @param filter A filter to use when browsing. Asterisks are treated as wildcards. For example,
- `"*.jpg"` will match any file ending in `.jpg`. Used only if targeting deskop.
- @param defaultPath The directory in which to start browsing and/or the default filename to
- suggest. When targeting destkop, this defaults to `Sys.getCwd()` with no default filename. When targeting
- HTML5, this defaults to the browser's download directory, with a default filename based on the MIME type.
- @param title The title to give the dialog window.
- @param type The default MIME type of the file, in case the type can't be determined from the
- file data. Used only if targeting HTML5.
- @return Whether `save()` is supported on this target.
- **/
- public function save(data:Resource, filter:String = null, defaultPath:String = null, title:String = null, type:String = "application/octet-stream"):Bool
- {
- if (data == null)
- {
- onCancel.dispatch();
- return false;
- }
-
- #if (desktop && sys)
- var worker = new BackgroundWorker();
-
- worker.doWork.add(function(_)
- {
- #if linux
- if (title == null) title = "Save File";
- #end
-
- var path = null;
- #if (!macro && lime_cffi)
- #if hl
- var bytes = NativeCFFI.lime_file_dialog_save_file(title, filter, defaultPath);
- path = @:privateAccess String.fromUTF8(cast bytes);
- #else
- path = NativeCFFI.lime_file_dialog_save_file(title, filter, defaultPath);
- #end
- #end
-
- worker.sendComplete(path);
- });
-
- worker.onComplete.add(function(path:String)
- {
- if (path != null)
- {
- try
- {
- File.saveBytes(path, data);
- onSave.dispatch(path);
- return;
- }
- catch (e:Dynamic) {}
- }
-
- onCancel.dispatch();
- });
-
- worker.run();
-
- return true;
- #elseif (js && html5)
- // TODO: Cleaner API for mimeType detection
-
- var defaultExtension = "";
-
- if (Image.__isPNG(data))
- {
- type = "image/png";
- defaultExtension = ".png";
- }
- else if (Image.__isJPG(data))
- {
- type = "image/jpeg";
- defaultExtension = ".jpg";
- }
- else if (Image.__isGIF(data))
- {
- type = "image/gif";
- defaultExtension = ".gif";
- }
- else if (Image.__isWebP(data))
- {
- type = "image/webp";
- defaultExtension = ".webp";
- }
-
- var path = defaultPath != null ? Path.withoutDirectory(defaultPath) : "download" + defaultExtension;
- var buffer = (data : Bytes).getData();
- buffer = buffer.slice(0, (data : Bytes).length);
-
- #if commonjs
- untyped #if haxe4 js.Syntax.code #else __js__ #end ("require ('file-saver')")(new Blob([buffer], {type: type}), path, true);
- #else
- untyped window.saveAs(new Blob([buffer], {type: type}), path, true);
- #end
- onSave.dispatch(path);
- return true;
- #else
- onCancel.dispatch();
- return false;
- #end
- }
-}
diff --git a/source/lime/utils/Log.hx b/source/lime/utils/Log.hx
index de13c0403f..91421d7ab1 100644
--- a/source/lime/utils/Log.hx
+++ b/source/lime/utils/Log.hx
@@ -19,7 +19,9 @@ class Log
if (level >= LogLevel.DEBUG)
{
#if js
- untyped #if haxe4 js.Syntax.code #else __js__ #end ("console").debug("[" + info.className + "] " + message);
+ untyped js.Syntax.code ("console").debug("[" + info.className + "] " + message);
+ #elseif !macro
+ FunkinLogs.trace('[${info.className}] $message', INFO, LIGHTGRAY);
#else
println("[" + info.className + "] " + Std.string(message));
#end
@@ -53,7 +55,7 @@ class Log
if (level >= LogLevel.INFO)
{
#if !macro
- FunkinLogs.trace('[${info.className}] $message', INFO, RED);
+ FunkinLogs.trace('[${info.className}] $message', INFO, CYAN);
#else
println("[" + info.className + "] " + Std.string(message));
#end
@@ -64,10 +66,8 @@ class Log
{
#if sys
Sys.print(Std.string(message));
- #elseif flash
- untyped __global__["trace"](Std.string(message));
#elseif js
- untyped #if haxe4 js.Syntax.code #else __js__ #end ("console").log(message);
+ untyped js.Syntax.code ("console").log(message);
#else
trace(message);
#end
@@ -77,10 +77,8 @@ class Log
{
#if sys
Sys.println(Std.string(message));
- #elseif flash
- untyped __global__["trace"](Std.string(message));
#elseif js
- untyped #if haxe4 js.Syntax.code #else __js__ #end ("console").log(message);
+ untyped js.Syntax.code ("console").log(message);
#else
trace(Std.string(message));
#end
@@ -135,14 +133,14 @@ class Log
#end
#if js
- if (untyped #if haxe4 js.Syntax.code #else __js__ #end ("typeof console") == "undefined")
+ if (untyped js.Syntax.code ("typeof console") == "undefined")
{
- untyped #if haxe4 js.Syntax.code #else __js__ #end ("console = {}");
+ untyped js.Syntax.code ("console = {}");
}
- if (untyped #if haxe4 js.Syntax.code #else __js__ #end ("console").log == null)
+ if (untyped js.Syntax.code ("console").log == null)
{
- untyped #if haxe4 js.Syntax.code #else __js__ #end ("console").log = function() {};
+ untyped js.Syntax.code ("console").log = function() {};
}
#end
}
-}
\ No newline at end of file
+}
diff --git a/source/lime/utils/ObjectPool.hx b/source/lime/utils/ObjectPool.hx
deleted file mode 100644
index f05715c35e..0000000000
--- a/source/lime/utils/ObjectPool.hx
+++ /dev/null
@@ -1,347 +0,0 @@
-package lime.utils;
-
-import haxe.ds.ObjectMap;
-
-
-/**
- A generic object pool for reusing objects.
- **/
-#if !lime_debug
-@:fileXml('tags="haxe,release"')
-@:noDebug
-#end
-#if !js @:generic #end class ObjectPool
-{
- /**
- The number of active objects in the pool.
- **/
- public var activeObjects(default, null):Int;
-
- /**
- The number of inactive objects in the pool.
- **/
- public var inactiveObjects(default, null):Int;
-
- /**
- The total size of the object pool (both active and inactive objects).
- **/
- public var size(get, set):Null;
-
- @:noCompletion private var __inactiveObject0:T;
- @:noCompletion private var __inactiveObject1:T;
- @:noCompletion private var __inactiveObjectList:List;
- @:noCompletion private var __pool:Map;
- @:noCompletion private var __size:Null;
-
- /**
- Creates a new ObjectPool instance.
-
- @param create A function that creates a new instance of type T.
- @param clean A function that cleans up an instance of type T before it is reused.
- @param size The maximum size of the object pool.
- **/
- public function new(create:Void->T = null, clean:T->Void = null, size:Null = null)
- {
- __pool = cast new ObjectMap();
-
- activeObjects = 0;
- inactiveObjects = 0;
-
- __inactiveObject0 = null;
- __inactiveObject1 = null;
- __inactiveObjectList = new List();
-
- if (create != null)
- {
- this.create = create;
- }
- if (clean != null)
- {
- this.clean = clean;
- }
- if (size != null)
- {
- this.size = size;
- }
- }
- /**
- Adds an object to the object pool.
-
- @param object The object to add to the pool.
- **/
- public function add(object:T):Void
- {
- if (object != null && !__pool.exists(object))
- {
- __pool.set(object, false);
- clean(object);
- __addInactive(object);
- }
- }
-
- /**
- Dynamic function.
-
- Cleans up an object before returning it to the pool.
-
- @param object The object to clean up.
- **/
- public dynamic function clean(object:T):Void {}
-
- /**
- Clears the object pool, removing all objects.
- **/
- public function clear():Void
- {
- __pool = cast new ObjectMap();
-
- activeObjects = 0;
- inactiveObjects = 0;
-
- __inactiveObject0 = null;
- __inactiveObject1 = null;
- __inactiveObjectList.clear();
- }
-
- /**
- Dynamic function.
-
- Creates a new Object.
- **/
- public dynamic function create():T
- {
- return null;
- }
-
- /**
- Creates a new object and adds it to the pool, or returns an existing inactive object from the pool.
-
- @return The object retrieved from the pool, or null if the pool is full and no new objects can be created.
- **/
- public function get():T
- {
- var object = null;
-
- if (inactiveObjects > 0)
- {
- object = __getInactive();
- }
- else if (__size == null || activeObjects < __size)
- {
- object = create();
-
- if (object != null)
- {
- __pool.set(object, true);
- activeObjects++;
- }
- }
-
- return object;
- }
-
- /**
- Releases an active object back into the pool.
-
- @param object The object to release.
- **/
- public function release(object:T):Void
- {
- #if lime_pool_debug
- if (object == null || !__pool.exists(object))
- {
- Log.error("Object is not a member of the pool");
- }
- else if (!__pool.get(object))
- {
- Log.error("Object has already been released");
- }
- #end
-
- activeObjects--;
-
- if (__size == null || activeObjects + inactiveObjects < __size)
- {
- clean(object);
- __addInactive(object);
- }
- else
- {
- __pool.remove(object);
- }
- }
-
- /**
- Removes an object from the pool.
-
- @param object The object to remove from the pool.
- **/
- public function remove(object:T):Void
- {
- if (object != null && __pool.exists(object))
- {
- __pool.remove(object);
-
- if (__inactiveObject0 == object)
- {
- __inactiveObject0 = null;
- inactiveObjects--;
- }
- else if (__inactiveObject1 == object)
- {
- __inactiveObject1 = null;
- inactiveObjects--;
- }
- else if (__inactiveObjectList.remove(object))
- {
- inactiveObjects--;
- }
- else
- {
- activeObjects--;
- }
- }
- }
-
- @:noCompletion private inline function __addInactive(object:T):Void
- {
- #if lime_pool_debug
- __pool.set(object, false);
- #end
-
- if (__inactiveObject0 == null)
- {
- __inactiveObject0 = object;
- }
- else if (__inactiveObject1 == null)
- {
- __inactiveObject1 = object;
- }
- else
- {
- __inactiveObjectList.add(object);
- }
-
- inactiveObjects++;
- }
-
- @:noCompletion private inline function __getInactive():T
- {
- var object = null;
-
- if (__inactiveObject0 != null)
- {
- object = __inactiveObject0;
- __inactiveObject0 = null;
- }
- else if (__inactiveObject1 != null)
- {
- object = __inactiveObject1;
- __inactiveObject1 = null;
- }
- else
- {
- object = __inactiveObjectList.pop();
-
- if (__inactiveObjectList.length > 0)
- {
- __inactiveObject0 = __inactiveObjectList.pop();
- }
-
- if (__inactiveObjectList.length > 0)
- {
- __inactiveObject1 = __inactiveObjectList.pop();
- }
- }
-
- #if lime_pool_debug
- __pool.set(object, true);
- #end
-
- inactiveObjects--;
- activeObjects++;
-
- return object;
- }
-
- @:noCompletion private function __removeInactive(count:Int):Void
- {
- if (count <= 0 || inactiveObjects == 0) return;
-
- if (__inactiveObject0 != null)
- {
- __pool.remove(__inactiveObject0);
- __inactiveObject0 = null;
- inactiveObjects--;
- count--;
- }
-
- if (count == 0 || inactiveObjects == 0) return;
-
- if (__inactiveObject1 != null)
- {
- __pool.remove(__inactiveObject1);
- __inactiveObject1 = null;
- inactiveObjects--;
- count--;
- }
-
- if (count == 0 || inactiveObjects == 0) return;
-
- for (object in __inactiveObjectList)
- {
- __pool.remove(object);
- __inactiveObjectList.remove(object);
- inactiveObjects--;
- count--;
-
- if (count == 0 || inactiveObjects == 0) return;
- }
- }
-
- // Get & Set Methods
- @:noCompletion private function get_size():Null
- {
- return __size;
- }
-
- @:noCompletion private function set_size(value:Null):Null
- {
- if (value == null)
- {
- __size = null;
- }
- else
- {
- var current = inactiveObjects + activeObjects;
- __size = value;
-
- if (current > value)
- {
- __removeInactive(current - value);
- }
- else if (value > current)
- {
- var object;
-
- for (i in 0...(value - current))
- {
- object = create();
-
- if (object != null)
- {
- __pool.set(object, false);
- __inactiveObjectList.add(object);
- inactiveObjects++;
- }
- else
- {
- break;
- }
- }
- }
- }
-
- return value;
- }
-}
diff --git a/source/openfl/display/DisplayObjectRenderer.hx b/source/openfl/display/DisplayObjectRenderer.hx
deleted file mode 100644
index 0267742463..0000000000
--- a/source/openfl/display/DisplayObjectRenderer.hx
+++ /dev/null
@@ -1,864 +0,0 @@
-package openfl.display;
-
-#if !flash
-import openfl.display._internal.Context3DGraphics;
-import openfl.display.Bitmap;
-import openfl.display.DisplayObject;
-import openfl.display.Tilemap;
-import openfl.events.EventDispatcher;
-import openfl.events.RenderEvent;
-import openfl.geom.ColorTransform;
-import openfl.geom.Matrix;
-import openfl.geom.Point;
-import openfl.geom.Rectangle;
-import openfl.text.TextField;
-#if lime
-import lime._internal.graphics.ImageCanvasUtil; // TODO
-import lime.graphics.cairo.Cairo;
-import lime.graphics.RenderContext;
-import lime.graphics.RenderContextType;
-#end
-
-#if !openfl_debug
-@:fileXml('tags="haxe,release"')
-@:noDebug
-#end
-@:access(openfl.display._internal.Context3DGraphics)
-@:access(lime.graphics.ImageBuffer)
-@:access(openfl.display.Bitmap)
-@:access(openfl.display.BitmapData)
-@:access(openfl.display.DisplayObject)
-@:access(openfl.display.Graphics)
-@:access(openfl.display.Tilemap)
-@:access(openfl.display3D.Context3D)
-@:access(openfl.events.RenderEvent)
-@:access(openfl.filters.BitmapFilter)
-@:access(openfl.geom.ColorTransform)
-@:access(openfl.geom.Rectangle)
-@:access(openfl.geom.Transform)
-@:access(openfl.text.TextField)
-@:allow(openfl.display._internal)
-@:allow(openfl.display)
-@:allow(openfl.text)
-class DisplayObjectRenderer extends EventDispatcher
-{
- @:noCompletion private var __allowSmoothing:Bool;
- @:noCompletion private var __blendMode:BlendMode;
- @:noCompletion private var __cleared:Bool;
- @SuppressWarnings("checkstyle:Dynamic") @:noCompletion private var __context:#if lime RenderContext #else Dynamic #end;
- @:noCompletion private var __overrideBlendMode:BlendMode;
- @:noCompletion private var __pixelRatio:Float;
- @:noCompletion private var __roundPixels:Bool;
- @:noCompletion private var __stage:Stage;
- @:noCompletion private var __tempColorTransform:ColorTransform;
- @:noCompletion private var __transparent:Bool;
- @SuppressWarnings("checkstyle:Dynamic") @:noCompletion private var __type:#if lime RenderContextType #else Dynamic #end;
- @:noCompletion private var __worldAlpha:Float;
- @:noCompletion private var __worldColorTransform:ColorTransform;
- @:noCompletion private var __worldTransform:Matrix;
-
- @:noCompletion private function new()
- {
- super();
-
- __allowSmoothing = true;
- __pixelRatio = 1;
- __tempColorTransform = new ColorTransform();
- __worldAlpha = 1;
- }
-
- @:noCompletion private function __clear():Void {}
-
- @:noCompletion private function __getAlpha(value:Float):Float
- {
- return value * __worldAlpha;
- }
-
- @:noCompletion private function __getColorTransform(value:ColorTransform):ColorTransform
- {
- if (__worldColorTransform != null)
- {
- __tempColorTransform.__copyFrom(__worldColorTransform);
- __tempColorTransform.__combine(value);
- return __tempColorTransform;
- }
- else
- {
- return value;
- }
- }
-
- @:noCompletion private function __popMask():Void {}
-
- @:noCompletion private function __popMaskObject(object:DisplayObject, handleScrollRect:Bool = true):Void {}
-
- @:noCompletion private function __popMaskRect():Void {}
-
- @:noCompletion private function __pushMask(mask:DisplayObject):Void {}
-
- @:noCompletion private function __pushMaskObject(object:DisplayObject, handleScrollRect:Bool = true):Void {}
-
- @:noCompletion private function __pushMaskRect(rect:Rectangle, transform:Matrix):Void {}
-
- @:noCompletion private function __render(object:IBitmapDrawable):Void {}
-
- @:noCompletion private function __renderEvent(displayObject:DisplayObject):Void
- {
- var renderer = this;
- #if lime
- if (displayObject.__customRenderEvent != null && displayObject.__renderable)
- {
- displayObject.__customRenderEvent.allowSmoothing = renderer.__allowSmoothing;
- displayObject.__customRenderEvent.objectMatrix.copyFrom(displayObject.__renderTransform);
- displayObject.__customRenderEvent.objectColorTransform.__copyFrom(displayObject.__worldColorTransform);
- displayObject.__customRenderEvent.renderer = renderer;
-
- switch (renderer.__type)
- {
- case OPENGL:
- if (!renderer.__cleared) renderer.__clear();
-
- var renderer:OpenGLRenderer = cast renderer;
- renderer.setShader(displayObject.__worldShader);
- renderer.__context3D.__flushGL();
-
- displayObject.__customRenderEvent.type = RenderEvent.RENDER_OPENGL;
-
- case CAIRO:
- displayObject.__customRenderEvent.type = RenderEvent.RENDER_CAIRO;
-
- case DOM:
- if (displayObject.stage != null && displayObject.__worldVisible)
- {
- displayObject.__customRenderEvent.type = RenderEvent.RENDER_DOM;
- }
- else
- {
- displayObject.__customRenderEvent.type = RenderEvent.CLEAR_DOM;
- }
-
- case CANVAS:
- displayObject.__customRenderEvent.type = RenderEvent.RENDER_CANVAS;
-
- default:
- return;
- }
-
- renderer.__setBlendMode(displayObject.__worldBlendMode);
- renderer.__pushMaskObject(displayObject);
-
- displayObject.dispatchEvent(displayObject.__customRenderEvent);
-
- renderer.__popMaskObject(displayObject);
-
- if (renderer.__type == OPENGL)
- {
- var renderer:OpenGLRenderer = cast renderer;
- renderer.setViewport();
- }
- }
- #end
- }
-
- @:noCompletion private function __resize(width:Int, height:Int):Void {}
-
- @:noCompletion private function __setBlendMode(value:BlendMode):Void {}
-
- @:noCompletion private function __shouldCacheHardware(displayObject:DisplayObject, value:Null):Null
- {
- if (displayObject == null) return null;
-
- switch (displayObject.__drawableType)
- {
- case SPRITE, STAGE:
- if (value == true) return true;
- value = __shouldCacheHardware_DisplayObject(displayObject, value);
- if (value == true) return true;
-
- if (displayObject.__children != null)
- {
- for (child in displayObject.__children)
- {
- value = __shouldCacheHardware_DisplayObject(child, value);
- if (value == true) return true;
- }
- }
-
- return value;
-
- case TEXT_FIELD:
- return value == true ? true : false;
-
- case TILEMAP:
- return true;
-
- default:
- return __shouldCacheHardware_DisplayObject(displayObject, value);
- }
- }
-
- @:noCompletion private function __shouldCacheHardware_DisplayObject(displayObject:DisplayObject, value:Null):Null