Compare commits

..

No commits in common. "master" and "1.7.1" have entirely different histories.

47 changed files with 463 additions and 962 deletions

View file

@ -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+$/

View file

@ -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.

View file

@ -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).
[![ko-fi](https://ko-fi.com/img/githubbutton_sm.svg)](https://ko-fi.com/H2H5FRTTL)
Support Discord: https://discord.gg/GsmTVQQAhG
[![ko-fi](https://ko-fi.com/img/githubbutton_sm.svg)](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.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.3 KiB

View file

@ -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));

View file

@ -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];

View file

@ -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;
}

View file

@ -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();
}
}

View file

@ -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);

View file

@ -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;

View file

@ -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);

View file

@ -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;

View file

@ -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;
}
}

View file

@ -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;
}

View file

@ -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 [];
}
}

View file

@ -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;
}
}

View file

@ -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) {

View file

@ -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;
}

View file

@ -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) {

View file

@ -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

View file

@ -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;
}
}

View file

@ -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)

View file

@ -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

View file

@ -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);
}
});
}
});
}

View file

@ -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) {

View file

@ -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);
}

View file

@ -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);

View file

@ -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");

View file

@ -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);

View file

@ -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)

View file

@ -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> ' : ""}');

View file

@ -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);

View file

@ -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);

View file

@ -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;
}
}

View file

@ -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!
}
}
}

View file

@ -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);
}

View file

@ -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) {

View file

@ -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;

View file

@ -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;
}
}

View file

@ -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();
}
}

View file

@ -36,7 +36,7 @@ class TouchEventState {
}
class TouchInput {
public var cameraInput:CameraInput;
var cameraInput:CameraInput;
public var movementInput:MovementInput;