mirror of
				https://github.com/genxium/DelayNoMore
				synced 2025-11-04 05:17:52 +00:00 
			
		
		
		
	Applied snapping on all-sides to avoid random zero-overlap detection uncertainty.
This commit is contained in:
		@@ -401,11 +401,11 @@ func (pR *Room) StartBattle() {
 | 
			
		||||
	pR.collisionSpaceOffsetX, pR.collisionSpaceOffsetY = float64(spaceW)*0.5, float64(spaceH)*0.5
 | 
			
		||||
	pR.refreshColliders(spaceW, spaceH)
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
		 * Will be triggered from a goroutine which executes the critical `Room.AddPlayerIfPossible`, thus the `battleMainLoop` should be detached.
 | 
			
		||||
		 * All of the consecutive stages, e.g. settlement, dismissal, should share the same goroutine with `battleMainLoop`.
 | 
			
		||||
	     *
 | 
			
		||||
	     * As "defer" is only applicable to function scope, the use of "pR.InputsBufferLock" within "battleMainLoop" is embedded into each subroutine call.
 | 
			
		||||
	/*
 | 
			
		||||
			  Will be triggered from a goroutine which executes the critical `Room.AddPlayerIfPossible`, thus the `battleMainLoop` should be detached.
 | 
			
		||||
			  All of the consecutive stages, e.g. settlement, dismissal, should share the same goroutine with `battleMainLoop`.
 | 
			
		||||
 | 
			
		||||
		      As "defer" is only applicable to function scope, the use of "pR.InputsBufferLock" within "battleMainLoop" is embedded into each subroutine call.
 | 
			
		||||
	*/
 | 
			
		||||
	battleMainLoop := func() {
 | 
			
		||||
		defer func() {
 | 
			
		||||
@@ -754,7 +754,7 @@ func (pR *Room) OnDismissed() {
 | 
			
		||||
	pR.RollbackEstimatedDtNanos = 16666666 // A little smaller than the actual per frame time, just for logging FAST FRAME
 | 
			
		||||
	dilutedServerFps := float64(55.0)
 | 
			
		||||
	pR.dilutedRollbackEstimatedDtNanos = int64(float64(pR.RollbackEstimatedDtNanos) * float64(pR.ServerFps) / dilutedServerFps)
 | 
			
		||||
	pR.BattleDurationFrames = 60 * pR.ServerFps
 | 
			
		||||
	pR.BattleDurationFrames = 120 * pR.ServerFps
 | 
			
		||||
	pR.BattleDurationNanos = int64(pR.BattleDurationFrames) * (pR.RollbackEstimatedDtNanos + 1)
 | 
			
		||||
	pR.InputFrameUpsyncDelayTolerance = 2
 | 
			
		||||
	pR.MaxChasingRenderFramesPerUpdate = 8
 | 
			
		||||
@@ -1278,6 +1278,7 @@ func (pR *Room) applyInputFrameDownsyncDynamics(fromRenderFrameId int32, toRende
 | 
			
		||||
 | 
			
		||||
// TODO: Write unit-test for this function to compare with its frontend counter part
 | 
			
		||||
func (pR *Room) applyInputFrameDownsyncDynamicsOnSingleRenderFrame(delayedInputFrame *InputFrameDownsync, currRenderFrame *RoomDownsyncFrame, collisionSysMap map[int32]*resolv.Object) *RoomDownsyncFrame {
 | 
			
		||||
	topPadding, bottomPadding, leftPadding, rightPadding := pR.SnapIntoPlatformOverlap, pR.SnapIntoPlatformOverlap, pR.SnapIntoPlatformOverlap, pR.SnapIntoPlatformOverlap
 | 
			
		||||
	// [WARNING] This function MUST BE called while "pR.InputsBufferLock" is locked!
 | 
			
		||||
	nextRenderFramePlayers := make(map[int32]*PlayerDownsync, pR.Capacity)
 | 
			
		||||
	// Make a copy first
 | 
			
		||||
@@ -1395,9 +1396,12 @@ func (pR *Room) applyInputFrameDownsyncDynamicsOnSingleRenderFrame(delayedInputF
 | 
			
		||||
		currPlayerDownsync, thatPlayerInNextFrame := currRenderFrame.Players[playerId], nextRenderFramePlayers[playerId]
 | 
			
		||||
		// Reset playerCollider position from the "virtual grid position"
 | 
			
		||||
		newVx, newVy := currPlayerDownsync.VirtualGridX+currPlayerDownsync.VelX, currPlayerDownsync.VirtualGridY+currPlayerDownsync.VelY
 | 
			
		||||
		if thatPlayerInNextFrame.VelY == pR.JumpingInitVelY {
 | 
			
		||||
			newVy += thatPlayerInNextFrame.VelY
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		colliderWidth, colliderHeight := player.ColliderRadius*2, player.ColliderRadius*4
 | 
			
		||||
		playerCollider.X, playerCollider.Y = VirtualGridToPolygonColliderBLPos(newVx, newVy, colliderWidth*0.5, colliderHeight*0.5, pR.SnapIntoPlatformOverlap, pR.collisionSpaceOffsetX, pR.collisionSpaceOffsetY, pR.VirtualGridToWorldRatio)
 | 
			
		||||
		halfColliderWidth, halfColliderHeight := player.ColliderRadius, player.ColliderRadius+player.ColliderRadius // avoid multiplying
 | 
			
		||||
		playerCollider.X, playerCollider.Y = VirtualGridToPolygonColliderBLPos(newVx, newVy, halfColliderWidth, halfColliderHeight, topPadding, bottomPadding, leftPadding, rightPadding, pR.collisionSpaceOffsetX, pR.collisionSpaceOffsetY, pR.VirtualGridToWorldRatio)
 | 
			
		||||
		// Update in the collision system
 | 
			
		||||
		playerCollider.Update()
 | 
			
		||||
 | 
			
		||||
@@ -1423,8 +1427,7 @@ func (pR *Room) applyInputFrameDownsyncDynamicsOnSingleRenderFrame(delayedInputF
 | 
			
		||||
			}
 | 
			
		||||
			offenderWx, offenderWy := VirtualGridToWorldPos(offender.VirtualGridX, offender.VirtualGridY, pR.VirtualGridToWorldRatio)
 | 
			
		||||
			bulletWx, bulletWy := offenderWx+xfac*meleeBullet.HitboxOffset, offenderWy
 | 
			
		||||
 | 
			
		||||
			newBulletCollider := GenerateRectCollider(bulletWx, bulletWy, meleeBullet.HitboxSize.X, meleeBullet.HitboxSize.Y, 0, pR.collisionSpaceOffsetX, pR.collisionSpaceOffsetY, "MeleeBullet")
 | 
			
		||||
			newBulletCollider := GenerateRectCollider(bulletWx, bulletWy, meleeBullet.HitboxSize.X, meleeBullet.HitboxSize.Y, topPadding, bottomPadding, leftPadding, rightPadding, pR.collisionSpaceOffsetX, pR.collisionSpaceOffsetY, "MeleeBullet")
 | 
			
		||||
			newBulletCollider.Data = meleeBullet
 | 
			
		||||
			pR.Space.Add(newBulletCollider)
 | 
			
		||||
			collisionSysMap[collisionBulletIndex] = newBulletCollider
 | 
			
		||||
@@ -1444,9 +1447,6 @@ func (pR *Room) applyInputFrameDownsyncDynamicsOnSingleRenderFrame(delayedInputF
 | 
			
		||||
		playerCollider := collisionSysMap[collisionPlayerIndex]
 | 
			
		||||
		playerShape := playerCollider.Shape.(*resolv.ConvexPolygon)
 | 
			
		||||
		hardPushbackNorms[joinIndex-1] = pR.calcHardPushbacksNorms(playerCollider, playerShape, pR.SnapIntoPlatformOverlap, &(effPushbacks[joinIndex-1]))
 | 
			
		||||
		if 0 < len(hardPushbackNorms[joinIndex-1]) {
 | 
			
		||||
			Logger.Debug(fmt.Sprintf("playerId=%d, joinIndex=%d got %d non-empty hardPushbacks at renderFrame.id=%d", playerId, joinIndex, len(hardPushbackNorms), currRenderFrame.Id))
 | 
			
		||||
		}
 | 
			
		||||
		currPlayerDownsync, thatPlayerInNextFrame := currRenderFrame.Players[playerId], nextRenderFramePlayers[playerId]
 | 
			
		||||
		fallStopping := false
 | 
			
		||||
		possiblyFallStoppedOnAnotherPlayer := false
 | 
			
		||||
@@ -1480,6 +1480,10 @@ func (pR *Room) applyInputFrameDownsyncDynamicsOnSingleRenderFrame(delayedInputF
 | 
			
		||||
				pushbackX, pushbackY = (overlapResult.Overlap-pR.SnapIntoPlatformOverlap)*overlapResult.OverlapX, (overlapResult.Overlap-pR.SnapIntoPlatformOverlap)*overlapResult.OverlapY
 | 
			
		||||
				thatPlayerInNextFrame.InAir = false
 | 
			
		||||
			}
 | 
			
		||||
			if isAnotherPlayer {
 | 
			
		||||
				// [WARNING] See comments of this substep in frontend.
 | 
			
		||||
				pushbackX, pushbackY = (overlapResult.Overlap-pR.SnapIntoPlatformOverlap*2)*overlapResult.OverlapX, (overlapResult.Overlap-pR.SnapIntoPlatformOverlap*2)*overlapResult.OverlapY
 | 
			
		||||
			}
 | 
			
		||||
			for _, hardPushbackNorm := range hardPushbackNorms[joinIndex-1] {
 | 
			
		||||
				projectedMagnitude := pushbackX*hardPushbackNorm.X + pushbackY*hardPushbackNorm.Y
 | 
			
		||||
				if isBarrier || (isAnotherPlayer && 0 > projectedMagnitude) {
 | 
			
		||||
@@ -1494,16 +1498,17 @@ func (pR *Room) applyInputFrameDownsyncDynamicsOnSingleRenderFrame(delayedInputF
 | 
			
		||||
				if isAnotherPlayer {
 | 
			
		||||
					possiblyFallStoppedOnAnotherPlayer = true
 | 
			
		||||
				}
 | 
			
		||||
				if 1 == thatPlayerInNextFrame.JoinIndex {
 | 
			
		||||
					Logger.Info(fmt.Sprintf("playerId=%d, joinIndex=%d fallStopping#1 at {renderFrame.id: %d, virtualX: %d, virtualY: %d, velX: %d, velY: %d} with effPushback={%.3f, %.3f}, overlapMag=%.4f, possiblyFallStoppedOnAnotherPlayer=%v", playerId, joinIndex, currRenderFrame.Id, currPlayerDownsync.VirtualGridX, currPlayerDownsync.VirtualGridY, currPlayerDownsync.VelX, currPlayerDownsync.VelY, effPushbacks[joinIndex-1].X, effPushbacks[joinIndex-1].Y, overlapResult.Overlap, possiblyFallStoppedOnAnotherPlayer))
 | 
			
		||||
			}
 | 
			
		||||
			if 1 == joinIndex {
 | 
			
		||||
				halfColliderWidth, halfColliderHeight := player.ColliderRadius, player.ColliderRadius+player.ColliderRadius // avoid multiplying
 | 
			
		||||
				if fallStopping {
 | 
			
		||||
					Logger.Info(fmt.Sprintf("playerId=%d, joinIndex=%d fallStopping#1\n{renderFrame.id: %d, possiblyFallStoppedOnAnotherPlayer: %v}\nplayerColliderPos=%v, effPushback={%.3f, %.3f}, overlapMag=%.4f", playerId, joinIndex, currRenderFrame.Id, possiblyFallStoppedOnAnotherPlayer, RectCenterStr(playerCollider, halfColliderWidth, halfColliderHeight, topPadding, bottomPadding, leftPadding, rightPadding, pR.collisionSpaceOffsetX, pR.collisionSpaceOffsetY), effPushbacks[joinIndex-1].X, effPushbacks[joinIndex-1].Y, overlapResult.Overlap))
 | 
			
		||||
				} else if currPlayerDownsync.InAir && isBarrier && !landedOnGravityPushback {
 | 
			
		||||
					Logger.Warn(fmt.Sprintf("playerId=%d, joinIndex=%d inAir & pushed back by barrier & not landed at {renderFrame.id: %d}\nplayerColliderPos=%v, effPushback={%.3f, %.3f}, overlapMag=%.4f, len(hardPushbackNorms)=%d", playerId, joinIndex, currRenderFrame.Id, RectCenterStr(playerCollider, halfColliderWidth, halfColliderHeight, topPadding, bottomPadding, leftPadding, rightPadding, pR.collisionSpaceOffsetX, pR.collisionSpaceOffsetY), effPushbacks[joinIndex-1].X, effPushbacks[joinIndex-1].Y, overlapResult.Overlap, len(hardPushbackNorms)))
 | 
			
		||||
				} else if currPlayerDownsync.InAir && isAnotherPlayer {
 | 
			
		||||
					Logger.Warn(fmt.Sprintf("playerId=%d, joinIndex=%d inAir & pushed back by another player\n{renderFrame.id: %d}\nplayerColliderPos=%v, anotherPlayerColliderPos=%v, effPushback={%.3f, %.3f}, landedOnGravityPushback=%v, fallStopping=%v, overlapMag=%.4f, len(hardPushbackNorms)=%d", playerId, joinIndex, currRenderFrame.Id, RectCenterStr(playerCollider, halfColliderWidth, halfColliderHeight, topPadding, bottomPadding, leftPadding, rightPadding, pR.collisionSpaceOffsetX, pR.collisionSpaceOffsetY), RectCenterStr(obj, halfColliderWidth, halfColliderHeight, topPadding, bottomPadding, leftPadding, rightPadding, pR.collisionSpaceOffsetX, pR.collisionSpaceOffsetY), effPushbacks[joinIndex-1].X, effPushbacks[joinIndex-1].Y, landedOnGravityPushback, fallStopping, overlapResult.Overlap, len(hardPushbackNorms)))
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			if 1 == joinIndex && currPlayerDownsync.InAir && isBarrier && !landedOnGravityPushback {
 | 
			
		||||
				Logger.Warn(fmt.Sprintf("playerId=%d, joinIndex=%d inAir & pushed back by barrier & not landed at {renderFrame.id: %d, virtualX: %d, virtualY: %d, velX: %d, velY: %d} with effPushback={%.3f, %.3f}, playerColliderPos={%.3f, %.3f}, barrierPos={%.3f, %.3f}, overlapMag=%.4f, len(hardPushbackNorms)=%d", playerId, joinIndex, currRenderFrame.Id, currPlayerDownsync.VirtualGridX, currPlayerDownsync.VirtualGridY, currPlayerDownsync.VelX, currPlayerDownsync.VelY, effPushbacks[joinIndex-1].X, effPushbacks[joinIndex-1].Y, playerCollider.X-pR.collisionSpaceOffsetX, playerCollider.Y-pR.collisionSpaceOffsetY, bShape.X-pR.collisionSpaceOffsetX, bShape.Y-pR.collisionSpaceOffsetY, overlapResult.Overlap, len(hardPushbackNorms)))
 | 
			
		||||
			}
 | 
			
		||||
			if 1 == joinIndex && currPlayerDownsync.InAir && isAnotherPlayer {
 | 
			
		||||
				Logger.Warn(fmt.Sprintf("playerId=%d, joinIndex=%d inAir & pushed back by another player at {renderFrame.id: %d, virtualX: %d, virtualY: %d, velX: %d, velY: %d} with effPushback={%.3f, %.3f}, landedOnGravityPushback=%v, fallStopping=%v, playerColliderPos={%.3f, %.3f}, anotherPlayerColliderPos={%.3f, %.3f}, overlapMag=%.4f, len(hardPushbackNorms)=%d", playerId, joinIndex, currRenderFrame.Id, currPlayerDownsync.VirtualGridX, currPlayerDownsync.VirtualGridY, currPlayerDownsync.VelX, currPlayerDownsync.VelY, effPushbacks[joinIndex-1].X, effPushbacks[joinIndex-1].Y, landedOnGravityPushback, fallStopping, playerCollider.X-pR.collisionSpaceOffsetX, playerCollider.Y-pR.collisionSpaceOffsetY, bShape.X-pR.collisionSpaceOffsetX, bShape.Y-pR.collisionSpaceOffsetY, overlapResult.Overlap, len(hardPushbackNorms)))
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		if fallStopping {
 | 
			
		||||
			thatPlayerInNextFrame.VelX = 0
 | 
			
		||||
@@ -1599,21 +1604,18 @@ func (pR *Room) applyInputFrameDownsyncDynamicsOnSingleRenderFrame(delayedInputF
 | 
			
		||||
		playerId := player.Id
 | 
			
		||||
		collisionPlayerIndex := COLLISION_PLAYER_INDEX_PREFIX + joinIndex
 | 
			
		||||
		playerCollider := collisionSysMap[collisionPlayerIndex]
 | 
			
		||||
		playerShape := playerCollider.Shape.(*resolv.ConvexPolygon)
 | 
			
		||||
		// Update "virtual grid position"
 | 
			
		||||
		currPlayerDownsync, thatPlayerInNextFrame := currRenderFrame.Players[playerId], nextRenderFramePlayers[playerId]
 | 
			
		||||
		colliderWidth, colliderHeight := player.ColliderRadius*2, player.ColliderRadius*4
 | 
			
		||||
		thatPlayerInNextFrame.VirtualGridX, thatPlayerInNextFrame.VirtualGridY = PolygonColliderBLToVirtualGridPos(playerCollider.X-effPushbacks[joinIndex-1].X, playerCollider.Y-effPushbacks[joinIndex-1].Y, colliderWidth*0.5, colliderHeight*0.5, pR.SnapIntoPlatformOverlap, pR.collisionSpaceOffsetX, pR.collisionSpaceOffsetY, pR.WorldToVirtualGridRatio)
 | 
			
		||||
		halfColliderWidth, halfColliderHeight := player.ColliderRadius, player.ColliderRadius+player.ColliderRadius // avoid multiplying
 | 
			
		||||
		thatPlayerInNextFrame.VirtualGridX, thatPlayerInNextFrame.VirtualGridY = PolygonColliderBLToVirtualGridPos(playerCollider.X-effPushbacks[joinIndex-1].X, playerCollider.Y-effPushbacks[joinIndex-1].Y, halfColliderWidth, halfColliderHeight, topPadding, bottomPadding, leftPadding, rightPadding, pR.collisionSpaceOffsetX, pR.collisionSpaceOffsetY, pR.WorldToVirtualGridRatio)
 | 
			
		||||
 | 
			
		||||
		if 1 == thatPlayerInNextFrame.JoinIndex {
 | 
			
		||||
			if thatPlayerInNextFrame.InAir && (0 != thatPlayerInNextFrame.VelY) {
 | 
			
		||||
				// Logger.Info(fmt.Sprintf("playerId=%d, joinIndex=%d inAir trajectory: {nextRenderFrame.id: %d, nextVirtualX: %d, nextVirtualY: %d, nextVelX: %d, nextVelY: %d}, with playerColliderPos={%.3f, %.3f}, effPushback={%.3f, %.3f}", playerId, joinIndex, currRenderFrame.Id+1, thatPlayerInNextFrame.VirtualGridX, thatPlayerInNextFrame.VirtualGridY, thatPlayerInNextFrame.VelX, thatPlayerInNextFrame.VelY, playerShape.X-pR.collisionSpaceOffsetX, playerShape.Y-pR.collisionSpaceOffsetY, effPushbacks[joinIndex-1].X, effPushbacks[joinIndex-1].Y))
 | 
			
		||||
			}
 | 
			
		||||
			if currPlayerDownsync.InAir && !thatPlayerInNextFrame.InAir {
 | 
			
		||||
				Logger.Warn(fmt.Sprintf("playerId=%d, joinIndex=%d fallStopping#2 at {nextRenderFrame.id: %d, nextVirtualX: %d, nextVirtualY: %d, nextVelX: %d, nextVelY: %d}, with playerColliderPos={%.3f, %.3f}, effPushback={%.3f, %.3f}", playerId, joinIndex, currRenderFrame.Id+1, thatPlayerInNextFrame.VirtualGridX, thatPlayerInNextFrame.VirtualGridY, thatPlayerInNextFrame.VelX, thatPlayerInNextFrame.VelY, playerShape.X-pR.collisionSpaceOffsetX, playerShape.Y-pR.collisionSpaceOffsetY, effPushbacks[joinIndex-1].X, effPushbacks[joinIndex-1].Y))
 | 
			
		||||
			}
 | 
			
		||||
			if !currPlayerDownsync.InAir && thatPlayerInNextFrame.InAir {
 | 
			
		||||
				Logger.Warn(fmt.Sprintf("playerId=%d, joinIndex=%d took off at {nextRenderFrame.id: %d, nextVirtualX: %d, nextVirtualY: %d, nextVelX: %d, nextVelY: %d}, with playerColliderPos={%.3f, %.3f}, effPushback={%.3f, %.3f}", playerId, joinIndex, currRenderFrame.Id+1, thatPlayerInNextFrame.VirtualGridX, thatPlayerInNextFrame.VirtualGridY, thatPlayerInNextFrame.VelX, thatPlayerInNextFrame.VelY, playerShape.X-pR.collisionSpaceOffsetX, playerShape.Y-pR.collisionSpaceOffsetY, effPushbacks[joinIndex-1].X, effPushbacks[joinIndex-1].Y))
 | 
			
		||||
				Logger.Warn(fmt.Sprintf("playerId=%d, joinIndex=%d fallStopping#2:\n{nextRenderFrame.id: %d, nextVirtualX: %d, nextVirtualY: %d, nextVelX: %d, nextVelY: %d}\n\tcalculated from <- playerColliderPos=%v, effPushback={%.3f, %.3f}", playerId, joinIndex, currRenderFrame.Id+1, thatPlayerInNextFrame.VirtualGridX, thatPlayerInNextFrame.VirtualGridY, thatPlayerInNextFrame.VelX, thatPlayerInNextFrame.VelY, RectCenterStr(playerCollider, halfColliderWidth, halfColliderHeight, topPadding, bottomPadding, leftPadding, rightPadding, pR.collisionSpaceOffsetX, pR.collisionSpaceOffsetY), effPushbacks[joinIndex-1].X, effPushbacks[joinIndex-1].Y))
 | 
			
		||||
			} else if !currPlayerDownsync.InAir && thatPlayerInNextFrame.InAir {
 | 
			
		||||
				Logger.Warn(fmt.Sprintf("playerId=%d, joinIndex=%d took off:\n{nextRenderFrame.id: %d, nextVirtualX: %d, nextVirtualY: %d, nextVelX: %d, nextVelY: %d}\n\tcalculated from <- playerColliderPos=%v, effPushback={%.3f, %.3f}", playerId, joinIndex, currRenderFrame.Id+1, thatPlayerInNextFrame.VirtualGridX, thatPlayerInNextFrame.VirtualGridY, thatPlayerInNextFrame.VelX, thatPlayerInNextFrame.VelY, RectCenterStr(playerCollider, halfColliderWidth, halfColliderHeight, topPadding, bottomPadding, leftPadding, rightPadding, pR.collisionSpaceOffsetX, pR.collisionSpaceOffsetY), effPushbacks[joinIndex-1].X, effPushbacks[joinIndex-1].Y))
 | 
			
		||||
			} else if thatPlayerInNextFrame.InAir && (0 != thatPlayerInNextFrame.VelY) {
 | 
			
		||||
				//Logger.Info(fmt.Sprintf("playerId=%d, joinIndex=%d inAir trajectory:\n{nextRenderFrame.id: %d, nextVirtualX: %d, nextVirtualY: %d, nextVelX: %d, nextVelY: %d}\n\tcalculated from <- playerColliderPos=%v, effPushback={%.3f, %.3f}", playerId, joinIndex, currRenderFrame.Id+1, thatPlayerInNextFrame.VirtualGridX, thatPlayerInNextFrame.VirtualGridY, thatPlayerInNextFrame.VelX, thatPlayerInNextFrame.VelY, RectCenterStr(playerCollider, halfColliderWidth, halfColliderHeight, topPadding, bottomPadding, leftPadding, rightPadding, pR.collisionSpaceOffsetX, pR.collisionSpaceOffsetY), effPushbacks[joinIndex-1].X, effPushbacks[joinIndex-1].Y))
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
@@ -1645,12 +1647,14 @@ func (pR *Room) inputFrameIdDebuggable(inputFrameId int32) bool {
 | 
			
		||||
func (pR *Room) refreshColliders(spaceW, spaceH int32) {
 | 
			
		||||
	// Kindly note that by now, we've already got all the shapes in the tmx file into "pR.(Players | Barriers)" from "ParseTmxLayersAndGroups"
 | 
			
		||||
 | 
			
		||||
	minStep := (int(float64(pR.PlayerDefaultSpeed)*pR.VirtualGridToWorldRatio) << 1) // the approx minimum distance a player can move per frame in world coordinate
 | 
			
		||||
	topPadding, bottomPadding, leftPadding, rightPadding := pR.SnapIntoPlatformOverlap, pR.SnapIntoPlatformOverlap, pR.SnapIntoPlatformOverlap, pR.SnapIntoPlatformOverlap
 | 
			
		||||
 | 
			
		||||
	minStep := (int(float64(pR.PlayerDefaultSpeed)*pR.VirtualGridToWorldRatio) << 2) // the approx minimum distance a player can move per frame in world coordinate
 | 
			
		||||
	pR.Space = resolv.NewSpace(int(spaceW), int(spaceH), minStep, minStep)           // allocate a new collision space everytime after a battle is settled
 | 
			
		||||
	for _, player := range pR.Players {
 | 
			
		||||
		wx, wy := VirtualGridToWorldPos(player.VirtualGridX, player.VirtualGridY, pR.VirtualGridToWorldRatio)
 | 
			
		||||
		colliderWidth, colliderHeight := player.ColliderRadius*2, player.ColliderRadius*4
 | 
			
		||||
		playerCollider := GenerateRectCollider(wx, wy, colliderWidth, colliderHeight, pR.SnapIntoPlatformOverlap, pR.collisionSpaceOffsetX, pR.collisionSpaceOffsetY, "Player") // the coords of all barrier boundaries are multiples of tileWidth(i.e. 16), by adding snapping y-padding when "landedOnGravityPushback" all "playerCollider.Y" would be a multiple of 1.0
 | 
			
		||||
		playerCollider := GenerateRectCollider(wx, wy, colliderWidth, colliderHeight, topPadding, bottomPadding, leftPadding, rightPadding, pR.collisionSpaceOffsetX, pR.collisionSpaceOffsetY, "Player") // the coords of all barrier boundaries are multiples of tileWidth(i.e. 16), by adding snapping y-padding when "landedOnGravityPushback" all "playerCollider.Y" would be a multiple of 1.0
 | 
			
		||||
		playerCollider.Data = player
 | 
			
		||||
		pR.Space.Add(playerCollider)
 | 
			
		||||
		// Keep track of the collider in "pR.CollisionSysMap"
 | 
			
		||||
 
 | 
			
		||||
@@ -37,14 +37,15 @@ func NewWorldColliderDisplay(game *Game, stageDiscreteW, stageDiscreteH, stageTi
 | 
			
		||||
	worldToVirtualGridRatio := float64(1000)
 | 
			
		||||
	virtualGridToWorldRatio := float64(1) / worldToVirtualGridRatio
 | 
			
		||||
	playerDefaultSpeed := 1 * worldToVirtualGridRatio
 | 
			
		||||
	minStep := (int(float64(playerDefaultSpeed)*virtualGridToWorldRatio) << 2)
 | 
			
		||||
	minStep := (int(float64(playerDefaultSpeed)*virtualGridToWorldRatio) << 3)
 | 
			
		||||
	playerColliderRadius := float64(12)
 | 
			
		||||
	playerColliders := make([]*resolv.Object, len(playerPosList.Eles))
 | 
			
		||||
	snapIntoPlatformOverlap := float64(0.1)
 | 
			
		||||
	space := resolv.NewSpace(int(spaceW), int(spaceH), minStep, minStep)
 | 
			
		||||
	topPadding, bottomPadding, leftPadding, rightPadding := snapIntoPlatformOverlap, snapIntoPlatformOverlap, snapIntoPlatformOverlap, snapIntoPlatformOverlap
 | 
			
		||||
	for i, playerPos := range playerPosList.Eles {
 | 
			
		||||
		colliderWidth, colliderHeight := playerColliderRadius*2, playerColliderRadius*4
 | 
			
		||||
		playerCollider := GenerateRectCollider(playerPos.X, playerPos.Y, colliderWidth, colliderHeight, snapIntoPlatformOverlap, spaceOffsetX, spaceOffsetY, "Player") // [WARNING] Deliberately not using a circle because "resolv v0.5.1" doesn't yet align circle center with space cell center, regardless of the "specified within-object offset"
 | 
			
		||||
		playerCollider := GenerateRectCollider(playerPos.X, playerPos.Y, colliderWidth, colliderHeight, topPadding, bottomPadding, leftPadding, rightPadding, spaceOffsetX, spaceOffsetY, "Player") // [WARNING] Deliberately not using a circle because "resolv v0.5.1" doesn't yet align circle center with space cell center, regardless of the "specified within-object offset"
 | 
			
		||||
		Logger.Info(fmt.Sprintf("Player Collider#%d: player world pos=(%.2f, %.2f), shape=%v", i, playerPos.X, playerPos.Y, ConvexPolygonStr(playerCollider.Shape.(*resolv.ConvexPolygon))))
 | 
			
		||||
		playerColliders[i] = playerCollider
 | 
			
		||||
		space.Add(playerCollider)
 | 
			
		||||
@@ -60,13 +61,13 @@ func NewWorldColliderDisplay(game *Game, stageDiscreteW, stageDiscreteH, stageTi
 | 
			
		||||
 | 
			
		||||
	world.Space = space
 | 
			
		||||
 | 
			
		||||
	moveToCollide := false
 | 
			
		||||
	moveToCollide := true
 | 
			
		||||
	if moveToCollide {
 | 
			
		||||
		effPushback := Vec2D{X: float64(0), Y: float64(0)}
 | 
			
		||||
		toTestPlayerCollider := playerColliders[0]
 | 
			
		||||
		newVx, newVy := int32(27999), int32(-420270)
 | 
			
		||||
		colliderWidth, colliderHeight := playerColliderRadius*2, playerColliderRadius*4
 | 
			
		||||
		toTestPlayerCollider.X, toTestPlayerCollider.Y = VirtualGridToPolygonColliderTLPos(newVx, newVy, colliderWidth, colliderHeight, spaceOffsetX, spaceOffsetY, virtualGridToWorldRatio)
 | 
			
		||||
		//colliderWidth, colliderHeight := playerColliderRadius*2, playerColliderRadius*4
 | 
			
		||||
		//newVx, newVy := int32(27999), int32(-420270)
 | 
			
		||||
		//toTestPlayerCollider.X, toTestPlayerCollider.Y = VirtualGridToPolygonColliderBLPos(newVx, newVy, colliderWidth, colliderHeight, topPadding, bottomPadding, leftPadding, rightPadding, spaceOffsetX, spaceOffsetY, virtualGridToWorldRatio)
 | 
			
		||||
 | 
			
		||||
		Logger.Info(fmt.Sprintf("Checking collision for playerShape=%v", ConvexPolygonStr(toTestPlayerCollider.Shape.(*resolv.ConvexPolygon))))
 | 
			
		||||
 | 
			
		||||
@@ -114,13 +115,12 @@ func NewWorldColliderDisplay(game *Game, stageDiscreteW, stageDiscreteH, stageTi
 | 
			
		||||
		ReleaseTriggerType: int32(1), // 1: rising-edge, 2: falling-edge
 | 
			
		||||
		Damage:             int32(5),
 | 
			
		||||
	}
 | 
			
		||||
	bulletLeftToRight := true
 | 
			
		||||
	bulletLeftToRight := false
 | 
			
		||||
	if bulletLeftToRight {
 | 
			
		||||
		xfac := float64(1.0)
 | 
			
		||||
		offenderWx, offenderWy := playerPosList.Eles[0].X, playerPosList.Eles[0].Y
 | 
			
		||||
		bulletWx, bulletWy := offenderWx+xfac*meleeBullet.HitboxOffset, offenderWy
 | 
			
		||||
 | 
			
		||||
		newBulletCollider := GenerateRectCollider(bulletWx, bulletWy, meleeBullet.HitboxSize.X, meleeBullet.HitboxSize.Y, 0, spaceOffsetX, spaceOffsetY, "MeleeBullet")
 | 
			
		||||
		newBulletCollider := GenerateRectCollider(bulletWx, bulletWy, meleeBullet.HitboxSize.X, meleeBullet.HitboxSize.Y, topPadding, bottomPadding, leftPadding, rightPadding, spaceOffsetX, spaceOffsetY, "MeleeBullet")
 | 
			
		||||
		space.Add(newBulletCollider)
 | 
			
		||||
		bulletShape := newBulletCollider.Shape.(*resolv.ConvexPolygon)
 | 
			
		||||
		Logger.Warn(fmt.Sprintf("bullet ->: Added bullet collider to space: a=%v", ConvexPolygonStr(bulletShape)))
 | 
			
		||||
@@ -137,13 +137,13 @@ func NewWorldColliderDisplay(game *Game, stageDiscreteW, stageDiscreteH, stageTi
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	bulletRightToLeft := true
 | 
			
		||||
	bulletRightToLeft := false
 | 
			
		||||
	if bulletRightToLeft {
 | 
			
		||||
		xfac := float64(-1.0)
 | 
			
		||||
		offenderWx, offenderWy := playerPosList.Eles[1].X, playerPosList.Eles[1].Y
 | 
			
		||||
		bulletWx, bulletWy := offenderWx+xfac*meleeBullet.HitboxOffset, offenderWy
 | 
			
		||||
 | 
			
		||||
		newBulletCollider := GenerateRectCollider(bulletWx, bulletWy, meleeBullet.HitboxSize.X, meleeBullet.HitboxSize.Y, 0, spaceOffsetX, spaceOffsetY, "MeleeBullet")
 | 
			
		||||
		newBulletCollider := GenerateRectCollider(bulletWx, bulletWy, meleeBullet.HitboxSize.X, meleeBullet.HitboxSize.Y, topPadding, bottomPadding, leftPadding, rightPadding, spaceOffsetX, spaceOffsetY, "MeleeBullet")
 | 
			
		||||
		space.Add(newBulletCollider)
 | 
			
		||||
		bulletShape := newBulletCollider.Shape.(*resolv.ConvexPolygon)
 | 
			
		||||
		Logger.Warn(fmt.Sprintf("bullet <-: Added bullet collider to space: a=%v", ConvexPolygonStr(bulletShape)))
 | 
			
		||||
 
 | 
			
		||||
@@ -18,9 +18,13 @@ func ConvexPolygonStr(body *resolv.ConvexPolygon) string {
 | 
			
		||||
	return fmt.Sprintf("{\n%s\n}", strings.Join(s, ",\n"))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func GenerateRectCollider(wx, wy, w, h, bottomPadding, spaceOffsetX, spaceOffsetY float64, tag string) *resolv.Object {
 | 
			
		||||
	blX, blY := WorldToPolygonColliderBLPos(wx, wy, w*0.5, h*0.5, bottomPadding, spaceOffsetX, spaceOffsetY)
 | 
			
		||||
	return generateRectColliderInCollisionSpace(blX, blY, w, h+bottomPadding, tag)
 | 
			
		||||
func RectCenterStr(body *resolv.Object, halfBoundingW, halfBoundingH, topPadding, bottomPadding, leftPadding, rightPadding, spaceOffsetX, spaceOffsetY float64) string {
 | 
			
		||||
	return fmt.Sprintf("{%.2f, %.2f}", body.X + leftPadding + halfBoundingW - spaceOffsetX, body.Y + bottomPadding + halfBoundingH - spaceOffsetY)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func GenerateRectCollider(wx, wy, w, h, topPadding, bottomPadding, leftPadding, rightPadding, spaceOffsetX, spaceOffsetY float64, tag string) *resolv.Object {
 | 
			
		||||
	blX, blY := WorldToPolygonColliderBLPos(wx, wy, w*0.5, h*0.5, topPadding, bottomPadding, leftPadding, rightPadding, spaceOffsetX, spaceOffsetY)
 | 
			
		||||
	return generateRectColliderInCollisionSpace(blX, blY, leftPadding+w+rightPadding, bottomPadding+h+topPadding, tag)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func generateRectColliderInCollisionSpace(blX, blY, w, h float64, tag string) *resolv.Object {
 | 
			
		||||
@@ -246,38 +250,20 @@ func VirtualGridToWorldPos(vx, vy int32, virtualGridToWorldRatio float64) (float
 | 
			
		||||
	return wx, wy
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func WorldToPolygonColliderBLPos(wx, wy, halfBoundingW, halfBoundingH, bottomPadding, collisionSpaceOffsetX, collisionSpaceOffsetY float64) (float64, float64) {
 | 
			
		||||
	return wx - halfBoundingW + collisionSpaceOffsetX, wy - halfBoundingH - bottomPadding + collisionSpaceOffsetY
 | 
			
		||||
func WorldToPolygonColliderBLPos(wx, wy, halfBoundingW, halfBoundingH, topPadding, bottomPadding, leftPadding, rightPadding, collisionSpaceOffsetX, collisionSpaceOffsetY float64) (float64, float64) {
 | 
			
		||||
	return wx - halfBoundingW - leftPadding + collisionSpaceOffsetX, wy - halfBoundingH - bottomPadding + collisionSpaceOffsetY
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func WorldToPolygonColliderTLPos(wx, wy, halfBoundingW, halfBoundingH, collisionSpaceOffsetX, collisionSpaceOffsetY float64) (float64, float64) {
 | 
			
		||||
	return wx - halfBoundingW + collisionSpaceOffsetX, wy + halfBoundingH + collisionSpaceOffsetY
 | 
			
		||||
func PolygonColliderBLToWorldPos(cx, cy, halfBoundingW, halfBoundingH, topPadding, bottomPadding, leftPadding, rightPadding, collisionSpaceOffsetX, collisionSpaceOffsetY float64) (float64, float64) {
 | 
			
		||||
	return cx + halfBoundingW + leftPadding - collisionSpaceOffsetX, cy + halfBoundingH + bottomPadding - collisionSpaceOffsetY
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func PolygonColliderBLToWorldPos(cx, cy, halfBoundingW, halfBoundingH, bottomPadding, collisionSpaceOffsetX, collisionSpaceOffsetY float64) (float64, float64) {
 | 
			
		||||
	return cx + halfBoundingW - collisionSpaceOffsetX, cy + halfBoundingH + bottomPadding - collisionSpaceOffsetY
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func PolygonColliderTLToWorldPos(cx, cy, halfBoundingW, halfBoundingH, collisionSpaceOffsetX, collisionSpaceOffsetY float64) (float64, float64) {
 | 
			
		||||
	return cx + halfBoundingW - collisionSpaceOffsetX, cy - halfBoundingH - collisionSpaceOffsetY
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func PolygonColliderBLToVirtualGridPos(cx, cy, halfBoundingW, halfBoundingH, bottomPadding, collisionSpaceOffsetX, collisionSpaceOffsetY float64, worldToVirtualGridRatio float64) (int32, int32) {
 | 
			
		||||
	wx, wy := PolygonColliderBLToWorldPos(cx, cy, halfBoundingW, halfBoundingH, bottomPadding, collisionSpaceOffsetX, collisionSpaceOffsetY)
 | 
			
		||||
func PolygonColliderBLToVirtualGridPos(cx, cy, halfBoundingW, halfBoundingH, topPadding, bottomPadding, leftPadding, rightPadding, collisionSpaceOffsetX, collisionSpaceOffsetY float64, worldToVirtualGridRatio float64) (int32, int32) {
 | 
			
		||||
	wx, wy := PolygonColliderBLToWorldPos(cx, cy, halfBoundingW, halfBoundingH, topPadding, bottomPadding, leftPadding, rightPadding, collisionSpaceOffsetX, collisionSpaceOffsetY)
 | 
			
		||||
	return WorldToVirtualGridPos(wx, wy, worldToVirtualGridRatio)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func PolygonColliderTLToVirtualGridPos(cx, cy, halfBoundingW, halfBoundingH, collisionSpaceOffsetX, collisionSpaceOffsetY float64, worldToVirtualGridRatio float64) (int32, int32) {
 | 
			
		||||
	wx, wy := PolygonColliderTLToWorldPos(cx, cy, halfBoundingW, halfBoundingH, collisionSpaceOffsetX, collisionSpaceOffsetY)
 | 
			
		||||
	return WorldToVirtualGridPos(wx, wy, worldToVirtualGridRatio)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func VirtualGridToPolygonColliderBLPos(vx, vy int32, halfBoundingW, halfBoundingH, bottomPadding, collisionSpaceOffsetX, collisionSpaceOffsetY float64, virtualGridToWorldRatio float64) (float64, float64) {
 | 
			
		||||
func VirtualGridToPolygonColliderBLPos(vx, vy int32, halfBoundingW, halfBoundingH, topPadding, bottomPadding, leftPadding, rightPadding, collisionSpaceOffsetX, collisionSpaceOffsetY float64, virtualGridToWorldRatio float64) (float64, float64) {
 | 
			
		||||
	wx, wy := VirtualGridToWorldPos(vx, vy, virtualGridToWorldRatio)
 | 
			
		||||
	return WorldToPolygonColliderBLPos(wx, wy, halfBoundingW, halfBoundingH, bottomPadding, collisionSpaceOffsetX, collisionSpaceOffsetY)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func VirtualGridToPolygonColliderTLPos(vx, vy int32, halfBoundingW, halfBoundingH, collisionSpaceOffsetX, collisionSpaceOffsetY float64, virtualGridToWorldRatio float64) (float64, float64) {
 | 
			
		||||
	wx, wy := VirtualGridToWorldPos(vx, vy, virtualGridToWorldRatio)
 | 
			
		||||
	return WorldToPolygonColliderTLPos(wx, wy, halfBoundingW, halfBoundingH, collisionSpaceOffsetX, collisionSpaceOffsetY)
 | 
			
		||||
	return WorldToPolygonColliderBLPos(wx, wy, halfBoundingW, halfBoundingH, topPadding, bottomPadding, leftPadding, rightPadding, collisionSpaceOffsetX, collisionSpaceOffsetY)
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -440,7 +440,7 @@
 | 
			
		||||
      "array": [
 | 
			
		||||
        0,
 | 
			
		||||
        0,
 | 
			
		||||
        216.19964242526865,
 | 
			
		||||
        216.50635094610968,
 | 
			
		||||
        0,
 | 
			
		||||
        0,
 | 
			
		||||
        0,
 | 
			
		||||
 
 | 
			
		||||
@@ -454,7 +454,7 @@
 | 
			
		||||
      "array": [
 | 
			
		||||
        0,
 | 
			
		||||
        0,
 | 
			
		||||
        216.67520680312998,
 | 
			
		||||
        216.50635094610968,
 | 
			
		||||
        0,
 | 
			
		||||
        0,
 | 
			
		||||
        0,
 | 
			
		||||
 
 | 
			
		||||
@@ -784,10 +784,16 @@ cc.Class({
 | 
			
		||||
    const [wx, wy] = self.virtualGridToWorldPos(vx, vy);
 | 
			
		||||
    newPlayerNode.setPosition(wx, wy);
 | 
			
		||||
    playerScriptIns.mapNode = self.node;
 | 
			
		||||
    const colliderWidth = playerDownsyncInfo.colliderRadius * 2,
 | 
			
		||||
      colliderHeight = playerDownsyncInfo.colliderRadius * 4;
 | 
			
		||||
    const cpos = self.virtualGridToPolygonColliderTLPos(vx, vy, colliderWidth * 0.5, colliderHeight * 0.5); // the top-left corner is kept having integer coords
 | 
			
		||||
    const pts = [[0, 0], [colliderWidth, 0], [colliderWidth, -colliderHeight - self.snapIntoPlatformOverlap], [0, -colliderHeight - self.snapIntoPlatformOverlap]];
 | 
			
		||||
    const halfColliderWidth = playerDownsyncInfo.colliderRadius,
 | 
			
		||||
      halfColliderHeight = playerDownsyncInfo.colliderRadius + playerDownsyncInfo.colliderRadius; // avoid multiplying
 | 
			
		||||
    const colliderWidth = halfColliderWidth + halfColliderWidth,
 | 
			
		||||
      colliderHeight = halfColliderHeight + halfColliderHeight; // avoid multiplying
 | 
			
		||||
    const leftPadding = self.snapIntoPlatformOverlap,
 | 
			
		||||
      rightPadding = self.snapIntoPlatformOverlap,
 | 
			
		||||
      topPadding = self.snapIntoPlatformOverlap,
 | 
			
		||||
      bottomPadding = self.snapIntoPlatformOverlap;
 | 
			
		||||
    const cpos = self.virtualGridToPolygonColliderBLPos(vx, vy, halfColliderWidth, halfColliderHeight, topPadding, bottomPadding, leftPadding, rightPadding); // the collider center is kept having integer coords
 | 
			
		||||
    const pts = [[0, 0], [leftPadding + colliderWidth + rightPadding, 0], [leftPadding + colliderWidth + rightPadding, bottomPadding + colliderHeight + topPadding], [0, bottomPadding + colliderHeight + topPadding]];
 | 
			
		||||
 | 
			
		||||
    // [WARNING] The animNode "anchor & offset" are tuned to fit in this collider by "ControlledCharacter prefab & AttackingCharacter.js"! 
 | 
			
		||||
    const newPlayerCollider = self.collisionSys.createPolygon(cpos[0], cpos[1], pts);
 | 
			
		||||
@@ -997,6 +1003,10 @@ cc.Class({
 | 
			
		||||
 | 
			
		||||
  showDebugBoundaries(rdf) {
 | 
			
		||||
    const self = this;
 | 
			
		||||
    const leftPadding = self.snapIntoPlatformOverlap,
 | 
			
		||||
      rightPadding = self.snapIntoPlatformOverlap,
 | 
			
		||||
      topPadding = self.snapIntoPlatformOverlap,
 | 
			
		||||
      bottomPadding = self.snapIntoPlatformOverlap;
 | 
			
		||||
    if (self.showCriticalCoordinateLabels) {
 | 
			
		||||
      let g = self.g;
 | 
			
		||||
      g.clear();
 | 
			
		||||
@@ -1048,8 +1058,10 @@ cc.Class({
 | 
			
		||||
          const [offenderWx, offenderWy] = self.virtualGridToWorldPos(offender.virtualGridX, offender.virtualGridY);
 | 
			
		||||
          const bulletWx = offenderWx + xfac * meleeBullet.hitboxOffset;
 | 
			
		||||
          const bulletWy = offenderWy;
 | 
			
		||||
          const bulletCpos = self.worldToPolygonColliderTLPos(bulletWx, bulletWy, meleeBullet.hitboxSize.x * 0.5, meleeBullet.hitboxSize.y * 0.5);
 | 
			
		||||
          const pts = [[0, 0], [meleeBullet.hitboxSize.x, 0], [meleeBullet.hitboxSize.x, -meleeBullet.hitboxSize.y], [0, -meleeBullet.hitboxSize.y]];
 | 
			
		||||
          const halfColliderWidth = meleeBullet.hitboxSize.x * 0.5,
 | 
			
		||||
            halfColliderHeight = meleeBullet.hitboxSize.y * 0.5; // avoid multiplying
 | 
			
		||||
          const bulletCpos = self.worldToPolygonColliderBLPos(bulletWx, bulletWy, halfColliderWidth, halfColliderHeight, topPadding, bottomPadding, leftPadding, rightPadding);
 | 
			
		||||
          const pts = [[0, 0], [leftPadding + meleeBullet.hitboxSize.x + rightPadding, 0], [leftPadding + meleeBullet.hitboxSize.x + rightPadding, bottomPadding + meleeBullet.hitboxSize.y + topPadding], [0, bottomPadding + meleeBullet.hitboxSize.y + topPadding]];
 | 
			
		||||
 | 
			
		||||
          g.moveTo(bulletCpos[0], bulletCpos[1]);
 | 
			
		||||
          for (let j = 0; j < pts.length; j += 1) {
 | 
			
		||||
@@ -1079,6 +1091,10 @@ cc.Class({
 | 
			
		||||
  // TODO: Write unit-test for this function to compare with its backend counter part
 | 
			
		||||
  applyInputFrameDownsyncDynamicsOnSingleRenderFrame(delayedInputFrame, currRenderFrame, collisionSys, collisionSysMap) {
 | 
			
		||||
    const self = this;
 | 
			
		||||
    const leftPadding = self.snapIntoPlatformOverlap,
 | 
			
		||||
      rightPadding = self.snapIntoPlatformOverlap,
 | 
			
		||||
      topPadding = self.snapIntoPlatformOverlap,
 | 
			
		||||
      bottomPadding = self.snapIntoPlatformOverlap;
 | 
			
		||||
    const nextRenderFramePlayers = {};
 | 
			
		||||
    for (let playerId in currRenderFrame.players) {
 | 
			
		||||
      const currPlayerDownsync = currRenderFrame.players[playerId];
 | 
			
		||||
@@ -1188,10 +1204,14 @@ cc.Class({
 | 
			
		||||
      const currPlayerDownsync = currRenderFrame.players[playerId];
 | 
			
		||||
      const thatPlayerInNextFrame = nextRenderFramePlayers[playerId];
 | 
			
		||||
      // Reset playerCollider position from the "virtual grid position"
 | 
			
		||||
      const [newVx, newVy] = [currPlayerDownsync.virtualGridX + currPlayerDownsync.velX, currPlayerDownsync.virtualGridY + currPlayerDownsync.velY];
 | 
			
		||||
      const colliderWidth = self.playerRichInfoArr[joinIndex - 1].colliderRadius * 2,
 | 
			
		||||
        colliderHeight = self.playerRichInfoArr[joinIndex - 1].colliderRadius * 4;
 | 
			
		||||
      const newCpos = self.virtualGridToPolygonColliderTLPos(newVx, newVy, colliderWidth * 0.5, colliderHeight * 0.5);
 | 
			
		||||
      const newVpos = [currPlayerDownsync.virtualGridX + currPlayerDownsync.velX, currPlayerDownsync.virtualGridY + currPlayerDownsync.velY];
 | 
			
		||||
      if (thatPlayerInNextFrame.velY == self.jumpingInitVelY) {
 | 
			
		||||
        // This step can be waived, but putting the jumping inclination here makes it easier to read logs. 
 | 
			
		||||
        newVpos[1] += self.jumpingInitVelY;
 | 
			
		||||
      }
 | 
			
		||||
      const halfColliderWidth = self.playerRichInfoArr[j].colliderRadius,
 | 
			
		||||
        halfColliderHeight = self.playerRichInfoArr[j].colliderRadius + self.playerRichInfoArr[j].colliderRadius; // avoid multiplying
 | 
			
		||||
      const newCpos = self.virtualGridToPolygonColliderBLPos(newVpos[0], newVpos[1], halfColliderWidth, halfColliderHeight, topPadding, bottomPadding, leftPadding, rightPadding);
 | 
			
		||||
      playerCollider.x = newCpos[0];
 | 
			
		||||
      playerCollider.y = newCpos[1];
 | 
			
		||||
 | 
			
		||||
@@ -1223,8 +1243,10 @@ cc.Class({
 | 
			
		||||
        const [offenderWx, offenderWy] = self.virtualGridToWorldPos(offender.virtualGridX, offender.virtualGridY);
 | 
			
		||||
        const bulletWx = offenderWx + xfac * meleeBullet.hitboxOffset;
 | 
			
		||||
        const bulletWy = offenderWy;
 | 
			
		||||
        const bulletCpos = self.worldToPolygonColliderTLPos(bulletWx, bulletWy, meleeBullet.hitboxSize.x * 0.5, meleeBullet.hitboxSize.y * 0.5),
 | 
			
		||||
          pts = [[0, 0], [meleeBullet.hitboxSize.x, 0], [meleeBullet.hitboxSize.x, -meleeBullet.hitboxSize.y], [0, -meleeBullet.hitboxSize.y]];
 | 
			
		||||
        const halfColliderWidth = meleeBullet.hitboxSize.x * 0.5,
 | 
			
		||||
          halfColliderHeight = meleeBullet.hitboxSize.y * 0.5;
 | 
			
		||||
        const bulletCpos = self.worldToPolygonColliderBLPos(bulletWx, bulletWy, halfColliderWidth, halfColliderHeight, topPadding, bottomPadding, leftPadding, rightPadding);
 | 
			
		||||
        const pts = [[0, 0], [leftPadding + meleeBullet.hitboxSize.x + rightPadding, 0], [leftPadding + meleeBullet.hitboxSize.x + rightPadding, bottomPadding + meleeBullet.hitboxSize.y + topPadding], [0, bottomPadding + meleeBullet.hitboxSize.y + topPadding]];
 | 
			
		||||
        const newBulletCollider = collisionSys.createPolygon(bulletCpos[0], bulletCpos[1], pts);
 | 
			
		||||
        newBulletCollider.data = meleeBullet;
 | 
			
		||||
        collisionSysMap.set(collisionBulletIndex, newBulletCollider);
 | 
			
		||||
@@ -1248,6 +1270,9 @@ cc.Class({
 | 
			
		||||
 | 
			
		||||
      const currPlayerDownsync = currRenderFrame.players[playerId];
 | 
			
		||||
      const thatPlayerInNextFrame = nextRenderFramePlayers[playerId];
 | 
			
		||||
      const halfColliderWidth = self.playerRichInfoArr[j].colliderRadius,
 | 
			
		||||
        halfColliderHeight = self.playerRichInfoArr[j].colliderRadius + self.playerRichInfoArr[j].colliderRadius; // avoid multiplying
 | 
			
		||||
 | 
			
		||||
      let fallStopping = false;
 | 
			
		||||
      let possiblyFallStoppedOnAnotherPlayer = false;
 | 
			
		||||
      for (const potential of potentials) {
 | 
			
		||||
@@ -1261,10 +1286,16 @@ cc.Class({
 | 
			
		||||
        const landedOnGravityPushback = (self.snapIntoPlatformThreshold < normAlignmentWithGravity); // prevents false snapping on the lateral sides
 | 
			
		||||
        let pushback = [result.overlap * result.overlap_x, result.overlap * result.overlap_y];
 | 
			
		||||
        if (landedOnGravityPushback) {
 | 
			
		||||
          // kindly note that one player might land on top of another player, and snapping is also required in such case
 | 
			
		||||
          // kindly note that one player might land on top of another player
 | 
			
		||||
          pushback = [(result.overlap - self.snapIntoPlatformOverlap) * result.overlap_x, (result.overlap - self.snapIntoPlatformOverlap) * result.overlap_y];
 | 
			
		||||
          thatPlayerInNextFrame.inAir = false;
 | 
			
		||||
        }
 | 
			
		||||
        if (isAnotherPlayer) {
 | 
			
		||||
          /*
 | 
			
		||||
            [WARNING] The "zero overlap collision" might be randomly detected/missed on either frontend or backend, to have deterministic result we added paddings to all sides of a playerCollider. As each velocity component of (velX, velY) being a multiple of 0.5 at any renderFrame, each position component of (x, y) can only be a multiple of 0.5 too, thus whenever a 1-dimensional collision happens between players from [player#1: i*0.5, player#2: j*0.5, not collided yet] to [player#1: (i+k)*0.5, player#2: j*0.5, collided], the overlap becomes (i+k-j)*0.5+2*s, and after snapping subtraction the effPushback magnitude for each player is (i+k-j)*0.5, resulting in 0.5-multiples-position for the next renderFrame.
 | 
			
		||||
          */
 | 
			
		||||
          pushback = [(result.overlap - self.snapIntoPlatformOverlap * 2) * result.overlap_x, (result.overlap - self.snapIntoPlatformOverlap * 2) * result.overlap_y]; // will overwrite the previous pushback value if "landedOnGravityPushback" is also true
 | 
			
		||||
        }
 | 
			
		||||
        for (let hardPushbackNorm of hardPushbackNorms[joinIndex - 1]) {
 | 
			
		||||
          // remove pushback component on the directions of "hardPushbackNorms[joinIndex-1]" (by now those hardPushbacks are already accounted in "effPushbacks[joinIndex-1]")
 | 
			
		||||
          const projectedMagnitude = pushback[0] * hardPushbackNorm[0] + pushback[1] * hardPushbackNorm[1];
 | 
			
		||||
@@ -1281,23 +1312,29 @@ cc.Class({
 | 
			
		||||
 | 
			
		||||
        effPushbacks[joinIndex - 1][0] += pushback[0];
 | 
			
		||||
        effPushbacks[joinIndex - 1][1] += pushback[1];
 | 
			
		||||
        // It's not meaningful to log the virtual positions and velocities inside this step.
 | 
			
		||||
        if (currPlayerDownsync.inAir && landedOnGravityPushback) {
 | 
			
		||||
          fallStopping = true;
 | 
			
		||||
          if (isAnotherPlayer) {
 | 
			
		||||
            possiblyFallStoppedOnAnotherPlayer = true;
 | 
			
		||||
          }
 | 
			
		||||
          if (1 == thatPlayerInNextFrame.joinIndex) {
 | 
			
		||||
            console.log(`playerId=${playerId}, joinIndex=${currPlayerDownsync.joinIndex} fallStopping#1 at {renderFrame.id: ${currRenderFrame.id}, virtualX: ${currPlayerDownsync.virtualGridX}, virtualY: ${currPlayerDownsync.virtualGridY}, velX: ${currPlayerDownsync.velX}, velY: ${currPlayerDownsync.velY}} with effPushback={${effPushbacks[joinIndex - 1][0].toFixed(3)}, ${effPushbacks[joinIndex - 1][1].toFixed(3)}}, overlayMag=${result.overlap.toFixed(4)}, possiblyFallStoppedOnAnotherPlayer=${possiblyFallStoppedOnAnotherPlayer}`);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (1 == joinIndex) {
 | 
			
		||||
          if (fallStopping) {
 | 
			
		||||
            console.info(`playerId=${playerId}, joinIndex=${thatPlayerInNextFrame.joinIndex} fallStopping#1:
 | 
			
		||||
{renderFrame.id: ${currRenderFrame.id}, possiblyFallStoppedOnAnotherPlayer: ${possiblyFallStoppedOnAnotherPlayer}}
 | 
			
		||||
playerColliderPos=${self.stringifyColliderCenterInWorld(playerCollider, halfColliderWidth, halfColliderHeight, topPadding, bottomPadding, leftPadding, rightPadding)}, effPushback={${effPushbacks[joinIndex - 1][0].toFixed(3)}, ${effPushbacks[joinIndex - 1][1].toFixed(3)}}, overlayMag=${result.overlap.toFixed(4)}`);
 | 
			
		||||
          } else if (currPlayerDownsync.inAir && isBarrier && !landedOnGravityPushback) {
 | 
			
		||||
            console.warn(`playerId=${playerId}, joinIndex=${currPlayerDownsync.joinIndex} inAir & pushed back by barrier & not landed:
 | 
			
		||||
{renderFrame.id: ${currRenderFrame.id}}
 | 
			
		||||
playerColliderPos=${self.stringifyColliderCenterInWorld(playerCollider, halfColliderWidth, halfColliderHeight, topPadding, bottomPadding, leftPadding, rightPadding)}, effPushback={${effPushbacks[joinIndex - 1][0].toFixed(3)}, ${effPushbacks[joinIndex - 1][1].toFixed(3)}}, overlayMag=${result.overlap.toFixed(4)}, len(hardPushbackNorms)=${hardPushbackNorms.length}`);
 | 
			
		||||
          } else if (currPlayerDownsync.inAir && isAnotherPlayer) {
 | 
			
		||||
            console.warn(`playerId=${playerId}, joinIndex=${currPlayerDownsync.joinIndex} inAir and pushed back by another player
 | 
			
		||||
{renderFrame.id: ${currRenderFrame.id}}
 | 
			
		||||
playerColliderPos=${self.stringifyColliderCenterInWorld(playerCollider, halfColliderWidth, halfColliderHeight, topPadding, bottomPadding, leftPadding, rightPadding)}, anotherPlayerColliderPos=${self.stringifyColliderCenterInWorld(potential, halfColliderWidth, halfColliderHeight, topPadding, bottomPadding, leftPadding, rightPadding)}, effPushback={${effPushbacks[joinIndex - 1][0].toFixed(3)}, ${effPushbacks[joinIndex - 1][1].toFixed(3)}}, landedOnGravityPushback=${landedOnGravityPushback}, fallStopping=${fallStopping}, overlayMag=${result.overlap.toFixed(4)}, len(hardPushbackNorms)=${hardPushbackNorms.length}`);
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (1 == joinIndex && currPlayerDownsync.inAir && isBarrier && !landedOnGravityPushback) {
 | 
			
		||||
          console.warn(`playerId=${playerId}, joinIndex=${currPlayerDownsync.joinIndex} inAir & pushed back by barrier & not landed at {renderFrame.id: ${currRenderFrame.id}, virtualX: ${currPlayerDownsync.virtualGridX}, virtualY: ${currPlayerDownsync.virtualGridY}, velX: ${currPlayerDownsync.velX}, velY: ${currPlayerDownsync.velY}} with effPushback={${effPushbacks[joinIndex - 1][0].toFixed(3)}, ${effPushbacks[joinIndex - 1][1].toFixed(3)}}, playerColliderPos={${playerCollider.x.toFixed(3)}, ${playerCollider.y.toFixed(3)}}, barrierPos={${potential.x.toFixed(3)}, ${potential.y.toFixed(3)}}, overlayMag=${result.overlap.toFixed(4)}, len(hardPushbackNorms)=${hardPushbackNorms.length}`);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (1 == joinIndex && currPlayerDownsync.inAir && isAnotherPlayer) {
 | 
			
		||||
          console.warn(`playerId=${playerId}, joinIndex=${currPlayerDownsync.joinIndex} inAir and pushed back by another player at {renderFrame.id: ${currRenderFrame.id}, virtualX: ${currPlayerDownsync.virtualGridX}, virtualY: ${currPlayerDownsync.virtualGridY}, velX: ${currPlayerDownsync.velX}, velY: ${currPlayerDownsync.velY}} with effPushback={${effPushbacks[joinIndex - 1][0].toFixed(3)}, ${effPushbacks[joinIndex - 1][1].toFixed(3)}}, landedOnGravityPushback=${landedOnGravityPushback}, fallStopping=${fallStopping}, playerColliderPos={${playerCollider.x.toFixed(3)}, ${playerCollider.y.toFixed(3)}}, anotherPlayerColliderPos={${potential.x.toFixed(3)}, ${potential.y.toFixed(3)}}, overlayMag=${result.overlap.toFixed(4)}, len(hardPushbackNorms)=${hardPushbackNorms.length}`);
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      if (fallStopping) {
 | 
			
		||||
@@ -1378,21 +1415,27 @@ cc.Class({
 | 
			
		||||
      // Update "virtual grid position"
 | 
			
		||||
      const currPlayerDownsync = currRenderFrame.players[playerId];
 | 
			
		||||
      const thatPlayerInNextFrame = nextRenderFramePlayers[playerId];
 | 
			
		||||
      const colliderWidth = self.playerRichInfoArr[joinIndex - 1].colliderRadius * 2,
 | 
			
		||||
        colliderHeight = self.playerRichInfoArr[joinIndex - 1].colliderRadius * 4;
 | 
			
		||||
      const newVpos = self.polygonColliderTLToVirtualGridPos(playerCollider.x - effPushbacks[joinIndex - 1][0], playerCollider.y - effPushbacks[joinIndex - 1][1], colliderWidth * 0.5, colliderHeight * 0.5);
 | 
			
		||||
      const halfColliderWidth = self.playerRichInfoArr[j].colliderRadius,
 | 
			
		||||
        halfColliderHeight = self.playerRichInfoArr[j].colliderRadius + self.playerRichInfoArr[j].colliderRadius; // avoid multiplying
 | 
			
		||||
      const newVpos = self.polygonColliderBLToVirtualGridPos(playerCollider.x - effPushbacks[joinIndex - 1][0], playerCollider.y - effPushbacks[joinIndex - 1][1], halfColliderWidth, halfColliderHeight, topPadding, bottomPadding, leftPadding, rightPadding);
 | 
			
		||||
      thatPlayerInNextFrame.virtualGridX = newVpos[0];
 | 
			
		||||
      thatPlayerInNextFrame.virtualGridY = newVpos[1];
 | 
			
		||||
 | 
			
		||||
      if (1 == thatPlayerInNextFrame.joinIndex) {
 | 
			
		||||
        if (thatPlayerInNextFrame.inAir && 0 != thatPlayerInNextFrame.velY) {
 | 
			
		||||
          // console.log(`playerId=${playerId}, joinIndex=${thatPlayerInNextFrame.joinIndex} inAir trajectory: {nextRenderFrame.id: ${currRenderFrame.id + 1}, nextVirtualX: ${thatPlayerInNextFrame.virtualGridX}, nextVirtualY: ${thatPlayerInNextFrame.virtualGridY}, nextVelX: ${thatPlayerInNextFrame.velX}, nextVelY: ${thatPlayerInNextFrame.velY}}, with playerColliderPos={${playerCollider.x.toFixed(3)}, ${playerCollider.y.toFixed(3)}}, effPushback={${effPushbacks[joinIndex - 1][0].toFixed(3)}, ${effPushbacks[joinIndex - 1][1].toFixed(3)}}`);
 | 
			
		||||
        }
 | 
			
		||||
        if (currPlayerDownsync.inAir && !thatPlayerInNextFrame.inAir) {
 | 
			
		||||
          console.warn(`playerId=${playerId}, joinIndex=${thatPlayerInNextFrame.joinIndex} fallStopping#2 at {nextRenderFrame.id: ${currRenderFrame.id + 1}, nextVirtualX: ${thatPlayerInNextFrame.virtualGridX}, nextVirtualY: ${thatPlayerInNextFrame.virtualGridY}, nextVelX: ${thatPlayerInNextFrame.velX}, nextVelY: ${thatPlayerInNextFrame.velY}}, with playerColliderPos={${playerCollider.x.toFixed(3)}, ${playerCollider.y.toFixed(3)}}, effPushback={${effPushbacks[joinIndex - 1][0].toFixed(3)}, ${effPushbacks[joinIndex - 1][1].toFixed(3)}}`);
 | 
			
		||||
        }
 | 
			
		||||
        if (!currPlayerDownsync.inAir && thatPlayerInNextFrame.inAir) {
 | 
			
		||||
          console.warn(`playerId=${playerId}, joinIndex=${thatPlayerInNextFrame.joinIndex} took off at {nextRenderFrame.id: ${currRenderFrame.id + 1}, nextVirtualX: ${thatPlayerInNextFrame.virtualGridX}, nextVirtualY: ${thatPlayerInNextFrame.virtualGridY}, nextVelX: ${thatPlayerInNextFrame.velX}, nextVelY: ${thatPlayerInNextFrame.velY}}, with playerColliderPos={${playerCollider.x.toFixed(3)}, ${playerCollider.y.toFixed(3)}}, effPushback={${effPushbacks[joinIndex - 1][0].toFixed(3)}, ${effPushbacks[joinIndex - 1][1].toFixed(3)}}`);
 | 
			
		||||
          console.warn(`playerId=${playerId}, joinIndex=${thatPlayerInNextFrame.joinIndex} fallStopping#2:
 | 
			
		||||
{nextRenderFrame.id: ${currRenderFrame.id + 1}, nextVirtualX: ${thatPlayerInNextFrame.virtualGridX}, nextVirtualY: ${thatPlayerInNextFrame.virtualGridY}, nextVelX: ${thatPlayerInNextFrame.velX}, nextVelY: ${thatPlayerInNextFrame.velY}}
 | 
			
		||||
	calculated from <- playerColliderPos=${self.stringifyColliderCenterInWorld(playerCollider, halfColliderWidth, halfColliderHeight, topPadding, bottomPadding, leftPadding, rightPadding)}, effPushback={${effPushbacks[joinIndex - 1][0].toFixed(3)}, ${effPushbacks[joinIndex - 1][1].toFixed(3)}}`);
 | 
			
		||||
        } else if (!currPlayerDownsync.inAir && thatPlayerInNextFrame.inAir) {
 | 
			
		||||
          console.warn(`playerId=${playerId}, joinIndex=${thatPlayerInNextFrame.joinIndex} took off:
 | 
			
		||||
{nextRenderFrame.id: ${currRenderFrame.id + 1}, nextVirtualX: ${thatPlayerInNextFrame.virtualGridX}, nextVirtualY: ${thatPlayerInNextFrame.virtualGridY}, nextVelX: ${thatPlayerInNextFrame.velX}, nextVelY: ${thatPlayerInNextFrame.velY}}
 | 
			
		||||
	calculated from <- playerColliderPos=${self.stringifyColliderCenterInWorld(playerCollider, halfColliderWidth, halfColliderHeight, topPadding, bottomPadding, leftPadding, rightPadding)}, effPushback={${effPushbacks[joinIndex - 1][0].toFixed(3)}, ${effPushbacks[joinIndex - 1][1].toFixed(3)}}`);
 | 
			
		||||
        } else if (thatPlayerInNextFrame.inAir && 0 != thatPlayerInNextFrame.velY) {
 | 
			
		||||
          /*
 | 
			
		||||
          console.log(`playerId=${playerId}, joinIndex=${thatPlayerInNextFrame.joinIndex} inAir trajectory:
 | 
			
		||||
{nextRenderFrame.id: ${currRenderFrame.id + 1}, nextVirtualX: ${thatPlayerInNextFrame.virtualGridX}, nextVirtualY: ${thatPlayerInNextFrame.virtualGridY}, nextVelX: ${thatPlayerInNextFrame.velX}, nextVelY: ${thatPlayerInNextFrame.velY}};
 | 
			
		||||
          calculated from <- playerColliderPos=${self.stringifyColliderCenterInWorld(playerCollider, halfColliderWidth, halfColliderHeight, topPadding, bottomPadding, leftPadding, rightPadding)}, effPushback={${effPushbacks[joinIndex - 1][0].toFixed(3)}, ${effPushbacks[joinIndex - 1][1].toFixed(3)}}`);
 | 
			
		||||
          */
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
@@ -1457,16 +1500,16 @@ cc.Class({
 | 
			
		||||
      const immediatePlayerInfo = players[playerId];
 | 
			
		||||
      self.playerRichInfoDict.set(playerId, immediatePlayerInfo);
 | 
			
		||||
 | 
			
		||||
      const [theNode, theScriptIns] = self.spawnPlayerNode(immediatePlayerInfo.joinIndex, immediatePlayerInfo.virtualGridX, immediatePlayerInfo.virtualGridY, immediatePlayerInfo);
 | 
			
		||||
      const nodeAndScriptIns = self.spawnPlayerNode(immediatePlayerInfo.joinIndex, immediatePlayerInfo.virtualGridX, immediatePlayerInfo.virtualGridY, immediatePlayerInfo);
 | 
			
		||||
 | 
			
		||||
      Object.assign(self.playerRichInfoDict.get(playerId), {
 | 
			
		||||
        node: theNode,
 | 
			
		||||
        scriptIns: theScriptIns,
 | 
			
		||||
        node: nodeAndScriptIns[0],
 | 
			
		||||
        scriptIns: nodeAndScriptIns[1],
 | 
			
		||||
      });
 | 
			
		||||
 | 
			
		||||
      if (self.selfPlayerInfo.id == playerId) {
 | 
			
		||||
        self.selfPlayerInfo = Object.assign(self.selfPlayerInfo, immediatePlayerInfo);
 | 
			
		||||
        theScriptIns.showArrowTipNode();
 | 
			
		||||
        nodeAndScriptIns[1].showArrowTipNode();
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
    self.playerRichInfoArr = new Array(self.playerRichInfoDict.size);
 | 
			
		||||
@@ -1513,29 +1556,31 @@ cc.Class({
 | 
			
		||||
  virtualGridToWorldPos(vx, vy) {
 | 
			
		||||
    // No loss of precision
 | 
			
		||||
    const self = this;
 | 
			
		||||
    let wx = parseFloat(vx) * self.virtualGridToWorldRatio;
 | 
			
		||||
    let wy = parseFloat(vy) * self.virtualGridToWorldRatio;
 | 
			
		||||
    return [wx, wy];
 | 
			
		||||
    return [vx * self.virtualGridToWorldRatio, vy * self.virtualGridToWorldRatio];
 | 
			
		||||
  },
 | 
			
		||||
 | 
			
		||||
  worldToPolygonColliderTLPos(wx, wy, halfBoundingW, halfBoundingH) {
 | 
			
		||||
    return [wx - halfBoundingW, wy + halfBoundingH];
 | 
			
		||||
  worldToPolygonColliderBLPos(wx, wy, halfBoundingW, halfBoundingH, topPadding, bottomPadding, leftPadding, rightPadding) {
 | 
			
		||||
    return [wx - halfBoundingW - leftPadding, wy - halfBoundingH - bottomPadding];
 | 
			
		||||
  },
 | 
			
		||||
 | 
			
		||||
  polygonColliderTLToWorldPos(cx, cy, halfBoundingW, halfBoundingH) {
 | 
			
		||||
    return [cx + halfBoundingW, cy - halfBoundingH];
 | 
			
		||||
  polygonColliderBLToWorldPos(cx, cy, halfBoundingW, halfBoundingH, topPadding, bottomPadding, leftPadding, rightPadding) {
 | 
			
		||||
    return [cx + halfBoundingW + leftPadding, cy + halfBoundingH + bottomPadding];
 | 
			
		||||
  },
 | 
			
		||||
 | 
			
		||||
  polygonColliderTLToVirtualGridPos(cx, cy, halfBoundingW, halfBoundingH) {
 | 
			
		||||
  polygonColliderBLToVirtualGridPos(cx, cy, halfBoundingW, halfBoundingH, topPadding, bottomPadding, leftPadding, rightPadding) {
 | 
			
		||||
    const self = this;
 | 
			
		||||
    const [wx, wy] = self.polygonColliderTLToWorldPos(cx, cy, halfBoundingW, halfBoundingH);
 | 
			
		||||
    const [wx, wy] = self.polygonColliderBLToWorldPos(cx, cy, halfBoundingW, halfBoundingH, topPadding, bottomPadding, leftPadding, rightPadding);
 | 
			
		||||
    return self.worldToVirtualGridPos(wx, wy)
 | 
			
		||||
  },
 | 
			
		||||
 | 
			
		||||
  virtualGridToPolygonColliderTLPos(vx, vy, halfBoundingW, halfBoundingH) {
 | 
			
		||||
  virtualGridToPolygonColliderBLPos(vx, vy, halfBoundingW, halfBoundingH, topPadding, bottomPadding, leftPadding, rightPadding) {
 | 
			
		||||
    const self = this;
 | 
			
		||||
    const [wx, wy] = self.virtualGridToWorldPos(vx, vy);
 | 
			
		||||
    return self.worldToPolygonColliderTLPos(wx, wy, halfBoundingW, halfBoundingH)
 | 
			
		||||
    return self.worldToPolygonColliderBLPos(wx, wy, halfBoundingW, halfBoundingH, topPadding, bottomPadding, leftPadding, rightPadding)
 | 
			
		||||
  },
 | 
			
		||||
 | 
			
		||||
  stringifyColliderCenterInWorld(playerCollider, halfBoundingW, halfBoundingH, topPadding, bottomPadding, leftPadding, rightPadding) {
 | 
			
		||||
    return `{${(playerCollider.x + leftPadding + halfBoundingW).toFixed(2)}, ${(playerCollider.y + bottomPadding + halfBoundingH).toFixed(2)}}`;
 | 
			
		||||
  },
 | 
			
		||||
 | 
			
		||||
  calcHardPushbacksNorms(collider, potentials, result, snapIntoPlatformOverlap, effPushback) {
 | 
			
		||||
@@ -1546,11 +1591,10 @@ cc.Class({
 | 
			
		||||
      if (!collider.collides(potential, result)) continue;
 | 
			
		||||
      // ALWAY snap into hardPushbacks!
 | 
			
		||||
      // [overlay_x, overlap_y] is the unit vector that points into the platform
 | 
			
		||||
      const [pushbackX, pushbackY] = [(result.overlap - snapIntoPlatformOverlap) * result.overlap_x, (result.overlap - snapIntoPlatformOverlap) * result.overlap_y];
 | 
			
		||||
 | 
			
		||||
      const pushback = [(result.overlap - snapIntoPlatformOverlap) * result.overlap_x, (result.overlap - snapIntoPlatformOverlap) * result.overlap_y];
 | 
			
		||||
      ret.push([result.overlap_x, result.overlap_y]);
 | 
			
		||||
      effPushback[0] += pushbackX;
 | 
			
		||||
      effPushback[1] += pushbackY;
 | 
			
		||||
      effPushback[0] += pushback[0];
 | 
			
		||||
      effPushback[1] += pushback[1];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return ret;
 | 
			
		||||
 
 | 
			
		||||
@@ -196,7 +196,7 @@ cc.Class({
 | 
			
		||||
        }
 | 
			
		||||
      });
 | 
			
		||||
      self.selfPlayerInfo = {
 | 
			
		||||
        id: 11
 | 
			
		||||
        id: 10
 | 
			
		||||
      };
 | 
			
		||||
      self._initPlayerRichInfoDict(startRdf.players);
 | 
			
		||||
      self.onRoomDownsyncFrame(startRdf);
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user