mirror of
https://github.com/genxium/DelayNoMore
synced 2025-10-09 08:36:52 +00:00
A temp broken commit except for OfflineMap - refactoring character/skill/bullet config hierarchy.
This commit is contained in:
@@ -20,9 +20,9 @@ const (
|
||||
GRAVITY_X = int32(0)
|
||||
GRAVITY_Y = -int32(float64(0.5) * WORLD_TO_VIRTUAL_GRID_RATIO) // makes all "playerCollider.Y" a multiple of 0.5 in all cases
|
||||
|
||||
INPUT_DELAY_FRAMES = int32(8)
|
||||
NST_DELAY_FRAMES = int32(16)
|
||||
INPUT_SCALE_FRAMES = uint32(2)
|
||||
INPUT_DELAY_FRAMES = int32(8) // in the count of render frames
|
||||
INPUT_SCALE_FRAMES = uint32(2) // inputDelayedAndScaledFrameId = ((originalFrameId - InputDelayFrames) >> InputScaleFrames)
|
||||
NST_DELAY_FRAMES = int32(16) // network-single-trip delay in the count of render frames, proposed to be (InputDelayFrames >> 1) because we expect a round-trip delay to be exactly "InputDelayFrames"
|
||||
|
||||
SNAP_INTO_PLATFORM_OVERLAP = float64(0.1)
|
||||
SNAP_INTO_PLATFORM_THRESHOLD = float64(0.5)
|
||||
@@ -249,18 +249,18 @@ func isPolygonPairSeparatedByDir(a, b *resolv.ConvexPolygon, e resolv.Vector, re
|
||||
return false
|
||||
}
|
||||
|
||||
func WorldToVirtualGridPos(wx, wy, worldToVirtualGridRatio float64) (int32, int32) {
|
||||
func WorldToVirtualGridPos(wx, wy float64) (int32, int32) {
|
||||
// [WARNING] Introduces loss of precision!
|
||||
// In JavaScript floating numbers suffer from seemingly non-deterministic arithmetics, and even if certain libs solved this issue by approaches such as fixed-point-number, they might not be used in other libs -- e.g. the "collision libs" we're interested in -- thus couldn't kill all pains.
|
||||
var virtualGridX int32 = int32(math.Floor(wx * worldToVirtualGridRatio))
|
||||
var virtualGridY int32 = int32(math.Floor(wy * worldToVirtualGridRatio))
|
||||
var virtualGridX int32 = int32(math.Floor(wx * WORLD_TO_VIRTUAL_GRID_RATIO))
|
||||
var virtualGridY int32 = int32(math.Floor(wy * WORLD_TO_VIRTUAL_GRID_RATIO))
|
||||
return virtualGridX, virtualGridY
|
||||
}
|
||||
|
||||
func VirtualGridToWorldPos(vx, vy int32, virtualGridToWorldRatio float64) (float64, float64) {
|
||||
func VirtualGridToWorldPos(vx, vy int32) (float64, float64) {
|
||||
// No loss of precision
|
||||
var wx float64 = float64(vx) * virtualGridToWorldRatio
|
||||
var wy float64 = float64(vy) * virtualGridToWorldRatio
|
||||
var wx float64 = float64(vx) * VIRTUAL_GRID_TO_WORLD_RATIO
|
||||
var wy float64 = float64(vy) * VIRTUAL_GRID_TO_WORLD_RATIO
|
||||
return wx, wy
|
||||
}
|
||||
|
||||
@@ -272,13 +272,13 @@ func PolygonColliderBLToWorldPos(cx, cy, halfBoundingW, halfBoundingH, topPaddin
|
||||
return cx + halfBoundingW + leftPadding - collisionSpaceOffsetX, cy + halfBoundingH + bottomPadding - collisionSpaceOffsetY
|
||||
}
|
||||
|
||||
func PolygonColliderBLToVirtualGridPos(cx, cy, halfBoundingW, halfBoundingH, topPadding, bottomPadding, leftPadding, rightPadding, collisionSpaceOffsetX, collisionSpaceOffsetY float64, worldToVirtualGridRatio float64) (int32, int32) {
|
||||
func PolygonColliderBLToVirtualGridPos(cx, cy, halfBoundingW, halfBoundingH, topPadding, bottomPadding, leftPadding, rightPadding, collisionSpaceOffsetX, collisionSpaceOffsetY float64) (int32, int32) {
|
||||
wx, wy := PolygonColliderBLToWorldPos(cx, cy, halfBoundingW, halfBoundingH, topPadding, bottomPadding, leftPadding, rightPadding, collisionSpaceOffsetX, collisionSpaceOffsetY)
|
||||
return WorldToVirtualGridPos(wx, wy, worldToVirtualGridRatio)
|
||||
return WorldToVirtualGridPos(wx, wy)
|
||||
}
|
||||
|
||||
func VirtualGridToPolygonColliderBLPos(vx, vy int32, halfBoundingW, halfBoundingH, topPadding, bottomPadding, leftPadding, rightPadding, collisionSpaceOffsetX, collisionSpaceOffsetY float64, virtualGridToWorldRatio float64) (float64, float64) {
|
||||
wx, wy := VirtualGridToWorldPos(vx, vy, virtualGridToWorldRatio)
|
||||
func VirtualGridToPolygonColliderBLPos(vx, vy int32, halfBoundingW, halfBoundingH, topPadding, bottomPadding, leftPadding, rightPadding, collisionSpaceOffsetX, collisionSpaceOffsetY float64) (float64, float64) {
|
||||
wx, wy := VirtualGridToWorldPos(vx, vy)
|
||||
return WorldToPolygonColliderBLPos(wx, wy, halfBoundingW, halfBoundingH, topPadding, bottomPadding, leftPadding, rightPadding, collisionSpaceOffsetX, collisionSpaceOffsetY)
|
||||
}
|
||||
|
||||
@@ -357,11 +357,11 @@ func deriveOpPattern(currPlayerDownsync, thatPlayerInNextFrame *PlayerDownsync,
|
||||
|
||||
patternId := PATTERN_ID_NO_OP
|
||||
if decodedInput.BtnALevel > prevBtnALevel {
|
||||
if currPlayerDownsync.InAir {
|
||||
patternId = 1
|
||||
} else {
|
||||
patternId = 0
|
||||
}
|
||||
if currPlayerDownsync.InAir {
|
||||
patternId = 1
|
||||
} else {
|
||||
patternId = 0
|
||||
}
|
||||
effDx, effDy = 0, 0 // Most patterns/skills should not allow simultaneous movement
|
||||
}
|
||||
|
||||
@@ -369,7 +369,7 @@ func deriveOpPattern(currPlayerDownsync, thatPlayerInNextFrame *PlayerDownsync,
|
||||
}
|
||||
|
||||
// [WARNING] The params of this method is carefully tuned such that only "battle.RoomDownsyncFrame" is a necessary custom struct.
|
||||
func ApplyInputFrameDownsyncDynamicsOnSingleRenderFrame(inputsBuffer *RingBuffer, currRenderFrame *RoomDownsyncFrame, collisionSys *resolv.Space, collisionSysMap map[int32]*resolv.Object, collisionSpaceOffsetX, collisionSpaceOffsetY float64, playerOpPatternToSkillId map[int]int, chConfigsOrderedByJoinIndex []*CharacterConfig) *RoomDownsyncFrame {
|
||||
func ApplyInputFrameDownsyncDynamicsOnSingleRenderFrame(inputsBuffer *RingBuffer, currRenderFrame *RoomDownsyncFrame, collisionSys *resolv.Space, collisionSysMap map[int32]*resolv.Object, collisionSpaceOffsetX, collisionSpaceOffsetY float64, chConfigsOrderedByJoinIndex []*CharacterConfig) *RoomDownsyncFrame {
|
||||
// [WARNING] On backend this function MUST BE called while "InputsBufferLock" is locked!
|
||||
roomCapacity := len(currRenderFrame.PlayersArr)
|
||||
nextRenderFramePlayers := make([]*PlayerDownsync, roomCapacity)
|
||||
@@ -408,6 +408,7 @@ func ApplyInputFrameDownsyncDynamicsOnSingleRenderFrame(inputsBuffer *RingBuffer
|
||||
// 1. Process player inputs
|
||||
for i, currPlayerDownsync := range currRenderFrame.PlayersArr {
|
||||
jumpedOrNotList[i] = false
|
||||
chConfig := chConfigsOrderedByJoinIndex[i]
|
||||
thatPlayerInNextFrame := nextRenderFramePlayers[i]
|
||||
patternId, jumpedOrNot, effDx, effDy := deriveOpPattern(currPlayerDownsync, thatPlayerInNextFrame, currRenderFrame, inputsBuffer, INPUT_DELAY_FRAMES, INPUT_SCALE_FRAMES)
|
||||
if PATTERN_ID_UNABLE_TO_OP == patternId {
|
||||
@@ -415,19 +416,22 @@ func ApplyInputFrameDownsyncDynamicsOnSingleRenderFrame(inputsBuffer *RingBuffer
|
||||
}
|
||||
|
||||
if jumpedOrNot {
|
||||
thatPlayerInNextFrame.VelY = int32(chConfigsOrderedByJoinIndex[i].JumpingInitVelY)
|
||||
thatPlayerInNextFrame.VelY = int32(chConfig.JumpingInitVelY)
|
||||
jumpedOrNotList[i] = true
|
||||
}
|
||||
joinIndex := currPlayerDownsync.JoinIndex
|
||||
if PATTERN_ID_NO_OP != patternId {
|
||||
if skillId, existent := playerOpPatternToSkillId[(int(joinIndex)<<uint(8))+patternId]; existent {
|
||||
skillConfig := skillIdToBullet[skillId].(*MeleeBullet) // Hardcoded type "MeleeBullet" for now
|
||||
var newMeleeBullet MeleeBullet = *skillConfig
|
||||
newMeleeBullet.OffenderJoinIndex = joinIndex
|
||||
newMeleeBullet.OffenderPlayerId = currPlayerDownsync.Id
|
||||
newMeleeBullet.OriginatedRenderFrameId = currRenderFrame.Id
|
||||
nextRenderFrameMeleeBullets = append(nextRenderFrameMeleeBullets, &newMeleeBullet)
|
||||
thatPlayerInNextFrame.FramesToRecover = newMeleeBullet.RecoveryFrames
|
||||
if skillId, existent := chConfig.PatternIdToSkillId[patternId]; existent {
|
||||
skillConfig := skills[skillId]
|
||||
// Hardcoded to use only the first hit for now
|
||||
switch v := skillConfig.Hits[0].(type) {
|
||||
case *MeleeBullet:
|
||||
var newBullet MeleeBullet = *v // Copied primitive fields into an onstack variable
|
||||
newBullet.OriginatedRenderFrameId = currRenderFrame.Id
|
||||
newBullet.OffenderJoinIndex = joinIndex
|
||||
nextRenderFrameMeleeBullets = append(nextRenderFrameMeleeBullets, &newBullet)
|
||||
thatPlayerInNextFrame.FramesToRecover = skillConfig.RecoveryFrames
|
||||
}
|
||||
thatPlayerInNextFrame.CharacterState = ATK_CHARACTER_STATE_ATK1
|
||||
if false == currPlayerDownsync.InAir {
|
||||
thatPlayerInNextFrame.VelX = 0
|
||||
@@ -453,13 +457,14 @@ func ApplyInputFrameDownsyncDynamicsOnSingleRenderFrame(inputsBuffer *RingBuffer
|
||||
collisionPlayerIndex := COLLISION_PLAYER_INDEX_PREFIX + joinIndex
|
||||
playerCollider := collisionSysMap[collisionPlayerIndex]
|
||||
thatPlayerInNextFrame := nextRenderFramePlayers[i]
|
||||
chConfig := chConfigsOrderedByJoinIndex[i]
|
||||
// Reset playerCollider position from the "virtual grid position"
|
||||
newVx, newVy := currPlayerDownsync.VirtualGridX+currPlayerDownsync.VelX, currPlayerDownsync.VirtualGridY+currPlayerDownsync.VelY
|
||||
if jumpedOrNotList[i] {
|
||||
newVy += int32(chConfigsOrderedByJoinIndex[i].JumpingInitVelY) // Immediately gets out of any snapping
|
||||
newVy += chConfig.JumpingInitVelY // Immediately gets out of any snapping
|
||||
}
|
||||
|
||||
playerCollider.X, playerCollider.Y = VirtualGridToPolygonColliderBLPos(newVx, newVy, playerCollider.W*0.5, playerCollider.H*0.5, 0, 0, 0, 0, collisionSpaceOffsetX, collisionSpaceOffsetY, VIRTUAL_GRID_TO_WORLD_RATIO)
|
||||
playerCollider.X, playerCollider.Y = VirtualGridToPolygonColliderBLPos(newVx, newVy, playerCollider.W*0.5, playerCollider.H*0.5, 0, 0, 0, 0, collisionSpaceOffsetX, collisionSpaceOffsetY)
|
||||
// Update in the collision system
|
||||
playerCollider.Update()
|
||||
|
||||
@@ -475,13 +480,13 @@ func ApplyInputFrameDownsyncDynamicsOnSingleRenderFrame(inputsBuffer *RingBuffer
|
||||
if (meleeBullet.OriginatedRenderFrameId+meleeBullet.StartupFrames <= currRenderFrame.Id) && (meleeBullet.OriginatedRenderFrameId+meleeBullet.StartupFrames+meleeBullet.ActiveFrames > currRenderFrame.Id) {
|
||||
offender := currRenderFrame.PlayersArr[meleeBullet.OffenderJoinIndex-1]
|
||||
|
||||
xfac := float64(1.0) // By now, straight Punch offset doesn't respect "y-axis"
|
||||
xfac := int32(1) // By now, straight Punch offset doesn't respect "y-axis"
|
||||
if 0 > offender.DirX {
|
||||
xfac = float64(-1.0)
|
||||
xfac = -xfac
|
||||
}
|
||||
offenderWx, offenderWy := VirtualGridToWorldPos(offender.VirtualGridX, offender.VirtualGridY, VIRTUAL_GRID_TO_WORLD_RATIO)
|
||||
bulletWx, bulletWy := offenderWx+xfac*meleeBullet.HitboxOffset, offenderWy
|
||||
newBulletCollider := GenerateRectCollider(bulletWx, bulletWy, meleeBullet.HitboxSizeX, meleeBullet.HitboxSizeY, SNAP_INTO_PLATFORM_OVERLAP, SNAP_INTO_PLATFORM_OVERLAP, SNAP_INTO_PLATFORM_OVERLAP, SNAP_INTO_PLATFORM_OVERLAP, collisionSpaceOffsetX, collisionSpaceOffsetY, meleeBullet, "MeleeBullet")
|
||||
bulletWx, bulletWy := VirtualGridToWorldPos(offender.VirtualGridX+xfac*meleeBullet.HitboxOffsetX, offender.VirtualGridY)
|
||||
hitboxSizeWx, hitboxSizeWy := VirtualGridToWorldPos(meleeBullet.HitboxSizeX, meleeBullet.HitboxSizeY)
|
||||
newBulletCollider := GenerateRectCollider(bulletWx, bulletWy, hitboxSizeWx, hitboxSizeWy, SNAP_INTO_PLATFORM_OVERLAP, SNAP_INTO_PLATFORM_OVERLAP, SNAP_INTO_PLATFORM_OVERLAP, SNAP_INTO_PLATFORM_OVERLAP, collisionSpaceOffsetX, collisionSpaceOffsetY, meleeBullet, "MeleeBullet")
|
||||
collisionSys.Add(newBulletCollider)
|
||||
bulletColliders = append(bulletColliders, newBulletCollider)
|
||||
} else {
|
||||
@@ -497,7 +502,9 @@ func ApplyInputFrameDownsyncDynamicsOnSingleRenderFrame(inputsBuffer *RingBuffer
|
||||
playerShape := playerCollider.Shape.(*resolv.ConvexPolygon)
|
||||
hardPushbackNorms[joinIndex-1] = calcHardPushbacksNorms(joinIndex, playerCollider, playerShape, SNAP_INTO_PLATFORM_OVERLAP, &(effPushbacks[joinIndex-1]))
|
||||
thatPlayerInNextFrame := nextRenderFramePlayers[i]
|
||||
chConfig := chConfigsOrderedByJoinIndex[i]
|
||||
landedOnGravityPushback := false
|
||||
|
||||
if collision := playerCollider.Check(0, 0); nil != collision {
|
||||
for _, obj := range collision.Objects {
|
||||
isBarrier, isAnotherPlayer, isBullet := false, false, false
|
||||
@@ -547,10 +554,97 @@ func ApplyInputFrameDownsyncDynamicsOnSingleRenderFrame(inputsBuffer *RingBuffer
|
||||
// fallStopping
|
||||
thatPlayerInNextFrame.VelX = 0
|
||||
thatPlayerInNextFrame.VelY = 0
|
||||
thatPlayerInNextFrame.CharacterState = ATK_CHARACTER_STATE_IDLE1
|
||||
thatPlayerInNextFrame.FramesToRecover = 0
|
||||
if ATK_CHARACTER_STATE_BLOWN_UP1 == thatPlayerInNextFrame.CharacterState {
|
||||
thatPlayerInNextFrame.CharacterState = ATK_CHARACTER_STATE_LAY_DOWN1
|
||||
thatPlayerInNextFrame.FramesToRecover = chConfig.LayDownFramesToRecover
|
||||
} else {
|
||||
thatPlayerInNextFrame.CharacterState = ATK_CHARACTER_STATE_IDLE1
|
||||
thatPlayerInNextFrame.FramesToRecover = 0
|
||||
}
|
||||
} else {
|
||||
// not fallStopping, could be in LayDown or GetUp
|
||||
if ATK_CHARACTER_STATE_LAY_DOWN1 == thatPlayerInNextFrame.CharacterState {
|
||||
if 0 == thatPlayerInNextFrame.FramesToRecover {
|
||||
thatPlayerInNextFrame.CharacterState = ATK_CHARACTER_STATE_GET_UP1
|
||||
thatPlayerInNextFrame.FramesToRecover = chConfig.GetUpFramesToRecover
|
||||
}
|
||||
} else if ATK_CHARACTER_STATE_GET_UP1 == thatPlayerInNextFrame.CharacterState {
|
||||
if thatPlayerInNextFrame.FramesInChState == chConfig.GetUpFrames {
|
||||
// [WARNING] Before reaching here, the player had 3 invinsible frames to either attack or jump, if it ever took any action then this condition wouldn't have been met, thus we hereby only transit it back to IDLE as it took no action
|
||||
thatPlayerInNextFrame.CharacterState = ATK_CHARACTER_STATE_IDLE1
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 5. Check bullet-anything collisions
|
||||
for _, bulletCollider := range bulletColliders {
|
||||
collision := bulletCollider.Check(0, 0)
|
||||
bulletCollider.Space.Remove(bulletCollider) // Make sure that the bulletCollider is always removed for each renderFrame
|
||||
switch v := bulletCollider.Data.(type) {
|
||||
case *MeleeBullet:
|
||||
if nil == collision {
|
||||
nextRenderFrameMeleeBullets = append(nextRenderFrameMeleeBullets, v)
|
||||
continue
|
||||
}
|
||||
bulletShape := bulletCollider.Shape.(*resolv.ConvexPolygon)
|
||||
offender := currRenderFrame.PlayersArr[v.OffenderJoinIndex-1]
|
||||
for _, obj := range collision.Objects {
|
||||
defenderShape := obj.Shape.(*resolv.ConvexPolygon)
|
||||
switch t := obj.Data.(type) {
|
||||
case *PlayerDownsync:
|
||||
if v.OffenderJoinIndex == t.JoinIndex {
|
||||
continue
|
||||
}
|
||||
overlapped, _, _, _ := CalcPushbacks(0, 0, bulletShape, defenderShape)
|
||||
if !overlapped {
|
||||
continue
|
||||
}
|
||||
joinIndex := t.JoinIndex
|
||||
xfac := int32(1) // By now, straight Punch offset doesn't respect "y-axis"
|
||||
if 0 > offender.DirX {
|
||||
xfac = -xfac
|
||||
}
|
||||
pushbackX, pushbackY := VirtualGridToWorldPos(-xfac*v.PushbackX, v.PushbackY)
|
||||
|
||||
for _, hardPushbackNorm := range *hardPushbackNorms[joinIndex-1] {
|
||||
projectedMagnitude := pushbackX*hardPushbackNorm.X + pushbackY*hardPushbackNorm.Y
|
||||
if 0 > projectedMagnitude {
|
||||
//fmt.Printf("defenderPlayerId=%d, joinIndex=%d reducing bullet pushback={%.3f, %.3f} by {%.3f, %.3f} where hardPushbackNorm={%.3f, %.3f}, projectedMagnitude=%.3f at renderFrame.id=%d", t.Id, joinIndex, pushbackX, pushbackY, projectedMagnitude*hardPushbackNorm.X, projectedMagnitude*hardPushbackNorm.Y, hardPushbackNorm.X, hardPushbackNorm.Y, projectedMagnitude, currRenderFrame.Id)
|
||||
pushbackX -= projectedMagnitude * hardPushbackNorm.X
|
||||
pushbackY -= projectedMagnitude * hardPushbackNorm.Y
|
||||
}
|
||||
}
|
||||
|
||||
effPushbacks[joinIndex-1].X += pushbackX
|
||||
effPushbacks[joinIndex-1].Y += pushbackY
|
||||
atkedPlayerInNextFrame := nextRenderFramePlayers[t.JoinIndex-1]
|
||||
if v.BlowUp {
|
||||
atkedPlayerInNextFrame.CharacterState = ATK_CHARACTER_STATE_BLOWN_UP1
|
||||
} else {
|
||||
atkedPlayerInNextFrame.CharacterState = ATK_CHARACTER_STATE_ATKED1
|
||||
}
|
||||
oldFramesToRecover := nextRenderFramePlayers[t.JoinIndex-1].FramesToRecover
|
||||
if v.HitStunFrames > oldFramesToRecover {
|
||||
atkedPlayerInNextFrame.FramesToRecover = v.HitStunFrames
|
||||
}
|
||||
default:
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 6. Get players out of stuck barriers if there's any
|
||||
for i, currPlayerDownsync := range currRenderFrame.PlayersArr {
|
||||
joinIndex := currPlayerDownsync.JoinIndex
|
||||
collisionPlayerIndex := COLLISION_PLAYER_INDEX_PREFIX + joinIndex
|
||||
playerCollider := collisionSysMap[collisionPlayerIndex]
|
||||
// Update "virtual grid position"
|
||||
thatPlayerInNextFrame := nextRenderFramePlayers[i]
|
||||
thatPlayerInNextFrame.VirtualGridX, thatPlayerInNextFrame.VirtualGridY = PolygonColliderBLToVirtualGridPos(playerCollider.X-effPushbacks[joinIndex-1].X, playerCollider.Y-effPushbacks[joinIndex-1].Y, playerCollider.W*0.5, playerCollider.H*0.5, 0, 0, 0, 0, collisionSpaceOffsetX, collisionSpaceOffsetY)
|
||||
|
||||
// Update "CharacterState"
|
||||
if thatPlayerInNextFrame.InAir {
|
||||
oldNextCharacterState := thatPlayerInNextFrame.CharacterState
|
||||
switch oldNextCharacterState {
|
||||
@@ -566,70 +660,6 @@ func ApplyInputFrameDownsyncDynamicsOnSingleRenderFrame(inputsBuffer *RingBuffer
|
||||
thatPlayerInNextFrame.CharacterState = ATK_CHARACTER_STATE_INAIR_ATKED1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 5. Check bullet-anything collisions
|
||||
for _, bulletCollider := range bulletColliders {
|
||||
meleeBullet := bulletCollider.Data.(*MeleeBullet)
|
||||
bulletShape := bulletCollider.Shape.(*resolv.ConvexPolygon)
|
||||
collision := bulletCollider.Check(0, 0)
|
||||
bulletCollider.Space.Remove(bulletCollider) // Make sure that the bulletCollider is always removed for each renderFrame
|
||||
if nil == collision {
|
||||
nextRenderFrameMeleeBullets = append(nextRenderFrameMeleeBullets, meleeBullet)
|
||||
continue
|
||||
}
|
||||
offender := currRenderFrame.PlayersArr[meleeBullet.OffenderJoinIndex-1]
|
||||
for _, obj := range collision.Objects {
|
||||
defenderShape := obj.Shape.(*resolv.ConvexPolygon)
|
||||
switch t := obj.Data.(type) {
|
||||
case *PlayerDownsync:
|
||||
if meleeBullet.OffenderPlayerId == t.Id {
|
||||
continue
|
||||
}
|
||||
overlapped, _, _, _ := CalcPushbacks(0, 0, bulletShape, defenderShape)
|
||||
if !overlapped {
|
||||
continue
|
||||
}
|
||||
joinIndex := t.JoinIndex
|
||||
xfac := float64(1.0) // By now, straight Punch offset doesn't respect "y-axis"
|
||||
if 0 > offender.DirX {
|
||||
xfac = float64(-1.0)
|
||||
}
|
||||
pushbackX, pushbackY := -xfac*meleeBullet.Pushback, float64(0)
|
||||
|
||||
for _, hardPushbackNorm := range *hardPushbackNorms[joinIndex-1] {
|
||||
projectedMagnitude := pushbackX*hardPushbackNorm.X + pushbackY*hardPushbackNorm.Y
|
||||
if 0 > projectedMagnitude {
|
||||
//fmt.Printf("defenderPlayerId=%d, joinIndex=%d reducing bullet pushback={%.3f, %.3f} by {%.3f, %.3f} where hardPushbackNorm={%.3f, %.3f}, projectedMagnitude=%.3f at renderFrame.id=%d", t.Id, joinIndex, pushbackX, pushbackY, projectedMagnitude*hardPushbackNorm.X, projectedMagnitude*hardPushbackNorm.Y, hardPushbackNorm.X, hardPushbackNorm.Y, projectedMagnitude, currRenderFrame.Id)
|
||||
pushbackX -= projectedMagnitude * hardPushbackNorm.X
|
||||
pushbackY -= projectedMagnitude * hardPushbackNorm.Y
|
||||
}
|
||||
}
|
||||
|
||||
effPushbacks[joinIndex-1].X += pushbackX
|
||||
effPushbacks[joinIndex-1].Y += pushbackY
|
||||
atkedPlayerInCurFrame, atkedPlayerInNextFrame := currRenderFrame.PlayersArr[t.JoinIndex-1], nextRenderFramePlayers[t.JoinIndex-1]
|
||||
atkedPlayerInNextFrame.CharacterState = ATK_CHARACTER_STATE_ATKED1
|
||||
if atkedPlayerInCurFrame.InAir {
|
||||
atkedPlayerInNextFrame.CharacterState = ATK_CHARACTER_STATE_INAIR_ATKED1
|
||||
}
|
||||
oldFramesToRecover := nextRenderFramePlayers[t.JoinIndex-1].FramesToRecover
|
||||
if meleeBullet.HitStunFrames > oldFramesToRecover {
|
||||
atkedPlayerInNextFrame.FramesToRecover = meleeBullet.HitStunFrames
|
||||
}
|
||||
default:
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 6. Get players out of stuck barriers if there's any
|
||||
for i, currPlayerDownsync := range currRenderFrame.PlayersArr {
|
||||
joinIndex := currPlayerDownsync.JoinIndex
|
||||
collisionPlayerIndex := COLLISION_PLAYER_INDEX_PREFIX + joinIndex
|
||||
playerCollider := collisionSysMap[collisionPlayerIndex]
|
||||
// Update "virtual grid position"
|
||||
thatPlayerInNextFrame := nextRenderFramePlayers[i]
|
||||
thatPlayerInNextFrame.VirtualGridX, thatPlayerInNextFrame.VirtualGridY = PolygonColliderBLToVirtualGridPos(playerCollider.X-effPushbacks[joinIndex-1].X, playerCollider.Y-effPushbacks[joinIndex-1].Y, playerCollider.W*0.5, playerCollider.H*0.5, 0, 0, 0, 0, collisionSpaceOffsetX, collisionSpaceOffsetY, WORLD_TO_VIRTUAL_GRID_RATIO)
|
||||
if thatPlayerInNextFrame.CharacterState != currPlayerDownsync.CharacterState {
|
||||
thatPlayerInNextFrame.FramesInChState = 0
|
||||
}
|
||||
|
@@ -1,5 +1,23 @@
|
||||
package battle
|
||||
|
||||
type CharacterConfig struct {
|
||||
SpeciesId int
|
||||
SpeciesName string
|
||||
|
||||
InAirIdleFrameIdxTurningPoint int
|
||||
InAirIdleFrameIdxTurnedCycle int
|
||||
|
||||
LayDownFrames int32
|
||||
LayDownFramesToRecover int32
|
||||
|
||||
GetUpFrames int32
|
||||
GetUpFramesToRecover int32
|
||||
|
||||
JumpingInitVelY int32
|
||||
|
||||
PatternIdToSkillId map[int]int
|
||||
}
|
||||
|
||||
var Characters = map[int]*CharacterConfig{
|
||||
0: &CharacterConfig{
|
||||
SpeciesId: 0,
|
||||
@@ -8,13 +26,13 @@ var Characters = map[int]*CharacterConfig{
|
||||
InAirIdleFrameIdxTurningPoint: 11,
|
||||
InAirIdleFrameIdxTurnedCycle: 1,
|
||||
|
||||
LayDownFrames: 16,
|
||||
LayDownFramesToRecover: 16,
|
||||
LayDownFrames: int32(16),
|
||||
LayDownFramesToRecover: int32(16),
|
||||
|
||||
GetUpFrames: 33,
|
||||
GetUpFramesToRecover: 30, // 3 invinsible frames for just-blown-up character to make a comeback
|
||||
GetUpFrames: int32(33),
|
||||
GetUpFramesToRecover: int32(30), // 3 invinsible frames for just-blown-up character to make a comeback
|
||||
|
||||
JumpingInitVelY: int(float64(8) * WORLD_TO_VIRTUAL_GRID_RATIO),
|
||||
JumpingInitVelY: int32(float64(8) * WORLD_TO_VIRTUAL_GRID_RATIO),
|
||||
|
||||
PatternIdToSkillId: map[int]int{
|
||||
0: 1, // Atk1
|
||||
@@ -23,52 +41,51 @@ var Characters = map[int]*CharacterConfig{
|
||||
},
|
||||
}
|
||||
|
||||
var skillIdToBullet = map[int]interface{}{
|
||||
1: &MeleeBullet{
|
||||
Bullet: Bullet{
|
||||
// for offender
|
||||
StartupFrames: int32(5),
|
||||
ActiveFrames: int32(10),
|
||||
RecoveryFrames: int32(34),
|
||||
RecoveryFramesOnBlock: int32(34),
|
||||
RecoveryFramesOnHit: int32(34),
|
||||
HitboxOffset: float64(12.0), // should be about the radius of the PlayerCollider
|
||||
|
||||
// for defender
|
||||
HitStunFrames: int32(18),
|
||||
BlockStunFrames: int32(9),
|
||||
Pushback: float64(8.0),
|
||||
ReleaseTriggerType: int32(1), // 1: rising-edge, 2: falling-edge
|
||||
Damage: int32(5),
|
||||
|
||||
SelfMoveforwardX: 0,
|
||||
SelfMoveforwardY: 0,
|
||||
HitboxSizeX: 24.0,
|
||||
HitboxSizeY: 32.0,
|
||||
var skills = map[int]*Skill{
|
||||
1: &Skill{
|
||||
RecoveryFrames: int32(34),
|
||||
RecoveryFramesOnBlock: int32(34),
|
||||
RecoveryFramesOnHit: int32(34),
|
||||
ReleaseTriggerType: int32(1),
|
||||
Hits: []interface{}{
|
||||
&MeleeBullet{
|
||||
Bullet: Bullet{
|
||||
StartupFrames: int32(5),
|
||||
ActiveFrames: int32(10),
|
||||
HitStunFrames: int32(18),
|
||||
BlockStunFrames: int32(9),
|
||||
Damage: int32(5),
|
||||
PushbackX: int32(float64(8) * WORLD_TO_VIRTUAL_GRID_RATIO),
|
||||
PushbackY: int32(0),
|
||||
HitboxOffsetX: int32(float64(12) * WORLD_TO_VIRTUAL_GRID_RATIO),
|
||||
HitboxOffsetY: int32(0),
|
||||
HitboxSizeX: int32(float64(24) * WORLD_TO_VIRTUAL_GRID_RATIO),
|
||||
HitboxSizeY: int32(float64(32) * WORLD_TO_VIRTUAL_GRID_RATIO),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
2: &MeleeBullet{
|
||||
Bullet: Bullet{
|
||||
// for offender
|
||||
StartupFrames: int32(3),
|
||||
ActiveFrames: int32(20),
|
||||
RecoveryFrames: int32(34),
|
||||
RecoveryFramesOnBlock: int32(34),
|
||||
RecoveryFramesOnHit: int32(34),
|
||||
HitboxOffset: float64(16.0), // should be about the radius of the PlayerCollider
|
||||
|
||||
// for defender
|
||||
HitStunFrames: int32(18),
|
||||
BlockStunFrames: int32(9),
|
||||
Pushback: float64(6.0),
|
||||
BlowUpVelY: int32(float64(3) * WORLD_TO_VIRTUAL_GRID_RATIO),
|
||||
ReleaseTriggerType: int32(1), // 1: rising-edge, 2: falling-edge
|
||||
Damage: int32(5),
|
||||
|
||||
SelfMoveforwardX: 0,
|
||||
SelfMoveforwardY: 0,
|
||||
HitboxSizeX: 32.0,
|
||||
HitboxSizeY: 24.0,
|
||||
2: &Skill{
|
||||
RecoveryFrames: int32(34),
|
||||
RecoveryFramesOnBlock: int32(34),
|
||||
RecoveryFramesOnHit: int32(34),
|
||||
ReleaseTriggerType: int32(1),
|
||||
Hits: []interface{}{
|
||||
&MeleeBullet{
|
||||
Bullet: Bullet{
|
||||
StartupFrames: int32(3),
|
||||
ActiveFrames: int32(20),
|
||||
HitStunFrames: int32(18),
|
||||
BlockStunFrames: int32(9),
|
||||
Damage: int32(5),
|
||||
PushbackX: int32(float64(6) * WORLD_TO_VIRTUAL_GRID_RATIO),
|
||||
PushbackY: int32(0),
|
||||
HitboxOffsetX: int32(float64(12) * WORLD_TO_VIRTUAL_GRID_RATIO),
|
||||
HitboxOffsetY: int32(0),
|
||||
HitboxSizeX: int32(float64(32) * WORLD_TO_VIRTUAL_GRID_RATIO),
|
||||
HitboxSizeY: int32(float64(24) * WORLD_TO_VIRTUAL_GRID_RATIO),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
@@ -53,30 +53,29 @@ type Barrier struct {
|
||||
|
||||
type Bullet struct {
|
||||
// for offender
|
||||
BattleLocalId int32
|
||||
StartupFrames int32
|
||||
OriginatedRenderFrameId int32 // Copied from the first bullet for all subsequent bullets
|
||||
OffenderJoinIndex int32 // Copied to favor collision handling of the dispatched bullet
|
||||
StartupFrames int32 // from "OriginatedRenderFrameId"
|
||||
CancellableStFrame int32 // from "OriginatedRenderFrameId"
|
||||
CancellableEdFrame int32 // from "OriginatedRenderFrameId"
|
||||
ActiveFrames int32
|
||||
RecoveryFrames int32
|
||||
RecoveryFramesOnBlock int32
|
||||
RecoveryFramesOnHit int32
|
||||
HitboxOffset float64
|
||||
OriginatedRenderFrameId int32
|
||||
|
||||
// for defender
|
||||
HitStunFrames int32
|
||||
BlockStunFrames int32
|
||||
Pushback float64
|
||||
ReleaseTriggerType int32
|
||||
Damage int32
|
||||
OffenderJoinIndex int32
|
||||
OffenderPlayerId int32
|
||||
HitStunFrames int32
|
||||
BlockStunFrames int32
|
||||
PushbackX int32
|
||||
PushbackY int32
|
||||
Damage int32
|
||||
|
||||
SelfMoveforwardX float64
|
||||
SelfMoveforwardY float64
|
||||
HitboxSizeX float64
|
||||
HitboxSizeY float64
|
||||
SelfLockVelX int32
|
||||
SelfLockVelY int32
|
||||
|
||||
BlowUpVelY int32
|
||||
HitboxOffsetX int32
|
||||
HitboxOffsetY int32
|
||||
HitboxSizeX int32
|
||||
HitboxSizeY int32
|
||||
|
||||
BlowUp bool
|
||||
}
|
||||
|
||||
type MeleeBullet struct {
|
||||
@@ -95,7 +94,12 @@ type FireballBullet struct {
|
||||
}
|
||||
|
||||
type Skill struct {
|
||||
Hits []Bullet // Hits within a "Skill" are automatically triggered
|
||||
BattleLocalId int32
|
||||
RecoveryFrames int32
|
||||
RecoveryFramesOnBlock int32
|
||||
RecoveryFramesOnHit int32
|
||||
ReleaseTriggerType int32 // 1: rising-edge, 2: falling-edge
|
||||
Hits []interface{} // Hits within a "Skill" are automatically triggered
|
||||
}
|
||||
|
||||
type RoomDownsyncFrame struct {
|
||||
@@ -114,21 +118,3 @@ type InputFrameDownsync struct {
|
||||
InputList []uint64
|
||||
ConfirmedList uint64
|
||||
}
|
||||
|
||||
type CharacterConfig struct {
|
||||
SpeciesId int
|
||||
SpeciesName string
|
||||
|
||||
InAirIdleFrameIdxTurningPoint int
|
||||
InAirIdleFrameIdxTurnedCycle int
|
||||
|
||||
LayDownFrames int
|
||||
LayDownFramesToRecover int
|
||||
|
||||
GetUpFrames int
|
||||
GetUpFramesToRecover int
|
||||
|
||||
JumpingInitVelY int
|
||||
|
||||
PatternIdToSkillId map[int]int
|
||||
}
|
||||
|
@@ -64,29 +64,32 @@ func NewPlayerDownsyncJs(id, virtualGridX, virtualGridY, dirX, dirY, velX, velY,
|
||||
})
|
||||
}
|
||||
|
||||
func NewMeleeBulletJs(battleLocalId, startupFrames, activeFrames, recoveryFrames, recoveryFramesOnBlock, recoveryFramesOnHit, hitStunFrames, blockStunFrames, releaseTriggerType, damage, offenderJoinIndex, offenderPlayerId int32, pushback, hitboxOffset, selfMoveforwardX, selfMoveforwardY, hitboxSizeX, hitboxSizeY float64) *js.Object {
|
||||
func NewMeleeBulletJs(originatedRenderFrameId, offenderJoinIndex, startupFrames, cancellableStFrame, cancellableEdFrame, activeFrames, hitStunFrames, blockStunFrames, pushbackX, pushbackY, damage, selfLockVelX, selfLockVelY, hitboxOffsetX, hitboxOffsetY, hitboxSizeX, hitboxSizeY int32, blowUp bool) *js.Object {
|
||||
return js.MakeWrapper(&MeleeBullet{
|
||||
Bullet: Bullet{
|
||||
BattleLocalId: battleLocalId,
|
||||
StartupFrames: startupFrames,
|
||||
ActiveFrames: activeFrames,
|
||||
RecoveryFrames: recoveryFrames,
|
||||
RecoveryFramesOnBlock: recoveryFramesOnBlock,
|
||||
RecoveryFramesOnHit: recoveryFramesOnHit,
|
||||
HitboxOffset: hitboxOffset,
|
||||
HitStunFrames: hitStunFrames,
|
||||
BlockStunFrames: blockStunFrames,
|
||||
Pushback: pushback,
|
||||
ReleaseTriggerType: releaseTriggerType,
|
||||
Damage: damage,
|
||||
OriginatedRenderFrameId: originatedRenderFrameId,
|
||||
OffenderJoinIndex: offenderJoinIndex,
|
||||
|
||||
SelfMoveforwardX: selfMoveforwardX,
|
||||
SelfMoveforwardY: selfMoveforwardY,
|
||||
HitboxSizeX: hitboxSizeX,
|
||||
HitboxSizeY: hitboxSizeY,
|
||||
StartupFrames: startupFrames,
|
||||
CancellableStFrame: cancellableStFrame,
|
||||
CancellableEdFrame: cancellableEdFrame,
|
||||
ActiveFrames: activeFrames,
|
||||
|
||||
OffenderJoinIndex: offenderJoinIndex,
|
||||
OffenderPlayerId: offenderPlayerId,
|
||||
HitStunFrames: hitStunFrames,
|
||||
BlockStunFrames: blockStunFrames,
|
||||
PushbackX: pushbackX,
|
||||
PushbackY: pushbackY,
|
||||
Damage: damage,
|
||||
|
||||
SelfLockVelX: selfLockVelX,
|
||||
SelfLockVelY: selfLockVelY,
|
||||
|
||||
HitboxOffsetX: hitboxOffsetX,
|
||||
HitboxOffsetY: hitboxOffsetY,
|
||||
HitboxSizeX: hitboxSizeX,
|
||||
HitboxSizeY: hitboxSizeY,
|
||||
|
||||
BlowUp: blowUp,
|
||||
},
|
||||
})
|
||||
}
|
||||
@@ -110,18 +113,19 @@ func GetCollisionSpaceObjsJs(space *resolv.Space) []*js.Object {
|
||||
return ret
|
||||
}
|
||||
|
||||
func GenerateRectColliderJs(wx, wy, w, h, topPadding, bottomPadding, leftPadding, rightPadding, spaceOffsetX, spaceOffsetY float64, data interface{}, tag string) *js.Object {
|
||||
func GenerateRectColliderJs(wx, wy, w, h, spaceOffsetX, spaceOffsetY float64, data interface{}, tag string) *js.Object {
|
||||
/*
|
||||
[WARNING] It's important to note that we don't need "js.MakeFullWrapper" for a call sequence as follows.
|
||||
```
|
||||
var space = gopkgs.NewCollisionSpaceJs(2048, 2048, 8, 8);
|
||||
var a = gopkgs.GenerateRectColliderJs(189, 497, 48, 48, snapIntoPlatformOverlap, snapIntoPlatformOverlap, snapIntoPlatformOverlap, snapIntoPlatformOverlap, spaceOffsetX, spaceOffsetY, "Player");
|
||||
var a = gopkgs.GenerateRectColliderJs(189, 497, 48, 48, spaceOffsetX, spaceOffsetY, "Player");
|
||||
space.Add(a);
|
||||
```
|
||||
The "space" variable doesn't need access to the field of "a" in JavaScript level to run "space.Add(...)" method, which is good.
|
||||
|
||||
However, the full wrapper access here is used for updating "collider.X/collider.Y" at JavaScript runtime.
|
||||
*/
|
||||
topPadding, bottomPadding, leftPadding, rightPadding := SNAP_INTO_PLATFORM_OVERLAP, SNAP_INTO_PLATFORM_OVERLAP, SNAP_INTO_PLATFORM_OVERLAP, SNAP_INTO_PLATFORM_OVERLAP
|
||||
return js.MakeFullWrapper(GenerateRectCollider(wx, wy, w, h, topPadding, bottomPadding, leftPadding, rightPadding, spaceOffsetX, spaceOffsetY, data, tag))
|
||||
|
||||
}
|
||||
@@ -138,28 +142,30 @@ func GetCharacterConfigsOrderedByJoinIndex(speciesIdList []int) []*js.Object {
|
||||
return ret
|
||||
}
|
||||
|
||||
func ApplyInputFrameDownsyncDynamicsOnSingleRenderFrameJs(inputsBuffer *RingBuffer, currRenderFrame *RoomDownsyncFrame, collisionSys *resolv.Space, collisionSysMap map[int32]*resolv.Object, collisionSpaceOffsetX, collisionSpaceOffsetY float64, playerOpPatternToSkillId map[int]int, chConfigsOrderedByJoinIndex []*CharacterConfig) *js.Object {
|
||||
func ApplyInputFrameDownsyncDynamicsOnSingleRenderFrameJs(inputsBuffer *RingBuffer, currRenderFrame *RoomDownsyncFrame, collisionSys *resolv.Space, collisionSysMap map[int32]*resolv.Object, collisionSpaceOffsetX, collisionSpaceOffsetY float64, chConfigsOrderedByJoinIndex []*CharacterConfig) *js.Object {
|
||||
// We need access to all fields of RoomDownsyncFrame for displaying in frontend
|
||||
return js.MakeFullWrapper(ApplyInputFrameDownsyncDynamicsOnSingleRenderFrame(inputsBuffer, currRenderFrame, collisionSys, collisionSysMap, collisionSpaceOffsetX, collisionSpaceOffsetY, playerOpPatternToSkillId, chConfigsOrderedByJoinIndex))
|
||||
return js.MakeFullWrapper(ApplyInputFrameDownsyncDynamicsOnSingleRenderFrame(inputsBuffer, currRenderFrame, collisionSys, collisionSysMap, collisionSpaceOffsetX, collisionSpaceOffsetY, chConfigsOrderedByJoinIndex))
|
||||
}
|
||||
|
||||
func main() {
|
||||
js.Global.Set("gopkgs", map[string]interface{}{
|
||||
"NewVec2DJs": NewVec2DJs,
|
||||
"NewPolygon2DJs": NewPolygon2DJs,
|
||||
"NewBarrierJs": NewBarrierJs,
|
||||
"NewPlayerDownsyncJs": NewPlayerDownsyncJs,
|
||||
"NewMeleeBulletJs": NewMeleeBulletJs,
|
||||
"NewRoomDownsyncFrameJs": NewRoomDownsyncFrameJs,
|
||||
"NewCollisionSpaceJs": NewCollisionSpaceJs,
|
||||
"NewInputFrameDownsync": NewInputFrameDownsync,
|
||||
"NewRingBufferJs": NewRingBufferJs,
|
||||
"GenerateRectColliderJs": GenerateRectColliderJs,
|
||||
"GenerateConvexPolygonColliderJs": GenerateConvexPolygonColliderJs,
|
||||
"GetCollisionSpaceObjsJs": GetCollisionSpaceObjsJs,
|
||||
"GetCharacterConfigsOrderedByJoinIndex": GetCharacterConfigsOrderedByJoinIndex,
|
||||
"ApplyInputFrameDownsyncDynamicsOnSingleRenderFrameJs": ApplyInputFrameDownsyncDynamicsOnSingleRenderFrameJs,
|
||||
"NewVec2DJs": NewVec2DJs,
|
||||
"NewPolygon2DJs": NewPolygon2DJs,
|
||||
"NewBarrierJs": NewBarrierJs,
|
||||
"NewPlayerDownsyncJs": NewPlayerDownsyncJs,
|
||||
"NewMeleeBulletJs": NewMeleeBulletJs,
|
||||
"NewRoomDownsyncFrameJs": NewRoomDownsyncFrameJs,
|
||||
"NewCollisionSpaceJs": NewCollisionSpaceJs,
|
||||
"NewInputFrameDownsync": NewInputFrameDownsync,
|
||||
"NewRingBufferJs": NewRingBufferJs,
|
||||
"GenerateRectColliderJs": GenerateRectColliderJs,
|
||||
"GenerateConvexPolygonColliderJs": GenerateConvexPolygonColliderJs,
|
||||
"GetCollisionSpaceObjsJs": GetCollisionSpaceObjsJs,
|
||||
"WorldToPolygonColliderBLPos": WorldToPolygonColliderBLPos, // No need to wrap primitive return types
|
||||
"PolygonColliderBLToWorldPos": PolygonColliderBLToWorldPos,
|
||||
"WorldToVirtualGridPos": WorldToVirtualGridPos,
|
||||
"VirtualGridToWorldPos": VirtualGridToWorldPos,
|
||||
"GetCharacterConfigsOrderedByJoinIndex": GetCharacterConfigsOrderedByJoinIndex,
|
||||
"ApplyInputFrameDownsyncDynamicsOnSingleRenderFrameJs": ApplyInputFrameDownsyncDynamicsOnSingleRenderFrameJs,
|
||||
})
|
||||
}
|
||||
|
Reference in New Issue
Block a user