From 50e49b4284782bcb78ac6767c3f02abe9d37b311 Mon Sep 17 00:00:00 2001 From: --global <--global> Date: Mon, 1 Jun 2026 16:44:41 -0700 Subject: [PATCH 1/4] fixed slopes --- assets/scripts/core/level.js | 62 +++++++++++++- assets/scripts/core/player.js | 152 +++++++++++++++++++++++++++++++++- 2 files changed, 211 insertions(+), 3 deletions(-) diff --git a/assets/scripts/core/level.js b/assets/scripts/core/level.js index 78c8a30f..b0878d39 100644 --- a/assets/scripts/core/level.js +++ b/assets/scripts/core/level.js @@ -29,6 +29,29 @@ class Collider { if (this.slopeFlipY) angleDeg = -angleDeg; return angleDeg * Math.PI / 180; } + isSlopeSolidAt(worldX, worldY) { + if (this.type !== slopeType) return false; + const halfW = this.w / 2; + const halfH = this.h / 2; + const left = this.x - halfW; + const right = this.x + halfW; + const bboxBottom = this.y - halfH; + const bboxTop = this.y + halfH; + if (worldX < left || worldX > right || worldY < bboxBottom || worldY > bboxTop) { + return false; + } + if (this.slopeIsFilled) { + return true; + } + const surfaceY = this.getSlopeSurfaceY(worldX); + if (surfaceY === null) return false; + return worldY < surfaceY; + } + getSlopeBackWallSide() { + let leftWall = this.slopeDir > 0; + if (this.slopeFlipY) leftWall = !leftWall; + return leftWall ? "left" : "right"; + } } function parseObject(objectString) { @@ -226,6 +249,37 @@ const _SLOPE_DATA = { 1901:{gw:0.367,gh:0.433,angle:45,sq:true},1902:{gw:0.967,gh:0.45,angle:45,sq:true}, 1906:{gw:1,gh:1,angle:45,sq:false},1907:{gw:2,gh:1,angle:22.5,sq:false}, }; +function _resolveSlopeDir(objectDef, flipX) { + const frames = []; + if (objectDef) { + if (objectDef.frame) frames.push(objectDef.frame); + if (objectDef.children) { + for (const child of objectDef.children) { + if (child.frame) frames.push(child.frame); + } + } + } + const text = frames.join(" "); + let dir = 1; + if (/slope_02[^0-9]|slope_04|slope_06|slope_02[bcd]_|triangle_a_04|triangle_b_02|pit_0[14]_slope_02|plank_01_slope_02|slope_square_02|slope_square_04|slope_square_05/.test(text)) { + dir = -1; + } + if (flipX) dir = -dir; + return dir; +} +function _createSlopeCollider(levelObj, objectDef, worldX, worldY) { + const slopeData = _SLOPE_DATA[levelObj.id]; + if (!slopeData) return null; + const w = slopeData.gw * a; + const h = slopeData.gh * a; + const collider = new Collider(slopeType, worldX, worldY, w, h, levelObj.rot || 0); + collider.objid = levelObj.id; + collider.slopeAngleDeg = slopeData.angle; + collider.slopeDir = _resolveSlopeDir(objectDef, levelObj.flipX); + collider.slopeIsFilled = slopeData.sq; + collider.slopeFlipY = levelObj.flipY; + return collider; +} const flyPortal = "fly"; const cubePortal = "cube"; const portalWaveType = "portal_wave"; @@ -1266,7 +1320,13 @@ window.LevelObject = class LevelObject { } }; - if (objectDef.type === solidType && objectDef.gridW > 0 && objectDef.gridH > 0) { + const slopeCollider = _createSlopeCollider(levelObj, objectDef, worldX, worldY); + if (slopeCollider) { + registerCollider(slopeCollider); + this.objects.push(slopeCollider); + hasCollisionEntry = true; + this._addCollisionToSection(slopeCollider); + } else if (objectDef.type === solidType && objectDef.gridW > 0 && objectDef.gridH > 0) { const w = objectDef.gridW * a; const h = objectDef.gridH * a; const collider = new Collider(solidType, worldX, worldY, w, h, levelObj.rot || 0); diff --git a/assets/scripts/core/player.js b/assets/scripts/core/player.js index d1ce8871..f3c922df 100644 --- a/assets/scripts/core/player.js +++ b/assets/scripts/core/player.js @@ -320,6 +320,7 @@ class PlayerObject { this.p = _0x3f50cc; this._gameLayer = _0x2811e1; this._rotation = 0; + this._onSlopeAngle = null; this.rotateActionActive = false; this.rotateActionTime = 0; this.rotateActionDuration = 0; @@ -1531,6 +1532,122 @@ if (this.p.isFlying || this.p.isUfo) { }); } } + _getSlopeHitSize(playerSize, waveHitSize) { + return this.p.isWave ? waveHitSize : playerSize; + } + _isOnSlopeSurface(surfaceY, footProbe, headProbe, hitSize, gravityFlipped) { + if (!gravityFlipped) { + return Math.abs(footProbe - surfaceY) < Math.max(14, hitSize * 0.45) && footProbe >= surfaceY - 2; + } + return Math.abs(headProbe - surfaceY) < Math.max(14, hitSize * 0.45) && headProbe <= surfaceY + 2; + } + _landOnSlopeFloor(gameObj, surfaceY, hitSize, footProbe, lastFootProbe, playersY) { + if (!this.p.gravityFlipped && + (footProbe >= surfaceY || lastFootProbe >= surfaceY) && + (this.p.yVelocity <= 0 || this.p.onGround) && + playersY <= surfaceY + hitSize + 8) { + this.p.y = surfaceY + hitSize; + this.hitGround(); + this.p.collideBottom = surfaceY; + this._onSlopeAngle = gameObj.getSlopeAngleRad(); + return true; + } + return false; + } + _landOnSlopeCeiling(gameObj, surfaceY, hitSize, headProbe, lastHeadProbe, playersY) { + if ((headProbe <= surfaceY || lastHeadProbe <= surfaceY) && + (this.p.yVelocity >= 0 || this.p.onGround) && + playersY >= surfaceY - hitSize - 8) { + this.p.y = surfaceY - hitSize; + this.hitGround(); + this.p.onCeiling = true; + this.p.collideTop = surfaceY; + this._onSlopeAngle = gameObj.getSlopeAngleRad(); + return true; + } + return false; + } + _landOnSlopeCeilingFlipped(gameObj, surfaceY, hitSize, footProbe, lastFootProbe, playersY) { + if (this.p.gravityFlipped && + (footProbe >= surfaceY || lastFootProbe >= surfaceY) && + (this.p.yVelocity <= 0 || this.p.onGround) && + playersY <= surfaceY + hitSize + 8) { + this.p.y = surfaceY + hitSize; + this.hitGround(); + this.p.onCeiling = true; + this.p.collideTop = surfaceY; + this._onSlopeAngle = gameObj.getSlopeAngleRad(); + return true; + } + return false; + } + _handleSlopeCollision(gameObj, pieceWidth, playersY, playersLastY, left, right, top, bottom, playerSize, waveHitSize, gamemodeAddition) { + const surfaceY = gameObj.getSlopeSurfaceY(pieceWidth); + if (surfaceY === null) return { landed: false, died: false }; + const hitSize = this._getSlopeHitSize(playerSize, waveHitSize); + const footProbe = playersY - hitSize + gamemodeAddition; + const lastFootProbe = playersLastY - hitSize + gamemodeAddition; + const headProbe = playersY + hitSize - gamemodeAddition; + const lastHeadProbe = playersLastY + hitSize - gamemodeAddition; + const tightPad = this.p.isWave ? 5 : 9; + const inXRange = pieceWidth + hitSize - 5 > left && pieceWidth - hitSize + 5 < right; + const onSlopeSurface = this._isOnSlopeSurface(surfaceY, footProbe, headProbe, hitSize, this.p.gravityFlipped); + let landed = false; + if (inXRange) { + if (this.p.isUfo) { + if (!this.p.gravityFlipped && this._landOnSlopeCeiling(gameObj, surfaceY, hitSize, headProbe, lastHeadProbe, playersY)) { + landed = true; + } else if (this.p.gravityFlipped && this._landOnSlopeCeilingFlipped(gameObj, surfaceY, hitSize, footProbe, lastFootProbe, playersY)) { + landed = true; + } + } else if (this.p.isFlying) { + if (!this.p.gravityFlipped) { + if (this._landOnSlopeFloor(gameObj, surfaceY, hitSize, footProbe, lastFootProbe, playersY)) { + landed = true; + } else if (this._landOnSlopeCeiling(gameObj, surfaceY, hitSize, headProbe, lastHeadProbe, playersY)) { + landed = true; + } + } else if (this._landOnSlopeCeilingFlipped(gameObj, surfaceY, hitSize, footProbe, lastFootProbe, playersY)) { + landed = true; + } + } else if (this.p.gravityFlipped) { + if (this._landOnSlopeCeiling(gameObj, surfaceY, hitSize, headProbe, lastHeadProbe, playersY)) { + landed = true; + } + } else if (this._landOnSlopeFloor(gameObj, surfaceY, hitSize, footProbe, lastFootProbe, playersY)) { + landed = true; + } + if (landed && !this.p.isFlying && !this.p.isWave) { + this._checkSnapJump(gameObj); + } + } + if (landed) return { landed: true, died: false }; + const overlapping = pieceWidth + tightPad > left && pieceWidth - tightPad < right && + playersY + tightPad > top && playersY - tightPad < bottom; + if (overlapping && !onSlopeSurface) { + if (!gameObj.slopeIsFilled && gameObj.isSlopeSolidAt(pieceWidth, playersY)) { + if (window.noClip) this.p.diedThisFrame = true; + else return { landed: false, died: true }; + } else { + const backSide = gameObj.getSlopeBackWallSide(); + const wallX = backSide === "left" ? left : right; + const wallSurface = gameObj.getSlopeSurfaceY(wallX); + let hitBackWall = false; + if (backSide === "left") { + hitBackWall = pieceWidth < wallX + tightPad && pieceWidth + hitSize > wallX && + footProbe < wallSurface - 2 && headProbe > top + 2; + } else { + hitBackWall = pieceWidth > wallX - tightPad && pieceWidth - hitSize < wallX && + footProbe < wallSurface - 2 && headProbe > top + 2; + } + if (hitBackWall || gameObj.slopeIsFilled) { + if (window.noClip) this.p.diedThisFrame = true; + else return { landed: false, died: true }; + } + } + } + return { landed: false, died: false }; + } _checkSnapJump(_0x1f801b) { const _0x483058 = [{ dx: 240, @@ -1543,7 +1660,7 @@ if (this.p.isFlying || this.p.isUfo) { dy: 120 }]; const _0x2b806a = this._lastLandObject; - if (_0x2b806a && _0x2b806a !== _0x1f801b && _0x2b806a.type === solidType) { + if (_0x2b806a && _0x2b806a !== _0x1f801b && (_0x2b806a.type === solidType || _0x2b806a.type === slopeType)) { const _0x34ef27 = _0x2b806a.x; const _0x4652bb = _0x2b806a.y; const _0x5de781 = _0x1f801b.x; @@ -1652,7 +1769,7 @@ if (this.p.isFlying || this.p.isUfo) { if (this.p.isBall || this.p.isWave || this.p.isSpider) { return; } - let _0x183c2a = this.convertToClosestRotation(); + let _0x183c2a = this._onSlopeAngle !== null ? this._onSlopeAngle : this.convertToClosestRotation(); let _0x108955 = 0.47250000000000003; let _0x17a9a6 = Math.min(_0x5c24f7 * 1, _0x108955 * _0x5c24f7); this._rotation = this.slerp2D(this._rotation, _0x183c2a, _0x17a9a6); @@ -1948,6 +2065,7 @@ _updateWaveJump() { this.p.collideBottom = 0; this.p.onCeiling = false; this.p.touchingRing = false; + this._onSlopeAngle = null; let _0x30410f = false; let _boostedThisStep = false; const _0x198534 = this._gameLayer.getNearbySectionObjects(pieceWidth); @@ -2405,6 +2523,18 @@ _updateWaveJump() { } this.killPlayer(); return; + } else if (_colType === slopeType) { + const slopeResult = this._handleSlopeCollision( + gameObj, pieceWidth, playersY, playersLastY, left, right, top, bottom, playerSize, waveHitSize, gamemodeAddition + ); + if (slopeResult.died) { + this.killPlayer(); + return; + } + if (slopeResult.landed) { + _0x30410f = true; + } + continue; } else if (_colType === solidType) { let _0x146a97 = playersY - playerSize + gamemodeAddition; let _0x869e42 = playersLastY - playerSize + gamemodeAddition; @@ -2628,6 +2758,8 @@ _updateWaveJump() { hitboxColor = 16744192; } else if (nearObject.type === jumpRingType) { hitboxColor = 16711935; + } else if (nearObject.type === slopeType) { + hitboxColor = 65535; } const xPos = isFlipped ? screenWidth - objXCenter : objXCenter; graphics.lineStyle(2, hitboxColor, 0.7); @@ -2658,6 +2790,22 @@ _updateWaveJump() { graphics.lineTo(rotations[3].x, rotations[3].y); graphics.closePath(); graphics.strokePath(); + if (nearObject.type === slopeType) { + const halfW = nearObject.w / 2; + const leftWorld = nearObject.x - halfW; + const rightWorld = nearObject.x + halfW; + const leftSurfY = nearObject.getSlopeSurfaceY(leftWorld); + const rightSurfY = nearObject.getSlopeSurfaceY(rightWorld); + if (leftSurfY !== null && rightSurfY !== null) { + const leftX = isFlipped ? screenWidth - (leftWorld - camX) : leftWorld - camX; + const rightX = isFlipped ? screenWidth - (rightWorld - camX) : rightWorld - camX; + graphics.lineStyle(2, 16777215, 0.9); + graphics.beginPath(); + graphics.moveTo(leftX, b(leftSurfY) + camY); + graphics.lineTo(rightX, b(rightSurfY) + camY); + graphics.strokePath(); + } + } } } From d4b28787ac286910619899bbdafdc37261a3b5ec Mon Sep 17 00:00:00 2001 From: --global <--global> Date: Mon, 1 Jun 2026 17:07:54 -0700 Subject: [PATCH 2/4] added a "bounce" --- assets/scripts/core/player.js | 48 ++++++++++++++++++++++++++++++++++- 1 file changed, 47 insertions(+), 1 deletion(-) diff --git a/assets/scripts/core/player.js b/assets/scripts/core/player.js index f3c922df..45a3873e 100644 --- a/assets/scripts/core/player.js +++ b/assets/scripts/core/player.js @@ -321,6 +321,10 @@ class PlayerObject { this._gameLayer = _0x2811e1; this._rotation = 0; this._onSlopeAngle = null; + this._activeSlopeObj = null; + this._activeSlopeAngle = null; + this._prevSlopeObj = null; + this._prevSlopeAngle = null; this.rotateActionActive = false; this.rotateActionTime = 0; this.rotateActionDuration = 0; @@ -1621,7 +1625,11 @@ if (this.p.isFlying || this.p.isUfo) { this._checkSnapJump(gameObj); } } - if (landed) return { landed: true, died: false }; + if (landed) { + this._activeSlopeObj = gameObj; + this._activeSlopeAngle = gameObj.getSlopeAngleRad(); + return { landed: true, died: false }; + } const overlapping = pieceWidth + tightPad > left && pieceWidth - tightPad < right && playersY + tightPad > top && playersY - tightPad < bottom; if (overlapping && !onSlopeSurface) { @@ -1648,6 +1656,33 @@ if (this.p.isFlying || this.p.isUfo) { } return { landed: false, died: false }; } + _applySlopeExitBounce(slopeObj, slopeAngle, pieceWidth) { + if (!slopeObj || slopeAngle === null || slopeAngle === undefined) return; + if (this.p.isJumping && !this.p.onGround) return; + const angleRad = Math.abs(slopeAngle); + if (angleRad < 0.05) return; + const halfW = slopeObj.w / 2; + const left = slopeObj.x - halfW; + const right = slopeObj.x + halfW; + const exitMargin = 14; + let exitHighEnd = slopeObj.slopeDir > 0 + ? pieceWidth >= right - exitMargin + : pieceWidth <= left + exitMargin; + if (slopeObj.slopeFlipY) exitHighEnd = !exitHighEnd; + if (!exitHighEnd) return; + let bounceVel = Math.tan(angleRad) * playerSpeed * this.flipMod(); + if (this.p.isWave) bounceVel *= 0.85; + else if (this.p.isMini) bounceVel *= 0.8; + if (Math.abs(bounceVel) < 0.5) return; + this.p.yVelocity = bounceVel; + this.p.onGround = false; + this.p.canJump = false; + if (!this.p.isFlying && !this.p.isWave && !this.p.isUfo && !this.p.isSpider) { + this.p.isJumping = true; + this._rotation = slopeAngle; + this.stopRotation(); + } + } _checkSnapJump(_0x1f801b) { const _0x483058 = [{ dx: 240, @@ -2066,6 +2101,8 @@ _updateWaveJump() { this.p.onCeiling = false; this.p.touchingRing = false; this._onSlopeAngle = null; + this._activeSlopeObj = null; + this._activeSlopeAngle = null; let _0x30410f = false; let _boostedThisStep = false; const _0x198534 = this._gameLayer.getNearbySectionObjects(pieceWidth); @@ -2712,6 +2749,11 @@ _updateWaveJump() { this.p.onGround = false; } } + if (this._prevSlopeObj && !this._activeSlopeObj && this.p.onGround) { + this._applySlopeExitBounce(this._prevSlopeObj, this._prevSlopeAngle, pieceWidth); + } + this._prevSlopeObj = this._activeSlopeObj; + this._prevSlopeAngle = this._activeSlopeAngle; this.p.wasUpKeyDown = this.p.upKeyDown; if (this.p.diedThisFrame == true && window.noClipAccuracy){ this.noclipStats.deathFrames++; @@ -2988,6 +3030,10 @@ _updateWaveJump() { this._endAnimating = false; this._lastLandObject = null; this._lastXOffset = 0; + this._prevSlopeObj = null; + this._prevSlopeAngle = null; + this._activeSlopeObj = null; + this._activeSlopeAngle = null; this.stopRotation(); this.rotateActionTime = 0; this._rotation = 0; From f6aaf3799466824e191b221f72b54563f4aa86ca Mon Sep 17 00:00:00 2001 From: sahandp2012p <91942867+sahandp2012p@users.noreply.github.com> Date: Mon, 1 Jun 2026 17:21:35 -0700 Subject: [PATCH 3/4] Add GitHub Actions workflow for static content deployment This workflow automates the deployment of static content to GitHub Pages on pushes to the main branch or manually via the Actions tab. --- .github/workflows/static.yml | 43 ++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 .github/workflows/static.yml diff --git a/.github/workflows/static.yml b/.github/workflows/static.yml new file mode 100644 index 00000000..460f7820 --- /dev/null +++ b/.github/workflows/static.yml @@ -0,0 +1,43 @@ +# Simple workflow for deploying static content to GitHub Pages +name: Deploy static content to Pages + +on: + # Runs on pushes targeting the default branch + push: + branches: ["main"] + + # Allows you to run this workflow manually from the Actions tab + workflow_dispatch: + +# Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages +permissions: + contents: read + pages: write + id-token: write + +# Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued. +# However, do NOT cancel in-progress runs as we want to allow these production deployments to complete. +concurrency: + group: "pages" + cancel-in-progress: false + +jobs: + # Single deploy job since we're just deploying + deploy: + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + - name: Setup Pages + uses: actions/configure-pages@v5 + - name: Upload artifact + uses: actions/upload-pages-artifact@v3 + with: + # Upload entire repository + path: '.' + - name: Deploy to GitHub Pages + id: deployment + uses: actions/deploy-pages@v5 From 021c4a567614458f49c50d01fbe828c84f29b80e Mon Sep 17 00:00:00 2001 From: --global <--global> Date: Mon, 1 Jun 2026 19:17:23 -0700 Subject: [PATCH 4/4] fixed ship slopes kinda weird tho --- assets/scripts/core/level.js | 14 +++-- assets/scripts/core/player.js | 97 +++++++++++++++++++++++++---------- 2 files changed, 80 insertions(+), 31 deletions(-) diff --git a/assets/scripts/core/level.js b/assets/scripts/core/level.js index b0878d39..4f1ce233 100644 --- a/assets/scripts/core/level.js +++ b/assets/scripts/core/level.js @@ -29,7 +29,13 @@ class Collider { if (this.slopeFlipY) angleDeg = -angleDeg; return angleDeg * Math.PI / 180; } - isSlopeSolidAt(worldX, worldY) { + usesFloorLanding(gravityFlipped) { + return !gravityFlipped; + } + isSolidBelowSurface(gravityFlipped) { + return this.slopeFlipY === gravityFlipped; + } + isSlopeSolidAt(worldX, worldY, gravityFlipped = false) { if (this.type !== slopeType) return false; const halfW = this.w / 2; const halfH = this.h / 2; @@ -45,11 +51,11 @@ class Collider { } const surfaceY = this.getSlopeSurfaceY(worldX); if (surfaceY === null) return false; - return worldY < surfaceY; + return this.isSolidBelowSurface(gravityFlipped) ? worldY < surfaceY : worldY > surfaceY; } - getSlopeBackWallSide() { + getSlopeBackWallSide(gravityFlipped = false) { let leftWall = this.slopeDir > 0; - if (this.slopeFlipY) leftWall = !leftWall; + if (!this.isSolidBelowSurface(gravityFlipped)) leftWall = !leftWall; return leftWall ? "left" : "right"; } } diff --git a/assets/scripts/core/player.js b/assets/scripts/core/player.js index 45a3873e..508adb48 100644 --- a/assets/scripts/core/player.js +++ b/assets/scripts/core/player.js @@ -1539,11 +1539,12 @@ if (this.p.isFlying || this.p.isUfo) { _getSlopeHitSize(playerSize, waveHitSize) { return this.p.isWave ? waveHitSize : playerSize; } - _isOnSlopeSurface(surfaceY, footProbe, headProbe, hitSize, gravityFlipped) { - if (!gravityFlipped) { - return Math.abs(footProbe - surfaceY) < Math.max(14, hitSize * 0.45) && footProbe >= surfaceY - 2; + _isOnSlopeSurface(surfaceY, footProbe, headProbe, hitSize, useFloorLanding) { + const tolerance = Math.max(14, hitSize * 0.45); + if (useFloorLanding) { + return Math.abs(footProbe - surfaceY) < tolerance && footProbe >= surfaceY - 2; } - return Math.abs(headProbe - surfaceY) < Math.max(14, hitSize * 0.45) && headProbe <= surfaceY + 2; + return Math.abs(headProbe - surfaceY) < tolerance && headProbe <= surfaceY + 2; } _landOnSlopeFloor(gameObj, surfaceY, hitSize, footProbe, lastFootProbe, playersY) { if (!this.p.gravityFlipped && @@ -1585,6 +1586,41 @@ if (this.p.isFlying || this.p.isUfo) { } return false; } + _trySnapToSlopeSurface(gameObj, pieceWidth, playersY, hitSize, gamemodeAddition, useFloorLanding) { + const surfaceY = gameObj.getSlopeSurfaceY(pieceWidth); + if (surfaceY === null) return false; + const pad = Math.max(10, hitSize * 0.4); + const footProbe = playersY - hitSize + gamemodeAddition; + const headProbe = playersY + hitSize - gamemodeAddition; + if (useFloorLanding) { + if (footProbe < surfaceY - pad) return false; + if (!gameObj.slopeFlipY && footProbe > surfaceY + pad * 2) return false; + if (gameObj.slopeFlipY && footProbe > surfaceY + pad) return false; + this.p.y = surfaceY + hitSize; + this.hitGround(); + this.p.collideBottom = surfaceY; + this.p.onCeiling = false; + this._onSlopeAngle = gameObj.getSlopeAngleRad(); + return true; + } + if (headProbe < surfaceY - pad * 2) return false; + if (headProbe > surfaceY + pad) return false; + this.p.y = surfaceY - hitSize; + this.hitGround(); + this.p.onCeiling = true; + this.p.collideTop = surfaceY; + this._onSlopeAngle = gameObj.getSlopeAngleRad(); + return true; + } + _trySlopeVelocityLanding(gameObj, surfaceY, hitSize, footProbe, lastFootProbe, headProbe, lastHeadProbe, playersY, useFloorLanding) { + if (useFloorLanding) { + if (this.p.gravityFlipped) { + return this._landOnSlopeCeilingFlipped(gameObj, surfaceY, hitSize, footProbe, lastFootProbe, playersY); + } + return this._landOnSlopeFloor(gameObj, surfaceY, hitSize, footProbe, lastFootProbe, playersY); + } + return this._landOnSlopeCeiling(gameObj, surfaceY, hitSize, headProbe, lastHeadProbe, playersY); + } _handleSlopeCollision(gameObj, pieceWidth, playersY, playersLastY, left, right, top, bottom, playerSize, waveHitSize, gamemodeAddition) { const surfaceY = gameObj.getSlopeSurfaceY(pieceWidth); if (surfaceY === null) return { landed: false, died: false }; @@ -1595,31 +1631,28 @@ if (this.p.isFlying || this.p.isUfo) { const lastHeadProbe = playersLastY + hitSize - gamemodeAddition; const tightPad = this.p.isWave ? 5 : 9; const inXRange = pieceWidth + hitSize - 5 > left && pieceWidth - hitSize + 5 < right; - const onSlopeSurface = this._isOnSlopeSurface(surfaceY, footProbe, headProbe, hitSize, this.p.gravityFlipped); + const useFloorLanding = gameObj.usesFloorLanding(this.p.gravityFlipped); + const onSlopeSurface = this._isOnSlopeSurface(surfaceY, footProbe, headProbe, hitSize, useFloorLanding); let landed = false; if (inXRange) { - if (this.p.isUfo) { - if (!this.p.gravityFlipped && this._landOnSlopeCeiling(gameObj, surfaceY, hitSize, headProbe, lastHeadProbe, playersY)) { - landed = true; - } else if (this.p.gravityFlipped && this._landOnSlopeCeilingFlipped(gameObj, surfaceY, hitSize, footProbe, lastFootProbe, playersY)) { - landed = true; - } - } else if (this.p.isFlying) { - if (!this.p.gravityFlipped) { - if (this._landOnSlopeFloor(gameObj, surfaceY, hitSize, footProbe, lastFootProbe, playersY)) { - landed = true; - } else if (this._landOnSlopeCeiling(gameObj, surfaceY, hitSize, headProbe, lastHeadProbe, playersY)) { + if (this.p.isFlying || this.p.isUfo) { + if (!useFloorLanding && !this.p.gravityFlipped && + (headProbe <= surfaceY || lastHeadProbe <= surfaceY) && + (this.p.yVelocity >= 0 || this.p.onGround)) { + if (this._landOnSlopeCeiling(gameObj, surfaceY, hitSize, headProbe, lastHeadProbe, playersY)) { landed = true; } - } else if (this._landOnSlopeCeilingFlipped(gameObj, surfaceY, hitSize, footProbe, lastFootProbe, playersY)) { - landed = true; } - } else if (this.p.gravityFlipped) { - if (this._landOnSlopeCeiling(gameObj, surfaceY, hitSize, headProbe, lastHeadProbe, playersY)) { + if (!landed) { + landed = this._trySnapToSlopeSurface(gameObj, pieceWidth, playersY, hitSize, gamemodeAddition, useFloorLanding); + } + } else { + if (this._trySlopeVelocityLanding(gameObj, surfaceY, hitSize, footProbe, lastFootProbe, headProbe, lastHeadProbe, playersY, useFloorLanding)) { landed = true; } - } else if (this._landOnSlopeFloor(gameObj, surfaceY, hitSize, footProbe, lastFootProbe, playersY)) { - landed = true; + if (!landed) { + landed = this._trySnapToSlopeSurface(gameObj, pieceWidth, playersY, hitSize, gamemodeAddition, useFloorLanding); + } } if (landed && !this.p.isFlying && !this.p.isWave) { this._checkSnapJump(gameObj); @@ -1633,20 +1666,28 @@ if (this.p.isFlying || this.p.isUfo) { const overlapping = pieceWidth + tightPad > left && pieceWidth - tightPad < right && playersY + tightPad > top && playersY - tightPad < bottom; if (overlapping && !onSlopeSurface) { - if (!gameObj.slopeIsFilled && gameObj.isSlopeSolidAt(pieceWidth, playersY)) { + if (!gameObj.slopeIsFilled && gameObj.isSlopeSolidAt(pieceWidth, playersY, this.p.gravityFlipped)) { if (window.noClip) this.p.diedThisFrame = true; else return { landed: false, died: true }; } else { - const backSide = gameObj.getSlopeBackWallSide(); + const backSide = gameObj.getSlopeBackWallSide(this.p.gravityFlipped); const wallX = backSide === "left" ? left : right; const wallSurface = gameObj.getSlopeSurfaceY(wallX); let hitBackWall = false; - if (backSide === "left") { + if (useFloorLanding) { + if (backSide === "left") { + hitBackWall = pieceWidth < wallX + tightPad && pieceWidth + hitSize > wallX && + footProbe < wallSurface - 2 && headProbe > top + 2; + } else { + hitBackWall = pieceWidth > wallX - tightPad && pieceWidth - hitSize < wallX && + footProbe < wallSurface - 2 && headProbe > top + 2; + } + } else if (backSide === "left") { hitBackWall = pieceWidth < wallX + tightPad && pieceWidth + hitSize > wallX && - footProbe < wallSurface - 2 && headProbe > top + 2; + headProbe > wallSurface + 2 && footProbe < bottom - 2; } else { hitBackWall = pieceWidth > wallX - tightPad && pieceWidth - hitSize < wallX && - footProbe < wallSurface - 2 && headProbe > top + 2; + headProbe > wallSurface + 2 && footProbe < bottom - 2; } if (hitBackWall || gameObj.slopeIsFilled) { if (window.noClip) this.p.diedThisFrame = true; @@ -1665,12 +1706,14 @@ if (this.p.isFlying || this.p.isUfo) { const left = slopeObj.x - halfW; const right = slopeObj.x + halfW; const exitMargin = 14; + const useFloorLanding = slopeObj.usesFloorLanding(this.p.gravityFlipped); let exitHighEnd = slopeObj.slopeDir > 0 ? pieceWidth >= right - exitMargin : pieceWidth <= left + exitMargin; if (slopeObj.slopeFlipY) exitHighEnd = !exitHighEnd; if (!exitHighEnd) return; let bounceVel = Math.tan(angleRad) * playerSpeed * this.flipMod(); + if (slopeObj.slopeFlipY) bounceVel = -bounceVel; if (this.p.isWave) bounceVel *= 0.85; else if (this.p.isMini) bounceVel *= 0.8; if (Math.abs(bounceVel) < 0.5) return;