mirror of
https://github.com/RandomityGuy/MBHaxe.git
synced 2026-02-16 03:05:59 +00:00
Compare commits
9 commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2ef28aae5f | ||
|
|
51c456e907 | ||
|
|
afa42fe498 | ||
|
|
a79f7c8fcc | ||
|
|
ef9b79f120 | ||
|
|
553ed365e9 | ||
|
|
d69cb92028 | ||
|
|
e06d871aaf | ||
|
|
8a9866db54 |
16 changed files with 172 additions and 93 deletions
|
|
@ -462,10 +462,14 @@ workflows:
|
|||
filters:
|
||||
tags:
|
||||
only: /^\d+.\d+.\d+$/
|
||||
branches:
|
||||
ignore: /.*/
|
||||
|
||||
build-windows:
|
||||
jobs:
|
||||
- build-win:
|
||||
filters:
|
||||
tags:
|
||||
only: /^\d+.\d+.\d+$/
|
||||
only: /^\d+.\d+.\d+$/
|
||||
branches:
|
||||
ignore: /.*/
|
||||
|
|
@ -1,3 +1,9 @@
|
|||
# 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.
|
||||
|
|
|
|||
24
README.md
24
README.md
|
|
@ -2,7 +2,8 @@
|
|||
A Haxe port of Marble Blast Gold, Ultra and Platinum, name subject to change.
|
||||
The marble physics code was taken from [OpenMBU](https://github.com/MBU-Team/OpenMBU) along with my own collision detection code, game logic was partially from scratch and taken with permission from [Marble Blast Web Port](https://github.com/Vanilagy/MarbleBlast).
|
||||
|
||||
[](https://ko-fi.com/H2H5FRTTL)
|
||||
[](https://ko-fi.com/H2H5FRTTL)
|
||||
Support Discord: https://discord.gg/GsmTVQQAhG
|
||||
# Play
|
||||
## Web Browser
|
||||
The browser port supports touch controls, meaning it can be played on mobile devices.
|
||||
|
|
@ -11,15 +12,20 @@ The browser port supports touch controls, meaning it can be played on mobile dev
|
|||
### Marble Blast Ultra: [Play](https://marbleblastultra.randomityguy.me/)
|
||||
## Windows and Mac
|
||||
### 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)
|
||||
### Marble Blast Platinum: [Download](https://github.com/RandomityGuy/MBHaxe/releases/tag/1.7.1)
|
||||
### Marble Blast Ultra: [Download](https://github.com/RandomityGuy/MBHaxe/releases/tag/1.2.5-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.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)
|
||||
### Marble Blast Platinum: [Download](https://github.com/RandomityGuy/MBHaxe/releases/download/1.7.1/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.
|
||||
|
||||
## 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.
|
||||
|
|
@ -60,6 +66,7 @@ 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)
|
||||
|
||||
|
|
@ -88,9 +95,6 @@ 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.
|
||||
|
||||
|
|
@ -100,11 +104,11 @@ In native version, you can just resize the window if windowed or use the resolut
|
|||
|
||||
## How do I change my FOV?
|
||||
Edit settings.json for native version, edit the MBHaxeSettings key in LocalStorage in browser.
|
||||
In the platinum version, there is an FOV slider.
|
||||
In the Platinum and Ultra versions, 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, edit settings.json or the options menu in the platinum.
|
||||
In the native version, use the options menu to unlock/lock fps, or edit settings.json and set "vsync" to false to unlock fps.
|
||||
|
||||
## 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.
|
||||
|
|
|
|||
|
|
@ -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=10', (b) -> {
|
||||
return Http.get('${host}/api/scores?mission=${StringTools.urlEncode(mission)}&game=${game}&view=${kind}&count=5', (b) -> {
|
||||
var s = b.toString();
|
||||
var scores:Array<LBScore> = Json.parse(s).scores;
|
||||
cb(scores);
|
||||
|
|
|
|||
|
|
@ -394,6 +394,7 @@ 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 = "";
|
||||
|
|
@ -817,7 +818,7 @@ class Marble extends GameObject {
|
|||
|
||||
function computeMoveForces(m:Move, aControl:Vector, desiredOmega:Vector) {
|
||||
var currentGravityDir = this.currentUp.multiply(-1);
|
||||
var R = currentGravityDir.multiply(-this._radius);
|
||||
var R = this.currentUp.multiply(this._radius);
|
||||
var rollVelocity = this.omega.cross(R);
|
||||
var axes = this.getMarbleAxis();
|
||||
// if (!level.isReplayingMovement)
|
||||
|
|
@ -850,15 +851,15 @@ class Marble extends GameObject {
|
|||
}
|
||||
var rsq = R.lengthSq();
|
||||
var crossP = R.cross(motionDir.multiply(desiredYVelocity).add(sideDir.multiply(desiredXVelocity))).multiply(1 / rsq);
|
||||
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);
|
||||
desiredOmega.load(crossP);
|
||||
aControl.load(desiredOmega.sub(this.omega));
|
||||
var aScalar = aControl.length();
|
||||
if (aScalar > this._angularAcceleration) {
|
||||
aControl.scale(this._angularAcceleration / aScalar);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return return true;
|
||||
return true;
|
||||
}
|
||||
|
||||
function velocityCancel(timeState:TimeState, surfaceSlide:Bool, noBounce:Bool, stoppedPaths:Bool, pi:Array<PathedInterior>) {
|
||||
|
|
@ -873,7 +874,7 @@ class Marble extends GameObject {
|
|||
var sVel = this.velocity.sub(contacts[i].velocity);
|
||||
var surfaceDot = contacts[i].normal.dot(sVel);
|
||||
|
||||
if ((!looped && surfaceDot < 0) || surfaceDot < -SurfaceDotThreshold) {
|
||||
if ((!looped && surfaceDot < 0.0) || surfaceDot < -SurfaceDotThreshold) {
|
||||
var velLen = this.velocity.length();
|
||||
var surfaceVel = this.contacts[i].normal.multiply(surfaceDot);
|
||||
|
||||
|
|
@ -908,7 +909,7 @@ class Marble extends GameObject {
|
|||
}
|
||||
contacts[i].velocity.load(otherMarble.velocity);
|
||||
} else {
|
||||
if (contacts[i].velocity.length() == 0 && !surfaceSlide && surfaceDot > -this._maxDotSlide * velLen) {
|
||||
if (contacts[i].velocity.length() == 0.0 && !surfaceSlide && surfaceDot > -this._maxDotSlide * velLen) {
|
||||
this.velocity.load(this.velocity.sub(surfaceVel));
|
||||
this.velocity.normalize();
|
||||
this.velocity.load(this.velocity.multiply(velLen));
|
||||
|
|
@ -934,7 +935,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) {
|
||||
if (vAtCMag != 0.0) {
|
||||
var friction = this._bounceKineticFriction * contacts[i].friction;
|
||||
|
||||
var angVMagnitude = friction * 5 * normalVel / (2 * this._radius);
|
||||
|
|
@ -970,7 +971,7 @@ class Marble extends GameObject {
|
|||
}
|
||||
}
|
||||
} while (!done && itersIn < 1e4); // Maximum limit pls
|
||||
if (this.velocity.lengthSq() < 625) {
|
||||
if (this.velocity.lengthSq() < 625.0) {
|
||||
var gotOne = false;
|
||||
var dir = new Vector(0, 0, 0);
|
||||
for (j in 0...contacts.length) {
|
||||
|
|
@ -994,10 +995,10 @@ class Marble extends GameObject {
|
|||
soFar += (dist - outVel * timeToSeparate) / timeToSeparate / contacts[k].normal.dot(dir);
|
||||
}
|
||||
}
|
||||
if (soFar < -25)
|
||||
soFar = -25;
|
||||
if (soFar > 25)
|
||||
soFar = 25;
|
||||
if (soFar < -25.0)
|
||||
soFar = -25.0;
|
||||
if (soFar > 25.0)
|
||||
soFar = 25.0;
|
||||
this.velocity.load(this.velocity.add(dir.multiply(soFar)));
|
||||
}
|
||||
}
|
||||
|
|
@ -1066,7 +1067,7 @@ class Marble extends GameObject {
|
|||
slipping = false;
|
||||
}
|
||||
var vAtCDir = vAtC.multiply(1 / vAtCMag);
|
||||
aFriction.load(bestContact.normal.multiply(-1).cross(vAtCDir.multiply(-1)).multiply(angAMagnitude));
|
||||
aFriction.load(bestContact.normal.cross(vAtCDir).multiply(angAMagnitude));
|
||||
AFriction.load(vAtCDir.multiply(-AMagnitude));
|
||||
this._slipAmount = vAtCMag - totalDeltaV;
|
||||
}
|
||||
|
|
@ -1093,16 +1094,16 @@ class Marble extends GameObject {
|
|||
friction2 = this._kineticFriction * bestContact.friction;
|
||||
Aadd.load(Aadd.multiply(friction2 * bestNormalForce / aAtCMag));
|
||||
}
|
||||
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(Aadd));
|
||||
a.load(a.add(aadd));
|
||||
}
|
||||
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);
|
||||
A.load(A.add(AFriction));
|
||||
a.load(a.add(aFriction));
|
||||
|
||||
lastContactNormal = bestContact.normal;
|
||||
lastContactPosition = this.getAbsPos().getPosition();
|
||||
}
|
||||
a.set(a.x + aControl.x, a.y + aControl.y, a.z + aControl.z);
|
||||
a.load(a.add(aControl));
|
||||
if (this.mode == Finish) {
|
||||
a.set(); // Zero it out
|
||||
}
|
||||
|
|
@ -1371,7 +1372,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.multiply(-1);
|
||||
surfaceNormal.load(v.sub(v0).cross(v2.sub(v0)).normalized().multiply(-1));
|
||||
var surfaceD = -surfaceNormal.dot(v0);
|
||||
|
||||
// If we're going the wrong direction or not going to touch the plane, ignore...
|
||||
|
|
@ -2390,6 +2391,8 @@ 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)) {
|
||||
|
|
|
|||
|
|
@ -2524,7 +2524,7 @@ class MarbleWorld extends Scheduler {
|
|||
} else {
|
||||
nextLevelCode();
|
||||
}
|
||||
}, mission, finishTime);
|
||||
}, mission, finishTime, this.replay.write());
|
||||
MarbleGame.canvas.pushDialog(egg);
|
||||
this.setCursorLock(false);
|
||||
return 0;
|
||||
|
|
|
|||
|
|
@ -52,6 +52,7 @@ 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();
|
||||
|
||||
|
|
@ -59,18 +60,20 @@ class ReplayFrame {
|
|||
interpFrame.time = time;
|
||||
interpFrame.bonusTime = this.bonusTime;
|
||||
interpFrame.clockTime = this.clockTime;
|
||||
if (interpFrame.bonusTime != 0 && time >= 3.5) {
|
||||
if (dt <= this.bonusTime) {
|
||||
interpFrame.bonusTime -= dt;
|
||||
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;
|
||||
}
|
||||
} else {
|
||||
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;
|
||||
if (this.time >= 3.5)
|
||||
interpFrame.clockTime += dt;
|
||||
else if (this.time + dt >= 3.5) {
|
||||
interpFrame.clockTime += (this.time + dt) - 3.5;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -130,14 +130,27 @@ 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();
|
||||
});
|
||||
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();
|
||||
});
|
||||
});
|
||||
}
|
||||
#else
|
||||
if (onReady != null)
|
||||
|
|
|
|||
|
|
@ -118,7 +118,7 @@ class ChatCtrl extends GuiControl {
|
|||
}
|
||||
|
||||
public function addChatMessage(text:String) {
|
||||
var realText = StringTools.htmlUnescape(text);
|
||||
var realText = StringTools.htmlEscape(text);
|
||||
this.chats.push({
|
||||
text: realText,
|
||||
age: 10.0
|
||||
|
|
|
|||
|
|
@ -19,7 +19,8 @@ class EndGameGui extends GuiControl {
|
|||
|
||||
var scoreSubmitted:Bool = false;
|
||||
|
||||
public function new(continueFunc:GuiControl->Void, restartFunc:GuiControl->Void, nextLevelFunc:GuiControl->Void, mission:Mission, timeState:TimeState) {
|
||||
public function new(continueFunc:GuiControl->Void, restartFunc:GuiControl->Void, nextLevelFunc:GuiControl->Void, mission:Mission, timeState:TimeState,
|
||||
replayData:haxe.io.Bytes) {
|
||||
super();
|
||||
this.horizSizing = Width;
|
||||
this.vertSizing = Height;
|
||||
|
|
@ -312,12 +313,11 @@ class EndGameGui extends GuiControl {
|
|||
scoreData.push({name: "Matan W.", time: 5999.999});
|
||||
}
|
||||
|
||||
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>';
|
||||
|
||||
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>';
|
||||
var lineelems = [
|
||||
egFirstLineScore,
|
||||
egSecondLineScore,
|
||||
|
|
@ -393,6 +393,8 @@ class EndGameGui extends GuiControl {
|
|||
// }
|
||||
Settings.save();
|
||||
|
||||
var rewindUsed = MarbleGame.instance.world.rewindUsed;
|
||||
|
||||
if (idx <= 4) {
|
||||
setButtonStates(false);
|
||||
var end = new EnterNameDlg(idx, (name) -> {
|
||||
|
|
@ -426,8 +428,7 @@ class EndGameGui extends GuiControl {
|
|||
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) -> {
|
||||
Leaderboards.submitScore(lbPath, myScore.time, rewindUsed, (sendReplay, rowId) -> {
|
||||
if (sendReplay && !mission.isClaMission) {
|
||||
Leaderboards.submitReplay(rowId, replayData);
|
||||
}
|
||||
|
|
@ -438,7 +439,6 @@ class EndGameGui extends GuiControl {
|
|||
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}';
|
||||
|
|
@ -453,7 +453,7 @@ class EndGameGui extends GuiControl {
|
|||
}
|
||||
}
|
||||
if (!hasMyScore || (hasMyScore && myTopScoreLB > timeState.gameplayClock)) {
|
||||
Leaderboards.submitScore(lbPath, timeState.gameplayClock, MarbleGame.instance.world.rewindUsed, (sendReplay, rowId) -> {
|
||||
Leaderboards.submitScore(lbPath, timeState.gameplayClock, rewindUsed, (sendReplay, rowId) -> {
|
||||
if (sendReplay && !mission.isClaMission) {
|
||||
Leaderboards.submitReplay(rowId, replayData);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ import h2d.Tile;
|
|||
import h2d.Graphics;
|
||||
import src.MarbleGame;
|
||||
import src.Util;
|
||||
import haxe.Timer;
|
||||
|
||||
class GuiScrollCtrl extends GuiControl {
|
||||
public var scrollY:Float = 0;
|
||||
|
|
@ -40,6 +41,12 @@ 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;
|
||||
|
|
@ -239,34 +246,59 @@ class GuiScrollCtrl extends GuiControl {
|
|||
}
|
||||
|
||||
public override function onMousePress(mouseState:MouseState) {
|
||||
if (Util.isTouchDevice()) {
|
||||
this.pressed = true;
|
||||
this.dirty = true;
|
||||
this.updateScrollVisual();
|
||||
this.prevMousePos = mouseState.position;
|
||||
}
|
||||
// if (Util.isTouchDevice()) {
|
||||
this.pressed = true;
|
||||
this.dirty = true;
|
||||
this.updateScrollVisual();
|
||||
this.prevMousePos = mouseState.position;
|
||||
this.scrollVelocity = 0;
|
||||
this.momentumActive = false;
|
||||
this.lastMoveStamp = Timer.stamp();
|
||||
// }
|
||||
}
|
||||
|
||||
public override function onMouseRelease(mouseState:MouseState) {
|
||||
if (Util.isTouchDevice()) {
|
||||
this.pressed = false;
|
||||
this.dirty = true;
|
||||
deltaY = 0;
|
||||
this.updateScrollVisual();
|
||||
}
|
||||
// if (Util.isTouchDevice()) {
|
||||
this.pressed = false;
|
||||
this.dirty = true;
|
||||
deltaY = 0;
|
||||
this.updateScrollVisual();
|
||||
this.momentumActive = Math.abs(scrollVelocity) > 0.01;
|
||||
this.lastMoveStamp = 0;
|
||||
// }
|
||||
}
|
||||
|
||||
public override function onMouseMove(mouseState:MouseState) {
|
||||
if (Util.isTouchDevice()) {
|
||||
super.onMouseMove(mouseState);
|
||||
if (this.pressed) {
|
||||
var dy = (mouseState.position.y - this.prevMousePos.y) * scrollSpeed / this.maxScrollY;
|
||||
deltaY = -dy;
|
||||
this.scrollY -= dy;
|
||||
this.prevMousePos = mouseState.position;
|
||||
this.updateScrollVisual();
|
||||
// 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);
|
||||
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) {
|
||||
|
|
@ -285,6 +317,21 @@ class GuiScrollCtrl extends GuiControl {
|
|||
this.updateScrollVisual();
|
||||
}
|
||||
super.update(dt, mouseState);
|
||||
|
||||
if (!pressed && momentumActive) {
|
||||
var damping = Math.exp(-MOMENTUM_DAMPING * dt);
|
||||
scrollVelocity *= damping;
|
||||
if (Math.abs(scrollVelocity) < 0.01) {
|
||||
scrollVelocity = 0;
|
||||
momentumActive = false;
|
||||
return;
|
||||
}
|
||||
var before = scrollY;
|
||||
scrollY += scrollVelocity * dt;
|
||||
updateScrollVisual();
|
||||
if (scrollY == 0 || scrollY == before)
|
||||
momentumActive = false;
|
||||
}
|
||||
}
|
||||
|
||||
// public override function onMouseDown(mouseState:MouseState) {
|
||||
|
|
|
|||
|
|
@ -186,7 +186,7 @@ class JoinServerGui extends GuiImage {
|
|||
serverInfo.text.text = '<p align="center">Select a Server</p><p align="center">or Host your own</p>';
|
||||
} else {
|
||||
var server = ourServerList[curSelection];
|
||||
serverInfo.text.text = '<p align="center">${server.name}</p><p align="center"><font face="MarkerFelt18" color="#DDDDEE">Hosted by ${server.host}</font></p><p align="left">${server.description}</p>';
|
||||
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>';
|
||||
}
|
||||
}
|
||||
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">${x.name} <offset value="${400 * Settings.uiScale}">${x.players}/${x.maxPlayers}</offset></font>');
|
||||
'<img src="${platformToString[x.platform]}"></img><font color="#FFFFFF">${StringTools.htmlEscape(x.name)} <offset value="${400 * Settings.uiScale}">${x.players}/${x.maxPlayers}</offset></font>');
|
||||
serverList.setTexts(serverDisplays);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -585,11 +585,11 @@ class MPPlayMissionGui extends GuiImage {
|
|||
currentSelection = -1;
|
||||
}
|
||||
|
||||
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>';
|
||||
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>';
|
||||
|
||||
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">${currentMission.artist}</font></font>';
|
||||
+ '<font face="MarkerFelt24" color="#E3F3FF">Author: <font color="#FFFFFF">${StringTools.htmlEscape(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">${player.name}<offset value="${220 * Settings.uiScale}">${player.ready ? "Ready" : ""}</offset></font>');
|
||||
'<img src="${platformToString(player.platform)}"></img><font color="#FFFFFF">${StringTools.htmlEscape(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 = StringTools.htmlUnescape(s);
|
||||
var realText = StringTools.htmlEscape(s);
|
||||
allChats.push(realText);
|
||||
if (allChats.length > 100) {
|
||||
allChats = allChats.slice(allChats.length - 100);
|
||||
|
|
|
|||
|
|
@ -738,7 +738,7 @@ class PlayGui {
|
|||
} else {
|
||||
isSpectating = Net.clientIdMap[item.id].spectator;
|
||||
}
|
||||
pl.push('<font color="${color}">${i + 1}. ${isSpectating ? "[S] " : ""}${Util.rightPad(item.name, 25, 3)}</font>');
|
||||
pl.push('<font color="${color}">${i + 1}. ${isSpectating ? "[S] " : ""}${Util.rightPad(StringTools.htmlEscape(item.name), 25, 3)}</font>');
|
||||
var connPing = item.us ? (Net.isHost ? 0 : Net.clientConnection.pingTicks) : (item.id == 0 ? 0 : Net.clientIdMap[item.id].pingTicks);
|
||||
var pingStatus = "unknown";
|
||||
if (connPing <= 5)
|
||||
|
|
|
|||
|
|
@ -1234,7 +1234,7 @@ class PlayMissionGui extends GuiImage {
|
|||
var i = 1;
|
||||
for (score in scoreList) {
|
||||
sFmt.push('${i}.
|
||||
<offset value="15">${score.name.substr(0, 30)}</offset>
|
||||
<offset value="15">${StringTools.htmlEscape(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> ' : ""}');
|
||||
|
|
|
|||
|
|
@ -562,7 +562,6 @@ class Net {
|
|||
serverInfo.players++;
|
||||
}
|
||||
|
||||
serverInfo.players++;
|
||||
MasterServerClient.instance.sendServerInfo(serverInfo); // notify the server of the new player
|
||||
|
||||
if (MarbleGame.canvas.content is MPPlayMissionGui) {
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue