Compare commits
No commits in common. "master" and "1.7.1" have entirely different histories.
|
|
@ -10,8 +10,8 @@ orbs:
|
|||
jobs:
|
||||
build:
|
||||
macos:
|
||||
xcode: 26.2.0 # Specify the Xcode version to use
|
||||
resource_class: m4pro.medium
|
||||
xcode: 14.1.0 # Specify the Xcode version to use
|
||||
resource_class: macos.m1.medium.gen1
|
||||
environment:
|
||||
HOMEBREW_NO_AUTO_UPDATE: 1
|
||||
NPROC: 4
|
||||
|
|
@ -61,10 +61,10 @@ jobs:
|
|||
command: |
|
||||
mkdir -p ~/deps
|
||||
cd ~/deps
|
||||
curl https://www.zlib.net/zlib-1.3.2.tar.xz | tar xz
|
||||
cd zlib-1.3.2
|
||||
if [ ! -f /usr/local/lib/libz.1.3.2.dylib ]; then
|
||||
cmake -S. -Bbuild -DCMAKE_BUILD_TYPE=Release -DCMAKE_OSX_ARCHITECTURES="x86_64;arm64" -DCMAKE_OSX_DEPLOYMENT_TARGET="10.15" -DCMAKE_MACOSX_RPATH=TRUE -DCMAKE_POLICY_VERSION_MINIMUM=3.5
|
||||
curl https://www.zlib.net/zlib-1.3.1.tar.xz | tar xz
|
||||
cd zlib-1.3.1
|
||||
if [ ! -f /usr/local/lib/libz.1.3.1.dylib ]; then
|
||||
cmake -S. -Bbuild -DCMAKE_BUILD_TYPE=Release -DCMAKE_OSX_ARCHITECTURES="x86_64;arm64" -DCMAKE_OSX_DEPLOYMENT_TARGET="10.15" -DCMAKE_MACOSX_RPATH=TRUE
|
||||
cmake --build build --config Release -j$NPROC
|
||||
sudo cmake --install build
|
||||
fi
|
||||
|
|
@ -77,7 +77,7 @@ jobs:
|
|||
curl https://openal-soft.org/openal-releases/openal-soft-1.22.2.tar.bz2 | tar xz
|
||||
cd openal-soft-1.22.2
|
||||
if [ ! -f /usr/local/lib/libopenal.1.22.2.dylib ]; then
|
||||
cmake -S. -Bbuild -DCMAKE_BUILD_TYPE=Release -DCMAKE_OSX_ARCHITECTURES="x86_64;arm64" -DCMAKE_OSX_DEPLOYMENT_TARGET="10.15" -DALSOFT_BACKEND_SNDIO=NO -DALSOFT_BACKEND_PORTAUDIO=NO -DALSOFT_BACKEND_WAVE=NO -DALSOFT_UTILS=NO -DALSOFT_EXAMPLES=NO -DCMAKE_MACOSX_RPATH=TRUE -DCMAKE_POLICY_VERSION_MINIMUM=3.5
|
||||
cmake -S. -Bbuild -DCMAKE_BUILD_TYPE=Release -DCMAKE_OSX_ARCHITECTURES="x86_64;arm64" -DCMAKE_OSX_DEPLOYMENT_TARGET="10.15" -DALSOFT_BACKEND_SNDIO=NO -DALSOFT_BACKEND_PORTAUDIO=NO -DALSOFT_BACKEND_WAVE=NO -DALSOFT_UTILS=NO -DALSOFT_EXAMPLES=NO -DCMAKE_MACOSX_RPATH=TRUE
|
||||
cmake --build build --config Release -j$NPROC
|
||||
sudo cmake --install build
|
||||
fi
|
||||
|
|
@ -89,7 +89,7 @@ jobs:
|
|||
curl -L https://downloads.sourceforge.net/project/libjpeg-turbo/2.1.4/libjpeg-turbo-2.1.4.tar.gz | tar xz
|
||||
cd libjpeg-turbo-2.1.4
|
||||
if [ ! -f /usr/local/lib/libturbojpeg.0.2.0.dylib ]; then
|
||||
cmake -S. -Bbuild-x86 -DCMAKE_BUILD_TYPE=Release -DCMAKE_OSX_ARCHITECTURES="x86_64" -DCMAKE_OSX_DEPLOYMENT_TARGET="10.15" -DCMAKE_MACOSX_RPATH=TRUE -DCMAKE_INSTALL_PREFIX=/usr/local -DCMAKE_POLICY_VERSION_MINIMUM=3.5
|
||||
cmake -S. -Bbuild-x86 -DCMAKE_BUILD_TYPE=Release -DCMAKE_OSX_ARCHITECTURES="x86_64" -DCMAKE_OSX_DEPLOYMENT_TARGET="10.15" -DCMAKE_MACOSX_RPATH=TRUE -DCMAKE_INSTALL_PREFIX=/usr/local
|
||||
cmake --build build-x86 --config release -j$NPROC
|
||||
fi
|
||||
|
||||
|
|
@ -98,7 +98,7 @@ jobs:
|
|||
command: |
|
||||
cd ~/deps/libjpeg-turbo-2.1.4
|
||||
if [ ! -f /usr/local/lib/libturbojpeg.0.2.0.dylib ]; then
|
||||
cmake -S. -Bbuild-arm64 -DCMAKE_BUILD_TYPE=Release -DCMAKE_OSX_ARCHITECTURES="arm64" -DCMAKE_OSX_DEPLOYMENT_TARGET="10.15" -DCMAKE_MACOSX_RPATH=TRUE -DCMAKE_INSTALL_PREFIX=/usr/local -DCMAKE_POLICY_VERSION_MINIMUM=3.5
|
||||
cmake -S. -Bbuild-arm64 -DCMAKE_BUILD_TYPE=Release -DCMAKE_OSX_ARCHITECTURES="arm64" -DCMAKE_OSX_DEPLOYMENT_TARGET="10.15" -DCMAKE_MACOSX_RPATH=TRUE -DCMAKE_INSTALL_PREFIX=/usr/local
|
||||
cmake --build build-arm64 --config release -j$NPROC
|
||||
fi
|
||||
|
||||
|
|
@ -122,7 +122,7 @@ jobs:
|
|||
curl -L https://downloads.xiph.org/releases/ogg/libogg-1.3.5.tar.xz | tar xz
|
||||
cd libogg-1.3.5
|
||||
if [ ! -f /usr/local/lib/libogg.0.8.5.dylib ]; then
|
||||
cmake -S. -Bbuild -DCMAKE_BUILD_TYPE=Release -DCMAKE_OSX_ARCHITECTURES="x86_64;arm64" -DCMAKE_OSX_DEPLOYMENT_TARGET="10.15" -DBUILD_SHARED_LIBS=ON -DCMAKE_MACOSX_RPATH=TRUE -DCMAKE_POLICY_VERSION_MINIMUM=3.5
|
||||
cmake -S. -Bbuild -DCMAKE_BUILD_TYPE=Release -DCMAKE_OSX_ARCHITECTURES="x86_64;arm64" -DCMAKE_OSX_DEPLOYMENT_TARGET="10.15" -DBUILD_SHARED_LIBS=ON -DCMAKE_MACOSX_RPATH=TRUE
|
||||
cmake --build build --config Release -j$NPROC
|
||||
sudo cmake --install build
|
||||
fi
|
||||
|
|
@ -135,7 +135,7 @@ jobs:
|
|||
curl -L https://downloads.xiph.org/releases/vorbis/libvorbis-1.3.7.tar.xz | tar xz
|
||||
cd libvorbis-1.3.7
|
||||
if [ ! -f /usr/local/lib/libvorbis.0.4.9.dylib ]; then
|
||||
cmake -S. -Bbuild -DCMAKE_BUILD_TYPE=Release -DCMAKE_OSX_ARCHITECTURES="x86_64;arm64" -DCMAKE_OSX_DEPLOYMENT_TARGET="10.15" -DBUILD_SHARED_LIBS=ON -DCMAKE_MACOSX_RPATH=TRUE -DCMAKE_POLICY_VERSION_MINIMUM=3.5
|
||||
cmake -S. -Bbuild -DCMAKE_BUILD_TYPE=Release -DCMAKE_OSX_ARCHITECTURES="x86_64;arm64" -DCMAKE_OSX_DEPLOYMENT_TARGET="10.15" -DBUILD_SHARED_LIBS=ON -DCMAKE_MACOSX_RPATH=TRUE
|
||||
cmake --build build --config Release -j$NPROC
|
||||
sudo cmake --install build
|
||||
fi
|
||||
|
|
@ -145,10 +145,10 @@ jobs:
|
|||
command: |
|
||||
mkdir -p ~/deps
|
||||
cd ~/deps
|
||||
curl -L https://download.sourceforge.net/libpng/libpng-1.6.56.tar.xz | tar xz
|
||||
cd libpng-1.6.56
|
||||
curl -L https://download.sourceforge.net/libpng/libpng-1.6.39.tar.xz | tar xz
|
||||
cd libpng-1.6.39
|
||||
if [ ! -f /usr/local/lib/libpng16.16.dylib ]; then
|
||||
cmake -S. -Bbuild-x86 -DCMAKE_BUILD_TYPE=Release -DCMAKE_OSX_ARCHITECTURES="x86_64" -DCMAKE_OSX_DEPLOYMENT_TARGET="10.15" -DCMAKE_MACOSX_RPATH=TRUE -DCMAKE_POLICY_VERSION_MINIMUM=3.5
|
||||
cmake -S. -Bbuild-x86 -DCMAKE_BUILD_TYPE=Release -DCMAKE_OSX_ARCHITECTURES="x86_64" -DCMAKE_OSX_DEPLOYMENT_TARGET="10.15" -DCMAKE_MACOSX_RPATH=TRUE
|
||||
cmake --build build-x86 --config release -j$NPROC
|
||||
fi
|
||||
|
||||
|
|
@ -157,9 +157,9 @@ jobs:
|
|||
command: |
|
||||
mkdir -p ~/deps
|
||||
cd ~/deps
|
||||
cd libpng-1.6.56
|
||||
cd libpng-1.6.39
|
||||
if [ ! -f /usr/local/lib/libpng16.16.dylib ]; then
|
||||
cmake -S. -Bbuild-arm64 -DCMAKE_BUILD_TYPE=Release -DCMAKE_OSX_ARCHITECTURES="arm64" -DCMAKE_OSX_DEPLOYMENT_TARGET="10.15" -DCMAKE_MACOSX_RPATH=TRUE -DCMAKE_POLICY_VERSION_MINIMUM=3.5
|
||||
cmake -S. -Bbuild-arm64 -DCMAKE_BUILD_TYPE=Release -DCMAKE_OSX_ARCHITECTURES="arm64" -DCMAKE_OSX_DEPLOYMENT_TARGET="10.15" -DCMAKE_MACOSX_RPATH=TRUE
|
||||
cmake --build build-arm64 --config release -j$NPROC
|
||||
fi
|
||||
|
||||
|
|
@ -167,8 +167,8 @@ jobs:
|
|||
name: Install libpng (Universal)
|
||||
command: |
|
||||
if [ ! -f /usr/local/lib/libpng16.16.dylib ]; then
|
||||
cd ~/deps/libpng-1.6.56/build-arm64
|
||||
for i in libpng16.16.56.0.dylib libpng16.a png-fix-itxt pngfix pngimage pngstest pngtest pngunknown pngvalid
|
||||
cd ~/deps/libpng-1.6.39/build-arm64
|
||||
for i in libpng16.16.39.0.dylib libpng16.a png-fix-itxt pngfix pngimage pngstest pngtest pngunknown pngvalid
|
||||
do
|
||||
lipo -create -output $i ../build-x86/$i $i
|
||||
done
|
||||
|
|
@ -183,7 +183,7 @@ jobs:
|
|||
curl -L https://github.com/libsdl-org/SDL/releases/download/release-2.26.1/SDL2-2.26.1.tar.gz | tar xz
|
||||
cd SDL2-2.26.1
|
||||
if [ ! -f /usr/local/lib/libSDL2-2.0.0.dylib ]; then
|
||||
cmake -S. -Bbuild -DCMAKE_BUILD_TYPE=Release -DCMAKE_OSX_ARCHITECTURES="x86_64;arm64" -DCMAKE_OSX_DEPLOYMENT_TARGET="10.15" -DBUILD_SHARED_LIBS=ON -DCMAKE_MACOSX_RPATH=TRUE -DCMAKE_POLICY_VERSION_MINIMUM=3.5
|
||||
cmake -S. -Bbuild -DCMAKE_BUILD_TYPE=Release -DCMAKE_OSX_ARCHITECTURES="x86_64;arm64" -DCMAKE_OSX_DEPLOYMENT_TARGET="10.15" -DBUILD_SHARED_LIBS=ON -DCMAKE_MACOSX_RPATH=TRUE
|
||||
cmake --build build --config Release -j$NPROC
|
||||
sudo cmake --install build
|
||||
fi
|
||||
|
|
@ -194,7 +194,7 @@ jobs:
|
|||
cd ~/deps
|
||||
curl -fsSL https://github.com/libuv/libuv/archive/refs/tags/v1.44.2.tar.gz | tar xz
|
||||
cd libuv-1.44.2
|
||||
cmake -S. -Bbuild -DCMAKE_BUILD_TYPE=Release -DCMAKE_OSX_ARCHITECTURES="x86_64;arm64" -DCMAKE_OSX_DEPLOYMENT_TARGET="10.15" -DCMAKE_MACOSX_RPATH=TRUE -DCMAKE_POLICY_VERSION_MINIMUM=3.5
|
||||
cmake -S. -Bbuild -DCMAKE_BUILD_TYPE=Release -DCMAKE_OSX_ARCHITECTURES="x86_64;arm64" -DCMAKE_OSX_DEPLOYMENT_TARGET="10.15" -DCMAKE_MACOSX_RPATH=TRUE
|
||||
cmake --build build --config Release -j$NPROC
|
||||
sudo cmake --install build
|
||||
|
||||
|
|
@ -213,7 +213,7 @@ jobs:
|
|||
curl -L https://raw.githubusercontent.com/RandomityGuy/hashlink/master/libs/ssl/CMakeLists.txt > libs/ssl/CMakeLists.txt
|
||||
# Fix OpenAL
|
||||
# curl -L https://github.com/nullobsi/hashlink/commit/a09491918cc4b83c2cb9fcded855fe967857385f.diff | git apply
|
||||
cmake -S. -Bbuild -DCMAKE_BUILD_TYPE=Release -DCMAKE_OSX_ARCHITECTURES="x86_64;arm64" -DCMAKE_OSX_DEPLOYMENT_TARGET="10.15" -DCMAKE_FIND_FRAMEWORK=LAST -DWITH_SQLITE=OFF -DBUILD_TESTING=OFF -DCMAKE_MACOSX_RPATH=TRUE -DHASHLINK_INCLUDE_DIR="~/deps/hashlink/src" -DHASHLINK_LIBRARY_DIR="/usr/local/lib/" -DCMAKE_POLICY_VERSION_MINIMUM=3.5
|
||||
cmake -S. -Bbuild -DCMAKE_BUILD_TYPE=Release -DCMAKE_OSX_ARCHITECTURES="x86_64;arm64" -DCMAKE_OSX_DEPLOYMENT_TARGET="10.15" -DCMAKE_FIND_FRAMEWORK=LAST -DWITH_SQLITE=OFF -DBUILD_TESTING=OFF -DCMAKE_MACOSX_RPATH=TRUE -DHASHLINK_INCLUDE_DIR="~/deps/hashlink/src" -DHASHLINK_LIBRARY_DIR="/usr/local/lib/"
|
||||
cmake --build build --config Release -j$NPROC
|
||||
sudo cmake --install build
|
||||
|
||||
|
|
@ -246,7 +246,7 @@ jobs:
|
|||
- /usr/local/lib/libvorbis.0.4.9.dylib
|
||||
- /usr/local/lib/libvorbisfile.3.3.8.dylib
|
||||
- /usr/local/lib/libvorbisenc.2.0.12.dylib
|
||||
- /usr/local/lib/libz.1.3.2.dylib
|
||||
- /usr/local/lib/libz.1.3.1.dylib
|
||||
- /usr/local/lib/datachannel.hdll
|
||||
|
||||
|
||||
|
|
@ -280,7 +280,7 @@ jobs:
|
|||
cp /usr/local/lib/libvorbis.0.4.9.dylib libvorbis.0.4.9.dylib
|
||||
cp /usr/local/lib/libvorbisfile.3.3.8.dylib libvorbisfile.3.3.8.dylib
|
||||
cp /usr/local/lib/libvorbisenc.2.0.12.dylib libvorbisenc.2.0.12.dylib
|
||||
cp /usr/local/lib/libz.1.3.2.dylib libz.1.dylib
|
||||
cp /usr/local/lib/libz.1.3.1.dylib libz.1.dylib
|
||||
cp /usr/local/lib/libuv.1.dylib libuv.1.dylib
|
||||
# These libraries have dangling RPATHs
|
||||
install_name_tool -delete_rpath /usr/local/lib libturbojpeg.0.dylib
|
||||
|
|
@ -387,7 +387,7 @@ jobs:
|
|||
cd ~/deps
|
||||
git clone https://github.com/RandomityGuy/hxDatachannel
|
||||
cd hxDatachannel/cpp
|
||||
"/c/Program Files/CMake/bin/cmake" -S. -Bbuild -DCMAKE_BUILD_TYPE=Release -DHASHLINK_LIBRARY_DIR="~/deps/hashlink/x64/Release" -DHASHLINK_INCLUDE_DIR="../../hashlink/src" -DCMAKE_POLICY_VERSION_MINIMUM=3.5
|
||||
"/c/Program Files/CMake/bin/cmake" -S. -Bbuild -DCMAKE_BUILD_TYPE=Release -DHASHLINK_LIBRARY_DIR="~/deps/hashlink/x64/Release" -DHASHLINK_INCLUDE_DIR="../../hashlink/src"
|
||||
"/c/Program Files/CMake/bin/cmake" --build build --config Release -j4
|
||||
mv ~/deps/hxDatachannel/cpp/build/Release/datachannel.hdll ~/deps/hashlink/x64/Release
|
||||
mv ~/deps/hxDatachannel/cpp/build/Release/datachannel.lib ~/deps/hashlink/x64/Release
|
||||
|
|
@ -419,7 +419,7 @@ jobs:
|
|||
haxe compile-c.hxml
|
||||
cd native
|
||||
HASHLINKPATH=~/deps/hashlink
|
||||
MSBuild.exe -m -nologo -p:Configuration=Release -p:Platform=x64 -p:PlatformToolset=v142 -p:MultiProcessorCompilation=true -p:HASHLINK=$HASHLINKPATH marblegame.sln
|
||||
MSBuild.exe -m -nologo -p:Configuration=Release -p:Platform=x64 -p:PlatformToolset=v142 -p:HASHLINK=$HASHLINKPATH marblegame.sln
|
||||
- run:
|
||||
name: Package app bundle
|
||||
command: |
|
||||
|
|
@ -462,14 +462,10 @@ workflows:
|
|||
filters:
|
||||
tags:
|
||||
only: /^\d+.\d+.\d+$/
|
||||
branches:
|
||||
ignore: /.*/
|
||||
|
||||
build-windows:
|
||||
jobs:
|
||||
- build-win:
|
||||
filters:
|
||||
tags:
|
||||
only: /^\d+.\d+.\d+$/
|
||||
branches:
|
||||
ignore: /.*/
|
||||
only: /^\d+.\d+.\d+$/
|
||||
29
CHANGELOG.md
|
|
@ -1,32 +1,3 @@
|
|||
# 1.7.3
|
||||
Hotfix time!
|
||||
- Fixed chat messages being escaped a bit too much.
|
||||
|
||||
# 1.7.2
|
||||
This update brings the following bugfixes:
|
||||
- Added Import and Export Progress to Options menu to transfer game progress between devices.
|
||||
- Added momentum based scrolling on touch devices for menus.
|
||||
- Added TURN server support for multiplayer. Players behind strict NATs should now be able to play multiplayer without issues.
|
||||
- Made the game files to be case insensitive to allow running the game on case sensitive filesystems without issues.
|
||||
- Escaped all user input to prevent HTML injection in the UI.
|
||||
- Fixed various race condition issues.
|
||||
- Improved camera sensitivity on touch devices.
|
||||
- Implemented camera centering for touch controls when free look is disabled.
|
||||
- Various performance improvements and crash fixes.
|
||||
- Implemented console cheat commands. DefaultMarble.attribute = value; to change marble attributes.
|
||||
- Fixed a bug with the timer when playing a replay.
|
||||
- Fixed a crash that could happen in multiplayer.
|
||||
- Fixed not being able to load textures in certain custom levels.
|
||||
- Fixed softlock when playing a user installed custom level.
|
||||
- Fixed gravity changes not rewinding properly.
|
||||
- Fixed skies not rendering correctly at times in the web version.
|
||||
|
||||
# 1.7.1
|
||||
This update brings the following bugfixes:
|
||||
- Fixed a crash when the marble goes out of bounds.
|
||||
- Fixed the FPS limiter not limiting rendered frames per second.
|
||||
- Fixed scores not being sent in certain cases.
|
||||
|
||||
# 1.7.0
|
||||
It's the fabled Leaderboards update!
|
||||
Leaderboards have been implemented for all the levels with automatic replay uploading for official levels as well as watching top replays. Additionally, segregation has been made to allow switching between rewind and non-rewind scores on the leaderboards.
|
||||
|
|
|
|||
29
README.md
|
|
@ -2,8 +2,7 @@
|
|||
A Haxe port of Marble Blast Gold, Ultra and Platinum, name subject to change.
|
||||
The marble physics code was taken from [OpenMBU](https://github.com/MBU-Team/OpenMBU) along with my own collision detection code, game logic was partially from scratch and taken with permission from [Marble Blast Web Port](https://github.com/Vanilagy/MarbleBlast).
|
||||
|
||||
[](https://ko-fi.com/H2H5FRTTL)
|
||||
Support Discord: https://discord.gg/GsmTVQQAhG
|
||||
[](https://ko-fi.com/H2H5FRTTL)
|
||||
# Play
|
||||
## Web Browser
|
||||
The browser port supports touch controls, meaning it can be played on mobile devices.
|
||||
|
|
@ -11,21 +10,16 @@ The browser port supports touch controls, meaning it can be played on mobile dev
|
|||
### Marble Blast Platinum: [Play](https://marbleblast.randomityguy.me/)
|
||||
### Marble Blast Ultra: [Play](https://marbleblastultra.randomityguy.me/)
|
||||
## Windows and Mac
|
||||
### Marble Blast Gold: [Download](https://github.com/RandomityGuy/MBHaxe/releases/tag/1.1.13)
|
||||
### Marble Blast Platinum: [Download](https://github.com/RandomityGuy/MBHaxe/releases/tag/1.7.3)
|
||||
### Marble Blast Ultra: [Download](https://github.com/RandomityGuy/MBHaxe/releases/tag/1.2.5-mbu)
|
||||
### Marble Blast Gold: [Download](https://github.com/RandomityGuy/MBHaxe/releases/tag/1.1.12)
|
||||
### Marble Blast Platinum: [Download](https://github.com/RandomityGuy/MBHaxe/releases/tag/1.7.0)
|
||||
### Marble Blast Ultra: [Download](https://github.com/RandomityGuy/MBHaxe/releases/tag/1.2.0-mbu)
|
||||
## Mac Instructions - Important
|
||||
Put the .app file in either /Applications or ~/Applications in order to run it properly.
|
||||
You will also have to bypass Gatekeeper since the .app is not signed.
|
||||
## Android
|
||||
### Marble Blast Gold: [Download](https://github.com/RandomityGuy/MBHaxe/releases/download/1.1.13/MBHaxe-Gold.apk)
|
||||
### Marble Blast Platinum: [Download](https://github.com/RandomityGuy/MBHaxe/releases/download/1.7.3/MBHaxe-Platinum.apk)
|
||||
### Marble Blast Ultra: [Download](https://github.com/RandomityGuy/MBHaxe/releases/download/1.2.5-mbu/MBHaxe-Ultra.apk)
|
||||
|
||||
## Xbox (NEW!)
|
||||
### Marble Blast Ultra: [Download](https://github.com/RandomityGuy/MBHaxe/releases/download/1.2.5-mbu/MBHaxe-Ultra-UWP-Xbox.msix)
|
||||
Ported to Xbox via UWP by [Daniel Worley](https://github.com/worleydl).
|
||||
You will need to enable Developer Mode on your Xbox in order to sideload the app. The walkthrough can be found at https://www.youtube.com/watch?v=2Ly9TIdu9uw.
|
||||
### Marble Blast Gold: [Download](https://github.com/RandomityGuy/MBHaxe/releases/download/1.1.12/MBHaxe-Gold.apk)
|
||||
### Marble Blast Platinum: [Download](https://github.com/RandomityGuy/MBHaxe/releases/download/1.7.0/MBHaxe-Platinum.apk)
|
||||
### Marble Blast Ultra: [Download](https://github.com/RandomityGuy/MBHaxe/releases/download/1.2.0-mbu/MBHaxe-Ultra.apk)
|
||||
|
||||
## Additional Features
|
||||
- Cross Platform Multiplayer: Available in Ultra and Platinum. You can host and join multiplayer matches in any of these platforms: Windows, Mac, Web, Android.
|
||||
|
|
@ -66,7 +60,6 @@ Requires Haxe 4.3.0 or above
|
|||
You require the following Haxe libraries:
|
||||
- heaps: The specific version located [here](https://github.com/RandomityGuy/heaps)
|
||||
- hlsdl (Obtain the haxelib version of hlsdl, then patch it with these files [here](https://github.com/RandomityGuy/hashlink/tree/master/libs/sdl)) (Hashlink/C native target)
|
||||
- datachannel: obtained from [here](https://github.com/RandomityGuy/hxDatachannel)
|
||||
- stb_ogg_sound (JS/Browser target)
|
||||
- zip 1.1.0 (JS/Browser target)
|
||||
|
||||
|
|
@ -95,6 +88,9 @@ This will build the apk file at Export/android/app/build/outputs/apk/release/app
|
|||
If you are on browser, please send the browser console log to me
|
||||
If you are on native, please run marbleblast-debug.bat and reproduce the crash, send the resulting stacktrace that occurs during the crash to me.
|
||||
|
||||
## Help it shows a black screen when playing a level!
|
||||
Your PC does not support the game, please upgrade it, there is nothing I can do about it to fix it.
|
||||
|
||||
## How accurate are the marble physics?
|
||||
Very accurate with up to 1% deviation from the original physics. The deviations are due to traplaunches being slightly different and occassional internal edge collisions, and the lower delta t values for physics simulations.
|
||||
|
||||
|
|
@ -103,11 +99,12 @@ In browser, you can just resize your window. You can use the browser zoom featur
|
|||
In native version, you can just resize the window if windowed or use the resolution options in the menu or just directly modify settings.json
|
||||
|
||||
## How do I change my FOV?
|
||||
There is an FOV slider in the options menu.
|
||||
Edit settings.json for native version, edit the MBHaxeSettings key in LocalStorage in browser.
|
||||
In the platinum version, there is an FOV slider.
|
||||
|
||||
## How do I unlock/lock FPS?
|
||||
You cannot unlock fps in the browser, it is forever set to vsync.
|
||||
In the native version, use the options menu to unlock/lock fps.
|
||||
In the native version, edit settings.json or the options menu in the platinum.
|
||||
|
||||
## Hey can you please add this new feature?
|
||||
If this new feature of yours already exists in MBG but not in this port, then I will try to add it, if I get time to do so, otherwise chances are, I won't add it since I have other things to do and would rather not waste my time on this any further. You are free to do pull requests if you have already implemented said feature.
|
||||
|
|
|
|||
|
Before Width: | Height: | Size: 21 KiB |
|
Before Width: | Height: | Size: 34 KiB |
|
Before Width: | Height: | Size: 11 KiB |
|
Before Width: | Height: | Size: 6.4 KiB |
|
Before Width: | Height: | Size: 6.3 KiB |
|
Before Width: | Height: | Size: 5.6 KiB |
|
Before Width: | Height: | Size: 6.3 KiB |
|
|
@ -82,12 +82,6 @@ class CameraController extends Object {
|
|||
|
||||
var _ignoreCursor:Bool = false;
|
||||
|
||||
var hasXInput:Bool = false;
|
||||
var hasYInput:Bool = false;
|
||||
var dt:Float;
|
||||
|
||||
var wasLastGamepadInput:Bool = false;
|
||||
|
||||
public function new(marble:Marble) {
|
||||
super();
|
||||
this.marble = marble;
|
||||
|
|
@ -172,37 +166,19 @@ class CameraController extends Object {
|
|||
deltaposY *= len / max;
|
||||
}
|
||||
}
|
||||
if (!Settings.controlsSettings.alwaysFreeLook && !Key.isDown(Settings.controlsSettings.freelook)) {
|
||||
deltaposY = 0;
|
||||
}
|
||||
|
||||
var factor = isTouch ? Util.lerp(1 / 25, 1 / 15,
|
||||
Settings.controlsSettings.cameraSensitivity) : Util.lerp(1 / 1000, 1 / 200, Settings.controlsSettings.cameraSensitivity);
|
||||
|
||||
if (!Settings.controlsSettings.alwaysFreeLook && !Key.isDown(Settings.controlsSettings.freelook) && !isTouch) {
|
||||
deltaposY = 0;
|
||||
}
|
||||
// CameraPitch += deltaposY * factor;
|
||||
// CameraYaw += deltaposX * factor;
|
||||
|
||||
nextCameraPitch += deltaposY * factor;
|
||||
nextCameraYaw += deltaposX * factor;
|
||||
|
||||
if (Math.abs(deltaposX) > 0.001)
|
||||
hasXInput = true;
|
||||
else
|
||||
hasXInput = false;
|
||||
if (Math.abs(deltaposY) > 0.001)
|
||||
hasYInput = true;
|
||||
else
|
||||
hasYInput = false;
|
||||
|
||||
if (MarbleGame.instance.touchInput.cameraInput.pressed) {
|
||||
hasXInput = true;
|
||||
hasYInput = true;
|
||||
}
|
||||
|
||||
if (!isTouch)
|
||||
wasLastGamepadInput = false;
|
||||
else
|
||||
wasLastGamepadInput = true;
|
||||
|
||||
// var rotX = deltaposX * 0.001 * Settings.controlsSettings.cameraSensitivity * Math.PI * 2;
|
||||
// var rotY = deltaposY * 0.001 * Settings.controlsSettings.cameraSensitivity * Math.PI * 2;
|
||||
// CameraYaw -= rotX;
|
||||
|
|
@ -255,14 +231,6 @@ class CameraController extends Object {
|
|||
overview = false;
|
||||
}
|
||||
|
||||
function computePitchSpeedFromDelta(delta:Float) {
|
||||
return Util.clamp(delta, Math.PI / 10, Math.PI / 2) * 4;
|
||||
}
|
||||
|
||||
function applyNonlinearScale(value:Float) {
|
||||
return Math.pow(Math.abs(value), 1.6) * (value >= 0 ? 1 : -1);
|
||||
}
|
||||
|
||||
function doOverviewCamera(currentTime:Float, dt:Float) {
|
||||
var angle = Util.adjustedMod(2 * currentTime * Math.PI / 100.0, 2 * Math.PI);
|
||||
var distance = overviewWidth.multiply(2.0 / 3.0);
|
||||
|
|
@ -288,65 +256,19 @@ class CameraController extends Object {
|
|||
function doSpectateCamera(currentTime:Float, dt:Float) {
|
||||
var camera = level.scene.camera;
|
||||
|
||||
var lerpt = 1 - Math.pow(0.5, dt / 0.016); // Math.min(1, 1 - Math.pow(0.6, dt / 0.032)); // hxd.Math.min(1, 1 - Math.pow(0.6, dt * 600));
|
||||
var lerpt = Math.pow(0.5, dt / 0.032); // Math.min(1, 1 - Math.pow(0.6, dt / 0.032)); // hxd.Math.min(1, 1 - Math.pow(0.6, dt * 600));
|
||||
|
||||
var gamepadX = applyNonlinearScale(rescaleDeadZone(Gamepad.getAxis(Settings.gamepadSettings.cameraXAxis), Settings.gamepadSettings.axisDeadzone));
|
||||
var gamepadY = rescaleDeadZone(Gamepad.getAxis(Settings.gamepadSettings.cameraYAxis), Settings.gamepadSettings.axisDeadzone);
|
||||
|
||||
if (gamepadX != 0.0 || gamepadY != 0.0) {
|
||||
wasLastGamepadInput = true;
|
||||
}
|
||||
var cameraPitchDelta = (Key.isDown(Settings.controlsSettings.camBackward) ? 1 : 0)
|
||||
- (Key.isDown(Settings.controlsSettings.camForward) ? 1 : 0)
|
||||
+ gamepadY;
|
||||
if (Settings.gamepadSettings.invertYAxis || Settings.controlsSettings.invertYAxis)
|
||||
+ Gamepad.getAxis(Settings.gamepadSettings.cameraYAxis);
|
||||
if (Settings.gamepadSettings.invertYAxis)
|
||||
cameraPitchDelta = -cameraPitchDelta;
|
||||
var cameraYawDelta = (Key.isDown(Settings.controlsSettings.camRight) ? 1 : 0) - (Key.isDown(Settings.controlsSettings.camLeft) ? 1 : 0) + gamepadX;
|
||||
nextCameraPitch += 0.75 * 5 * cameraPitchDelta * dt * Settings.gamepadSettings.cameraSensitivity;
|
||||
var cameraYawDelta = (Key.isDown(Settings.controlsSettings.camRight) ? 1 : 0) - (Key.isDown(Settings.controlsSettings.camLeft) ? 1 : 0)
|
||||
+ Gamepad.getAxis(Settings.gamepadSettings.cameraXAxis);
|
||||
if (Settings.gamepadSettings.invertXAxis)
|
||||
cameraYawDelta = -cameraYawDelta;
|
||||
|
||||
if (MarbleGame.instance.paused) {
|
||||
cameraYawDelta = 0;
|
||||
cameraPitchDelta = 0;
|
||||
}
|
||||
var gamePadSensitivity = 1.0;
|
||||
if (wasLastGamepadInput) {
|
||||
gamePadSensitivity = Settings.gamepadSettings.cameraSensitivity; // It defaults to 0.6
|
||||
}
|
||||
|
||||
var deltaX = 0.75 * 5 * cameraYawDelta * dt * gamePadSensitivity;
|
||||
var deltaY = 0.75 * 5 * cameraPitchDelta * dt * gamePadSensitivity;
|
||||
|
||||
if (spectateMarbleIndex != -1) {
|
||||
// Center the pitch
|
||||
if (Util.isTouchDevice()) { // Do this only on touch devices
|
||||
if (!Settings.controlsSettings.alwaysFreeLook
|
||||
&& !Key.isDown(Settings.controlsSettings.freelook)
|
||||
&& !MarbleGame.instance.touchInput.cameraInput.pressed
|
||||
&& deltaY == 0.0) {
|
||||
var rescaledY = deltaY;
|
||||
if (rescaledY <= 0.0)
|
||||
rescaledY = 0.4 - rescaledY * -0.75;
|
||||
else
|
||||
rescaledY = rescaledY * 1.1 + 0.4;
|
||||
var movePitchDelta = (rescaledY - CameraPitch);
|
||||
var movePitchSpeed = computePitchSpeedFromDelta(Math.abs(movePitchDelta)) * dt * 0.8;
|
||||
if (movePitchDelta <= 0.0) {
|
||||
movePitchDelta = -movePitchDelta;
|
||||
if (movePitchDelta < movePitchSpeed)
|
||||
movePitchSpeed = movePitchDelta;
|
||||
movePitchDelta = -movePitchSpeed;
|
||||
movePitchSpeed = movePitchDelta;
|
||||
} else if (movePitchSpeed > movePitchDelta) {
|
||||
movePitchSpeed = movePitchDelta;
|
||||
}
|
||||
deltaY = movePitchSpeed;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
nextCameraYaw += deltaX;
|
||||
nextCameraPitch += deltaY;
|
||||
nextCameraYaw += 0.75 * 5 * cameraYawDelta * dt * Settings.gamepadSettings.cameraSensitivity;
|
||||
|
||||
var limits = spectateMarbleIndex == -1 ? 0.0001 : Math.PI / 4;
|
||||
|
||||
|
|
@ -536,10 +458,9 @@ class CameraController extends Object {
|
|||
&& (firstHit == null || (rayCastOrigin.distance(result.point) < firstHitDistance))) {
|
||||
firstHit = result;
|
||||
firstHitDistance = rayCastOrigin.distance(result.point);
|
||||
processedShapes.push(result.object);
|
||||
}
|
||||
}
|
||||
if (firstHit != null)
|
||||
processedShapes.push(firstHit.object);
|
||||
|
||||
if (firstHit != null) {
|
||||
if (firstHitDistance < CameraDistance) {
|
||||
|
|
@ -552,7 +473,7 @@ class CameraController extends Object {
|
|||
var dist = plane.distance(camera.pos.toPoint());
|
||||
|
||||
if (dist >= closeness)
|
||||
continue;
|
||||
break;
|
||||
|
||||
camera.pos = projected.toVector().add(normal.multiply(-closeness));
|
||||
|
||||
|
|
@ -572,6 +493,10 @@ class CameraController extends Object {
|
|||
this.setPosition(camera.pos.x, camera.pos.y, camera.pos.z);
|
||||
}
|
||||
|
||||
function applyNonlinearScale(value:Float) {
|
||||
return Math.pow(Math.abs(value), 3.2) * (value >= 0 ? 1 : -1);
|
||||
}
|
||||
|
||||
function rescaleDeadZone(value:Float, deadZone:Float) {
|
||||
if (deadZone >= value) {
|
||||
if (-deadZone <= value)
|
||||
|
|
@ -601,8 +526,8 @@ class CameraController extends Object {
|
|||
|
||||
var lerpt = 1 - Math.pow(0.5, dt / 0.016); // Math.min(1, 1 - Math.pow(0.6, dt / 0.032)); // hxd.Math.min(1, 1 - Math.pow(0.6, dt * 600));
|
||||
|
||||
var gamepadX = applyNonlinearScale(rescaleDeadZone(Gamepad.getAxis(Settings.gamepadSettings.cameraXAxis), Settings.gamepadSettings.axisDeadzone));
|
||||
var gamepadY = rescaleDeadZone(Gamepad.getAxis(Settings.gamepadSettings.cameraYAxis), Settings.gamepadSettings.axisDeadzone);
|
||||
var gamepadX = applyNonlinearScale(rescaleDeadZone(Gamepad.getAxis(Settings.gamepadSettings.cameraXAxis), 0.25));
|
||||
var gamepadY = applyNonlinearScale(rescaleDeadZone(Gamepad.getAxis(Settings.gamepadSettings.cameraYAxis), 0.25));
|
||||
|
||||
var cameraPitchDelta = (Key.isDown(Settings.controlsSettings.camBackward) ? 1 : 0)
|
||||
- (Key.isDown(Settings.controlsSettings.camForward) ? 1 : 0)
|
||||
|
|
@ -613,53 +538,8 @@ class CameraController extends Object {
|
|||
var cameraYawDelta = (Key.isDown(Settings.controlsSettings.camRight) ? 1 : 0) - (Key.isDown(Settings.controlsSettings.camLeft) ? 1 : 0) + gamepadX;
|
||||
if (Settings.gamepadSettings.invertXAxis)
|
||||
cameraYawDelta = -cameraYawDelta;
|
||||
nextCameraYaw += 0.75 * 5 * cameraYawDelta * dt * Settings.gamepadSettings.cameraSensitivity;
|
||||
|
||||
if (MarbleGame.instance.paused) {
|
||||
cameraYawDelta = 0;
|
||||
cameraPitchDelta = 0;
|
||||
}
|
||||
|
||||
var gamePadSensitivity = 1.0;
|
||||
if (wasLastGamepadInput) {
|
||||
gamePadSensitivity = (1.6 - Settings.controlsSettings.cameraSensitivity); // It defaults to 0.6
|
||||
}
|
||||
|
||||
var deltaX = 0.75 * 5 * cameraYawDelta * dt * gamePadSensitivity;
|
||||
var deltaY = 0.75 * 5 * cameraPitchDelta * dt * gamePadSensitivity;
|
||||
|
||||
// Center the pitch
|
||||
if (Util.isTouchDevice()) { // Do this only on touch devices
|
||||
if (!Settings.controlsSettings.alwaysFreeLook
|
||||
&& !Key.isDown(Settings.controlsSettings.freelook)
|
||||
&& !MarbleGame.instance.touchInput.cameraInput.pressed
|
||||
&& deltaY == 0.0) {
|
||||
var rescaledY = deltaY;
|
||||
if (rescaledY <= 0.0)
|
||||
rescaledY = 0.4 - rescaledY * -0.75;
|
||||
else
|
||||
rescaledY = rescaledY * 1.1 + 0.4;
|
||||
var movePitchDelta = (rescaledY - CameraPitch);
|
||||
var movePitchSpeed = computePitchSpeedFromDelta(Math.abs(movePitchDelta)) * dt * 0.8;
|
||||
if (movePitchDelta <= 0.0) {
|
||||
movePitchDelta = -movePitchDelta;
|
||||
if (movePitchDelta < movePitchSpeed)
|
||||
movePitchSpeed = movePitchDelta;
|
||||
movePitchDelta = -movePitchSpeed;
|
||||
movePitchSpeed = movePitchDelta;
|
||||
} else if (movePitchSpeed > movePitchDelta) {
|
||||
movePitchSpeed = movePitchDelta;
|
||||
}
|
||||
deltaY = movePitchSpeed;
|
||||
}
|
||||
}
|
||||
|
||||
if (!MarbleGame.instance.touchInput.cameraInput.pressed) {
|
||||
hasXInput = false;
|
||||
hasYInput = false;
|
||||
}
|
||||
|
||||
nextCameraYaw += deltaX;
|
||||
nextCameraPitch += deltaY;
|
||||
nextCameraPitch = Math.max(-Math.PI / 2 + Math.PI / 4, Math.min(Math.PI / 2 - 0.0001, nextCameraPitch));
|
||||
|
||||
CameraYaw = Util.lerp(CameraYaw, nextCameraYaw, lerpt);
|
||||
|
|
@ -752,10 +632,9 @@ class CameraController extends Object {
|
|||
&& (firstHit == null || (rayCastOrigin.distance(result.point) < firstHitDistance))) {
|
||||
firstHit = result;
|
||||
firstHitDistance = rayCastOrigin.distance(result.point);
|
||||
processedShapes.push(result.object);
|
||||
}
|
||||
}
|
||||
if (firstHit != null)
|
||||
processedShapes.push(firstHit.object);
|
||||
|
||||
if (firstHit != null) {
|
||||
if (firstHitDistance < CameraDistance) {
|
||||
|
|
@ -768,7 +647,7 @@ class CameraController extends Object {
|
|||
var dist = plane.distance(camera.pos.toPoint());
|
||||
|
||||
if (dist >= closeness)
|
||||
continue;
|
||||
break;
|
||||
|
||||
camera.pos = projected.toVector().add(normal.multiply(-closeness));
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
package src;
|
||||
|
||||
import net.Net;
|
||||
#if !js
|
||||
import sys.FileSystem;
|
||||
#end
|
||||
|
|
@ -114,29 +113,6 @@ class Console {
|
|||
}
|
||||
|
||||
public static function eval(cmd:String) {
|
||||
var cmdLower = cmd.toLowerCase();
|
||||
if (StringTools.startsWith(cmdLower, "defaultmarble")) {
|
||||
// parse regex DefaultMarble.<attribName> = <value>
|
||||
var regex = ~/defaultmarble\.(\w+)\s*=\s*(.+)/;
|
||||
var matched = regex.match(cmdLower);
|
||||
if (matched) {
|
||||
var attribName = regex.matched(1);
|
||||
var valueStr = regex.matched(2);
|
||||
var numValue = Std.parseFloat(valueStr);
|
||||
if (Math.isNaN(numValue))
|
||||
numValue = 0;
|
||||
if (MarbleGame.instance.world != null && !Net.isMP && MarbleGame.instance.world.marble != null) {
|
||||
MarbleGame.instance.world.marble.setMarbleAttribute(attribName, numValue);
|
||||
MarbleGame.instance.world.cheatsUsed = true;
|
||||
log("Set DefaultMarble." + attribName + " to " + numValue);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
error("Invalid command format. Expected: DefaultMarble.<attribName> = <value>");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
var cmdSplit = cmd.split(" ");
|
||||
if (cmdSplit.length != 0) {
|
||||
var cmdType = cmdSplit[0];
|
||||
|
|
|
|||
|
|
@ -714,40 +714,17 @@ class DifBuilder {
|
|||
tex = spl[spl.length - 1];
|
||||
}
|
||||
|
||||
// search with extension first
|
||||
if (ResourceLoader.exists(Path.directory(path) + "/" + tex)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
var prevDir = Path.directory(Path.directory(path));
|
||||
if (ResourceLoader.exists(prevDir + "/" + tex)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
prevDir = Path.directory(prevDir);
|
||||
if (ResourceLoader.exists(prevDir + "/" + tex))
|
||||
return true;
|
||||
|
||||
// remove extension from it
|
||||
if (tex.lastIndexOf(".") != -1) {
|
||||
tex = tex.substring(0, tex.lastIndexOf("."));
|
||||
}
|
||||
|
||||
#if (js || android)
|
||||
path = StringTools.replace(path, "data/", "");
|
||||
#end
|
||||
|
||||
// search jpg, png and bmp
|
||||
if (ResourceLoader.exists(Path.directory(path) + "/" + tex + ".jpg")) {
|
||||
return true;
|
||||
}
|
||||
if (ResourceLoader.exists(Path.directory(path) + "/" + tex + ".png")) {
|
||||
return true;
|
||||
}
|
||||
if (ResourceLoader.exists(Path.directory(path) + "/" + tex + ".bmp")) {
|
||||
return true;
|
||||
}
|
||||
prevDir = Path.directory(Path.directory(path));
|
||||
var prevDir = Path.directory(Path.directory(path));
|
||||
|
||||
if (ResourceLoader.exists(prevDir + "/" + tex + ".jpg")) {
|
||||
return true;
|
||||
|
|
@ -755,9 +732,6 @@ class DifBuilder {
|
|||
if (ResourceLoader.exists(prevDir + "/" + tex + ".png")) {
|
||||
return true;
|
||||
}
|
||||
if (ResourceLoader.exists(prevDir + "/" + tex + ".bmp")) {
|
||||
return true;
|
||||
}
|
||||
|
||||
prevDir = Path.directory(prevDir);
|
||||
|
||||
|
|
@ -767,9 +741,6 @@ class DifBuilder {
|
|||
if (ResourceLoader.exists(prevDir + "/" + tex + ".png")) {
|
||||
return true;
|
||||
}
|
||||
if (ResourceLoader.exists(prevDir + "/" + tex + ".bmp")) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
|
@ -779,36 +750,12 @@ class DifBuilder {
|
|||
tex = spl[spl.length - 1];
|
||||
}
|
||||
|
||||
// search with extension first
|
||||
if (ResourceLoader.exists(Path.directory(path) + "/" + tex)) {
|
||||
return Path.directory(path) + "/" + tex;
|
||||
}
|
||||
|
||||
var prevDir = Path.directory(Path.directory(path));
|
||||
if (ResourceLoader.exists(prevDir + "/" + tex)) {
|
||||
return prevDir + "/" + tex;
|
||||
}
|
||||
|
||||
prevDir = Path.directory(prevDir);
|
||||
|
||||
if (ResourceLoader.exists(prevDir + "/" + tex)) {
|
||||
return prevDir + "/" + tex;
|
||||
}
|
||||
|
||||
// remove extension from it
|
||||
if (tex.lastIndexOf(".") != -1) {
|
||||
tex = tex.substring(0, tex.lastIndexOf("."));
|
||||
}
|
||||
|
||||
if (ResourceLoader.exists(Path.directory(path) + "/" + tex + ".jpg")) {
|
||||
return Path.directory(path) + "/" + tex + ".jpg";
|
||||
}
|
||||
if (ResourceLoader.exists(Path.directory(path) + "/" + tex + ".png")) {
|
||||
return Path.directory(path) + "/" + tex + ".png";
|
||||
}
|
||||
if (ResourceLoader.exists(Path.directory(path) + "/" + tex + ".bmp")) {
|
||||
return Path.directory(path) + "/" + tex + ".bmp";
|
||||
}
|
||||
|
||||
var prevDir = Path.directory(Path.directory(path));
|
||||
|
||||
|
|
@ -818,9 +765,6 @@ class DifBuilder {
|
|||
if (ResourceLoader.exists(prevDir + "/" + tex + ".png")) {
|
||||
return prevDir + "/" + tex + ".png";
|
||||
}
|
||||
if (ResourceLoader.exists(prevDir + "/" + tex + ".bmp")) {
|
||||
return prevDir + "/" + tex + ".bmp";
|
||||
}
|
||||
|
||||
var prevDir = Path.directory(prevDir);
|
||||
|
||||
|
|
@ -830,9 +774,6 @@ class DifBuilder {
|
|||
if (ResourceLoader.exists(prevDir + "/" + tex + ".png")) {
|
||||
return prevDir + "/" + tex + ".png";
|
||||
}
|
||||
if (ResourceLoader.exists(prevDir + "/" + tex + ".bmp")) {
|
||||
return prevDir + "/" + tex + ".bmp";
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -158,37 +158,51 @@ class InstanceManager {
|
|||
inst.visibleTicks = inst.visibleTicks - 1;
|
||||
}
|
||||
|
||||
var opacity = inst.gameObject.currentOpacity;
|
||||
if (opacity == 1)
|
||||
if (inst.gameObject.currentOpacity == 1)
|
||||
opaqueinstances.push(inst);
|
||||
else if (opacity != 0)
|
||||
else if (inst.gameObject.currentOpacity != 0)
|
||||
transparentinstances.push(inst);
|
||||
// break;
|
||||
// }
|
||||
// }
|
||||
}
|
||||
}
|
||||
var dtsShader = minfo.dtsShader;
|
||||
|
||||
// Emit non culled primitives
|
||||
if (minfo.meshbatch != null) {
|
||||
minfo.meshbatch.begin(opaqueinstances.length);
|
||||
var i = 0;
|
||||
while (i < opaqueinstances.length) {
|
||||
var instance = @:privateAccess opaqueinstances.array[i++];
|
||||
if (dtsShader != null)
|
||||
for (instance in opaqueinstances) { // Draw the opaque shit first
|
||||
var dtsShader = minfo.dtsShader;
|
||||
if (dtsShader != null) {
|
||||
dtsShader.currentOpacity = instance.gameObject.currentOpacity;
|
||||
minfo.meshbatch.worldPosition = instance.emptyObj.getAbsPos();
|
||||
}
|
||||
var transform = instance.emptyObj.getAbsPos();
|
||||
// minfo.meshbatch.shadersChanged = true;
|
||||
// minfo.meshbatch.material.mainPass.setPassName(minfo.mesh.material.mainPass.name);
|
||||
// minfo.meshbatch.material.mainPass.enableLights = minfo.mesh.material.mainPass.enableLights;
|
||||
minfo.meshbatch.worldPosition = transform;
|
||||
minfo.meshbatch.emitInstance();
|
||||
}
|
||||
}
|
||||
if (minfo.transparencymeshbatch != null) {
|
||||
minfo.transparencymeshbatch.begin(transparentinstances.length);
|
||||
var i = 0;
|
||||
while (i < transparentinstances.length) {
|
||||
var instance = @:privateAccess transparentinstances.array[i++];
|
||||
if (dtsShader != null)
|
||||
for (instance in transparentinstances) { // Non opaque shit
|
||||
var dtsShader = minfo.dtsShader;
|
||||
if (dtsShader != null) {
|
||||
dtsShader.currentOpacity = instance.gameObject.currentOpacity;
|
||||
minfo.transparencymeshbatch.worldPosition = instance.emptyObj.getAbsPos();
|
||||
}
|
||||
// minfo.transparencymeshbatch.material.blendMode = Alpha;
|
||||
// minfo.transparencymeshbatch.material.color.a = instance.gameObject.currentOpacity;
|
||||
// minfo.transparencymeshbatch.material.mainPass.setPassName(minfo.mesh.material.mainPass.name);
|
||||
// minfo.transparencymeshbatch.shadersChanged = true;
|
||||
// minfo.transparencymeshbatch.material.mainPass.enableLights = minfo.mesh.material.mainPass.enableLights;
|
||||
// minfo.transparencymeshbatch.material.mainPass.depthWrite = false;
|
||||
// if (dtsShader != null) {
|
||||
// dtsShader.currentOpacity = instance.gameObject.currentOpacity;
|
||||
// minfo.transparencymeshbatch.shadersChanged = true;
|
||||
// }
|
||||
var transform = instance.emptyObj.getAbsPos();
|
||||
minfo.transparencymeshbatch.worldPosition = transform;
|
||||
minfo.transparencymeshbatch.emitInstance();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -49,7 +49,7 @@ class Leaderboards {
|
|||
public static function getScores(mission:String, kind:LeaderboardsKind, cb:Array<LBScore>->Void) {
|
||||
if (!StringTools.startsWith(mission, "data/"))
|
||||
mission = "data/" + mission;
|
||||
return Http.get('${host}/api/scores?mission=${StringTools.urlEncode(mission)}&game=${game}&view=${kind}&count=5', (b) -> {
|
||||
return Http.get('${host}/api/scores?mission=${StringTools.urlEncode(mission)}&game=${game}&view=${kind}&count=10', (b) -> {
|
||||
var s = b.toString();
|
||||
var scores:Array<LBScore> = Json.parse(s).scores;
|
||||
cb(scores);
|
||||
|
|
|
|||
106
src/Marble.hx
|
|
@ -260,9 +260,7 @@ class Marble extends GameObject {
|
|||
|
||||
public var contacts:Array<CollisionInfo> = [];
|
||||
public var bestContact:CollisionInfo;
|
||||
|
||||
static var contactScratch:Array<CollisionEntity> = [];
|
||||
static var surfaceScratch:Array<CollisionSurface> = [];
|
||||
public var contactEntities:Array<CollisionEntity> = [];
|
||||
|
||||
var queuedContacts:Array<CollisionInfo> = [];
|
||||
var appliedImpulses:Array<{impulse:Vector, contactImpulse:Bool}> = [];
|
||||
|
|
@ -396,7 +394,6 @@ class Marble extends GameObject {
|
|||
this.netSmoothOffset = new Vector();
|
||||
this.netCorrected = false;
|
||||
this.currentUp = new Vector(0, 0, 1);
|
||||
this.lastContactNormal = new Vector(0, 0, 1);
|
||||
|
||||
var marbleDts = new DtsObject();
|
||||
var marbleShader = "";
|
||||
|
|
@ -700,41 +697,12 @@ class Marble extends GameObject {
|
|||
this._bounceKineticFriction = MisParser.parseNumber(attribs.get("bouncekineticfriction"));
|
||||
}
|
||||
|
||||
public function setMarbleAttribute(attr:String, value:Float) {
|
||||
switch (attr.toLowerCase()) {
|
||||
case "maxrollvelocity":
|
||||
this._maxRollVelocity = value;
|
||||
case "angularacceleration":
|
||||
this._angularAcceleration = value;
|
||||
case "jumpimpulse":
|
||||
this._jumpImpulse = value;
|
||||
case "kineticfriction":
|
||||
this._kineticFriction = value;
|
||||
case "staticfriction":
|
||||
this._staticFriction = value;
|
||||
case "brakingacceleration":
|
||||
this._brakingAcceleration = value;
|
||||
case "gravity":
|
||||
this._gravity = value;
|
||||
case "airaccel":
|
||||
this._airAccel = value;
|
||||
case "maxdotslide":
|
||||
this._maxDotSlide = value;
|
||||
case "minbouncevel":
|
||||
this._minBounceVel = value;
|
||||
case "minbouncespeed":
|
||||
this._minBounceSpeed = value;
|
||||
case "mintrailvel":
|
||||
this._minTrailVel = value;
|
||||
case "bouncekineticfriction":
|
||||
this._bounceKineticFriction = value;
|
||||
}
|
||||
}
|
||||
|
||||
function findContacts(collisiomWorld:CollisionWorld, timeState:TimeState) {
|
||||
this.contacts = queuedContacts;
|
||||
CollisionPool.clear();
|
||||
collisiomWorld.sphereIntersection(this.collider, timeState, this.contacts);
|
||||
var c = collisiomWorld.sphereIntersection(this.collider, timeState);
|
||||
this.contactEntities = c.foundEntities;
|
||||
contacts = contacts.concat(c.contacts);
|
||||
}
|
||||
|
||||
public function queueCollision(collisionInfo:CollisionInfo) {
|
||||
|
|
@ -849,7 +817,7 @@ class Marble extends GameObject {
|
|||
|
||||
function computeMoveForces(m:Move, aControl:Vector, desiredOmega:Vector) {
|
||||
var currentGravityDir = this.currentUp.multiply(-1);
|
||||
var R = this.currentUp.multiply(this._radius);
|
||||
var R = currentGravityDir.multiply(-this._radius);
|
||||
var rollVelocity = this.omega.cross(R);
|
||||
var axes = this.getMarbleAxis();
|
||||
// if (!level.isReplayingMovement)
|
||||
|
|
@ -882,15 +850,15 @@ class Marble extends GameObject {
|
|||
}
|
||||
var rsq = R.lengthSq();
|
||||
var crossP = R.cross(motionDir.multiply(desiredYVelocity).add(sideDir.multiply(desiredXVelocity))).multiply(1 / rsq);
|
||||
desiredOmega.load(crossP);
|
||||
aControl.load(desiredOmega.sub(this.omega));
|
||||
desiredOmega.set(crossP.x, crossP.y, crossP.z);
|
||||
aControl.set(desiredOmega.x - this.omega.x, desiredOmega.y - this.omega.y, desiredOmega.z - this.omega.z);
|
||||
var aScalar = aControl.length();
|
||||
if (aScalar > this._angularAcceleration) {
|
||||
aControl.scale(this._angularAcceleration / aScalar);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
return return true;
|
||||
}
|
||||
|
||||
function velocityCancel(timeState:TimeState, surfaceSlide:Bool, noBounce:Bool, stoppedPaths:Bool, pi:Array<PathedInterior>) {
|
||||
|
|
@ -905,7 +873,7 @@ class Marble extends GameObject {
|
|||
var sVel = this.velocity.sub(contacts[i].velocity);
|
||||
var surfaceDot = contacts[i].normal.dot(sVel);
|
||||
|
||||
if ((!looped && surfaceDot < 0.0) || surfaceDot < -SurfaceDotThreshold) {
|
||||
if ((!looped && surfaceDot < 0) || surfaceDot < -SurfaceDotThreshold) {
|
||||
var velLen = this.velocity.length();
|
||||
var surfaceVel = this.contacts[i].normal.multiply(surfaceDot);
|
||||
|
||||
|
|
@ -940,7 +908,7 @@ class Marble extends GameObject {
|
|||
}
|
||||
contacts[i].velocity.load(otherMarble.velocity);
|
||||
} else {
|
||||
if (contacts[i].velocity.length() == 0.0 && !surfaceSlide && surfaceDot > -this._maxDotSlide * velLen) {
|
||||
if (contacts[i].velocity.length() == 0 && !surfaceSlide && surfaceDot > -this._maxDotSlide * velLen) {
|
||||
this.velocity.load(this.velocity.sub(surfaceVel));
|
||||
this.velocity.normalize();
|
||||
this.velocity.load(this.velocity.multiply(velLen));
|
||||
|
|
@ -966,7 +934,7 @@ class Marble extends GameObject {
|
|||
vAtC.load(vAtC.sub(contacts[i].normal.multiply(contacts[i].normal.dot(sVel))));
|
||||
|
||||
var vAtCMag = vAtC.length();
|
||||
if (vAtCMag != 0.0) {
|
||||
if (vAtCMag != 0) {
|
||||
var friction = this._bounceKineticFriction * contacts[i].friction;
|
||||
|
||||
var angVMagnitude = friction * 5 * normalVel / (2 * this._radius);
|
||||
|
|
@ -1002,7 +970,7 @@ class Marble extends GameObject {
|
|||
}
|
||||
}
|
||||
} while (!done && itersIn < 1e4); // Maximum limit pls
|
||||
if (this.velocity.lengthSq() < 625.0) {
|
||||
if (this.velocity.lengthSq() < 625) {
|
||||
var gotOne = false;
|
||||
var dir = new Vector(0, 0, 0);
|
||||
for (j in 0...contacts.length) {
|
||||
|
|
@ -1026,10 +994,10 @@ class Marble extends GameObject {
|
|||
soFar += (dist - outVel * timeToSeparate) / timeToSeparate / contacts[k].normal.dot(dir);
|
||||
}
|
||||
}
|
||||
if (soFar < -25.0)
|
||||
soFar = -25.0;
|
||||
if (soFar > 25.0)
|
||||
soFar = 25.0;
|
||||
if (soFar < -25)
|
||||
soFar = -25;
|
||||
if (soFar > 25)
|
||||
soFar = 25;
|
||||
this.velocity.load(this.velocity.add(dir.multiply(soFar)));
|
||||
}
|
||||
}
|
||||
|
|
@ -1098,7 +1066,7 @@ class Marble extends GameObject {
|
|||
slipping = false;
|
||||
}
|
||||
var vAtCDir = vAtC.multiply(1 / vAtCMag);
|
||||
aFriction.load(bestContact.normal.cross(vAtCDir).multiply(angAMagnitude));
|
||||
aFriction.load(bestContact.normal.multiply(-1).cross(vAtCDir.multiply(-1)).multiply(angAMagnitude));
|
||||
AFriction.load(vAtCDir.multiply(-AMagnitude));
|
||||
this._slipAmount = vAtCMag - totalDeltaV;
|
||||
}
|
||||
|
|
@ -1125,16 +1093,16 @@ class Marble extends GameObject {
|
|||
friction2 = this._kineticFriction * bestContact.friction;
|
||||
Aadd.load(Aadd.multiply(friction2 * bestNormalForce / aAtCMag));
|
||||
}
|
||||
A.load(A.add(Aadd));
|
||||
a.load(a.add(aadd));
|
||||
A.set(A.x + Aadd.x, A.y + Aadd.y, A.z + Aadd.z);
|
||||
a.set(a.x + aadd.x, a.y + aadd.y, a.z + aadd.z);
|
||||
}
|
||||
A.load(A.add(AFriction));
|
||||
a.load(a.add(aFriction));
|
||||
A.set(A.x + AFriction.x, A.y + AFriction.y, A.z + AFriction.z);
|
||||
a.set(a.x + aFriction.x, a.y + aFriction.y, a.z + aFriction.z);
|
||||
|
||||
lastContactNormal = bestContact.normal;
|
||||
lastContactPosition = this.getAbsPos().getPosition();
|
||||
}
|
||||
a.load(a.add(aControl));
|
||||
a.set(a.x + aControl.x, a.y + aControl.y, a.z + aControl.z);
|
||||
if (this.mode == Finish) {
|
||||
a.set(); // Zero it out
|
||||
}
|
||||
|
|
@ -1306,10 +1274,7 @@ class Marble extends GameObject {
|
|||
searchbox.addSpherePos(position.x, position.y, position.z, _radius);
|
||||
searchbox.addSpherePos(position.x + velocity.x * deltaT, position.y + velocity.y * deltaT, position.z + velocity.z * deltaT, _radius);
|
||||
|
||||
contactScratch.resize(0);
|
||||
this.collisionWorld.boundingSearch(searchbox, contactScratch);
|
||||
|
||||
var foundObjs = contactScratch;
|
||||
var foundObjs = this.collisionWorld.boundingSearch(searchbox);
|
||||
|
||||
var finalT = deltaT;
|
||||
var found = false;
|
||||
|
|
@ -1352,7 +1317,7 @@ class Marble extends GameObject {
|
|||
// var iterationFound = false;
|
||||
for (obj in foundObjs) {
|
||||
// Its an MP so bruh
|
||||
if (obj.go == this || (obj.go != null && !obj.go.isCollideable))
|
||||
if (obj.go != null && !obj.go.isCollideable)
|
||||
continue;
|
||||
|
||||
var isDts = obj.go is DtsObject;
|
||||
|
|
@ -1380,13 +1345,11 @@ class Marble extends GameObject {
|
|||
Math.max(Math.max(sphereRadius.x, sphereRadius.y), sphereRadius.z) * 2);
|
||||
|
||||
var currentFinalPos = position.add(relVel.multiply(finalT)); // localpos.add(relLocalVel.multiply(finalT));
|
||||
surfaceScratch.resize(0);
|
||||
if (@:privateAccess obj.grid != null)
|
||||
@:privateAccess obj.grid.boundingSearch(boundThing, surfaceScratch);
|
||||
var surfaces = surfaceScratch;
|
||||
var surfaces = @:privateAccess obj.grid != null ? @:privateAccess obj.grid.boundingSearch(boundThing) : (obj.bvh == null ? obj.octree.boundingSearch(boundThing)
|
||||
.map(x -> cast x) : obj.bvh.boundingSearch(boundThing));
|
||||
|
||||
for (surf in surfaces) {
|
||||
var surface:CollisionSurface = surf;
|
||||
var surface:CollisionSurface = cast surf;
|
||||
|
||||
currentFinalPos = position.add(relVel.multiply(finalT));
|
||||
|
||||
|
|
@ -1408,7 +1371,7 @@ class Marble extends GameObject {
|
|||
var surfaceNormal = new Vector(verts.nx, verts.ny,
|
||||
verts.nz); // surface.normals[surface.indices[i]].transformed3x3(obj.transform).normalized();
|
||||
if (obj is DtsObject)
|
||||
surfaceNormal.load(v.sub(v0).cross(v2.sub(v0)).normalized().multiply(-1));
|
||||
surfaceNormal.multiply(-1);
|
||||
var surfaceD = -surfaceNormal.dot(v0);
|
||||
|
||||
// If we're going the wrong direction or not going to touch the plane, ignore...
|
||||
|
|
@ -1941,7 +1904,7 @@ class Marble extends GameObject {
|
|||
}
|
||||
// }
|
||||
}
|
||||
this.queuedContacts.resize(0);
|
||||
this.queuedContacts = [];
|
||||
|
||||
newPos = this.collider.transform.getPosition(); // this.getAbsPos().getPosition().clone();
|
||||
|
||||
|
|
@ -2020,9 +1983,7 @@ class Marble extends GameObject {
|
|||
// marbleHitbox.offset(end.x, end.y, end.z);
|
||||
|
||||
// spherebounds.addSpherePos(gjkCapsule.p2.x, gjkCapsule.p2.y, gjkCapsule.p2.z, gjkCapsule.radius);
|
||||
contactScratch.resize(0);
|
||||
this.collisionWorld.boundingSearch(box, contactScratch);
|
||||
var contacts = contactScratch;
|
||||
var contacts = this.collisionWorld.boundingSearch(box);
|
||||
// var contacts = marble.contactEntities;
|
||||
var inside = [];
|
||||
|
||||
|
|
@ -2075,9 +2036,7 @@ class Marble extends GameObject {
|
|||
var checkSphereRadius = checkBounds.getMax().sub(checkBoundsCenter).length();
|
||||
var checkSphere = new Bounds();
|
||||
checkSphere.addSpherePos(checkBoundsCenter.x, checkBoundsCenter.y, checkBoundsCenter.z, checkSphereRadius);
|
||||
contactScratch.resize(0);
|
||||
this.collisionWorld.boundingSearch(checkSphere, contactScratch, false);
|
||||
var endpadBB = contactScratch;
|
||||
var endpadBB = this.collisionWorld.boundingSearch(checkSphere, false);
|
||||
var found = false;
|
||||
for (collider in endpadBB) {
|
||||
if (collider.go == @:privateAccess this.level.endPad) {
|
||||
|
|
@ -2431,8 +2390,6 @@ class Marble extends GameObject {
|
|||
if (Key.isDown(Settings.controlsSettings.right)) {
|
||||
move.d.y -= 1;
|
||||
}
|
||||
move.d.x = Util.clamp(move.d.x, -1, 1);
|
||||
move.d.y = Util.clamp(move.d.y, -1, 1);
|
||||
if (Key.isDown(Settings.controlsSettings.jump)
|
||||
|| MarbleGame.instance.touchInput.jumpButton.pressed
|
||||
|| Gamepad.isDown(Settings.gamepadSettings.jump)) {
|
||||
|
|
@ -2861,6 +2818,7 @@ class Marble extends GameObject {
|
|||
this.megaMarbleUseTick = 0;
|
||||
this.netFlags = MarbleNetFlags.DoBlast | MarbleNetFlags.DoMega | MarbleNetFlags.DoHelicopter | MarbleNetFlags.DoShockAbsorber | MarbleNetFlags.DoSuperBounce | MarbleNetFlags.PickupPowerup | MarbleNetFlags.GravityChange | MarbleNetFlags.UsePowerup;
|
||||
this.lastContactNormal = new Vector(0, 0, 1);
|
||||
this.contactEntities = [];
|
||||
this.cloak = false;
|
||||
this._firstTick = true;
|
||||
this.lastRespawnTick = -100000;
|
||||
|
|
|
|||
|
|
@ -42,7 +42,7 @@ class MarbleGame {
|
|||
|
||||
static var instance:MarbleGame;
|
||||
|
||||
static var currentVersion = "1.7.3";
|
||||
static var currentVersion = "1.7.1";
|
||||
|
||||
var world:MarbleWorld;
|
||||
|
||||
|
|
@ -88,6 +88,7 @@ class MarbleGame {
|
|||
return; // don't pause
|
||||
}
|
||||
|
||||
paused = true;
|
||||
handlePauseGame();
|
||||
// Focus the shit again
|
||||
var jsCanvas = @:privateAccess Window.getInstance().canvas;
|
||||
|
|
@ -259,6 +260,7 @@ class MarbleGame {
|
|||
|| (Net.isMP && paused && !(MarbleGame.canvas.children[MarbleGame.canvas.children.length - 1] is MPExitGameDlg))) {
|
||||
return; // don't pause
|
||||
}
|
||||
paused = !paused;
|
||||
handlePauseGame();
|
||||
}
|
||||
}
|
||||
|
|
@ -286,63 +288,57 @@ class MarbleGame {
|
|||
}
|
||||
}
|
||||
|
||||
public function showPauseUI() {
|
||||
if (world.isMultiplayer) {
|
||||
exitGameDlg = new MPExitGameDlg(() -> {
|
||||
canvas.popDialog(exitGameDlg);
|
||||
paused = !paused;
|
||||
var w = getWorld();
|
||||
w.setCursorLock(true);
|
||||
}, () -> {
|
||||
canvas.popDialog(exitGameDlg);
|
||||
quitMission(Net.isClient);
|
||||
if (Net.isMP && Net.isClient) {
|
||||
Net.disconnect();
|
||||
canvas.setContent(new JoinServerGui());
|
||||
}
|
||||
});
|
||||
} else {
|
||||
exitGameDlg = new ExitGameDlg((sender) -> {
|
||||
canvas.popDialog(exitGameDlg);
|
||||
var w = getWorld();
|
||||
if (MarbleGame.instance.toRecord) {
|
||||
MarbleGame.canvas.pushDialog(new ReplayNameDlg(() -> {
|
||||
quitMission();
|
||||
}));
|
||||
} else {
|
||||
quitMission(Net.isClient);
|
||||
if (Net.isMP && Net.isClient) {
|
||||
Net.disconnect();
|
||||
}
|
||||
}
|
||||
}, (sender) -> {
|
||||
canvas.popDialog(exitGameDlg);
|
||||
paused = !paused;
|
||||
var w = getWorld();
|
||||
w.setCursorLock(true);
|
||||
}, (sender) -> {
|
||||
canvas.popDialog(exitGameDlg);
|
||||
var w = getWorld();
|
||||
w.restart(w.marble, true);
|
||||
// world.setCursorLock(true);
|
||||
paused = !paused;
|
||||
});
|
||||
}
|
||||
canvas.pushDialog(exitGameDlg);
|
||||
}
|
||||
|
||||
public function handlePauseGame() {
|
||||
if (!paused && world._ready) {
|
||||
paused = true;
|
||||
if (paused && world._ready) {
|
||||
Console.log("Game paused");
|
||||
world.setCursorLock(false);
|
||||
if (Util.isTouchDevice()) {
|
||||
this.touchInput.movementInput.forceRelease();
|
||||
}
|
||||
showPauseUI();
|
||||
if (world.isMultiplayer) {
|
||||
exitGameDlg = new MPExitGameDlg(() -> {
|
||||
canvas.popDialog(exitGameDlg);
|
||||
paused = !paused;
|
||||
var w = getWorld();
|
||||
w.setCursorLock(true);
|
||||
}, () -> {
|
||||
canvas.popDialog(exitGameDlg);
|
||||
quitMission(Net.isClient);
|
||||
if (Net.isMP && Net.isClient) {
|
||||
Net.disconnect();
|
||||
canvas.setContent(new JoinServerGui());
|
||||
}
|
||||
});
|
||||
} else {
|
||||
exitGameDlg = new ExitGameDlg((sender) -> {
|
||||
canvas.popDialog(exitGameDlg);
|
||||
var w = getWorld();
|
||||
if (MarbleGame.instance.toRecord) {
|
||||
MarbleGame.canvas.pushDialog(new ReplayNameDlg(() -> {
|
||||
quitMission();
|
||||
}));
|
||||
} else {
|
||||
quitMission(Net.isClient);
|
||||
if (Net.isMP && Net.isClient) {
|
||||
Net.disconnect();
|
||||
}
|
||||
}
|
||||
}, (sender) -> {
|
||||
canvas.popDialog(exitGameDlg);
|
||||
paused = !paused;
|
||||
var w = getWorld();
|
||||
w.setCursorLock(true);
|
||||
}, (sender) -> {
|
||||
canvas.popDialog(exitGameDlg);
|
||||
var w = getWorld();
|
||||
w.restart(w.marble, true);
|
||||
// world.setCursorLock(true);
|
||||
paused = !paused;
|
||||
});
|
||||
}
|
||||
canvas.pushDialog(exitGameDlg);
|
||||
} else {
|
||||
if (world._ready) {
|
||||
paused = false;
|
||||
Console.log("Game unpaused");
|
||||
if (exitGameDlg != null)
|
||||
canvas.popDialog(exitGameDlg);
|
||||
|
|
|
|||
|
|
@ -218,8 +218,6 @@ class MarbleWorld extends Scheduler {
|
|||
public var rewinding:Bool = false;
|
||||
public var rewindUsed:Bool = false;
|
||||
|
||||
public var cheatsUsed:Bool = false;
|
||||
|
||||
public var inputRecorder:InputRecorder;
|
||||
public var isReplayingMovement:Bool = false;
|
||||
public var currentInputMoves:Array<InputRecorderFrame>;
|
||||
|
|
@ -1491,31 +1489,13 @@ class MarbleWorld extends Scheduler {
|
|||
return packets;
|
||||
}
|
||||
|
||||
inline function hasPredictionFlag(mask:Int, clientId:Int):Bool {
|
||||
return (mask & (1 << clientId)) != 0;
|
||||
}
|
||||
|
||||
public function applyReceivedMoves() {
|
||||
var needsPrediction = 0;
|
||||
|
||||
inline function correctPrediction(target:Marble, packet:MarbleUpdatePacket, tick:Int, bitMask:Int, ?pending:Array<MarbleUpdatePacket>) {
|
||||
target.unpackUpdate(packet);
|
||||
needsPrediction |= bitMask;
|
||||
if (pending != null)
|
||||
pending.insert(0, packet);
|
||||
if (tick >= 0)
|
||||
predictions.clearStatesAfterTick(target, tick);
|
||||
}
|
||||
|
||||
if (!lastMoves.ourMoveApplied) {
|
||||
var ourMove = lastMoves.myMarbleUpdate;
|
||||
if (ourMove != null) {
|
||||
var ourMoveStruct = Net.clientConnection.acknowledgeMove(ourMove.move, timeState);
|
||||
lastMoves.ourMoveApplied = true;
|
||||
|
||||
var hasStruct = ourMoveStruct != null;
|
||||
var ourTick = hasStruct ? ourMoveStruct.timeState.ticks : -1;
|
||||
|
||||
for (client => arr in lastMoves.otherMarbleUpdates) {
|
||||
var lastMove = null;
|
||||
while (arr.packets.length > 0) {
|
||||
|
|
@ -1526,35 +1506,68 @@ class MarbleWorld extends Scheduler {
|
|||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (lastMove == null || ourMove.serverTicks != lastMove.serverTicks)
|
||||
continue;
|
||||
|
||||
var connection = Net.clientIdMap[client];
|
||||
if (connection == null)
|
||||
continue;
|
||||
var clientMarble = clientMarbles[connection];
|
||||
if (clientMarble == null)
|
||||
continue;
|
||||
|
||||
var mask = 1 << client;
|
||||
if (hasStruct) {
|
||||
var otherPred = predictions.retrieveState(clientMarble, ourMoveStruct.timeState.ticks);
|
||||
if (otherPred == null || otherPred.getError(lastMove) > 0.01) {
|
||||
correctPrediction(clientMarble, lastMove, ourTick, mask, arr.packets);
|
||||
if (lastMove != null) {
|
||||
// clientMarbles[Net.clientIdMap[client]].unpackUpdate(lastMove);
|
||||
// needsPrediction |= 1 << client;
|
||||
// arr.insert(0, lastMove);
|
||||
var clientMarble = clientMarbles[Net.clientIdMap[client]];
|
||||
if (clientMarble != null) {
|
||||
if (ourMove.serverTicks == lastMove.serverTicks) {
|
||||
if (ourMoveStruct != null) {
|
||||
var otherPred = predictions.retrieveState(clientMarble, ourMoveStruct.timeState.ticks);
|
||||
if (otherPred != null) {
|
||||
if (otherPred.getError(lastMove) > 0.01) {
|
||||
// Debug.drawSphere(@:privateAccess clientMarbles[Net.clientIdMap[client]].newPos, 0.2, 0.5);
|
||||
// trace('Prediction error: ${otherPred.getError(lastMove)}');
|
||||
// trace('Desync for tick ${ourMoveStruct.timeState.ticks}');
|
||||
clientMarble.unpackUpdate(lastMove);
|
||||
needsPrediction |= 1 << client;
|
||||
arr.packets.insert(0, lastMove);
|
||||
predictions.clearStatesAfterTick(clientMarbles[Net.clientIdMap[client]], ourMoveStruct.timeState.ticks);
|
||||
}
|
||||
} else {
|
||||
// Debug.drawSphere(@:privateAccess clientMarbles[Net.clientIdMap[client]].newPos, 0.2, 0.5);
|
||||
// trace('Desync for tick ${ourMoveStruct.timeState.ticks}');
|
||||
clientMarble.unpackUpdate(lastMove);
|
||||
needsPrediction |= 1 << client;
|
||||
arr.packets.insert(0, lastMove);
|
||||
predictions.clearStatesAfterTick(clientMarble, ourMoveStruct.timeState.ticks);
|
||||
}
|
||||
} else {
|
||||
// Debug.drawSphere(@:privateAccess clientMarbles[Net.clientIdMap[client]].newPos, 0.2, 0.5);
|
||||
// trace('Desync in General');
|
||||
clientMarble.unpackUpdate(lastMove);
|
||||
needsPrediction |= 1 << client;
|
||||
arr.packets.insert(0, lastMove);
|
||||
// predictions.clearStatesAfterTick(clientMarbles[Net.clientIdMap[client]], ourMoveStruct.timeState.ticks);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
correctPrediction(clientMarble, lastMove, -1, mask, arr.packets);
|
||||
}
|
||||
}
|
||||
// marble.unpackUpdate(ourMove);
|
||||
// needsPrediction |= 1 << Net.clientId;
|
||||
if (!Net.clientSpectate) {
|
||||
if (hasStruct) {
|
||||
var ourPred = predictions.retrieveState(marble, ourTick);
|
||||
if (ourPred == null || ourPred.getError(ourMove) > 0.01) {
|
||||
correctPrediction(marble, ourMove, ourTick, 1 << Net.clientId);
|
||||
if (ourMoveStruct != null) {
|
||||
var ourPred = predictions.retrieveState(marble, ourMoveStruct.timeState.ticks);
|
||||
if (ourPred != null) {
|
||||
if (ourPred.getError(ourMove) > 0.01) {
|
||||
// trace('Desync for tick ${ourMoveStruct.timeState.ticks}');
|
||||
marble.unpackUpdate(ourMove);
|
||||
needsPrediction |= 1 << Net.clientId;
|
||||
predictions.clearStatesAfterTick(marble, ourMoveStruct.timeState.ticks);
|
||||
}
|
||||
} else {
|
||||
// trace('Desync for tick ${ourMoveStruct.timeState.ticks}');
|
||||
marble.unpackUpdate(ourMove);
|
||||
needsPrediction |= 1 << Net.clientId;
|
||||
predictions.clearStatesAfterTick(marble, ourMoveStruct.timeState.ticks);
|
||||
}
|
||||
} else {
|
||||
correctPrediction(marble, ourMove, -1, 1 << Net.clientId);
|
||||
// trace('Desync in General');
|
||||
marble.unpackUpdate(ourMove);
|
||||
needsPrediction |= 1 << Net.clientId;
|
||||
// predictions.clearStatesAfterTick(marble, ourMoveStruct.timeState.ticks);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -2511,7 +2524,7 @@ class MarbleWorld extends Scheduler {
|
|||
} else {
|
||||
nextLevelCode();
|
||||
}
|
||||
}, mission, finishTime, this.replay.write());
|
||||
}, mission, finishTime);
|
||||
MarbleGame.canvas.pushDialog(egg);
|
||||
this.setCursorLock(false);
|
||||
return 0;
|
||||
|
|
|
|||
|
|
@ -52,7 +52,6 @@ class ReplayFrame {
|
|||
var t = (time - this.time) / (next.time - this.time);
|
||||
|
||||
var dt = time - this.time;
|
||||
var clockDt = next.clockTime - this.clockTime;
|
||||
|
||||
var interpFrame = new ReplayFrame();
|
||||
|
||||
|
|
@ -60,20 +59,18 @@ class ReplayFrame {
|
|||
interpFrame.time = time;
|
||||
interpFrame.bonusTime = this.bonusTime;
|
||||
interpFrame.clockTime = this.clockTime;
|
||||
if (clockDt > 0) {
|
||||
if (interpFrame.bonusTime != 0 && time >= 3.5) {
|
||||
if (dt <= this.bonusTime) {
|
||||
interpFrame.bonusTime -= dt;
|
||||
} else {
|
||||
interpFrame.clockTime += dt - this.bonusTime;
|
||||
interpFrame.bonusTime = 0;
|
||||
}
|
||||
if (interpFrame.bonusTime != 0 && time >= 3.5) {
|
||||
if (dt <= this.bonusTime) {
|
||||
interpFrame.bonusTime -= dt;
|
||||
} else {
|
||||
if (this.time >= 3.5)
|
||||
interpFrame.clockTime += dt;
|
||||
else if (this.time + dt >= 3.5) {
|
||||
interpFrame.clockTime += (this.time + dt) - 3.5;
|
||||
}
|
||||
interpFrame.clockTime += dt - this.bonusTime;
|
||||
interpFrame.bonusTime = 0;
|
||||
}
|
||||
} else {
|
||||
if (this.time >= 3.5)
|
||||
interpFrame.clockTime += dt;
|
||||
else if (this.time + dt >= 3.5) {
|
||||
interpFrame.clockTime += (this.time + dt) - 3.5;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -30,14 +30,6 @@ class Util {
|
|||
return value;
|
||||
}
|
||||
|
||||
public static inline function imin(a:Int, b:Int) {
|
||||
return a < b ? a : b;
|
||||
}
|
||||
|
||||
public static inline function imax(a:Int, b:Int) {
|
||||
return a > b ? a : b;
|
||||
}
|
||||
|
||||
public static inline function lerp(a:Float, b:Float, t:Float) {
|
||||
return a + (b - a) * t;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -56,5 +56,7 @@ class BoxCollisionEntity extends CollisionEntity implements IBVHObject {
|
|||
return Math.POSITIVE_INFINITY;
|
||||
}
|
||||
|
||||
public override function sphereIntersection(collisionEntity:SphereCollisionEntity, timeState:TimeState, contacts:Array<CollisionInfo>) {}
|
||||
public override function sphereIntersection(collisionEntity:SphereCollisionEntity, timeState:TimeState) {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,7 +23,8 @@ class CollisionEntity implements IOctreeObject implements IBVHObject {
|
|||
|
||||
public var octree:Octree;
|
||||
|
||||
// public var bvh:BVHTree<CollisionSurface>;
|
||||
public var bvh:BVHTree<CollisionSurface>;
|
||||
|
||||
var grid:Grid;
|
||||
|
||||
public var surfaces:Array<CollisionSurface>;
|
||||
|
|
@ -66,12 +67,12 @@ class CollisionEntity implements IOctreeObject implements IBVHObject {
|
|||
// Generates the bvh
|
||||
public function finalize() {
|
||||
this.generateBoundingBox();
|
||||
// #if hl
|
||||
// this.bvh = new BVHTree();
|
||||
// for (surface in this.surfaces) {
|
||||
// this.bvh.add(surface);
|
||||
// }
|
||||
// #end
|
||||
#if hl
|
||||
this.bvh = new BVHTree();
|
||||
for (surface in this.surfaces) {
|
||||
this.bvh.add(surface);
|
||||
}
|
||||
#end
|
||||
var bbox = new Bounds();
|
||||
for (surface in this.surfaces)
|
||||
bbox.add(surface.boundingBox);
|
||||
|
|
@ -89,8 +90,7 @@ class CollisionEntity implements IOctreeObject implements IBVHObject {
|
|||
}
|
||||
go = null;
|
||||
surfaces = null;
|
||||
grid = null;
|
||||
// bvh = null;
|
||||
bvh = null;
|
||||
octree = null;
|
||||
}
|
||||
|
||||
|
|
@ -200,9 +200,7 @@ class CollisionEntity implements IOctreeObject implements IBVHObject {
|
|||
this.priority = priority;
|
||||
}
|
||||
|
||||
static var surfaceSearchPool:Array<CollisionSurface> = [];
|
||||
|
||||
public function sphereIntersection(collisionEntity:SphereCollisionEntity, timeState:TimeState, contacts:Array<CollisionInfo>) {
|
||||
public function sphereIntersection(collisionEntity:SphereCollisionEntity, timeState:TimeState) {
|
||||
var position = collisionEntity.transform.getPosition();
|
||||
var radius = collisionEntity.radius + 0.001;
|
||||
|
||||
|
|
@ -217,9 +215,7 @@ class CollisionEntity implements IOctreeObject implements IBVHObject {
|
|||
var invScale = invMatrix.getScale();
|
||||
var sphereRadius = new Vector(radius * invScale.x, radius * invScale.y, radius * invScale.z);
|
||||
sphereBounds.addSpherePos(localPos.x, localPos.y, localPos.z, Math.max(Math.max(sphereRadius.x, sphereRadius.y), sphereRadius.z) * 1.1);
|
||||
surfaceSearchPool.resize(0);
|
||||
grid.boundingSearch(sphereBounds, surfaceSearchPool);
|
||||
var surfaces = surfaceSearchPool;
|
||||
var surfaces = grid.boundingSearch(sphereBounds); // bvh == null ? octree.boundingSearch(sphereBounds).map(x -> cast x) : bvh.boundingSearch(sphereBounds);
|
||||
var invtform = invMatrix.clone();
|
||||
invtform.transpose();
|
||||
|
||||
|
|
@ -231,6 +227,8 @@ class CollisionEntity implements IOctreeObject implements IBVHObject {
|
|||
invtform.load(Matrix.I());
|
||||
}
|
||||
|
||||
var contacts = [];
|
||||
|
||||
for (obj in surfaces) {
|
||||
var surface:CollisionSurface = cast obj;
|
||||
|
||||
|
|
@ -303,5 +301,7 @@ class CollisionEntity implements IOctreeObject implements IBVHObject {
|
|||
// if (surfaceBestContact != null)
|
||||
// contacts.push(surfaceBestContact);
|
||||
}
|
||||
|
||||
return contacts;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ class CollisionHull extends CollisionEntity {
|
|||
super(go);
|
||||
}
|
||||
|
||||
public override function sphereIntersection(collisionEntity:SphereCollisionEntity, timeState:TimeState, contacts:Array<CollisionInfo>) {
|
||||
public override function sphereIntersection(collisionEntity:SphereCollisionEntity, timeState:TimeState):Array<CollisionInfo> {
|
||||
var bbox = this.boundingBox;
|
||||
var box = new Bounds();
|
||||
var pos = collisionEntity.transform.getPosition();
|
||||
|
|
@ -51,9 +51,10 @@ class CollisionHull extends CollisionEntity {
|
|||
cinfo.friction = friction;
|
||||
cinfo.force = force;
|
||||
this.go.onMarbleContact(collisionEntity.marble, timeState, cinfo);
|
||||
contacts.push(cinfo);
|
||||
return [cinfo];
|
||||
}
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
public override function addSurface(surface:CollisionSurface) {
|
||||
|
|
|
|||
|
|
@ -38,12 +38,11 @@ class CollisionWorld {
|
|||
this.dynamicGrid.build();
|
||||
}
|
||||
|
||||
var contactList:Array<CollisionInfo> = [];
|
||||
var intersectionList:Array<CollisionEntity> = [];
|
||||
|
||||
public function sphereIntersection(spherecollision:SphereCollisionEntity, timeState:TimeState, contacts:Array<CollisionInfo>) {
|
||||
public function sphereIntersection(spherecollision:SphereCollisionEntity, timeState:TimeState):SphereIntersectionResult {
|
||||
var position = spherecollision.transform.getPosition();
|
||||
var radius = spherecollision.radius;
|
||||
// var velocity = spherecollision.velocity;
|
||||
// var intersections = this.octree.radiusSearch(position, searchdist);
|
||||
|
||||
var box = new Bounds();
|
||||
box.addSpherePos(0, 0, 0, radius);
|
||||
|
|
@ -51,24 +50,60 @@ class CollisionWorld {
|
|||
box.transform(rotQuat.toMatrix());
|
||||
box.offset(position.x, position.y, position.z);
|
||||
// box.addSpherePos(position.x + velocity.x * timeState.dt, position.y + velocity.y * timeState.dt, position.z + velocity.z * timeState.dt, radius);
|
||||
this.intersectionList.resize(0);
|
||||
this.grid.boundingSearch(box, this.intersectionList);
|
||||
dynamicGrid.boundingSearch(box, this.intersectionList);
|
||||
var intersections = this.grid.boundingSearch(box);
|
||||
|
||||
for (obj in this.intersectionList) {
|
||||
if (obj != spherecollision) {
|
||||
var entity = obj;
|
||||
// var intersections = this.rtree.search([box.xMin, box.yMax, box.zMin], [box.xSize, box.ySize, box.zSize]);
|
||||
|
||||
if (obj.boundingBox.collide(box) && entity.go.isCollideable) {
|
||||
entity.sphereIntersection(spherecollision, timeState, contacts);
|
||||
}
|
||||
var contacts = [];
|
||||
var foundEntities = [];
|
||||
|
||||
for (obj in intersections) {
|
||||
var entity:CollisionEntity = cast obj;
|
||||
|
||||
foundEntities.push(entity);
|
||||
if (entity.go.isCollideable) {
|
||||
contacts = contacts.concat(entity.sphereIntersection(spherecollision, timeState));
|
||||
}
|
||||
}
|
||||
|
||||
// if (marbleEntities.length > 1) {
|
||||
// marbleSap.recompute();
|
||||
// var sapCollisions = marbleSap.getIntersections(spherecollision);
|
||||
// for (obj in sapCollisions) {
|
||||
// if (obj.go.isCollideable) {
|
||||
// contacts = contacts.concat(obj.sphereIntersection(spherecollision, timeState));
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
// contacts = contacts.concat(this.staticWorld.sphereIntersection(spherecollision, timeState));
|
||||
|
||||
var dynSearch = dynamicGrid.boundingSearch(box);
|
||||
for (obj in dynSearch) {
|
||||
if (obj != spherecollision) {
|
||||
var col = cast(obj, CollisionEntity);
|
||||
if (col.boundingBox.collide(box) && col.go.isCollideable)
|
||||
contacts = contacts.concat(col.sphereIntersection(spherecollision, timeState));
|
||||
}
|
||||
}
|
||||
|
||||
// for (marb in marbleEntities) {
|
||||
// if (marb != spherecollision) {
|
||||
// if (spherecollision.go.isCollideable) {
|
||||
// var isecs = marb.sphereIntersection(spherecollision, timeState);
|
||||
// if (isecs.length > 0)
|
||||
// foundEntities.push(marb);
|
||||
// contacts = contacts.concat(isecs);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
return {foundEntities: foundEntities, contacts: contacts};
|
||||
}
|
||||
|
||||
public function boundingSearch(bounds:Bounds, contacts:Array<CollisionEntity>, useCache:Bool = true) {
|
||||
this.grid.boundingSearch(bounds, contacts);
|
||||
dynamicGrid.boundingSearch(bounds, contacts);
|
||||
public function boundingSearch(bounds:Bounds, useCache:Bool = true) {
|
||||
var contacts = this.grid.boundingSearch(bounds).map(x -> cast(x, CollisionEntity));
|
||||
contacts = contacts.concat(dynamicGrid.boundingSearch(bounds).map(x -> cast(x, CollisionEntity)));
|
||||
return contacts;
|
||||
}
|
||||
|
||||
public function rayCast(rayStart:Vector, rayDirection:Vector, rayLength:Float) {
|
||||
|
|
@ -81,17 +116,19 @@ class CollisionWorld {
|
|||
+ rayDirection.x * rayLength, rayStart.y
|
||||
+ rayDirection.y * rayLength, rayStart.z
|
||||
+ rayDirection.z * rayLength);
|
||||
this.intersectionList.resize(0);
|
||||
|
||||
this.grid.boundingSearch(bounds, this.intersectionList);
|
||||
dynamicGrid.boundingSearch(bounds, this.intersectionList);
|
||||
|
||||
var objs = this.grid.boundingSearch(bounds);
|
||||
var dynObjs = dynamicGrid.boundingSearch(bounds);
|
||||
var results = [];
|
||||
for (obj in this.intersectionList) {
|
||||
var oo = obj;
|
||||
for (obj in objs) {
|
||||
var oo = cast(obj, CollisionEntity);
|
||||
oo.rayCast(rayStart, rayDirection, results, rayLength);
|
||||
}
|
||||
|
||||
for (obj in dynObjs) {
|
||||
var oo = cast(obj, CollisionEntity);
|
||||
oo.rayCast(rayStart, rayDirection, results, rayLength);
|
||||
}
|
||||
// results = results.concat(this.staticWorld.rayCast(rayStart, rayDirection));
|
||||
return results;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -75,7 +75,7 @@ class Grid {
|
|||
}
|
||||
|
||||
// searchbox should be in LOCAL coordinates
|
||||
public function boundingSearch(searchbox:Bounds, foundSurfaces:Array<CollisionSurface>) {
|
||||
public function boundingSearch(searchbox:Bounds) {
|
||||
var queryMinX = Math.max(searchbox.xMin, bounds.xMin);
|
||||
var queryMinY = Math.max(searchbox.yMin, bounds.yMin);
|
||||
var queryMaxX = Math.min(searchbox.xMax, bounds.xMax);
|
||||
|
|
@ -94,6 +94,8 @@ class Grid {
|
|||
if (yEnd > CELL_SIZE)
|
||||
yEnd = CELL_SIZE;
|
||||
|
||||
var foundSurfaces = [];
|
||||
|
||||
searchKey++;
|
||||
|
||||
// Insert the surface references from [xStart, yStart, zStart] to [xEnd, yEnd, zEnd] into the map
|
||||
|
|
@ -111,6 +113,8 @@ class Grid {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
return foundSurfaces;
|
||||
}
|
||||
|
||||
function elegantPair(x:Int, y:Int) {
|
||||
|
|
|
|||
|
|
@ -111,10 +111,10 @@ class GridBroadphase {
|
|||
var queryMinY = Math.max(object.boundingBox.yMin, bounds.yMin);
|
||||
var queryMaxX = Math.min(object.boundingBox.xMax, bounds.xMax);
|
||||
var queryMaxY = Math.min(object.boundingBox.yMax, bounds.yMax);
|
||||
var xStart = Util.imax(0, Math.floor((queryMinX - bounds.xMin) / this.cellSize.x));
|
||||
var yStart = Util.imax(0, Math.floor((queryMinY - bounds.yMin) / this.cellSize.y));
|
||||
var xEnd = Util.imin(CELL_SIZE - 1, Math.floor((queryMaxX - bounds.xMin) / this.cellSize.x));
|
||||
var yEnd = Util.imin(CELL_SIZE - 1, Math.floor((queryMaxY - bounds.yMin) / this.cellSize.y));
|
||||
var xStart = Math.floor((queryMinX - bounds.xMin) / this.cellSize.x);
|
||||
var yStart = Math.floor((queryMinY - bounds.yMin) / this.cellSize.y);
|
||||
var xEnd = Math.floor((queryMaxX - bounds.xMin) / this.cellSize.x);
|
||||
var yEnd = Math.floor((queryMaxY - bounds.yMin) / this.cellSize.y);
|
||||
var proxy = objectToProxy.get(object);
|
||||
if (proxy == null) {
|
||||
insert(object);
|
||||
|
|
@ -221,7 +221,7 @@ class GridBroadphase {
|
|||
}
|
||||
|
||||
// searchbox should be in LOCAL coordinates
|
||||
public function boundingSearch(searchbox:Bounds, foundSurfaces:Array<CollisionEntity>) {
|
||||
public function boundingSearch(searchbox:Bounds) {
|
||||
var queryMinX = Math.max(searchbox.xMin, bounds.xMin);
|
||||
var queryMinY = Math.max(searchbox.yMin, bounds.yMin);
|
||||
var queryMaxX = Math.min(searchbox.xMax, bounds.xMax);
|
||||
|
|
@ -240,6 +240,8 @@ class GridBroadphase {
|
|||
if (yEnd > CELL_SIZE)
|
||||
yEnd = CELL_SIZE;
|
||||
|
||||
var foundSurfaces = [];
|
||||
|
||||
searchKey++;
|
||||
|
||||
// Insert the surface references from [xStart, yStart, zStart] to [xEnd, yEnd, zEnd] into the map
|
||||
|
|
|
|||
|
|
@ -75,9 +75,10 @@ class SphereCollisionEntity extends CollisionEntity {
|
|||
return Math.POSITIVE_INFINITY;
|
||||
}
|
||||
|
||||
public override function sphereIntersection(collisionEntity:SphereCollisionEntity, timeState:TimeState, contacts:Array<CollisionInfo>) {
|
||||
public override function sphereIntersection(collisionEntity:SphereCollisionEntity, timeState:TimeState) {
|
||||
if (ignore)
|
||||
return;
|
||||
return [];
|
||||
var contacts = [];
|
||||
var thispos = transform.getPosition();
|
||||
var position = collisionEntity.transform.getPosition();
|
||||
var velocity = collisionEntity.velocity;
|
||||
|
|
@ -112,5 +113,6 @@ class SphereCollisionEntity extends CollisionEntity {
|
|||
// othercontact.penetration = this.radius - (thispos.sub(othercontact.point).dot(othercontact.normal));
|
||||
// this.marble.queueCollision(othercontact);
|
||||
}
|
||||
return contacts;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -130,27 +130,14 @@ class ManifestEntry extends FileEntry {
|
|||
if (onReady != null)
|
||||
onReady();
|
||||
} else {
|
||||
js.Browser.window.fetch(file)
|
||||
.then((res:js.html.Response) -> {
|
||||
return res.arrayBuffer();
|
||||
})
|
||||
.then((buf:js.lib.ArrayBuffer) -> {
|
||||
loaded = true;
|
||||
bytes = Bytes.ofData(buf);
|
||||
if (onReady != null)
|
||||
onReady();
|
||||
})
|
||||
.catchError((e) -> {
|
||||
// Try the original file path
|
||||
js.Browser.window.fetch('data/' + originalFile).then((res:js.html.Response) -> {
|
||||
return res.arrayBuffer();
|
||||
}).then((buf:js.lib.ArrayBuffer) -> {
|
||||
loaded = true;
|
||||
bytes = Bytes.ofData(buf);
|
||||
if (onReady != null)
|
||||
onReady();
|
||||
});
|
||||
});
|
||||
js.Browser.window.fetch(file).then((res:js.html.Response) -> {
|
||||
return res.arrayBuffer();
|
||||
}).then((buf:js.lib.ArrayBuffer) -> {
|
||||
loaded = true;
|
||||
bytes = Bytes.ofData(buf);
|
||||
if (onReady != null)
|
||||
onReady();
|
||||
});
|
||||
}
|
||||
#else
|
||||
if (onReady != null)
|
||||
|
|
|
|||
|
|
@ -89,10 +89,10 @@ class ChatCtrl extends GuiControl {
|
|||
if (StringTools.trim(this.chatHudInput.text.text) != "") {
|
||||
sendText = '<font color="#F29515">${StringTools.htmlEscape(Settings.highscoreName.substr(0, 20))}:</font> ${StringTools.htmlEscape(this.chatHudInput.text.text.substr(0, 150))}';
|
||||
if (Net.isClient) {
|
||||
NetCommands.sendChatMessage(sendText);
|
||||
NetCommands.sendChatMessage(StringTools.htmlEscape(sendText));
|
||||
}
|
||||
if (Net.isHost) {
|
||||
NetCommands.sendServerChatMessage(sendText);
|
||||
NetCommands.sendServerChatMessage(StringTools.htmlEscape(sendText));
|
||||
}
|
||||
}
|
||||
this.chatHudInput.text.text = "";
|
||||
|
|
@ -118,7 +118,7 @@ class ChatCtrl extends GuiControl {
|
|||
}
|
||||
|
||||
public function addChatMessage(text:String) {
|
||||
var realText = text;
|
||||
var realText = StringTools.htmlUnescape(text);
|
||||
this.chats.push({
|
||||
text: realText,
|
||||
age: 10.0
|
||||
|
|
|
|||
|
|
@ -19,8 +19,7 @@ class EndGameGui extends GuiControl {
|
|||
|
||||
var scoreSubmitted:Bool = false;
|
||||
|
||||
public function new(continueFunc:GuiControl->Void, restartFunc:GuiControl->Void, nextLevelFunc:GuiControl->Void, mission:Mission, timeState:TimeState,
|
||||
replayData:haxe.io.Bytes) {
|
||||
public function new(continueFunc:GuiControl->Void, restartFunc:GuiControl->Void, nextLevelFunc:GuiControl->Void, mission:Mission, timeState:TimeState) {
|
||||
super();
|
||||
this.horizSizing = Width;
|
||||
this.vertSizing = Height;
|
||||
|
|
@ -71,7 +70,7 @@ class EndGameGui extends GuiControl {
|
|||
nextLevelPreview.extent = new Vector(160, 110);
|
||||
nextLevel.addChild(nextLevelPreview);
|
||||
|
||||
mission.getNextMission()?.getPreviewImage(t -> {
|
||||
mission.getNextMission().getPreviewImage(t -> {
|
||||
nextLevelPreview.bmp.tile = t;
|
||||
});
|
||||
|
||||
|
|
@ -313,11 +312,12 @@ class EndGameGui extends GuiControl {
|
|||
scoreData.push({name: "Matan W.", time: 5999.999});
|
||||
}
|
||||
|
||||
egFirstLine.text.text = '<p align="left"><font color="#EEC884">1. </font>${StringTools.htmlEscape(scoreData[0].name)}</p>';
|
||||
egSecondLine.text.text = '<p align="left"><font color="#CDCDCD">2. </font>${StringTools.htmlEscape(scoreData[1].name)}</p>';
|
||||
egThirdLine.text.text = '<p align="left"><font color="#C9AFA0">3. </font>${StringTools.htmlEscape(scoreData[2].name)}</p>';
|
||||
egFourthLine.text.text = '<p align="left"><font color="#A4A4A4">4. </font>${StringTools.htmlEscape(scoreData[3].name)}</p>';
|
||||
egFifthLine.text.text = '<p align="left"><font color="#949494">5. </font>${StringTools.htmlEscape(scoreData[4].name)}</p>';
|
||||
egFirstLine.text.text = '<p align="left"><font color="#EEC884">1. </font>${scoreData[0].name}</p>';
|
||||
egSecondLine.text.text = '<p align="left"><font color="#CDCDCD">2. </font>${scoreData[1].name}</p>';
|
||||
egThirdLine.text.text = '<p align="left"><font color="#C9AFA0">3. </font>${scoreData[2].name}</p>';
|
||||
egFourthLine.text.text = '<p align="left"><font color="#A4A4A4">4. </font>${scoreData[3].name}</p>';
|
||||
egFifthLine.text.text = '<p align="left"><font color="#949494">5. </font>${scoreData[4].name}</p>';
|
||||
|
||||
var lineelems = [
|
||||
egFirstLineScore,
|
||||
egSecondLineScore,
|
||||
|
|
@ -393,9 +393,6 @@ class EndGameGui extends GuiControl {
|
|||
// }
|
||||
Settings.save();
|
||||
|
||||
var rewindUsed = MarbleGame.instance.world.rewindUsed;
|
||||
var cheatsUsed = MarbleGame.instance.world.cheatsUsed;
|
||||
|
||||
if (idx <= 4) {
|
||||
setButtonStates(false);
|
||||
var end = new EnterNameDlg(idx, (name) -> {
|
||||
|
|
@ -425,23 +422,23 @@ class EndGameGui extends GuiControl {
|
|||
}
|
||||
}
|
||||
|
||||
if (!cheatsUsed) { // dont submit or save if we have cheated
|
||||
Settings.saveScore(mission.path, myScore);
|
||||
var lbPath = mission.path;
|
||||
if (mission.isClaMission)
|
||||
lbPath = 'custom/${mission.id}';
|
||||
Leaderboards.submitScore(lbPath, myScore.time, rewindUsed, (sendReplay, rowId) -> {
|
||||
if (sendReplay && !mission.isClaMission) {
|
||||
Leaderboards.submitReplay(rowId, replayData);
|
||||
}
|
||||
});
|
||||
}
|
||||
Settings.saveScore(mission.path, myScore);
|
||||
var lbPath = mission.path;
|
||||
if (mission.isClaMission)
|
||||
lbPath = 'custom/${mission.id}';
|
||||
var replayData = MarbleGame.instance.world.replay.write();
|
||||
Leaderboards.submitScore(lbPath, myScore.time, MarbleGame.instance.world.rewindUsed, (sendReplay, rowId) -> {
|
||||
if (sendReplay && !mission.isClaMission) {
|
||||
Leaderboards.submitReplay(rowId, replayData);
|
||||
}
|
||||
});
|
||||
|
||||
scoreSubmitted = true;
|
||||
});
|
||||
this.addChild(end);
|
||||
} else {
|
||||
// Check if we can submit LB scores
|
||||
var replayData = MarbleGame.instance.world.replay.write();
|
||||
var lbPath = mission.path;
|
||||
if (mission.isClaMission)
|
||||
lbPath = 'custom/${mission.id}';
|
||||
|
|
@ -455,14 +452,12 @@ class EndGameGui extends GuiControl {
|
|||
break;
|
||||
}
|
||||
}
|
||||
if (!cheatsUsed) {
|
||||
if (!hasMyScore || (hasMyScore && myTopScoreLB > timeState.gameplayClock)) {
|
||||
Leaderboards.submitScore(lbPath, timeState.gameplayClock, rewindUsed, (sendReplay, rowId) -> {
|
||||
if (sendReplay && !mission.isClaMission) {
|
||||
Leaderboards.submitReplay(rowId, replayData);
|
||||
}
|
||||
});
|
||||
}
|
||||
if (!hasMyScore || (hasMyScore && myTopScoreLB > timeState.gameplayClock)) {
|
||||
Leaderboards.submitScore(lbPath, timeState.gameplayClock, MarbleGame.instance.world.rewindUsed, (sendReplay, rowId) -> {
|
||||
if (sendReplay && !mission.isClaMission) {
|
||||
Leaderboards.submitReplay(rowId, replayData);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,7 +10,6 @@ import h2d.Tile;
|
|||
import h2d.Graphics;
|
||||
import src.MarbleGame;
|
||||
import src.Util;
|
||||
import haxe.Timer;
|
||||
|
||||
class GuiScrollCtrl extends GuiControl {
|
||||
public var scrollY:Float = 0;
|
||||
|
|
@ -41,12 +40,6 @@ class GuiScrollCtrl extends GuiControl {
|
|||
var dirty:Bool = true;
|
||||
var prevMousePos:Vector;
|
||||
|
||||
var scrollVelocity:Float = 0;
|
||||
var lastMoveStamp:Float = 0;
|
||||
var momentumActive:Bool = false;
|
||||
|
||||
static inline var MOMENTUM_DAMPING:Float = 8;
|
||||
|
||||
var _contentYPositions:Map<h2d.Object, Float> = [];
|
||||
|
||||
var deltaY:Float = 0;
|
||||
|
|
@ -251,9 +244,6 @@ class GuiScrollCtrl extends GuiControl {
|
|||
this.dirty = true;
|
||||
this.updateScrollVisual();
|
||||
this.prevMousePos = mouseState.position;
|
||||
this.scrollVelocity = 0;
|
||||
this.momentumActive = false;
|
||||
this.lastMoveStamp = Timer.stamp();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -263,8 +253,6 @@ class GuiScrollCtrl extends GuiControl {
|
|||
this.dirty = true;
|
||||
deltaY = 0;
|
||||
this.updateScrollVisual();
|
||||
this.momentumActive = Math.abs(scrollVelocity) > 0.01;
|
||||
this.lastMoveStamp = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -272,35 +260,15 @@ class GuiScrollCtrl extends GuiControl {
|
|||
if (Util.isTouchDevice()) {
|
||||
super.onMouseMove(mouseState);
|
||||
if (this.pressed) {
|
||||
var renderRect = this.getRenderRectangle();
|
||||
var scrollExtentY = renderRect.extent.y;
|
||||
var dy = (mouseState.position.y - this.prevMousePos.y) / ((maxScrollY * Settings.uiScale) / scrollExtentY);
|
||||
var dy = (mouseState.position.y - this.prevMousePos.y) * scrollSpeed / this.maxScrollY;
|
||||
deltaY = -dy;
|
||||
this.scrollY -= dy;
|
||||
this.prevMousePos = mouseState.position;
|
||||
var now = Timer.stamp();
|
||||
if (lastMoveStamp > 0) {
|
||||
var dt = now - lastMoveStamp;
|
||||
if (dt > 0)
|
||||
scrollVelocity = -dy / dt;
|
||||
}
|
||||
lastMoveStamp = now;
|
||||
momentumActive = false;
|
||||
this.updateScrollVisual();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override function onMouseLeave(mouseState:MouseState) {
|
||||
if (Util.isTouchDevice()) {
|
||||
this.pressed = false;
|
||||
this.dirty = true;
|
||||
this.updateScrollVisual();
|
||||
this.momentumActive = Math.abs(scrollVelocity) > 0.01;
|
||||
this.lastMoveStamp = 0;
|
||||
}
|
||||
}
|
||||
|
||||
public override function update(dt:Float, mouseState:MouseState) {
|
||||
if (Key.isPressed(Key.MOUSE_WHEEL_DOWN) && Math.abs(mouseState.wheel) >= 1) {
|
||||
var renderRect = this.getRenderRectangle();
|
||||
|
|
@ -317,21 +285,6 @@ class GuiScrollCtrl extends GuiControl {
|
|||
this.updateScrollVisual();
|
||||
}
|
||||
super.update(dt, mouseState);
|
||||
|
||||
if (!pressed && momentumActive) {
|
||||
var damping = Math.exp(-MOMENTUM_DAMPING * dt);
|
||||
scrollVelocity *= damping;
|
||||
if (Math.abs(scrollVelocity) < 0.01) {
|
||||
scrollVelocity = 0;
|
||||
momentumActive = false;
|
||||
return;
|
||||
}
|
||||
var before = scrollY;
|
||||
scrollY += scrollVelocity * dt;
|
||||
updateScrollVisual();
|
||||
if (scrollY == 0 || scrollY == before)
|
||||
momentumActive = false;
|
||||
}
|
||||
}
|
||||
|
||||
// public override function onMouseDown(mouseState:MouseState) {
|
||||
|
|
|
|||
|
|
@ -186,7 +186,7 @@ class JoinServerGui extends GuiImage {
|
|||
serverInfo.text.text = '<p align="center">Select a Server</p><p align="center">or Host your own</p>';
|
||||
} else {
|
||||
var server = ourServerList[curSelection];
|
||||
serverInfo.text.text = '<p align="center">${StringTools.htmlEscape(server.name)}</p><p align="center"><font face="MarkerFelt18" color="#DDDDEE">Hosted by ${StringTools.htmlEscape(server.host)}</font></p><p align="left">${StringTools.htmlEscape(server.description)}</p>';
|
||||
serverInfo.text.text = '<p align="center">${server.name}</p><p align="center"><font face="MarkerFelt18" color="#DDDDEE">Hosted by ${server.host}</font></p><p align="left">${server.description}</p>';
|
||||
}
|
||||
}
|
||||
serverListContainer.addChild(serverList);
|
||||
|
|
@ -197,7 +197,7 @@ class JoinServerGui extends GuiImage {
|
|||
|
||||
function updateServerListDisplay() {
|
||||
serverDisplays = ourServerList.map(x ->
|
||||
'<img src="${platformToString[x.platform]}"></img><font color="#FFFFFF">${StringTools.htmlEscape(x.name)} <offset value="${400 * Settings.uiScale}">${x.players}/${x.maxPlayers}</offset></font>');
|
||||
'<img src="${platformToString[x.platform]}"></img><font color="#FFFFFF">${x.name} <offset value="${400 * Settings.uiScale}">${x.players}/${x.maxPlayers}</offset></font>');
|
||||
serverList.setTexts(serverDisplays);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -434,10 +434,10 @@ class MPPlayMissionGui extends GuiImage {
|
|||
if (StringTools.trim(chatInput.text.text) != "") {
|
||||
var sendText = '<font color="#F29515">${StringTools.htmlEscape(Settings.highscoreName.substr(0, 20))}:</font> ${StringTools.htmlEscape(chatInput.text.text.substr(0, 100))}';
|
||||
if (Net.isClient) {
|
||||
NetCommands.sendChatMessage(sendText);
|
||||
NetCommands.sendChatMessage(StringTools.htmlEscape(sendText));
|
||||
}
|
||||
if (Net.isHost) {
|
||||
NetCommands.sendServerChatMessage(sendText);
|
||||
NetCommands.sendServerChatMessage(StringTools.htmlEscape(sendText));
|
||||
}
|
||||
}
|
||||
chatInput.text.text = "";
|
||||
|
|
@ -585,11 +585,11 @@ class MPPlayMissionGui extends GuiImage {
|
|||
currentSelection = -1;
|
||||
}
|
||||
|
||||
pmDesc.text.text = '<font face="MarkerFelt32" color="#E3F3FF"><p align="center">#${currentSelection + 1}: ${StringTools.htmlEscape(currentMission.title)}</p></font>'
|
||||
+ '<font face="MarkerFelt18" color="#CEE0F4">${StringTools.htmlEscape(currentMission.description)}</font>';
|
||||
pmDesc.text.text = '<font face="MarkerFelt32" color="#E3F3FF"><p align="center">#${currentSelection + 1}: ${currentMission.title}</p></font>'
|
||||
+ '<font face="MarkerFelt18" color="#CEE0F4">${currentMission.description}</font>';
|
||||
|
||||
parTime.text.text = '<font face="MarkerFelt24" color="#E3F3FF">Duration: <font color="#FFFFFF">${Util.formatTime(currentMission.qualifyTime)}</font></font><br/>'
|
||||
+ '<font face="MarkerFelt24" color="#E3F3FF">Author: <font color="#FFFFFF">${StringTools.htmlEscape(currentMission.artist)}</font></font>';
|
||||
+ '<font face="MarkerFelt24" color="#E3F3FF">Author: <font color="#FFFFFF">${currentMission.artist}</font></font>';
|
||||
|
||||
// pmPreview.bmp.tile = tmpprevtile;
|
||||
#if js
|
||||
|
|
@ -718,7 +718,7 @@ class MPPlayMissionGui extends GuiImage {
|
|||
}
|
||||
|
||||
var playerListCompiled = playerListArr.map(player ->
|
||||
'<img src="${platformToString(player.platform)}"></img><font color="#FFFFFF">${StringTools.htmlEscape(player.name)}<offset value="${220 * Settings.uiScale}">${player.ready ? "Ready" : ""}</offset></font>');
|
||||
'<img src="${platformToString(player.platform)}"></img><font color="#FFFFFF">${player.name}<offset value="${220 * Settings.uiScale}">${player.ready ? "Ready" : ""}</offset></font>');
|
||||
playerListCtrl.setTexts(playerListCompiled);
|
||||
|
||||
// if (!showingCustoms)
|
||||
|
|
@ -728,7 +728,7 @@ class MPPlayMissionGui extends GuiImage {
|
|||
}
|
||||
|
||||
public static function addChatMessage(s:String) {
|
||||
var realText = s;
|
||||
var realText = StringTools.htmlUnescape(s);
|
||||
allChats.push(realText);
|
||||
if (allChats.length > 100) {
|
||||
allChats = allChats.slice(allChats.length - 100);
|
||||
|
|
|
|||
|
|
@ -47,13 +47,6 @@ class MainMenuGui extends GuiImage {
|
|||
return [normal, hover, pressed];
|
||||
}
|
||||
|
||||
function loadStaticButtonImages(path:String) {
|
||||
var normal = ResourceLoader.getResource('${path}.png', ResourceLoader.getImage, this.imageResources).toTile();
|
||||
var hover = ResourceLoader.getResource('${path}.png', ResourceLoader.getImage, this.imageResources).toTile();
|
||||
var pressed = ResourceLoader.getResource('${path}.png', ResourceLoader.getImage, this.imageResources).toTile();
|
||||
return [normal, hover, pressed];
|
||||
}
|
||||
|
||||
var siteButton = new GuiButton(loadButtonImages('data/ui/menu/site'));
|
||||
siteButton.horizSizing = Right;
|
||||
siteButton.vertSizing = Top;
|
||||
|
|
@ -239,38 +232,6 @@ class MainMenuGui extends GuiImage {
|
|||
}
|
||||
this.addChild(github);
|
||||
|
||||
#if js
|
||||
var mbg = new GuiButton(loadStaticButtonImages("data/ui/icon_mbg"));
|
||||
mbg.horizSizing = Right;
|
||||
mbg.vertSizing = Top;
|
||||
mbg.position = new Vector(0, 380);
|
||||
mbg.extent = new Vector(76, 76);
|
||||
mbg.pressedAction = (sender) -> {
|
||||
js.Browser.window.open("https://marbleblastgold.randomityguy.me");
|
||||
}
|
||||
this.addChild(mbg);
|
||||
|
||||
var mbu = new GuiButton(loadStaticButtonImages("data/ui/icon_mbu"));
|
||||
mbu.horizSizing = Right;
|
||||
mbu.vertSizing = Top;
|
||||
mbu.position = new Vector(76, 380);
|
||||
mbu.extent = new Vector(76, 76);
|
||||
mbu.pressedAction = (sender) -> {
|
||||
js.Browser.window.open("https://marbleblastultra.randomityguy.me");
|
||||
}
|
||||
this.addChild(mbu);
|
||||
|
||||
var discord = new GuiButton(loadStaticButtonImages("data/ui/discord"));
|
||||
discord.horizSizing = Right;
|
||||
discord.vertSizing = Top;
|
||||
discord.position = new Vector(0, 320);
|
||||
discord.extent = new Vector(152, 60);
|
||||
discord.pressedAction = (sender) -> {
|
||||
js.Browser.window.open("https://discord.gg/q4JdnRbVhF");
|
||||
}
|
||||
this.addChild(discord);
|
||||
#end
|
||||
|
||||
#if js
|
||||
var urlParams = new js.html.URLSearchParams(js.Browser.window.location.search);
|
||||
var playParam = urlParams.get("app");
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
package gui;
|
||||
|
||||
import haxe.DynamicAccess;
|
||||
import hxd.BitmapData;
|
||||
import h2d.filter.DropShadow;
|
||||
import h2d.Text;
|
||||
|
|
@ -71,10 +70,10 @@ class OptionsDlg extends GuiImage {
|
|||
hotkeysBtn.extent = new Vector(134, 65);
|
||||
window.addChild(hotkeysBtn);
|
||||
|
||||
var miscBtn = new GuiButton(loadButtonImages('data/ui/options/misc'));
|
||||
miscBtn.position = new Vector(548, 19);
|
||||
miscBtn.extent = new Vector(134, 65);
|
||||
window.addChild(miscBtn);
|
||||
var onlineBtn = new GuiImage(ResourceLoader.getResource("data/ui/options/online_i.png", ResourceLoader.getImage, this.imageResources).toTile());
|
||||
onlineBtn.position = new Vector(548, 19);
|
||||
onlineBtn.extent = new Vector(134, 65);
|
||||
window.addChild(onlineBtn);
|
||||
|
||||
var generalPanel:GuiScrollCtrl = null;
|
||||
|
||||
|
|
@ -115,10 +114,6 @@ class OptionsDlg extends GuiImage {
|
|||
hotkeysPanel.position = new Vector(30, 88);
|
||||
hotkeysPanel.extent = new Vector(726, 394);
|
||||
|
||||
var miscPanel = new GuiControl();
|
||||
miscPanel.position = new Vector(30, 88);
|
||||
miscPanel.extent = new Vector(726, 394);
|
||||
|
||||
var markerFelt32fontdata = ResourceLoader.getFileEntry("data/font/MarkerFelt.fnt");
|
||||
var markerFelt32b = new BitmapFont(markerFelt32fontdata.entry);
|
||||
@:privateAccess markerFelt32b.loader = ResourceLoader.loader;
|
||||
|
|
@ -475,31 +470,6 @@ class OptionsDlg extends GuiImage {
|
|||
parent.addChild(remapBtn);
|
||||
}
|
||||
|
||||
function makeButton(text:String, yPos:Int, buttonText:String, pressedAction:() -> Void, parent:GuiControl, right:Bool = false) {
|
||||
var textObj = new GuiText(markerFelt32);
|
||||
textObj.position = new Vector(right ? 368 : 5, yPos);
|
||||
textObj.extent = new Vector(212, 14);
|
||||
textObj.text.text = text;
|
||||
textObj.text.textColor = 0xFFFFFF;
|
||||
textObj.text.dropShadow = {
|
||||
dx: 1 * Settings.uiScale,
|
||||
dy: 1 * Settings.uiScale,
|
||||
alpha: 0.5,
|
||||
color: 0
|
||||
};
|
||||
parent.addChild(textObj);
|
||||
|
||||
var btn = new GuiButtonText(loadButtonImages("data/ui/options/bind"), markerFelt24);
|
||||
btn.position = new Vector(right ? 363 + 203 : 203, yPos - 3);
|
||||
btn.txtCtrl.text.text = buttonText;
|
||||
btn.setExtent(new Vector(152, 49));
|
||||
btn.pressedAction = (sender) -> {
|
||||
pressedAction();
|
||||
}
|
||||
|
||||
parent.addChild(btn);
|
||||
}
|
||||
|
||||
if (Util.isTouchDevice()) {
|
||||
var textObj = new GuiText(markerFelt32);
|
||||
textObj.position = new Vector(5, 38);
|
||||
|
|
@ -570,85 +540,10 @@ class OptionsDlg extends GuiImage {
|
|||
hotkeysPanel, true);
|
||||
}
|
||||
|
||||
// MISC PANEL
|
||||
makeButton("Import Progress:", 38, "Import", () -> {
|
||||
hxd.File.browse((sel) -> {
|
||||
sel.load((data) -> {
|
||||
try {
|
||||
// convert to string
|
||||
var jsonStr = data.toString();
|
||||
// parse JSON
|
||||
var json = haxe.Json.parse(jsonStr);
|
||||
|
||||
var highScoreData:DynamicAccess<Array<Score>> = json.highScores;
|
||||
for (key => value in highScoreData) {
|
||||
Settings.highScores.set(key, value);
|
||||
}
|
||||
var easterEggData:DynamicAccess<Float> = json.easterEggs;
|
||||
if (easterEggData != null) {
|
||||
for (key => value in easterEggData) {
|
||||
Settings.easterEggs.set(key, value);
|
||||
}
|
||||
}
|
||||
MarbleGame.canvas.pushDialog(new MessageBoxOkDlg("Progress data imported successfully!"));
|
||||
Settings.save();
|
||||
} catch (e) {
|
||||
MarbleGame.canvas.pushDialog(new MessageBoxOkDlg("Failed to import progress data: " + e.message));
|
||||
}
|
||||
});
|
||||
}, {
|
||||
title: "Select a progress file to import",
|
||||
fileTypes: [
|
||||
{name: "JSON files", extensions: ["json"]},
|
||||
{name: "All files", extensions: ["*"]}
|
||||
],
|
||||
});
|
||||
}, miscPanel);
|
||||
makeButton("Export Progress:", 38, "Export", () -> {
|
||||
#if sys
|
||||
#if MACOS_BUNDLE
|
||||
// open the finder to that folder
|
||||
Sys.command('open "${Settings.settingsDir}"');
|
||||
#else
|
||||
// Just open the folder in the explorer.exe
|
||||
Sys.command('explorer.exe "${Settings.settingsDir}"');
|
||||
#end
|
||||
MarbleGame.canvas.pushDialog(new MessageBoxOkDlg("The settings.json file contains your progress data. You can copy it to another device or share it with others."));
|
||||
#end
|
||||
#if js
|
||||
// Serialize Settings to JSON
|
||||
var localStorage = js.Browser.getLocalStorage();
|
||||
if (localStorage != null) {
|
||||
var settingsData = localStorage.getItem("MBHaxeSettings");
|
||||
if (settingsData != null) {
|
||||
// Download this
|
||||
var replayBytes = settingsData;
|
||||
var blob = new js.html.Blob([haxe.io.Bytes.ofString(replayBytes).getData()], {
|
||||
type: 'application/octet-stream'
|
||||
});
|
||||
var url = js.html.URL.createObjectURL(blob);
|
||||
var fname = 'settings.json';
|
||||
var element = js.Browser.document.createElement('a');
|
||||
element.setAttribute('href', url);
|
||||
element.setAttribute('download', fname);
|
||||
|
||||
element.style.display = 'none';
|
||||
js.Browser.document.body.appendChild(element);
|
||||
|
||||
element.click();
|
||||
|
||||
js.Browser.document.body.removeChild(element);
|
||||
js.html.URL.revokeObjectURL(url);
|
||||
}
|
||||
}
|
||||
#end
|
||||
}, miscPanel, true);
|
||||
|
||||
generalBtn.pressedAction = (e) -> {
|
||||
if (currentTab != "general") {
|
||||
currentTab = "general";
|
||||
hotkeysPanel.parent?.removeChild(hotkeysPanel);
|
||||
miscPanel.parent?.removeChild(miscPanel);
|
||||
hotkeysPanel.parent.removeChild(hotkeysPanel);
|
||||
generalPanel.scrollY = 0;
|
||||
window.addChild(generalPanel);
|
||||
MarbleGame.canvas.render(MarbleGame.canvas.scene2d); // Force refresh
|
||||
|
|
@ -658,23 +553,12 @@ class OptionsDlg extends GuiImage {
|
|||
hotkeysBtn.pressedAction = (e) -> {
|
||||
if (currentTab != "hotkeys") {
|
||||
currentTab = "hotkeys";
|
||||
generalPanel.parent?.removeChild(generalPanel);
|
||||
miscPanel.parent?.removeChild(miscPanel);
|
||||
generalPanel.parent.removeChild(generalPanel);
|
||||
window.addChild(hotkeysPanel);
|
||||
MarbleGame.canvas.render(MarbleGame.canvas.scene2d); // Force refresh
|
||||
}
|
||||
};
|
||||
|
||||
miscBtn.pressedAction = (e) -> {
|
||||
if (currentTab != "misc") {
|
||||
currentTab = "misc";
|
||||
generalPanel.parent?.removeChild(generalPanel);
|
||||
hotkeysPanel.parent?.removeChild(hotkeysPanel);
|
||||
window.addChild(miscPanel);
|
||||
MarbleGame.canvas.render(MarbleGame.canvas.scene2d); // Force refresh
|
||||
}
|
||||
};
|
||||
|
||||
// // Touch Controls buttons???
|
||||
// if (Util.isTouchDevice()) {
|
||||
// var touchControlsTxt = new GuiText(domcasual24);
|
||||
|
|
|
|||
|
|
@ -738,7 +738,7 @@ class PlayGui {
|
|||
} else {
|
||||
isSpectating = Net.clientIdMap[item.id].spectator;
|
||||
}
|
||||
pl.push('<font color="${color}">${i + 1}. ${isSpectating ? "[S] " : ""}${Util.rightPad(StringTools.htmlEscape(item.name), 25, 3)}</font>');
|
||||
pl.push('<font color="${color}">${i + 1}. ${isSpectating ? "[S] " : ""}${Util.rightPad(item.name, 25, 3)}</font>');
|
||||
var connPing = item.us ? (Net.isHost ? 0 : Net.clientConnection.pingTicks) : (item.id == 0 ? 0 : Net.clientIdMap[item.id].pingTicks);
|
||||
var pingStatus = "unknown";
|
||||
if (connPing <= 5)
|
||||
|
|
|
|||
|
|
@ -1234,7 +1234,7 @@ class PlayMissionGui extends GuiImage {
|
|||
var i = 1;
|
||||
for (score in scoreList) {
|
||||
sFmt.push('${i}.
|
||||
<offset value="15">${StringTools.htmlEscape(score.name.substr(0, 30))}</offset>
|
||||
<offset value="15">${score.name.substr(0, 30)}</offset>
|
||||
<offset value="215">${Util.formatTime(score.score)}</offset>
|
||||
<offset value="279"><img src="${platformToString(score.platform)}"/></offset>
|
||||
${score.rewind == 1 ? '<offset value="299"><img src="rewind"/></offset> ' : ""}');
|
||||
|
|
|
|||
|
|
@ -96,7 +96,7 @@ class HuntMode extends NullMode {
|
|||
}
|
||||
} else if (element._type == MissionElementType.SimGroup) {
|
||||
var scanPls = true;
|
||||
if (Net.isMP && Net.connectedServerInfo.oldSpawns) {
|
||||
if (Net.connectedServerInfo.oldSpawns) {
|
||||
if (element._name.toLowerCase() == "newversion") {
|
||||
// Remove this
|
||||
elToRemove.push(element);
|
||||
|
|
@ -121,8 +121,8 @@ class HuntMode extends NullMode {
|
|||
};
|
||||
|
||||
override function getSpawnTransform() {
|
||||
var idx = (Net.isMP && Net.connectedServerInfo.competitiveMode) ? idealSpawnIndex : Math.floor(rng2.randRange(0, playerSpawnPoints.length - 1));
|
||||
if (!(Net.isMP && Net.connectedServerInfo.competitiveMode)) {
|
||||
var idx = Net.connectedServerInfo.competitiveMode ? idealSpawnIndex : Math.floor(rng2.randRange(0, playerSpawnPoints.length - 1));
|
||||
if (!Net.connectedServerInfo.competitiveMode) {
|
||||
var allTaken = true;
|
||||
for (spw in spawnPointTaken) {
|
||||
if (!spw) {
|
||||
|
|
@ -370,7 +370,7 @@ class HuntMode extends NullMode {
|
|||
var gemPos = gemElem.gem.getAbsPos().getPosition();
|
||||
|
||||
if (level.mission.missionInfo.game == "PlatinumQuest") {
|
||||
if (Net.isMP && Net.connectedServerInfo.oldSpawns) {
|
||||
if (Net.connectedServerInfo.oldSpawns) {
|
||||
// Spawn chances!
|
||||
var chance = switch (gemElem.gem.gemColor.toLowerCase()) {
|
||||
case "red.gem":
|
||||
|
|
@ -836,7 +836,7 @@ class HuntMode extends NullMode {
|
|||
}
|
||||
|
||||
override function update(t:src.TimeState) {
|
||||
if (this.level.isMultiplayer && Net.connectedServerInfo.competitiveMode) {
|
||||
if (Net.connectedServerInfo.competitiveMode) {
|
||||
if (competitiveTimerStartTicks != 0) {
|
||||
var currentTime = Net.isHost ? t.ticks : @:privateAccess level.marble.serverTicks;
|
||||
var endTime = competitiveTimerStartTicks + (20000 >> 5);
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ class InputBitStream {
|
|||
this.shift = 0;
|
||||
}
|
||||
|
||||
inline function readBits(bits:Int = 8) {
|
||||
function readBits(bits:Int = 8) {
|
||||
if (this.shift + bits >= 8) {
|
||||
var extra = (this.shift + bits) % 8;
|
||||
var remain = bits - extra;
|
||||
|
|
@ -37,7 +37,7 @@ class InputBitStream {
|
|||
}
|
||||
}
|
||||
|
||||
public inline function readInt(bits:Int = 32) {
|
||||
public function readInt(bits:Int = 32) {
|
||||
var value = 0;
|
||||
var shift = 0;
|
||||
while (bits > 0) {
|
||||
|
|
@ -48,40 +48,39 @@ class InputBitStream {
|
|||
return value;
|
||||
}
|
||||
|
||||
public inline function readFlag() {
|
||||
public function readFlag() {
|
||||
return readInt(1) != 0;
|
||||
}
|
||||
|
||||
public inline function readByte() {
|
||||
public function readByte() {
|
||||
return readInt(8);
|
||||
}
|
||||
|
||||
public inline function readUInt16() {
|
||||
public function readUInt16() {
|
||||
return readInt(16);
|
||||
}
|
||||
|
||||
public inline function readInt32() {
|
||||
public function readInt32() {
|
||||
return readInt(32);
|
||||
}
|
||||
|
||||
public inline function readFloat() {
|
||||
public function readFloat() {
|
||||
return FPHelper.i32ToFloat(readInt32());
|
||||
}
|
||||
|
||||
public inline function readDouble() {
|
||||
public function readDouble() {
|
||||
var lo = readInt32();
|
||||
var hi = readInt32();
|
||||
return FPHelper.i64ToDouble(lo, hi);
|
||||
}
|
||||
|
||||
public inline function readString() {
|
||||
public function readString() {
|
||||
var length = readUInt16();
|
||||
var str = "";
|
||||
var buf = new StringBuf();
|
||||
for (i in 0...length) {
|
||||
buf.addChar(readByte());
|
||||
str += String.fromCharCode(readByte());
|
||||
}
|
||||
return buf.toString();
|
||||
return str;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -100,7 +99,7 @@ class OutputBitStream {
|
|||
this.lastByte = 0;
|
||||
}
|
||||
|
||||
inline function writeBits(value:Int, bits:Int) {
|
||||
function writeBits(value:Int, bits:Int) {
|
||||
value = value & (0xFF >> (8 - bits));
|
||||
if (this.shift + bits >= 8) {
|
||||
var extra = (shift + bits) % 8;
|
||||
|
|
@ -119,7 +118,7 @@ class OutputBitStream {
|
|||
}
|
||||
}
|
||||
|
||||
public inline function writeInt(value:Int, bits:Int = 32) {
|
||||
public function writeInt(value:Int, bits:Int = 32) {
|
||||
while (bits > 0) {
|
||||
this.writeBits(value & 0xFF, bits < 8 ? bits : 8);
|
||||
value >>= 8;
|
||||
|
|
@ -127,39 +126,39 @@ class OutputBitStream {
|
|||
}
|
||||
}
|
||||
|
||||
public inline function writeFlag(value:Bool) {
|
||||
public function writeFlag(value:Bool) {
|
||||
writeInt(value ? 1 : 0, 1);
|
||||
}
|
||||
|
||||
public inline function writeByte(value:Int) {
|
||||
public function writeByte(value:Int) {
|
||||
writeInt(value, 8);
|
||||
}
|
||||
|
||||
public inline function writeUInt16(value:Int) {
|
||||
public function writeUInt16(value:Int) {
|
||||
writeInt(value, 16);
|
||||
}
|
||||
|
||||
public inline function writeInt32(value:Int) {
|
||||
public function writeInt32(value:Int) {
|
||||
writeInt(value, 32);
|
||||
}
|
||||
|
||||
public inline function getBytes() {
|
||||
public function getBytes() {
|
||||
this.data.writeByte(this.lastByte);
|
||||
return this.data.getBytes();
|
||||
}
|
||||
|
||||
public inline function writeFloat(value:Float) {
|
||||
public function writeFloat(value:Float) {
|
||||
writeInt(FPHelper.floatToI32(value), 32);
|
||||
}
|
||||
|
||||
public inline function writeString(value:String) {
|
||||
public function writeString(value:String) {
|
||||
writeUInt16(value.length);
|
||||
for (i in 0...value.length) {
|
||||
writeByte(StringTools.fastCodeAt(value, i));
|
||||
}
|
||||
}
|
||||
|
||||
public inline function writeDouble(value:Float) {
|
||||
public function writeDouble(value:Float) {
|
||||
var i64 = FPHelper.doubleToI64(value);
|
||||
writeInt32(i64.low);
|
||||
writeInt32(i64.high);
|
||||
|
|
|
|||
|
|
@ -46,61 +46,41 @@ class MarblePredictionStore {
|
|||
}
|
||||
|
||||
public function storeState(marble:Marble, tick:Int) {
|
||||
var arr = ensureHistory(marble);
|
||||
truncateFromTick(arr, tick);
|
||||
arr.push(new MarblePrediction(marble, tick));
|
||||
var state = new MarblePrediction(marble, tick);
|
||||
if (predictions.exists(marble)) {
|
||||
var arr = predictions[marble];
|
||||
while (arr.length != 0 && arr[0].tick >= tick)
|
||||
arr.shift();
|
||||
arr.push(state);
|
||||
} else {
|
||||
predictions.set(marble, [state]);
|
||||
}
|
||||
}
|
||||
|
||||
public function retrieveState(marble:Marble, tick:Int) {
|
||||
var arr = predictions.get(marble);
|
||||
if (arr == null)
|
||||
if (predictions.exists(marble)) {
|
||||
var arr = predictions[marble];
|
||||
while (arr.length != 0 && arr[0].tick < tick)
|
||||
arr.shift();
|
||||
if (arr.length == 0)
|
||||
return null;
|
||||
var p = arr[0];
|
||||
if (p.tick == tick)
|
||||
return p;
|
||||
return null;
|
||||
|
||||
dropBeforeTick(arr, tick);
|
||||
return (arr.length != 0 && arr[0].tick == tick) ? arr[0] : null;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public function clearStatesAfterTick(marble:Marble, tick:Int) {
|
||||
var arr = predictions.get(marble);
|
||||
if (arr != null)
|
||||
truncateFromTick(arr, tick);
|
||||
if (predictions.exists(marble)) {
|
||||
var arr = predictions[marble];
|
||||
while (arr.length != 0 && arr[arr.length - 1].tick >= tick)
|
||||
arr.pop();
|
||||
}
|
||||
}
|
||||
|
||||
public function removeMarbleFromPrediction(marble:Marble) {
|
||||
this.predictions.remove(marble);
|
||||
}
|
||||
|
||||
inline function ensureHistory(marble:Marble) {
|
||||
var arr = predictions.get(marble);
|
||||
if (arr == null) {
|
||||
arr = [];
|
||||
predictions.set(marble, arr);
|
||||
}
|
||||
return arr;
|
||||
}
|
||||
|
||||
inline function dropBeforeTick(arr:Array<MarblePrediction>, tick:Int) {
|
||||
var idx = lowerBound(arr, tick);
|
||||
if (idx > 0)
|
||||
arr.splice(0, idx);
|
||||
}
|
||||
|
||||
inline function truncateFromTick(arr:Array<MarblePrediction>, tick:Int) {
|
||||
var idx = lowerBound(arr, tick);
|
||||
if (idx < arr.length)
|
||||
arr.splice(idx, arr.length - idx);
|
||||
}
|
||||
|
||||
static inline function lowerBound(arr:Array<MarblePrediction>, tick:Int) {
|
||||
var lo = 0;
|
||||
var hi = arr.length;
|
||||
while (lo < hi) {
|
||||
var mid = (lo + hi) >> 1;
|
||||
if (arr[mid].tick < tick)
|
||||
lo = mid + 1;
|
||||
else
|
||||
hi = mid;
|
||||
}
|
||||
return lo;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ class MasterServerClient {
|
|||
#if js
|
||||
static var serverIp = "wss://mbpmaster.randomityguy.me:8443";
|
||||
#else
|
||||
static var serverIp = "ws://51.75.65.148:8084";
|
||||
static var serverIp = "ws://89.58.58.191:8084";
|
||||
#end
|
||||
public static var instance:MasterServerClient;
|
||||
|
||||
|
|
@ -102,6 +102,7 @@ class MasterServerClient {
|
|||
instance = null;
|
||||
}
|
||||
#if hl
|
||||
stopMutex.acquire();
|
||||
stopping = true;
|
||||
stopMutex.release();
|
||||
if (myToken == wsToken) {
|
||||
|
|
@ -194,14 +195,6 @@ class MasterServerClient {
|
|||
}
|
||||
}
|
||||
|
||||
public static function requestTurnCredentials() {
|
||||
if (instance != null && instance.open) {
|
||||
instance.queueMessage(Json.stringify({
|
||||
type: "turn_credentials"
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
function queueMessage(m:String) {
|
||||
#if hl
|
||||
toSend.add(m);
|
||||
|
|
@ -314,11 +307,8 @@ class MasterServerClient {
|
|||
}
|
||||
}
|
||||
}
|
||||
if (conts.type == "turn_credentials") {
|
||||
Net.turnServers = conts.turn_servers;
|
||||
if (@:privateAccess Net.onTurnServersReceived != null) {
|
||||
@:privateAccess Net.onTurnServersReceived();
|
||||
}
|
||||
if (conts.type == "turnserver") {
|
||||
Net.turnServer = conts.server; // Turn server!
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -147,7 +147,7 @@ class MoveManager {
|
|||
return netMove;
|
||||
}
|
||||
|
||||
inline function copyMove(to:Int, from:Int) {
|
||||
function copyMove(to:Int, from:Int) {
|
||||
queuedMoves[to].move = queuedMoves[from].move;
|
||||
queuedMoves[to].motionDir.load(queuedMoves[from].motionDir);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -106,9 +106,7 @@ class Net {
|
|||
|
||||
static var stunServers = ["stun:stun.l.google.com:19302"];
|
||||
|
||||
public static var turnServers:Array<String> = [];
|
||||
|
||||
static var onTurnServersReceived:Null<() -> Void> = null;
|
||||
public static var turnServer:String = "";
|
||||
|
||||
public static function hostServer(name:String, description:String, maxPlayers:Int, password:String, onHosted:() -> Void) {
|
||||
serverInfo = new ServerInfo(name, Settings.highscoreName, description, 1, maxPlayers, password, "LOBBY", getPlatform());
|
||||
|
|
@ -130,16 +128,8 @@ class Net {
|
|||
});
|
||||
}
|
||||
|
||||
public static function addClientFromSdp(sdpString:String, onFinishSdp:String->Void, turnTried:Bool = false) {
|
||||
if (Net.turnServers.length == 0 && !turnTried) {
|
||||
MasterServerClient.requestTurnCredentials();
|
||||
Net.onTurnServersReceived = () -> {
|
||||
Net.onTurnServersReceived = null;
|
||||
addClientFromSdp(sdpString, onFinishSdp, true);
|
||||
};
|
||||
return;
|
||||
}
|
||||
var peer = new RTCPeerConnection(stunServers.concat(Net.turnServers), "0.0.0.0");
|
||||
public static function addClientFromSdp(sdpString:String, onFinishSdp:String->Void) {
|
||||
var peer = new RTCPeerConnection(stunServers, "0.0.0.0");
|
||||
var sdpObj = Json.parse(sdpString);
|
||||
peer.setRemoteDescription(sdpObj.sdp, sdpObj.type);
|
||||
addClient(peer, onFinishSdp);
|
||||
|
|
@ -222,18 +212,9 @@ class Net {
|
|||
clientIdMap[id] = ghost;
|
||||
}
|
||||
|
||||
public static function joinServer(serverName:String, password:String, connectedCb:() -> Void, turnTried:Bool = false) {
|
||||
public static function joinServer(serverName:String, password:String, connectedCb:() -> Void) {
|
||||
MasterServerClient.connectToMasterServer(() -> {
|
||||
if (Net.turnServers.length == 0 && !turnTried) {
|
||||
MasterServerClient.requestTurnCredentials();
|
||||
Net.onTurnServersReceived = () -> {
|
||||
Net.onTurnServersReceived = null;
|
||||
joinServer(serverName, password, connectedCb, true);
|
||||
};
|
||||
return;
|
||||
}
|
||||
|
||||
client = new RTCPeerConnection(stunServers.concat(Net.turnServers), "0.0.0.0");
|
||||
client = new RTCPeerConnection(stunServers, "0.0.0.0");
|
||||
var candidates = [];
|
||||
|
||||
var closing = false;
|
||||
|
|
@ -581,6 +562,7 @@ class Net {
|
|||
serverInfo.players++;
|
||||
}
|
||||
|
||||
serverInfo.players++;
|
||||
MasterServerClient.instance.sendServerInfo(serverInfo); // notify the server of the new player
|
||||
|
||||
if (MarbleGame.canvas.content is MPPlayMissionGui) {
|
||||
|
|
|
|||
|
|
@ -154,13 +154,13 @@ class RewindManager {
|
|||
|| level.marble.currentUp.z != rf.currentUp.z) {
|
||||
level.setUp(level.marble, rf.currentUp, level.timeState);
|
||||
// Hacky things
|
||||
@:privateAccess level.orientationChangeTime = level.timeState.currentAttemptTime - 0.3;
|
||||
@:privateAccess level.orientationChangeTime = level.timeState.currentAttemptTime - 300;
|
||||
var oldorient = level.newOrientationQuat;
|
||||
level.newOrientationQuat = @:privateAccess level.oldOrientationQuat;
|
||||
@:privateAccess level.oldOrientationQuat = oldorient;
|
||||
}
|
||||
|
||||
var gravitycompletion = Util.clamp((level.timeState.currentAttemptTime - @:privateAccess level.orientationChangeTime) / 0.3, 0, 1);
|
||||
var gravitycompletion = Util.clamp((level.timeState.currentAttemptTime - @:privateAccess level.orientationChangeTime) / 300, 0, 1);
|
||||
if (gravitycompletion == 0) {
|
||||
level.newOrientationQuat = @:privateAccess level.oldOrientationQuat;
|
||||
@:privateAccess level.orientationChangeTime = -1e8;
|
||||
|
|
|
|||
|
|
@ -14,7 +14,6 @@ class CameraInput {
|
|||
var identifier:Int = -1;
|
||||
|
||||
public var enabled = false;
|
||||
public var pressed = false;
|
||||
|
||||
var added = false;
|
||||
|
||||
|
|
@ -37,6 +36,8 @@ class CameraInput {
|
|||
this.collider.horizSizing = Width;
|
||||
this.collider.vertSizing = Height;
|
||||
|
||||
var pressed = false;
|
||||
|
||||
var prevMouse = new Vector(0, 0);
|
||||
interactive.onPush = (e) -> {
|
||||
e.propagate = true;
|
||||
|
|
@ -86,24 +87,14 @@ class CameraInput {
|
|||
if (jumpcam) {
|
||||
scaleFactor /= Settings.touchSettings.buttonJoystickMultiplier;
|
||||
}
|
||||
var inpX = delta.x / scaleFactor;
|
||||
var inpY = delta.y / scaleFactor;
|
||||
|
||||
if (jumpcam) {
|
||||
if (Math.abs(inpX) < 1.3)
|
||||
inpX = 0;
|
||||
if (Math.abs(inpY) < 1.3)
|
||||
inpY = 0;
|
||||
}
|
||||
|
||||
var dt = MarbleGame.instance.world.timeState.dt;
|
||||
|
||||
MarbleGame.instance.world.marble.camera.orbit(applyNonlinearScale((inpX / dt) * (1 / 60.0)) * (1 / 60.0) * 35,
|
||||
applyNonlinearScale((inpY / dt) * (1 / 60.0)) * (1 / 60.0) * 35, true);
|
||||
|
||||
if (inpX != 0)
|
||||
if (Math.abs(delta.x) < 0.05)
|
||||
delta.x = 0;
|
||||
if (Math.abs(delta.y) < 0.05)
|
||||
delta.y = 0;
|
||||
MarbleGame.instance.world.marble.camera.orbit(applyNonlinearScale(delta.x / scaleFactor), applyNonlinearScale(delta.y / scaleFactor), true);
|
||||
if (delta.x != 0)
|
||||
prevMouse.x = e.relX;
|
||||
if (inpY != 0)
|
||||
if (delta.y != 0)
|
||||
prevMouse.y = e.relY;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ class PauseButton extends TouchButton {
|
|||
|
||||
this.onClick = () -> {
|
||||
if (MarbleGame.instance.world != null && @:privateAccess !MarbleGame.instance.paused) {
|
||||
@:privateAccess MarbleGame.instance.paused = true;
|
||||
MarbleGame.instance.handlePauseGame();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@ class TouchEventState {
|
|||
}
|
||||
|
||||
class TouchInput {
|
||||
public var cameraInput:CameraInput;
|
||||
var cameraInput:CameraInput;
|
||||
|
||||
public var movementInput:MovementInput;
|
||||
|
||||
|
|
|
|||