mirror of
				https://github.com/genxium/DelayNoMore
				synced 2025-11-04 13:27:15 +00:00 
			
		
		
		
	Fixed fireball rollback mechanism.
This commit is contained in:
		@@ -55,62 +55,62 @@ func toPbRoomDownsyncFrame(rdf *battle.RoomDownsyncFrame) *pb.RoomDownsyncFrame
 | 
			
		||||
 | 
			
		||||
	for i, last := range rdf.MeleeBullets {
 | 
			
		||||
		pbBullet := &pb.MeleeBullet{
 | 
			
		||||
			BulletLocalId:           last.BulletLocalId,
 | 
			
		||||
			OriginatedRenderFrameId: last.OriginatedRenderFrameId,
 | 
			
		||||
			OffenderJoinIndex:       last.OffenderJoinIndex,
 | 
			
		||||
			BulletLocalId:           last.Bullet.BulletLocalId,
 | 
			
		||||
			OriginatedRenderFrameId: last.Bullet.OriginatedRenderFrameId,
 | 
			
		||||
			OffenderJoinIndex:       last.Bullet.OffenderJoinIndex,
 | 
			
		||||
 | 
			
		||||
			StartupFrames:      last.StartupFrames,
 | 
			
		||||
			CancellableStFrame: last.CancellableStFrame,
 | 
			
		||||
			CancellableEdFrame: last.CancellableEdFrame,
 | 
			
		||||
			ActiveFrames:       last.ActiveFrames,
 | 
			
		||||
			StartupFrames:      last.Bullet.StartupFrames,
 | 
			
		||||
			CancellableStFrame: last.Bullet.CancellableStFrame,
 | 
			
		||||
			CancellableEdFrame: last.Bullet.CancellableEdFrame,
 | 
			
		||||
			ActiveFrames:       last.Bullet.ActiveFrames,
 | 
			
		||||
 | 
			
		||||
			HitStunFrames:   last.HitStunFrames,
 | 
			
		||||
			BlockStunFrames: last.BlockStunFrames,
 | 
			
		||||
			PushbackVelX:    last.PushbackVelX,
 | 
			
		||||
			PushbackVelY:    last.PushbackVelY,
 | 
			
		||||
			Damage:          last.Damage,
 | 
			
		||||
			HitStunFrames:   last.Bullet.HitStunFrames,
 | 
			
		||||
			BlockStunFrames: last.Bullet.BlockStunFrames,
 | 
			
		||||
			PushbackVelX:    last.Bullet.PushbackVelX,
 | 
			
		||||
			PushbackVelY:    last.Bullet.PushbackVelY,
 | 
			
		||||
			Damage:          last.Bullet.Damage,
 | 
			
		||||
 | 
			
		||||
			SelfLockVelX: last.SelfLockVelX,
 | 
			
		||||
			SelfLockVelY: last.SelfLockVelY,
 | 
			
		||||
			SelfLockVelX: last.Bullet.SelfLockVelX,
 | 
			
		||||
			SelfLockVelY: last.Bullet.SelfLockVelY,
 | 
			
		||||
 | 
			
		||||
			HitboxOffsetX: last.HitboxOffsetX,
 | 
			
		||||
			HitboxOffsetY: last.HitboxOffsetY,
 | 
			
		||||
			HitboxSizeX:   last.HitboxSizeX,
 | 
			
		||||
			HitboxSizeY:   last.HitboxSizeY,
 | 
			
		||||
			HitboxOffsetX: last.Bullet.HitboxOffsetX,
 | 
			
		||||
			HitboxOffsetY: last.Bullet.HitboxOffsetY,
 | 
			
		||||
			HitboxSizeX:   last.Bullet.HitboxSizeX,
 | 
			
		||||
			HitboxSizeY:   last.Bullet.HitboxSizeY,
 | 
			
		||||
 | 
			
		||||
			BlowUp: last.BlowUp,
 | 
			
		||||
			TeamId: last.TeamId,
 | 
			
		||||
			BlowUp: last.Bullet.BlowUp,
 | 
			
		||||
			TeamId: last.Bullet.TeamId,
 | 
			
		||||
		}
 | 
			
		||||
		ret.MeleeBullets[i] = pbBullet
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for i, last := range rdf.FireballBullets {
 | 
			
		||||
		pbBullet := &pb.FireballBullet{
 | 
			
		||||
			BulletLocalId:           last.BulletLocalId,
 | 
			
		||||
			OriginatedRenderFrameId: last.OriginatedRenderFrameId,
 | 
			
		||||
			OffenderJoinIndex:       last.OffenderJoinIndex,
 | 
			
		||||
			BulletLocalId:           last.Bullet.BulletLocalId,
 | 
			
		||||
			OriginatedRenderFrameId: last.Bullet.OriginatedRenderFrameId,
 | 
			
		||||
			OffenderJoinIndex:       last.Bullet.OffenderJoinIndex,
 | 
			
		||||
 | 
			
		||||
			StartupFrames:      last.StartupFrames,
 | 
			
		||||
			CancellableStFrame: last.CancellableStFrame,
 | 
			
		||||
			CancellableEdFrame: last.CancellableEdFrame,
 | 
			
		||||
			ActiveFrames:       last.ActiveFrames,
 | 
			
		||||
			StartupFrames:      last.Bullet.StartupFrames,
 | 
			
		||||
			CancellableStFrame: last.Bullet.CancellableStFrame,
 | 
			
		||||
			CancellableEdFrame: last.Bullet.CancellableEdFrame,
 | 
			
		||||
			ActiveFrames:       last.Bullet.ActiveFrames,
 | 
			
		||||
 | 
			
		||||
			HitStunFrames:   last.HitStunFrames,
 | 
			
		||||
			BlockStunFrames: last.BlockStunFrames,
 | 
			
		||||
			PushbackVelX:    last.PushbackVelX,
 | 
			
		||||
			PushbackVelY:    last.PushbackVelY,
 | 
			
		||||
			Damage:          last.Damage,
 | 
			
		||||
			HitStunFrames:   last.Bullet.HitStunFrames,
 | 
			
		||||
			BlockStunFrames: last.Bullet.BlockStunFrames,
 | 
			
		||||
			PushbackVelX:    last.Bullet.PushbackVelX,
 | 
			
		||||
			PushbackVelY:    last.Bullet.PushbackVelY,
 | 
			
		||||
			Damage:          last.Bullet.Damage,
 | 
			
		||||
 | 
			
		||||
			SelfLockVelX: last.SelfLockVelX,
 | 
			
		||||
			SelfLockVelY: last.SelfLockVelY,
 | 
			
		||||
			SelfLockVelX: last.Bullet.SelfLockVelX,
 | 
			
		||||
			SelfLockVelY: last.Bullet.SelfLockVelY,
 | 
			
		||||
 | 
			
		||||
			HitboxOffsetX: last.HitboxOffsetX,
 | 
			
		||||
			HitboxOffsetY: last.HitboxOffsetY,
 | 
			
		||||
			HitboxSizeX:   last.HitboxSizeX,
 | 
			
		||||
			HitboxSizeY:   last.HitboxSizeY,
 | 
			
		||||
			HitboxOffsetX: last.Bullet.HitboxOffsetX,
 | 
			
		||||
			HitboxOffsetY: last.Bullet.HitboxOffsetY,
 | 
			
		||||
			HitboxSizeX:   last.Bullet.HitboxSizeX,
 | 
			
		||||
			HitboxSizeY:   last.Bullet.HitboxSizeY,
 | 
			
		||||
 | 
			
		||||
			BlowUp: last.BlowUp,
 | 
			
		||||
			TeamId: last.TeamId,
 | 
			
		||||
			BlowUp: last.Bullet.BlowUp,
 | 
			
		||||
			TeamId: last.Bullet.TeamId,
 | 
			
		||||
 | 
			
		||||
			VirtualGridX: last.VirtualGridX,
 | 
			
		||||
			VirtualGridY: last.VirtualGridY,
 | 
			
		||||
 
 | 
			
		||||
@@ -335,7 +335,20 @@ func (pR *Room) playerDownsyncStr(player *battle.PlayerDownsync) string {
 | 
			
		||||
	if player.InAir {
 | 
			
		||||
		inAirInt = 1
 | 
			
		||||
	}
 | 
			
		||||
	s := fmt.Sprintf("{%d,%d,%d,%d,%d,%d,%d}", player.JoinIndex, player.VirtualGridX, player.VirtualGridY, player.VelX, player.VelY, player.FramesToRecover, inAirInt)
 | 
			
		||||
	onWallInt := 0
 | 
			
		||||
	if player.OnWall {
 | 
			
		||||
		onWallInt = 1
 | 
			
		||||
	}
 | 
			
		||||
	s := fmt.Sprintf("{%d,%d,%d,%d,%d,%d,%d,%d}", player.JoinIndex, player.VirtualGridX, player.VirtualGridY, player.VelX, player.VelY, player.FramesToRecover, inAirInt, onWallInt)
 | 
			
		||||
 | 
			
		||||
	return s
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (pR *Room) fireballDownsyncStr(fireball *battle.FireballBullet) string {
 | 
			
		||||
	if nil == fireball {
 | 
			
		||||
		return ""
 | 
			
		||||
	}
 | 
			
		||||
	s := fmt.Sprintf("{%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d}", fireball.Bullet.BulletLocalId, fireball.Bullet.OriginatedRenderFrameId, fireball.Bullet.OffenderJoinIndex, fireball.VirtualGridX, fireball.VirtualGridY, fireball.VelX, fireball.VelY, fireball.DirX, fireball.DirY, fireball.Bullet.HitboxSizeX, fireball.Bullet.HitboxSizeY)
 | 
			
		||||
 | 
			
		||||
	return s
 | 
			
		||||
}
 | 
			
		||||
@@ -365,7 +378,11 @@ func (pR *Room) rdfIdToActuallyUsedInputString() string {
 | 
			
		||||
		for _, player := range rdf.PlayersArr {
 | 
			
		||||
			playersStrBldr = append(playersStrBldr, pR.playerDownsyncStr(player))
 | 
			
		||||
		}
 | 
			
		||||
		s = append(s, fmt.Sprintf("rdfId:%d\nplayers:[%v]\nactuallyUsedinputList:{%v}", rdfId, strings.Join(playersStrBldr, ","), pR.inputFrameDownsyncStr(pR.rdfIdToActuallyUsedInput[rdfId])))
 | 
			
		||||
		fireballsStrBldr := make([]string, 0, len(rdf.FireballBullets))
 | 
			
		||||
		for _, fireball := range rdf.FireballBullets {
 | 
			
		||||
			fireballsStrBldr = append(fireballsStrBldr, pR.fireballDownsyncStr(fireball))
 | 
			
		||||
		}
 | 
			
		||||
		s = append(s, fmt.Sprintf("rdfId:%d\nplayers:[%v]\nfireballs:[%v]\nactuallyUsedinputList:{%v}", rdfId, strings.Join(playersStrBldr, ","), strings.Join(fireballsStrBldr, ","), pR.inputFrameDownsyncStr(pR.rdfIdToActuallyUsedInput[rdfId])))
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return strings.Join(s, "\n")
 | 
			
		||||
 
 | 
			
		||||
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							@@ -62,7 +62,7 @@
 | 
			
		||||
          {
 | 
			
		||||
            "frame": 0.13333333333333333,
 | 
			
		||||
            "value": {
 | 
			
		||||
              "__uuid__": "2ad9becb-20e0-4bbb-b83b-de21e085e706"
 | 
			
		||||
              "__uuid__": "dbe67025-9878-4e13-8f3d-81e04734810a"
 | 
			
		||||
            }
 | 
			
		||||
          },
 | 
			
		||||
          {
 | 
			
		||||
 
 | 
			
		||||
@@ -5,14 +5,14 @@
 | 
			
		||||
 <tileset firstgid="129" source="tiles2.tsx"/>
 | 
			
		||||
 <layer id="6" name="Ground" width="128" height="64">
 | 
			
		||||
  <data encoding="base64" compression="zlib">
 | 
			
		||||
   eJzt2z1uAjEURWELlIYuiKRHyk4iGjo2wP6XkRDGUmThnxne+FncU3wNEDJ+x3aqbEMIWwAAAAAAAAAAAADo4IQ/3h08+3s/gzflGSivnRlor50ZaK+dGdTX/mb0e6y+h/7+/Q+NHn1P6z7YG32G/svWXur1TP/SvqL/OP1LnSz6t7Qt9a29T//x+7fcASW9ZvDKWvpbmbPHRprBKxt97e8ZSjPo3f/L4TnSOyDXfY19QP+xtLa32gcjzsCjf+u5Pxp9ZhT0v9tN6K/jtvbvRLwH0tejY6Pcz48mzsC7hVf/zSSe/+uvc8bGQO5veO39pWrPo37+0/438Q74/5pV/9HQ/+6c9H7EevafK5h799A/b5eZmdX9W+u1ZD/NaU//ED4mpZk9c/5Le6DX+S/tT/rn+1so3QM9+tfuJ/X+8O/g2f8ijv7a6K+N/tror43+2uivjf7a6K+N/tror43+2uivjf7a6K+N/tror43+2uivjf7a6K+N/trory3+D5R3C/rTn/70790f6/X/AbS6syo=
 | 
			
		||||
   eJzt2ztuAjEURmELlIYuiKRHyk4iGjo2wP6XkRDGUmThxwx3fC3+U3wNw8v3eEyabEMIWwAAAAAAAAAAAADo4IQ/3h08+3t/B2/KM1BeOzPQXjsz0F47M6iv/c3oc6zeh/7+/Q+NHr1P6z7YGz2H/svWXur1TP/SvqL/OP1LnSz6t7Qt9a1dp//4/VvOgJJeM3hlLf2tzNljI83glY2+9vcMpRko9E/PgFz3NfbBKDNQ7r+kvdU+GHEGHv2/Gl9zNHrOKOh/t5vQX8dt7d+JeC19PDo2yr1+NHEG3i28+m8m8f6//jpnbAzkfsNr15eqfR/1+z/tfxP/Fvj/mFX/0dD/7pz0fsR69p8rmHv20D9vl5mZ1flb67VkP81pT/8QPialmT1z/5f2QK/7v7Q/6Z/vb6F0DvToXzuf1PvDv4Nn/4s4+mujvzb6a6O/Nvpro782+mujvzb6a6O/Nvpro782+mujvzb6a6O/Nvpro782+mujvzb6a6O/tvg/UN4t6E9/+tO/d3+s1/8HUhSy6A==
 | 
			
		||||
  </data>
 | 
			
		||||
 </layer>
 | 
			
		||||
 <objectgroup id="1" name="PlayerStartingPos">
 | 
			
		||||
  <object id="135" x="1630" y="518">
 | 
			
		||||
  <object id="135" x="1140" y="488">
 | 
			
		||||
   <point/>
 | 
			
		||||
  </object>
 | 
			
		||||
  <object id="137" x="888.003" y="535">
 | 
			
		||||
  <object id="137" x="1527" y="488">
 | 
			
		||||
   <point/>
 | 
			
		||||
  </object>
 | 
			
		||||
 </objectgroup>
 | 
			
		||||
@@ -179,11 +179,6 @@
 | 
			
		||||
    <property name="boundary_type" value="barrier"/>
 | 
			
		||||
   </properties>
 | 
			
		||||
  </object>
 | 
			
		||||
  <object id="126" x="720" y="480" width="16" height="16">
 | 
			
		||||
   <properties>
 | 
			
		||||
    <property name="boundary_type" value="barrier"/>
 | 
			
		||||
   </properties>
 | 
			
		||||
  </object>
 | 
			
		||||
  <object id="127" x="1088" y="320" width="16" height="16">
 | 
			
		||||
   <properties>
 | 
			
		||||
    <property name="boundary_type" value="barrier"/>
 | 
			
		||||
@@ -209,10 +204,5 @@
 | 
			
		||||
    <property name="boundary_type" value="barrier"/>
 | 
			
		||||
   </properties>
 | 
			
		||||
  </object>
 | 
			
		||||
  <object id="134" x="720" y="416" width="16" height="16">
 | 
			
		||||
   <properties>
 | 
			
		||||
    <property name="boundary_type" value="barrier"/>
 | 
			
		||||
   </properties>
 | 
			
		||||
  </object>
 | 
			
		||||
 </objectgroup>
 | 
			
		||||
</map>
 | 
			
		||||
 
 | 
			
		||||
@@ -24,11 +24,11 @@
 | 
			
		||||
    "_active": true,
 | 
			
		||||
    "_components": [
 | 
			
		||||
      {
 | 
			
		||||
        "__id__": 6
 | 
			
		||||
        "__id__": 7
 | 
			
		||||
      }
 | 
			
		||||
    ],
 | 
			
		||||
    "_prefab": {
 | 
			
		||||
      "__id__": 7
 | 
			
		||||
      "__id__": 8
 | 
			
		||||
    },
 | 
			
		||||
    "_opacity": 255,
 | 
			
		||||
    "_color": {
 | 
			
		||||
@@ -79,23 +79,20 @@
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "__type__": "cc.Node",
 | 
			
		||||
    "_name": "Fireball1",
 | 
			
		||||
    "_name": "animNode",
 | 
			
		||||
    "_objFlags": 0,
 | 
			
		||||
    "_parent": {
 | 
			
		||||
      "__id__": 1
 | 
			
		||||
    },
 | 
			
		||||
    "_children": [],
 | 
			
		||||
    "_active": false,
 | 
			
		||||
    "_components": [
 | 
			
		||||
    "_children": [
 | 
			
		||||
      {
 | 
			
		||||
        "__id__": 3
 | 
			
		||||
      },
 | 
			
		||||
      {
 | 
			
		||||
        "__id__": 4
 | 
			
		||||
      }
 | 
			
		||||
    ],
 | 
			
		||||
    "_active": true,
 | 
			
		||||
    "_components": [],
 | 
			
		||||
    "_prefab": {
 | 
			
		||||
      "__id__": 5
 | 
			
		||||
      "__id__": 6
 | 
			
		||||
    },
 | 
			
		||||
    "_opacity": 255,
 | 
			
		||||
    "_color": {
 | 
			
		||||
@@ -145,20 +142,67 @@
 | 
			
		||||
    "_id": ""
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "__type__": "cc.Animation",
 | 
			
		||||
    "_name": "",
 | 
			
		||||
    "__type__": "cc.Node",
 | 
			
		||||
    "_name": "Fireball1",
 | 
			
		||||
    "_objFlags": 0,
 | 
			
		||||
    "node": {
 | 
			
		||||
    "_parent": {
 | 
			
		||||
      "__id__": 2
 | 
			
		||||
    },
 | 
			
		||||
    "_enabled": true,
 | 
			
		||||
    "_defaultClip": null,
 | 
			
		||||
    "_clips": [
 | 
			
		||||
    "_children": [],
 | 
			
		||||
    "_active": true,
 | 
			
		||||
    "_components": [
 | 
			
		||||
      {
 | 
			
		||||
        "__uuid__": "ba12416b-eec3-4260-8402-7fc25b125624"
 | 
			
		||||
        "__id__": 4
 | 
			
		||||
      }
 | 
			
		||||
    ],
 | 
			
		||||
    "playOnLoad": false,
 | 
			
		||||
    "_prefab": {
 | 
			
		||||
      "__id__": 5
 | 
			
		||||
    },
 | 
			
		||||
    "_opacity": 255,
 | 
			
		||||
    "_color": {
 | 
			
		||||
      "__type__": "cc.Color",
 | 
			
		||||
      "r": 255,
 | 
			
		||||
      "g": 255,
 | 
			
		||||
      "b": 255,
 | 
			
		||||
      "a": 255
 | 
			
		||||
    },
 | 
			
		||||
    "_contentSize": {
 | 
			
		||||
      "__type__": "cc.Size",
 | 
			
		||||
      "width": 117,
 | 
			
		||||
      "height": 55
 | 
			
		||||
    },
 | 
			
		||||
    "_anchorPoint": {
 | 
			
		||||
      "__type__": "cc.Vec2",
 | 
			
		||||
      "x": 0.5,
 | 
			
		||||
      "y": 0.5
 | 
			
		||||
    },
 | 
			
		||||
    "_trs": {
 | 
			
		||||
      "__type__": "TypedArray",
 | 
			
		||||
      "ctor": "Float64Array",
 | 
			
		||||
      "array": [
 | 
			
		||||
        -32,
 | 
			
		||||
        0,
 | 
			
		||||
        0,
 | 
			
		||||
        0,
 | 
			
		||||
        0,
 | 
			
		||||
        0,
 | 
			
		||||
        1,
 | 
			
		||||
        1,
 | 
			
		||||
        1,
 | 
			
		||||
        1
 | 
			
		||||
      ]
 | 
			
		||||
    },
 | 
			
		||||
    "_eulerAngles": {
 | 
			
		||||
      "__type__": "cc.Vec3",
 | 
			
		||||
      "x": 0,
 | 
			
		||||
      "y": 0,
 | 
			
		||||
      "z": 0
 | 
			
		||||
    },
 | 
			
		||||
    "_skewX": 0,
 | 
			
		||||
    "_skewY": 0,
 | 
			
		||||
    "_is3DNode": false,
 | 
			
		||||
    "_groupIndex": 0,
 | 
			
		||||
    "groupIndex": 0,
 | 
			
		||||
    "_id": ""
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
@@ -166,13 +210,19 @@
 | 
			
		||||
    "_name": "",
 | 
			
		||||
    "_objFlags": 0,
 | 
			
		||||
    "node": {
 | 
			
		||||
      "__id__": 2
 | 
			
		||||
      "__id__": 3
 | 
			
		||||
    },
 | 
			
		||||
    "_enabled": true,
 | 
			
		||||
    "_materials": [],
 | 
			
		||||
    "_materials": [
 | 
			
		||||
      {
 | 
			
		||||
        "__uuid__": "eca5d2f2-8ef6-41c2-bbe6-f9c79d09c432"
 | 
			
		||||
      }
 | 
			
		||||
    ],
 | 
			
		||||
    "_srcBlendFactor": 770,
 | 
			
		||||
    "_dstBlendFactor": 771,
 | 
			
		||||
    "_spriteFrame": null,
 | 
			
		||||
    "_spriteFrame": {
 | 
			
		||||
      "__uuid__": "e92702d5-d5fd-49e6-ab6b-2296b43fa6d6"
 | 
			
		||||
    },
 | 
			
		||||
    "_type": 0,
 | 
			
		||||
    "_sizeMode": 1,
 | 
			
		||||
    "_fillType": 0,
 | 
			
		||||
@@ -197,7 +247,18 @@
 | 
			
		||||
    "asset": {
 | 
			
		||||
      "__uuid__": "d92d4831-cd65-4eb5-90bd-b77021aec35b"
 | 
			
		||||
    },
 | 
			
		||||
    "fileId": "9ds3kDxvVFFqyr760jrV4a",
 | 
			
		||||
    "fileId": "5f1s6pDt5F3rknJTu0gQW7",
 | 
			
		||||
    "sync": false
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "__type__": "cc.PrefabInfo",
 | 
			
		||||
    "root": {
 | 
			
		||||
      "__id__": 1
 | 
			
		||||
    },
 | 
			
		||||
    "asset": {
 | 
			
		||||
      "__uuid__": "d92d4831-cd65-4eb5-90bd-b77021aec35b"
 | 
			
		||||
    },
 | 
			
		||||
    "fileId": "3824oBeVVL1KOAQ6Zd9CC5",
 | 
			
		||||
    "sync": false
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
@@ -208,6 +269,9 @@
 | 
			
		||||
      "__id__": 1
 | 
			
		||||
    },
 | 
			
		||||
    "_enabled": true,
 | 
			
		||||
    "animNode": {
 | 
			
		||||
      "__id__": 2
 | 
			
		||||
    },
 | 
			
		||||
    "_id": ""
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
 
 | 
			
		||||
@@ -440,7 +440,7 @@
 | 
			
		||||
      "array": [
 | 
			
		||||
        0,
 | 
			
		||||
        0,
 | 
			
		||||
        216.50635094610968,
 | 
			
		||||
        215.94663282292512,
 | 
			
		||||
        0,
 | 
			
		||||
        0,
 | 
			
		||||
        0,
 | 
			
		||||
 
 | 
			
		||||
@@ -191,8 +191,8 @@
 | 
			
		||||
        0,
 | 
			
		||||
        0,
 | 
			
		||||
        1,
 | 
			
		||||
        1.2,
 | 
			
		||||
        1.2,
 | 
			
		||||
        0.8,
 | 
			
		||||
        0.8,
 | 
			
		||||
        1
 | 
			
		||||
      ]
 | 
			
		||||
    },
 | 
			
		||||
@@ -464,7 +464,7 @@
 | 
			
		||||
      "array": [
 | 
			
		||||
        0,
 | 
			
		||||
        0,
 | 
			
		||||
        209.57814771583418,
 | 
			
		||||
        215.64032554232523,
 | 
			
		||||
        0,
 | 
			
		||||
        0,
 | 
			
		||||
        0,
 | 
			
		||||
 
 | 
			
		||||
@@ -1,37 +1,34 @@
 | 
			
		||||
cc.Class({
 | 
			
		||||
    extends: cc.Component,
 | 
			
		||||
  extends: cc.Component,
 | 
			
		||||
 | 
			
		||||
    properties: {
 | 
			
		||||
      mapNode: {
 | 
			
		||||
        type: cc.Node,
 | 
			
		||||
        default: null
 | 
			
		||||
      },
 | 
			
		||||
      speed: {
 | 
			
		||||
        type: cc.Float,
 | 
			
		||||
        default: 500
 | 
			
		||||
      }, 
 | 
			
		||||
  properties: {
 | 
			
		||||
    mapNode: {
 | 
			
		||||
      type: cc.Node,
 | 
			
		||||
      default: null
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    onLoad () {
 | 
			
		||||
      this.mainCamera = this.mapNode.parent.getChildByName("Main Camera").getComponent(cc.Camera);
 | 
			
		||||
      this.mapScriptIns = this.mapNode.getComponent("Map");
 | 
			
		||||
    speed: {
 | 
			
		||||
      type: cc.Float,
 | 
			
		||||
      default: 500
 | 
			
		||||
    },
 | 
			
		||||
  },
 | 
			
		||||
 | 
			
		||||
    start() {},
 | 
			
		||||
  onLoad() {
 | 
			
		||||
    this.mainCamera = this.mapNode.parent.getChildByName("Main Camera").getComponent(cc.Camera);
 | 
			
		||||
    this.mapScriptIns = this.mapNode.getComponent("Map");
 | 
			
		||||
  },
 | 
			
		||||
 | 
			
		||||
    update(dt) {
 | 
			
		||||
      const self = this;
 | 
			
		||||
      if (!self.mainCamera) return;
 | 
			
		||||
      if (!self.mapScriptIns) return;
 | 
			
		||||
      if (!self.mapScriptIns.selfPlayerInfo) return;
 | 
			
		||||
      if (!self.mapScriptIns.playerRichInfoDict) return;
 | 
			
		||||
      const selfPlayerRichInfo = self.mapScriptIns.playerRichInfoDict.get(self.mapScriptIns.selfPlayerInfo.Id);
 | 
			
		||||
      if (!selfPlayerRichInfo) return;
 | 
			
		||||
      const selfPlayerNode = selfPlayerRichInfo.node; 
 | 
			
		||||
      if (!selfPlayerNode) return;
 | 
			
		||||
      const pDiff = selfPlayerNode.position.sub(self.mainCamera.node.position); 
 | 
			
		||||
      pDiff.normalizeSelf();
 | 
			
		||||
      const newCamPos = self.mainCamera.node.position.add(pDiff.mul(dt*self.speed));
 | 
			
		||||
      self.mainCamera.node.setPosition(newCamPos);
 | 
			
		||||
    }
 | 
			
		||||
  start() {},
 | 
			
		||||
 | 
			
		||||
  update(dt) {
 | 
			
		||||
    const self = this;
 | 
			
		||||
    if (!self.mainCamera) return;
 | 
			
		||||
    if (!self.mapScriptIns) return;
 | 
			
		||||
    if (!self.mapScriptIns.selfPlayerInfo) return;
 | 
			
		||||
    if (!self.mapScriptIns.playerRichInfoDict) return;
 | 
			
		||||
    const selfPlayerRichInfo = self.mapScriptIns.playerRichInfoDict.get(self.mapScriptIns.selfPlayerInfo.Id);
 | 
			
		||||
    if (!selfPlayerRichInfo) return;
 | 
			
		||||
    const selfPlayerNode = selfPlayerRichInfo.node;
 | 
			
		||||
    if (!selfPlayerNode) return;
 | 
			
		||||
    self.mapNode.setPosition(cc.v2().sub(selfPlayerNode.position));
 | 
			
		||||
  }
 | 
			
		||||
});
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,13 @@
 | 
			
		||||
cc.Class({
 | 
			
		||||
  extends: cc.Component,
 | 
			
		||||
 | 
			
		||||
  properties: {
 | 
			
		||||
    animNode: {
 | 
			
		||||
      type: cc.Node,
 | 
			
		||||
      default: null
 | 
			
		||||
    },
 | 
			
		||||
  },
 | 
			
		||||
 | 
			
		||||
  ctor() {
 | 
			
		||||
    this.lastUsed = -1;
 | 
			
		||||
    this.bulletLocalId = -1;
 | 
			
		||||
@@ -9,28 +16,30 @@ cc.Class({
 | 
			
		||||
 | 
			
		||||
  setSpecies(speciesName, fireballBullet, rdf) {
 | 
			
		||||
    if (speciesName == this.speciesName) return;
 | 
			
		||||
    this.speciesName = speciesName;
 | 
			
		||||
    this.effAnimNode = this.node.getChildByName(this.speciesName);
 | 
			
		||||
    this.animComp = this.effAnimNode.getComponent(cc.Animation);
 | 
			
		||||
    this.effAnimNode.active = true;
 | 
			
		||||
    for (let k in this.children) {
 | 
			
		||||
      const child = this.children[k];
 | 
			
		||||
      if (!child.active) continue;
 | 
			
		||||
      if (child == effAnimNode || child.name == speciesName) continue;
 | 
			
		||||
      child.active = false;
 | 
			
		||||
    if (null != this.speciesName) {
 | 
			
		||||
      for (let k in this.animNode.children) {
 | 
			
		||||
        const child = this.children[k];
 | 
			
		||||
        if (!child.active) continue;
 | 
			
		||||
        if (child == effAnimNode || child.name == speciesName) continue;
 | 
			
		||||
        child.active = false;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
    this.updateAnim(speciesName, fireballBullet, rdf);
 | 
			
		||||
    this.speciesName = speciesName;
 | 
			
		||||
    this.effAnimNode = this.animNode.getChildByName(this.speciesName);
 | 
			
		||||
    this.effAnimNode.active = true;
 | 
			
		||||
    //this.updateAnim(speciesName, fireballBullet, rdf);
 | 
			
		||||
  },
 | 
			
		||||
 | 
			
		||||
  onLoad() {},
 | 
			
		||||
 | 
			
		||||
  updateAnim(speciesName, fireballBullet, rdf) {
 | 
			
		||||
    this.animComp = this.effAnimNode.getComponent(cc.Animation);
 | 
			
		||||
    // Update directions
 | 
			
		||||
    if (this.animComp && this.animComp.node) {
 | 
			
		||||
      if (0 > fireballBullet.DirX) {
 | 
			
		||||
        this.effAnimNode.scaleX = (-1.0);
 | 
			
		||||
        this.animNode.scaleX = (-1.0);
 | 
			
		||||
      } else if (0 < fireballBullet.DirX) {
 | 
			
		||||
        this.effAnimNode.scaleX = (1.0);
 | 
			
		||||
        this.animNode.scaleX = (1.0);
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -413,7 +413,7 @@ cc.Class({
 | 
			
		||||
    window.mapIns = self;
 | 
			
		||||
    window.forceBigEndianFloatingNumDecoding = self.forceBigEndianFloatingNumDecoding;
 | 
			
		||||
 | 
			
		||||
    self.showCriticalCoordinateLabels = true;
 | 
			
		||||
    self.showCriticalCoordinateLabels = false;
 | 
			
		||||
 | 
			
		||||
    console.warn("+++++++ Map onLoad()");
 | 
			
		||||
 | 
			
		||||
@@ -1095,23 +1095,6 @@ othersForcedDownsyncRenderFrame=${JSON.stringify(othersForcedDownsyncRenderFrame
 | 
			
		||||
 | 
			
		||||
  _renderFireballBullet(fireballBullet, rdf) {
 | 
			
		||||
    const self = this;
 | 
			
		||||
    let pqNode = self.cachedFireballs.popAny(fireballBullet.Bullet.BulletLocalId);
 | 
			
		||||
    const speciesName = `Fireball${fireballBullet.SpeciesId}`;
 | 
			
		||||
 | 
			
		||||
    if (null == pqNode) {
 | 
			
		||||
      pqNode = self.cachedFireballs.pop();
 | 
			
		||||
    } else {
 | 
			
		||||
      console.log(`Using a cached fireball node for rendering for bulletLocalId=${fireballBullet.Bullet.BulletLocalId}`);
 | 
			
		||||
    }
 | 
			
		||||
    const cachedFireball = pqNode.value;
 | 
			
		||||
    cachedFireball.setSpecies(speciesName, fireballBullet, rdf);
 | 
			
		||||
    cachedFireball.lastUsed = self.renderFrameId;
 | 
			
		||||
    cachedFireball.bulletLocalId = fireballBullet.Bullet.BulletLocalId;
 | 
			
		||||
 | 
			
		||||
    const [wx, wy] = gopkgs.VirtualGridToWorldPos(fireballBullet.VirtualGridX, fireballBullet.VirtualGridY);
 | 
			
		||||
    cachedFireball.node.setPosition(cc.v2(wx, wy));
 | 
			
		||||
 | 
			
		||||
    self.cachedFireballs.push(cachedFireball.lastUsed, cachedFireball, cachedFireball.bulletLocalId);
 | 
			
		||||
  },
 | 
			
		||||
 | 
			
		||||
  applyRoomDownsyncFrameDynamics(rdf, prevRdf) {
 | 
			
		||||
@@ -1137,8 +1120,28 @@ othersForcedDownsyncRenderFrame=${JSON.stringify(othersForcedDownsyncRenderFrame
 | 
			
		||||
    const fireballBullets = rdf.FireballBullets;
 | 
			
		||||
    for (let k in fireballBullets) {
 | 
			
		||||
      const fireballBullet = fireballBullets[k];
 | 
			
		||||
      if ((fireballBullet.Bullet.OriginatedRenderFrameId + fireballBullet.Bullet.StartupFrames <= rdf.Id) && (fireballBullet.Bullet.OriginatedRenderFrameId + fireballBullet.Bullet.StartupFrames + fireballBullet.Bullet.ActiveFrames > rdf.Id)) {
 | 
			
		||||
        self._renderFireballBullet(fireballBullet, rdf);
 | 
			
		||||
      if (
 | 
			
		||||
        fireballBullet.Bullet.OriginatedRenderFrameId + fireballBullet.Bullet.StartupFrames <= rdf.Id
 | 
			
		||||
        &&
 | 
			
		||||
        fireballBullet.Bullet.OriginatedRenderFrameId + fireballBullet.Bullet.StartupFrames + fireballBullet.Bullet.ActiveFrames > rdf.Id
 | 
			
		||||
      ) {
 | 
			
		||||
        let pqNode = self.cachedFireballs.popAny(fireballBullet.Bullet.BulletLocalId);
 | 
			
		||||
        const speciesName = `Fireball${fireballBullet.SpeciesId}`;
 | 
			
		||||
        const [wx, wy] = gopkgs.VirtualGridToWorldPos(fireballBullet.VirtualGridX, fireballBullet.VirtualGridY);
 | 
			
		||||
 | 
			
		||||
        if (null == pqNode) {
 | 
			
		||||
          pqNode = self.cachedFireballs.pop();
 | 
			
		||||
          console.log(`@rdf.Id=${rdf.Id}, origRdfId=${fireballBullet.Bullet.OriginatedRenderFrameId}, startupFrames=${fireballBullet.Bullet.StartupFrames}, activeFrames=${fireballBullet.Bullet.ActiveFrames}, using a new fireball node for rendering for bulletLocalId=${fireballBullet.Bullet.BulletLocalId} at wpos=(${wx},${wy})`);
 | 
			
		||||
        } else {
 | 
			
		||||
          console.log(`@rdf.Id=${rdf.Id}, origRdfId=${fireballBullet.Bullet.OriginatedRenderFrameId}, startupFrames=${fireballBullet.Bullet.StartupFrames}, activeFrames=${fireballBullet.Bullet.ActiveFrames}, using a cached fireball node for rendering for bulletLocalId=${fireballBullet.Bullet.BulletLocalId} at wpos=(${wx},${wy})`);
 | 
			
		||||
        }
 | 
			
		||||
        const cachedFireball = pqNode.value;
 | 
			
		||||
        cachedFireball.setSpecies(speciesName, fireballBullet, rdf);
 | 
			
		||||
        cachedFireball.lastUsed = self.renderFrameId;
 | 
			
		||||
        cachedFireball.bulletLocalId = fireballBullet.Bullet.BulletLocalId;
 | 
			
		||||
        cachedFireball.node.setPosition(cc.v2(wx, wy));
 | 
			
		||||
 | 
			
		||||
        self.cachedFireballs.push(cachedFireball.lastUsed, cachedFireball, fireballBullet.Bullet.BulletLocalId);
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -1263,7 +1266,12 @@ othersForcedDownsyncRenderFrame=${JSON.stringify(othersForcedDownsyncRenderFrame
 | 
			
		||||
 | 
			
		||||
  playerDownsyncStr(playerDownsync) {
 | 
			
		||||
    if (null == playerDownsync) return "";
 | 
			
		||||
    return `{${playerDownsync.JoinIndex},${playerDownsync.VirtualGridX},${playerDownsync.VirtualGridY},${playerDownsync.VelX},${playerDownsync.VelY},${playerDownsync.FramesToRecover},${playerDownsync.InAir ? 1 : 0}}`;
 | 
			
		||||
    return `{${playerDownsync.JoinIndex},${playerDownsync.VirtualGridX},${playerDownsync.VirtualGridY},${playerDownsync.VelX},${playerDownsync.VelY},${playerDownsync.FramesToRecover},${playerDownsync.InAir ? 1 : 0},${playerDownsync.OnWall ? 1 : 0}}`;
 | 
			
		||||
  },
 | 
			
		||||
 | 
			
		||||
  fireballDownsyncStr(fireball) {
 | 
			
		||||
    if (null == fireball) return "";
 | 
			
		||||
    return `{${fireball.Bullet.BulletLocalId},${fireball.Bullet.OriginatedRenderFrameId},${fireball.Bullet.OffenderJoinIndex},${fireball.VirtualGridX},${fireball.VirtualGridY},${fireball.VelX},${fireball.VelY},${fireball.DirX},${fireball.DirY},${fireball.Bullet.HitboxSizeX},${fireball.Bullet.HitboxSizeY}}`;
 | 
			
		||||
  },
 | 
			
		||||
 | 
			
		||||
  inputFrameDownsyncStr(inputFrameDownsync) {
 | 
			
		||||
@@ -1292,8 +1300,13 @@ othersForcedDownsyncRenderFrame=${JSON.stringify(othersForcedDownsyncRenderFrame
 | 
			
		||||
      for (let k in rdf.PlayersArr) {
 | 
			
		||||
        playersStrBldr.push(self.playerDownsyncStr(rdf.PlayersArr[k]));
 | 
			
		||||
      }
 | 
			
		||||
      const fireballsStrBldr = [];
 | 
			
		||||
      for (let k in rdf.FireballBullets) {
 | 
			
		||||
        fireballsStrBldr.push(self.fireballDownsyncStr(rdf.FireballBullets[k]));
 | 
			
		||||
      }
 | 
			
		||||
      s.push(`rdfId:${i}
 | 
			
		||||
players:[${playersStrBldr.join(',')}]
 | 
			
		||||
fireballs:[${fireballsStrBldr.join(',')}]
 | 
			
		||||
actuallyUsedinputList:{${self.inputFrameDownsyncStr(actuallyUsedInputClone)}}`);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -94,7 +94,7 @@ cc.Class({
 | 
			
		||||
      const p2Vpos = gopkgs.WorldToVirtualGridPos(boundaryObjs.playerStartingPositions[1].x, boundaryObjs.playerStartingPositions[1].y);
 | 
			
		||||
      const colliderRadiusV = gopkgs.WorldToVirtualGridPos(12.0, 0);
 | 
			
		||||
 | 
			
		||||
      const speciesIdList = [1, 4096];
 | 
			
		||||
      const speciesIdList = [4096, 1];
 | 
			
		||||
      const chConfigsOrderedByJoinIndex = gopkgs.GetCharacterConfigsOrderedByJoinIndex(speciesIdList);
 | 
			
		||||
 | 
			
		||||
      const startRdf = window.pb.protos.RoomDownsyncFrame.create({
 | 
			
		||||
 
 | 
			
		||||
@@ -3,6 +3,7 @@ package battle
 | 
			
		||||
import (
 | 
			
		||||
	"math"
 | 
			
		||||
	"resolv"
 | 
			
		||||
	//"fmt"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
@@ -21,7 +22,7 @@ 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)  // in the count of render frames
 | 
			
		||||
	INPUT_DELAY_FRAMES = int32(4)  // 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"
 | 
			
		||||
 | 
			
		||||
@@ -360,22 +361,22 @@ func VirtualGridToPolygonColliderBLPos(vx, vy int32, halfBoundingW, halfBounding
 | 
			
		||||
 | 
			
		||||
func calcHardPushbacksNorms(joinIndex int32, currPlayerDownsync, thatPlayerInNextFrame *PlayerDownsync, playerCollider *resolv.Object, playerShape *resolv.ConvexPolygon, snapIntoPlatformOverlap float64, pEffPushback *Vec2D) *[]Vec2D {
 | 
			
		||||
	ret := make([]Vec2D, 0, 10) // no one would simultaneously have more than 5 hardPushbacks
 | 
			
		||||
    virtualGripToWall := float64(0) 
 | 
			
		||||
    if ATK_CHARACTER_STATE_ONWALL == currPlayerDownsync.CharacterState && 0 == thatPlayerInNextFrame.VelX && currPlayerDownsync.DirX == thatPlayerInNextFrame.DirX {
 | 
			
		||||
        /*
 | 
			
		||||
        I'm not sure whether this is a bug of "resolv_tailored" (maybe due to my changes), on the x-axis a playerCollider whose right edge reaches "1680.1" is not deemed collided with a side wall whose left edge is "1680.0", while the same extent of intersection is OK in y-axis. 
 | 
			
		||||
	virtualGripToWall := float64(0)
 | 
			
		||||
	if ATK_CHARACTER_STATE_ONWALL == currPlayerDownsync.CharacterState && 0 == thatPlayerInNextFrame.VelX && currPlayerDownsync.DirX == thatPlayerInNextFrame.DirX {
 | 
			
		||||
		/*
 | 
			
		||||
		   I'm not sure whether this is a bug of "resolv_tailored" (maybe due to my changes), on the x-axis a playerCollider whose right edge reaches "1680.1" is not deemed collided with a side wall whose left edge is "1680.0", while the same extent of intersection is OK in y-axis.
 | 
			
		||||
 | 
			
		||||
        The workaround here is to grant a "virtualGripToWall" in x-axis to guarantee that if 
 | 
			
		||||
        - "currPlayerDownsync" is on wall, and 
 | 
			
		||||
        - "thatPlayerInNextFrame.VelX" is 0 (i.e. no proactive move against the wall), and
 | 
			
		||||
        - there's no change in player facing direction 
 | 
			
		||||
        */
 | 
			
		||||
        xfac := float64(1)
 | 
			
		||||
        if 0 > thatPlayerInNextFrame.DirX {
 | 
			
		||||
            xfac = -xfac
 | 
			
		||||
        }
 | 
			
		||||
        virtualGripToWall = xfac*float64(currPlayerDownsync.Speed)*VIRTUAL_GRID_TO_WORLD_RATIO
 | 
			
		||||
    }
 | 
			
		||||
		   The workaround here is to grant a "virtualGripToWall" in x-axis to guarantee that if
 | 
			
		||||
		   - "currPlayerDownsync" is on wall, and
 | 
			
		||||
		   - "thatPlayerInNextFrame.VelX" is 0 (i.e. no proactive move against the wall), and
 | 
			
		||||
		   - there's no change in player facing direction
 | 
			
		||||
		*/
 | 
			
		||||
		xfac := float64(1)
 | 
			
		||||
		if 0 > thatPlayerInNextFrame.DirX {
 | 
			
		||||
			xfac = -xfac
 | 
			
		||||
		}
 | 
			
		||||
		virtualGripToWall = xfac * float64(currPlayerDownsync.Speed) * VIRTUAL_GRID_TO_WORLD_RATIO
 | 
			
		||||
	}
 | 
			
		||||
	collision := playerCollider.Check(virtualGripToWall, 0)
 | 
			
		||||
	if nil == collision {
 | 
			
		||||
		return &ret
 | 
			
		||||
@@ -498,8 +499,8 @@ func ApplyInputFrameDownsyncDynamicsOnSingleRenderFrame(inputsBuffer *RingBuffer
 | 
			
		||||
			ActiveSkillHit:   currPlayerDownsync.ActiveSkillHit,
 | 
			
		||||
			FramesInvinsible: currPlayerDownsync.FramesInvinsible - 1,
 | 
			
		||||
			ColliderRadius:   currPlayerDownsync.ColliderRadius,
 | 
			
		||||
            OnWallNormX:      currPlayerDownsync.OnWallNormX, 
 | 
			
		||||
            OnWallNormY:      currPlayerDownsync.OnWallNormY,
 | 
			
		||||
			OnWallNormX:      currPlayerDownsync.OnWallNormX,
 | 
			
		||||
			OnWallNormY:      currPlayerDownsync.OnWallNormY,
 | 
			
		||||
		}
 | 
			
		||||
		if nextRenderFramePlayers[i].FramesToRecover < 0 {
 | 
			
		||||
			nextRenderFramePlayers[i].FramesToRecover = 0
 | 
			
		||||
@@ -509,6 +510,7 @@ func ApplyInputFrameDownsyncDynamicsOnSingleRenderFrame(inputsBuffer *RingBuffer
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// [WARNING] For rollback compatibility, MeleeBullets are composed of only static BulletConfig data and move along with the offenders, therefore they can just be copies of the pointers in "RenderFrameBuffer", however, FireballBullets move on their own and must be copies of instances for each RenderFrame!
 | 
			
		||||
	nextRenderFrameMeleeBullets := make([]*MeleeBullet, 0, len(currRenderFrame.MeleeBullets)) // Is there any better way to reduce malloc/free impact, e.g. smart prediction for fixed memory allocation?
 | 
			
		||||
	nextRenderFrameFireballBullets := make([]*FireballBullet, 0, len(currRenderFrame.FireballBullets))
 | 
			
		||||
	effPushbacks := make([]Vec2D, roomCapacity)
 | 
			
		||||
@@ -539,38 +541,39 @@ func ApplyInputFrameDownsyncDynamicsOnSingleRenderFrame(inputsBuffer *RingBuffer
 | 
			
		||||
			switch v := skillConfig.Hits[thatPlayerInNextFrame.ActiveSkillHit].(type) {
 | 
			
		||||
			case *MeleeBullet:
 | 
			
		||||
				var newBullet MeleeBullet = *v // Copied primitive fields into an onstack variable
 | 
			
		||||
				newBullet.BulletLocalId = bulletLocalId
 | 
			
		||||
				newBullet.Bullet.BulletLocalId = bulletLocalId
 | 
			
		||||
				bulletLocalId++
 | 
			
		||||
				newBullet.OriginatedRenderFrameId = currRenderFrame.Id
 | 
			
		||||
				newBullet.OffenderJoinIndex = joinIndex
 | 
			
		||||
				newBullet.Bullet.OriginatedRenderFrameId = currRenderFrame.Id
 | 
			
		||||
				newBullet.Bullet.OffenderJoinIndex = joinIndex
 | 
			
		||||
				nextRenderFrameMeleeBullets = append(nextRenderFrameMeleeBullets, &newBullet)
 | 
			
		||||
				if NO_LOCK_VEL != v.SelfLockVelX {
 | 
			
		||||
				if NO_LOCK_VEL != v.Bullet.SelfLockVelX {
 | 
			
		||||
					hasLockVel = true
 | 
			
		||||
					thatPlayerInNextFrame.VelX = xfac * v.SelfLockVelX
 | 
			
		||||
					thatPlayerInNextFrame.VelX = xfac * v.Bullet.SelfLockVelX
 | 
			
		||||
				}
 | 
			
		||||
				if NO_LOCK_VEL != v.SelfLockVelY {
 | 
			
		||||
				if NO_LOCK_VEL != v.Bullet.SelfLockVelY {
 | 
			
		||||
					hasLockVel = true
 | 
			
		||||
					thatPlayerInNextFrame.VelY = v.SelfLockVelY
 | 
			
		||||
					thatPlayerInNextFrame.VelY = v.Bullet.SelfLockVelY
 | 
			
		||||
				}
 | 
			
		||||
			case *FireballBullet:
 | 
			
		||||
				var newBullet FireballBullet = *v // Copied primitive fields into an onstack variable
 | 
			
		||||
				newBullet.BulletLocalId = bulletLocalId
 | 
			
		||||
				newBullet.Bullet.BulletLocalId = bulletLocalId
 | 
			
		||||
				bulletLocalId++
 | 
			
		||||
				newBullet.VirtualGridX, newBullet.VirtualGridY = currPlayerDownsync.VirtualGridX+xfac*newBullet.HitboxOffsetX, currPlayerDownsync.VirtualGridY+newBullet.HitboxOffsetY
 | 
			
		||||
				newBullet.OriginatedRenderFrameId = currRenderFrame.Id
 | 
			
		||||
				newBullet.OffenderJoinIndex = joinIndex
 | 
			
		||||
				newBullet.Bullet.OriginatedRenderFrameId = currRenderFrame.Id
 | 
			
		||||
				newBullet.Bullet.OffenderJoinIndex = joinIndex
 | 
			
		||||
				newBullet.VirtualGridX, newBullet.VirtualGridY = currPlayerDownsync.VirtualGridX+xfac*newBullet.Bullet.HitboxOffsetX, currPlayerDownsync.VirtualGridY+newBullet.Bullet.HitboxOffsetY
 | 
			
		||||
				newBullet.DirX = xfac
 | 
			
		||||
				newBullet.DirY = 0
 | 
			
		||||
				newBullet.VelX = newBullet.Speed * xfac
 | 
			
		||||
				newBullet.VelY = 0
 | 
			
		||||
				nextRenderFrameFireballBullets = append(nextRenderFrameFireballBullets, &newBullet)
 | 
			
		||||
				if NO_LOCK_VEL != v.SelfLockVelX {
 | 
			
		||||
				//fmt.Printf("Created new fireball @currRenderFrame.Id=%d, %p, bulletLocalId=%d, virtualGridX=%d, virtualGridY=%d, offenderVpos=(%d,%d)\n", currRenderFrame.Id, &newBullet, bulletLocalId, newBullet.VirtualGridX, newBullet.VirtualGridY, currPlayerDownsync.VirtualGridX, currPlayerDownsync.VirtualGridY)
 | 
			
		||||
				if NO_LOCK_VEL != v.Bullet.SelfLockVelX {
 | 
			
		||||
					hasLockVel = true
 | 
			
		||||
					thatPlayerInNextFrame.VelX = xfac * v.SelfLockVelX
 | 
			
		||||
					thatPlayerInNextFrame.VelX = xfac * v.Bullet.SelfLockVelX
 | 
			
		||||
				}
 | 
			
		||||
				if NO_LOCK_VEL != v.SelfLockVelY {
 | 
			
		||||
				if NO_LOCK_VEL != v.Bullet.SelfLockVelY {
 | 
			
		||||
					hasLockVel = true
 | 
			
		||||
					thatPlayerInNextFrame.VelY = v.SelfLockVelY
 | 
			
		||||
					thatPlayerInNextFrame.VelY = v.Bullet.SelfLockVelY
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
@@ -616,12 +619,12 @@ func ApplyInputFrameDownsyncDynamicsOnSingleRenderFrame(inputsBuffer *RingBuffer
 | 
			
		||||
		if jumpedOrNotList[i] {
 | 
			
		||||
			// We haven't proceeded with "OnWall" calculation for "thatPlayerInNextFrame", thus use "currPlayerDownsync.OnWall" for checking
 | 
			
		||||
			if ATK_CHARACTER_STATE_ONWALL == currPlayerDownsync.CharacterState {
 | 
			
		||||
                if 0 < currPlayerDownsync.VelX * currPlayerDownsync.OnWallNormX {
 | 
			
		||||
                    newVx -= currPlayerDownsync.VelX // Cancel the alleged horizontal movement pointing to same direction of wall inward norm first
 | 
			
		||||
                }
 | 
			
		||||
				if 0 < currPlayerDownsync.VelX*currPlayerDownsync.OnWallNormX {
 | 
			
		||||
					newVx -= currPlayerDownsync.VelX // Cancel the alleged horizontal movement pointing to same direction of wall inward norm first
 | 
			
		||||
				}
 | 
			
		||||
				xfac := int32(-1)
 | 
			
		||||
				if 0 > currPlayerDownsync.OnWallNormX {
 | 
			
		||||
                    // Always jump to the opposite direction of wall inward norm
 | 
			
		||||
					// Always jump to the opposite direction of wall inward norm
 | 
			
		||||
					xfac = -xfac
 | 
			
		||||
				}
 | 
			
		||||
				newVx += xfac * chConfig.WallJumpingInitVelX
 | 
			
		||||
@@ -666,31 +669,43 @@ func ApplyInputFrameDownsyncDynamicsOnSingleRenderFrame(inputsBuffer *RingBuffer
 | 
			
		||||
	// 3. Add bullet colliders into collision system
 | 
			
		||||
	bulletColliders := make([]*resolv.Object, 0, len(currRenderFrame.MeleeBullets)) // Will all be removed at the end of this function due to the need for being rollback-compatible
 | 
			
		||||
	for _, meleeBullet := range currRenderFrame.MeleeBullets {
 | 
			
		||||
		if (meleeBullet.OriginatedRenderFrameId+meleeBullet.StartupFrames <= currRenderFrame.Id) && (meleeBullet.OriginatedRenderFrameId+meleeBullet.StartupFrames+meleeBullet.ActiveFrames > currRenderFrame.Id) {
 | 
			
		||||
			offender := currRenderFrame.PlayersArr[meleeBullet.OffenderJoinIndex-1]
 | 
			
		||||
		if (meleeBullet.Bullet.OriginatedRenderFrameId+meleeBullet.Bullet.StartupFrames <= currRenderFrame.Id) && (meleeBullet.Bullet.OriginatedRenderFrameId+meleeBullet.Bullet.StartupFrames+meleeBullet.Bullet.ActiveFrames > currRenderFrame.Id) {
 | 
			
		||||
			offender := currRenderFrame.PlayersArr[meleeBullet.Bullet.OffenderJoinIndex-1]
 | 
			
		||||
 | 
			
		||||
			xfac := int32(1) // By now, straight Punch offset doesn't respect "y-axis"
 | 
			
		||||
			if 0 > offender.DirX {
 | 
			
		||||
				xfac = -xfac
 | 
			
		||||
			}
 | 
			
		||||
			bulletWx, bulletWy := VirtualGridToWorldPos(offender.VirtualGridX+xfac*meleeBullet.HitboxOffsetX, offender.VirtualGridY)
 | 
			
		||||
			hitboxSizeWx, hitboxSizeWy := VirtualGridToWorldPos(meleeBullet.HitboxSizeX, meleeBullet.HitboxSizeY)
 | 
			
		||||
			bulletWx, bulletWy := VirtualGridToWorldPos(offender.VirtualGridX+xfac*meleeBullet.Bullet.HitboxOffsetX, offender.VirtualGridY)
 | 
			
		||||
			hitboxSizeWx, hitboxSizeWy := VirtualGridToWorldPos(meleeBullet.Bullet.HitboxSizeX, meleeBullet.Bullet.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 if meleeBullet.OriginatedRenderFrameId+meleeBullet.StartupFrames+meleeBullet.ActiveFrames > currRenderFrame.Id {
 | 
			
		||||
		} else if meleeBullet.Bullet.OriginatedRenderFrameId+meleeBullet.Bullet.StartupFrames+meleeBullet.Bullet.ActiveFrames > currRenderFrame.Id {
 | 
			
		||||
			nextRenderFrameMeleeBullets = append(nextRenderFrameMeleeBullets, meleeBullet)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for _, fireballBullet := range currRenderFrame.FireballBullets {
 | 
			
		||||
		if (fireballBullet.OriginatedRenderFrameId+fireballBullet.StartupFrames < currRenderFrame.Id) && (fireballBullet.OriginatedRenderFrameId+fireballBullet.StartupFrames+fireballBullet.ActiveFrames > currRenderFrame.Id) {
 | 
			
		||||
	for _, prevFireball := range currRenderFrame.FireballBullets {
 | 
			
		||||
		fireballBullet := &FireballBullet{
 | 
			
		||||
			VirtualGridX: prevFireball.VirtualGridX,
 | 
			
		||||
			VirtualGridY: prevFireball.VirtualGridY,
 | 
			
		||||
			DirX:         prevFireball.DirX,
 | 
			
		||||
			DirY:         prevFireball.DirY,
 | 
			
		||||
			VelX:         prevFireball.VelX,
 | 
			
		||||
			VelY:         prevFireball.VelY,
 | 
			
		||||
			Speed:        prevFireball.Speed,
 | 
			
		||||
			SpeciesId:    prevFireball.SpeciesId,
 | 
			
		||||
			Bullet:       prevFireball.Bullet,
 | 
			
		||||
		}
 | 
			
		||||
		if (fireballBullet.Bullet.OriginatedRenderFrameId+fireballBullet.Bullet.StartupFrames < currRenderFrame.Id) && (fireballBullet.Bullet.OriginatedRenderFrameId+fireballBullet.Bullet.StartupFrames+fireballBullet.Bullet.ActiveFrames > currRenderFrame.Id) {
 | 
			
		||||
			bulletWx, bulletWy := VirtualGridToWorldPos(fireballBullet.VirtualGridX, fireballBullet.VirtualGridY)
 | 
			
		||||
			hitboxSizeWx, hitboxSizeWy := VirtualGridToWorldPos(fireballBullet.HitboxSizeX, fireballBullet.HitboxSizeY)
 | 
			
		||||
			hitboxSizeWx, hitboxSizeWy := VirtualGridToWorldPos(fireballBullet.Bullet.HitboxSizeX, fireballBullet.Bullet.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, fireballBullet, "FireballBullet")
 | 
			
		||||
			collisionSys.Add(newBulletCollider)
 | 
			
		||||
			bulletColliders = append(bulletColliders, newBulletCollider)
 | 
			
		||||
		} else if fireballBullet.OriginatedRenderFrameId+fireballBullet.StartupFrames+fireballBullet.ActiveFrames > currRenderFrame.Id {
 | 
			
		||||
		} else if fireballBullet.Bullet.OriginatedRenderFrameId+fireballBullet.Bullet.StartupFrames+fireballBullet.Bullet.ActiveFrames > currRenderFrame.Id {
 | 
			
		||||
			// fmt.Printf("Pushing static fireball to next frame @currRenderFrame.Id=%d, bulletLocalId=%d, virtualGridX=%d, virtualGridY=%d\n", currRenderFrame.Id, fireballBullet.BulletLocalId, fireballBullet.VirtualGridX, fireballBullet.VirtualGridY)
 | 
			
		||||
			nextRenderFrameFireballBullets = append(nextRenderFrameFireballBullets, fireballBullet)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
@@ -786,36 +801,36 @@ func ApplyInputFrameDownsyncDynamicsOnSingleRenderFrame(inputsBuffer *RingBuffer
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
        if chConfig.OnWallEnabled {
 | 
			
		||||
            if thatPlayerInNextFrame.InAir {
 | 
			
		||||
                // [WARNING] Sticking to wall MUST BE based on "InAir", otherwise we would get gravity reduction from ground up incorrectly!
 | 
			
		||||
                if _, existent := noOpSet[currPlayerDownsync.CharacterState]; !existent {
 | 
			
		||||
                    // [WARNING] Sticking to wall could only be triggered by proactive player input
 | 
			
		||||
                    for _, hardPushbackNorm := range *hardPushbackNorms[joinIndex-1] {
 | 
			
		||||
                        normAlignmentWithHorizon1 := (hardPushbackNorm.X*float64(1.0) + hardPushbackNorm.Y*float64(0.0))
 | 
			
		||||
                        normAlignmentWithHorizon2 := (hardPushbackNorm.X*float64(-1.0) + hardPushbackNorm.Y*float64(0.0))
 | 
			
		||||
                        if VERTICAL_PLATFORM_THRESHOLD < normAlignmentWithHorizon1 {
 | 
			
		||||
                            thatPlayerInNextFrame.OnWall = true
 | 
			
		||||
                            thatPlayerInNextFrame.OnWallNormX, thatPlayerInNextFrame.OnWallNormY = int32(hardPushbackNorm.X), int32(hardPushbackNorm.Y)
 | 
			
		||||
                            break
 | 
			
		||||
                        }
 | 
			
		||||
                        if VERTICAL_PLATFORM_THRESHOLD < normAlignmentWithHorizon2 {
 | 
			
		||||
                            thatPlayerInNextFrame.OnWall = true
 | 
			
		||||
                            thatPlayerInNextFrame.OnWallNormX, thatPlayerInNextFrame.OnWallNormY = int32(hardPushbackNorm.X), int32(hardPushbackNorm.Y)
 | 
			
		||||
                            break
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
		if chConfig.OnWallEnabled {
 | 
			
		||||
			if thatPlayerInNextFrame.InAir {
 | 
			
		||||
				// [WARNING] Sticking to wall MUST BE based on "InAir", otherwise we would get gravity reduction from ground up incorrectly!
 | 
			
		||||
				if _, existent := noOpSet[currPlayerDownsync.CharacterState]; !existent {
 | 
			
		||||
					// [WARNING] Sticking to wall could only be triggered by proactive player input
 | 
			
		||||
					for _, hardPushbackNorm := range *hardPushbackNorms[joinIndex-1] {
 | 
			
		||||
						normAlignmentWithHorizon1 := (hardPushbackNorm.X*float64(1.0) + hardPushbackNorm.Y*float64(0.0))
 | 
			
		||||
						normAlignmentWithHorizon2 := (hardPushbackNorm.X*float64(-1.0) + hardPushbackNorm.Y*float64(0.0))
 | 
			
		||||
						if VERTICAL_PLATFORM_THRESHOLD < normAlignmentWithHorizon1 {
 | 
			
		||||
							thatPlayerInNextFrame.OnWall = true
 | 
			
		||||
							thatPlayerInNextFrame.OnWallNormX, thatPlayerInNextFrame.OnWallNormY = int32(hardPushbackNorm.X), int32(hardPushbackNorm.Y)
 | 
			
		||||
							break
 | 
			
		||||
						}
 | 
			
		||||
						if VERTICAL_PLATFORM_THRESHOLD < normAlignmentWithHorizon2 {
 | 
			
		||||
							thatPlayerInNextFrame.OnWall = true
 | 
			
		||||
							thatPlayerInNextFrame.OnWallNormX, thatPlayerInNextFrame.OnWallNormY = int32(hardPushbackNorm.X), int32(hardPushbackNorm.Y)
 | 
			
		||||
							break
 | 
			
		||||
						}
 | 
			
		||||
					}
 | 
			
		||||
 | 
			
		||||
                    if !currPlayerDownsync.OnWall && thatPlayerInNextFrame.OnWall {
 | 
			
		||||
                        // To avoid mysterious climbing up the wall after sticking on it
 | 
			
		||||
                        thatPlayerInNextFrame.VelY = 0
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            if !thatPlayerInNextFrame.OnWall {
 | 
			
		||||
                thatPlayerInNextFrame.OnWallNormX, thatPlayerInNextFrame.OnWallNormY = 0, 0
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
					if !currPlayerDownsync.OnWall && thatPlayerInNextFrame.OnWall {
 | 
			
		||||
						// To avoid mysterious climbing up the wall after sticking on it
 | 
			
		||||
						thatPlayerInNextFrame.VelY = 0
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			if !thatPlayerInNextFrame.OnWall {
 | 
			
		||||
				thatPlayerInNextFrame.OnWallNormX, thatPlayerInNextFrame.OnWallNormY = 0, 0
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@@ -828,12 +843,12 @@ func ApplyInputFrameDownsyncDynamicsOnSingleRenderFrame(inputsBuffer *RingBuffer
 | 
			
		||||
			switch v := bulletCollider.Data.(type) {
 | 
			
		||||
			case *MeleeBullet:
 | 
			
		||||
				bulletShape := bulletCollider.Shape.(*resolv.ConvexPolygon)
 | 
			
		||||
				offender := currRenderFrame.PlayersArr[v.OffenderJoinIndex-1]
 | 
			
		||||
				offender := currRenderFrame.PlayersArr[v.Bullet.OffenderJoinIndex-1]
 | 
			
		||||
				for _, obj := range collision.Objects {
 | 
			
		||||
					defenderShape := obj.Shape.(*resolv.ConvexPolygon)
 | 
			
		||||
					switch t := obj.Data.(type) {
 | 
			
		||||
					case *PlayerDownsync:
 | 
			
		||||
						if v.OffenderJoinIndex == t.JoinIndex {
 | 
			
		||||
						if v.Bullet.OffenderJoinIndex == t.JoinIndex {
 | 
			
		||||
							continue
 | 
			
		||||
						}
 | 
			
		||||
						overlapped, _, _, _ := calcPushbacks(0, 0, bulletShape, defenderShape)
 | 
			
		||||
@@ -851,18 +866,18 @@ func ApplyInputFrameDownsyncDynamicsOnSingleRenderFrame(inputsBuffer *RingBuffer
 | 
			
		||||
						if 0 > offender.DirX {
 | 
			
		||||
							xfac = -xfac
 | 
			
		||||
						}
 | 
			
		||||
						pushbackVelX, pushbackVelY := xfac*v.PushbackVelX, v.PushbackVelY
 | 
			
		||||
						pushbackVelX, pushbackVelY := xfac*v.Bullet.PushbackVelX, v.Bullet.PushbackVelY
 | 
			
		||||
						atkedPlayerInNextFrame := nextRenderFramePlayers[t.JoinIndex-1]
 | 
			
		||||
						atkedPlayerInNextFrame.VelX = pushbackVelX
 | 
			
		||||
						atkedPlayerInNextFrame.VelY = pushbackVelY
 | 
			
		||||
						if v.BlowUp {
 | 
			
		||||
						if v.Bullet.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
 | 
			
		||||
						if v.Bullet.HitStunFrames > oldFramesToRecover {
 | 
			
		||||
							atkedPlayerInNextFrame.FramesToRecover = v.Bullet.HitStunFrames
 | 
			
		||||
						}
 | 
			
		||||
					default:
 | 
			
		||||
						addToNextRenderFrame = false
 | 
			
		||||
@@ -870,12 +885,12 @@ func ApplyInputFrameDownsyncDynamicsOnSingleRenderFrame(inputsBuffer *RingBuffer
 | 
			
		||||
				}
 | 
			
		||||
			case *FireballBullet:
 | 
			
		||||
				bulletShape := bulletCollider.Shape.(*resolv.ConvexPolygon)
 | 
			
		||||
				offender := currRenderFrame.PlayersArr[v.OffenderJoinIndex-1]
 | 
			
		||||
				offender := currRenderFrame.PlayersArr[v.Bullet.OffenderJoinIndex-1]
 | 
			
		||||
				for _, obj := range collision.Objects {
 | 
			
		||||
					defenderShape := obj.Shape.(*resolv.ConvexPolygon)
 | 
			
		||||
					switch t := obj.Data.(type) {
 | 
			
		||||
					case *PlayerDownsync:
 | 
			
		||||
						if v.OffenderJoinIndex == t.JoinIndex {
 | 
			
		||||
						if v.Bullet.OffenderJoinIndex == t.JoinIndex {
 | 
			
		||||
							continue
 | 
			
		||||
						}
 | 
			
		||||
						overlapped, _, _, _ := calcPushbacks(0, 0, bulletShape, defenderShape)
 | 
			
		||||
@@ -893,18 +908,18 @@ func ApplyInputFrameDownsyncDynamicsOnSingleRenderFrame(inputsBuffer *RingBuffer
 | 
			
		||||
						if 0 > offender.DirX {
 | 
			
		||||
							xfac = -xfac
 | 
			
		||||
						}
 | 
			
		||||
						pushbackVelX, pushbackVelY := xfac*v.PushbackVelX, v.PushbackVelY
 | 
			
		||||
						pushbackVelX, pushbackVelY := xfac*v.Bullet.PushbackVelX, v.Bullet.PushbackVelY
 | 
			
		||||
						atkedPlayerInNextFrame := nextRenderFramePlayers[t.JoinIndex-1]
 | 
			
		||||
						atkedPlayerInNextFrame.VelX = pushbackVelX
 | 
			
		||||
						atkedPlayerInNextFrame.VelY = pushbackVelY
 | 
			
		||||
						if v.BlowUp {
 | 
			
		||||
						if v.Bullet.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
 | 
			
		||||
						if v.Bullet.HitStunFrames > oldFramesToRecover {
 | 
			
		||||
							atkedPlayerInNextFrame.FramesToRecover = v.Bullet.HitStunFrames
 | 
			
		||||
						}
 | 
			
		||||
					default:
 | 
			
		||||
						addToNextRenderFrame = false
 | 
			
		||||
@@ -1053,3 +1068,77 @@ func AlignPolygon2DToBoundingBox(input *Polygon2D) *Polygon2D {
 | 
			
		||||
 | 
			
		||||
	return output
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func NewMeleeBullet(bulletLocalId, originatedRenderFrameId, offenderJoinIndex, startupFrames, cancellableStFrame, cancellableEdFrame, activeFrames, hitStunFrames, blockStunFrames, pushbackVelX, pushbackVelY, damage, selfLockVelX, selfLockVelY, hitboxOffsetX, hitboxOffsetY, hitboxSizeX, hitboxSizeY int32, blowUp bool, teamId int32) *MeleeBullet {
 | 
			
		||||
	return &MeleeBullet{
 | 
			
		||||
		Bullet: &BulletConfig{
 | 
			
		||||
			BulletLocalId:           bulletLocalId,
 | 
			
		||||
			OriginatedRenderFrameId: originatedRenderFrameId,
 | 
			
		||||
			OffenderJoinIndex:       offenderJoinIndex,
 | 
			
		||||
 | 
			
		||||
			StartupFrames:      startupFrames,
 | 
			
		||||
			CancellableStFrame: cancellableStFrame,
 | 
			
		||||
			CancellableEdFrame: cancellableEdFrame,
 | 
			
		||||
			ActiveFrames:       activeFrames,
 | 
			
		||||
 | 
			
		||||
			HitStunFrames:   hitStunFrames,
 | 
			
		||||
			BlockStunFrames: blockStunFrames,
 | 
			
		||||
			PushbackVelX:    pushbackVelX,
 | 
			
		||||
			PushbackVelY:    pushbackVelY,
 | 
			
		||||
			Damage:          damage,
 | 
			
		||||
 | 
			
		||||
			SelfLockVelX: selfLockVelX,
 | 
			
		||||
			SelfLockVelY: selfLockVelY,
 | 
			
		||||
 | 
			
		||||
			HitboxOffsetX: hitboxOffsetX,
 | 
			
		||||
			HitboxOffsetY: hitboxOffsetY,
 | 
			
		||||
			HitboxSizeX:   hitboxSizeX,
 | 
			
		||||
			HitboxSizeY:   hitboxSizeY,
 | 
			
		||||
 | 
			
		||||
			BlowUp: blowUp,
 | 
			
		||||
 | 
			
		||||
			TeamId: teamId,
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func NewFireballBullet(bulletLocalId, originatedRenderFrameId, offenderJoinIndex, startupFrames, cancellableStFrame, cancellableEdFrame, activeFrames, hitStunFrames, blockStunFrames, pushbackVelX, pushbackVelY, damage, selfLockVelX, selfLockVelY, hitboxOffsetX, hitboxOffsetY, hitboxSizeX, hitboxSizeY int32, blowUp bool, teamId int32, virtualGridX, virtualGridY, dirX, dirY, velX, velY, speed, speciesId int32) *FireballBullet {
 | 
			
		||||
	return &FireballBullet{
 | 
			
		||||
		VirtualGridX: virtualGridX,
 | 
			
		||||
		VirtualGridY: virtualGridY,
 | 
			
		||||
		DirX:         dirX,
 | 
			
		||||
		DirY:         dirY,
 | 
			
		||||
		VelX:         velX,
 | 
			
		||||
		VelY:         velY,
 | 
			
		||||
		Speed:        speed,
 | 
			
		||||
		SpeciesId:    speciesId,
 | 
			
		||||
		Bullet: &BulletConfig{
 | 
			
		||||
			BulletLocalId:           bulletLocalId,
 | 
			
		||||
			OriginatedRenderFrameId: originatedRenderFrameId,
 | 
			
		||||
			OffenderJoinIndex:       offenderJoinIndex,
 | 
			
		||||
 | 
			
		||||
			StartupFrames:      startupFrames,
 | 
			
		||||
			CancellableStFrame: cancellableStFrame,
 | 
			
		||||
			CancellableEdFrame: cancellableEdFrame,
 | 
			
		||||
			ActiveFrames:       activeFrames,
 | 
			
		||||
 | 
			
		||||
			HitStunFrames:   hitStunFrames,
 | 
			
		||||
			BlockStunFrames: blockStunFrames,
 | 
			
		||||
			PushbackVelX:    pushbackVelX,
 | 
			
		||||
			PushbackVelY:    pushbackVelY,
 | 
			
		||||
			Damage:          damage,
 | 
			
		||||
 | 
			
		||||
			SelfLockVelX: selfLockVelX,
 | 
			
		||||
			SelfLockVelY: selfLockVelY,
 | 
			
		||||
 | 
			
		||||
			HitboxOffsetX: hitboxOffsetX,
 | 
			
		||||
			HitboxOffsetY: hitboxOffsetY,
 | 
			
		||||
			HitboxSizeX:   hitboxSizeX,
 | 
			
		||||
			HitboxSizeY:   hitboxSizeY,
 | 
			
		||||
 | 
			
		||||
			BlowUp: blowUp,
 | 
			
		||||
 | 
			
		||||
			TeamId: teamId,
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -61,8 +61,8 @@ var Characters = map[int]*CharacterConfig{
 | 
			
		||||
					if skillConfig, existent1 := skills[int(currPlayerDownsync.ActiveSkillId)]; existent1 {
 | 
			
		||||
						switch v := skillConfig.Hits[currPlayerDownsync.ActiveSkillHit].(type) {
 | 
			
		||||
						case *MeleeBullet:
 | 
			
		||||
							if v.CancellableStFrame <= currPlayerDownsync.FramesInChState && currPlayerDownsync.FramesInChState < v.CancellableEdFrame {
 | 
			
		||||
								if nextSkillId, existent2 := v.CancelTransit[patternId]; existent2 {
 | 
			
		||||
							if v.Bullet.CancellableStFrame <= currPlayerDownsync.FramesInChState && currPlayerDownsync.FramesInChState < v.Bullet.CancellableEdFrame {
 | 
			
		||||
								if nextSkillId, existent2 := v.Bullet.CancelTransit[patternId]; existent2 {
 | 
			
		||||
									return nextSkillId
 | 
			
		||||
								}
 | 
			
		||||
							}
 | 
			
		||||
@@ -93,7 +93,7 @@ var Characters = map[int]*CharacterConfig{
 | 
			
		||||
 | 
			
		||||
		DashingEnabled:             true,
 | 
			
		||||
		OnWallEnabled:              true,
 | 
			
		||||
		WallJumpingFramesToRecover: int32(9), // 8 would be the minimum for an avg human
 | 
			
		||||
		WallJumpingFramesToRecover: int32(9),                                          // 8 would be the minimum for an avg human
 | 
			
		||||
		WallJumpingInitVelX:        int32(float64(2.5) * WORLD_TO_VIRTUAL_GRID_RATIO), // Default is "appeared facing right", but actually holding ctrl against left
 | 
			
		||||
		WallJumpingInitVelY:        int32(float64(7) * WORLD_TO_VIRTUAL_GRID_RATIO),
 | 
			
		||||
		WallSlidingVelY:            int32(float64(-1) * WORLD_TO_VIRTUAL_GRID_RATIO),
 | 
			
		||||
@@ -111,8 +111,8 @@ var Characters = map[int]*CharacterConfig{
 | 
			
		||||
					if skillConfig, existent1 := skills[int(currPlayerDownsync.ActiveSkillId)]; existent1 {
 | 
			
		||||
						switch v := skillConfig.Hits[currPlayerDownsync.ActiveSkillHit].(type) {
 | 
			
		||||
						case *MeleeBullet:
 | 
			
		||||
							if v.CancellableStFrame <= currPlayerDownsync.FramesInChState && currPlayerDownsync.FramesInChState < v.CancellableEdFrame {
 | 
			
		||||
								if nextSkillId, existent2 := v.CancelTransit[patternId]; existent2 {
 | 
			
		||||
							if v.Bullet.CancellableStFrame <= currPlayerDownsync.FramesInChState && currPlayerDownsync.FramesInChState < v.Bullet.CancellableEdFrame {
 | 
			
		||||
								if nextSkillId, existent2 := v.Bullet.CancelTransit[patternId]; existent2 {
 | 
			
		||||
									return nextSkillId
 | 
			
		||||
								}
 | 
			
		||||
							}
 | 
			
		||||
@@ -157,8 +157,8 @@ var Characters = map[int]*CharacterConfig{
 | 
			
		||||
					if skillConfig, existent1 := skills[int(currPlayerDownsync.ActiveSkillId)]; existent1 {
 | 
			
		||||
						switch v := skillConfig.Hits[currPlayerDownsync.ActiveSkillHit].(type) {
 | 
			
		||||
						case *MeleeBullet:
 | 
			
		||||
							if v.CancellableStFrame <= currPlayerDownsync.FramesInChState && currPlayerDownsync.FramesInChState < v.CancellableEdFrame {
 | 
			
		||||
								if nextSkillId, existent2 := v.CancelTransit[patternId]; existent2 {
 | 
			
		||||
							if v.Bullet.CancellableStFrame <= currPlayerDownsync.FramesInChState && currPlayerDownsync.FramesInChState < v.Bullet.CancellableEdFrame {
 | 
			
		||||
								if nextSkillId, existent2 := v.Bullet.CancelTransit[patternId]; existent2 {
 | 
			
		||||
									return nextSkillId
 | 
			
		||||
								}
 | 
			
		||||
							}
 | 
			
		||||
@@ -190,7 +190,7 @@ var skills = map[int]*Skill{
 | 
			
		||||
		BoundChState:          ATK_CHARACTER_STATE_ATK1,
 | 
			
		||||
		Hits: []interface{}{
 | 
			
		||||
			&MeleeBullet{
 | 
			
		||||
				Bullet: Bullet{
 | 
			
		||||
				Bullet: &BulletConfig{
 | 
			
		||||
					StartupFrames:      int32(7),
 | 
			
		||||
					ActiveFrames:       int32(22),
 | 
			
		||||
					HitStunFrames:      int32(13),
 | 
			
		||||
@@ -223,7 +223,7 @@ var skills = map[int]*Skill{
 | 
			
		||||
		BoundChState:          ATK_CHARACTER_STATE_ATK2,
 | 
			
		||||
		Hits: []interface{}{
 | 
			
		||||
			&MeleeBullet{
 | 
			
		||||
				Bullet: Bullet{
 | 
			
		||||
				Bullet: &BulletConfig{
 | 
			
		||||
					StartupFrames:      int32(18),
 | 
			
		||||
					ActiveFrames:       int32(18),
 | 
			
		||||
					HitStunFrames:      int32(18),
 | 
			
		||||
@@ -254,7 +254,7 @@ var skills = map[int]*Skill{
 | 
			
		||||
		BoundChState:          ATK_CHARACTER_STATE_ATK3,
 | 
			
		||||
		Hits: []interface{}{
 | 
			
		||||
			&MeleeBullet{
 | 
			
		||||
				Bullet: Bullet{
 | 
			
		||||
				Bullet: &BulletConfig{
 | 
			
		||||
					StartupFrames:   int32(8),
 | 
			
		||||
					ActiveFrames:    int32(30),
 | 
			
		||||
					HitStunFrames:   MAX_INT32,
 | 
			
		||||
@@ -281,7 +281,7 @@ var skills = map[int]*Skill{
 | 
			
		||||
		BoundChState:          ATK_CHARACTER_STATE_ATK1,
 | 
			
		||||
		Hits: []interface{}{
 | 
			
		||||
			&MeleeBullet{
 | 
			
		||||
				Bullet: Bullet{
 | 
			
		||||
				Bullet: &BulletConfig{
 | 
			
		||||
					StartupFrames:      int32(7),
 | 
			
		||||
					ActiveFrames:       int32(22),
 | 
			
		||||
					HitStunFrames:      int32(13),
 | 
			
		||||
@@ -314,7 +314,7 @@ var skills = map[int]*Skill{
 | 
			
		||||
		BoundChState:          ATK_CHARACTER_STATE_ATK2,
 | 
			
		||||
		Hits: []interface{}{
 | 
			
		||||
			&MeleeBullet{
 | 
			
		||||
				Bullet: Bullet{
 | 
			
		||||
				Bullet: &BulletConfig{
 | 
			
		||||
					StartupFrames:      int32(18),
 | 
			
		||||
					ActiveFrames:       int32(18),
 | 
			
		||||
					HitStunFrames:      int32(18),
 | 
			
		||||
@@ -345,7 +345,7 @@ var skills = map[int]*Skill{
 | 
			
		||||
		BoundChState:          ATK_CHARACTER_STATE_ATK3,
 | 
			
		||||
		Hits: []interface{}{
 | 
			
		||||
			&MeleeBullet{
 | 
			
		||||
				Bullet: Bullet{
 | 
			
		||||
				Bullet: &BulletConfig{
 | 
			
		||||
					StartupFrames:   int32(8),
 | 
			
		||||
					ActiveFrames:    int32(28),
 | 
			
		||||
					HitStunFrames:   MAX_INT32,
 | 
			
		||||
@@ -372,7 +372,7 @@ var skills = map[int]*Skill{
 | 
			
		||||
		BoundChState:          ATK_CHARACTER_STATE_ATK1,
 | 
			
		||||
		Hits: []interface{}{
 | 
			
		||||
			&MeleeBullet{
 | 
			
		||||
				Bullet: Bullet{
 | 
			
		||||
				Bullet: &BulletConfig{
 | 
			
		||||
					StartupFrames:      int32(7),
 | 
			
		||||
					ActiveFrames:       int32(22),
 | 
			
		||||
					HitStunFrames:      int32(13),
 | 
			
		||||
@@ -405,7 +405,7 @@ var skills = map[int]*Skill{
 | 
			
		||||
		BoundChState:          ATK_CHARACTER_STATE_ATK2,
 | 
			
		||||
		Hits: []interface{}{
 | 
			
		||||
			&MeleeBullet{
 | 
			
		||||
				Bullet: Bullet{
 | 
			
		||||
				Bullet: &BulletConfig{
 | 
			
		||||
					StartupFrames:      int32(18),
 | 
			
		||||
					ActiveFrames:       int32(18),
 | 
			
		||||
					HitStunFrames:      int32(18),
 | 
			
		||||
@@ -436,7 +436,7 @@ var skills = map[int]*Skill{
 | 
			
		||||
		BoundChState:          ATK_CHARACTER_STATE_ATK3,
 | 
			
		||||
		Hits: []interface{}{
 | 
			
		||||
			&MeleeBullet{
 | 
			
		||||
				Bullet: Bullet{
 | 
			
		||||
				Bullet: &BulletConfig{
 | 
			
		||||
					StartupFrames:   int32(7),
 | 
			
		||||
					ActiveFrames:    int32(30),
 | 
			
		||||
					HitStunFrames:   MAX_INT32,
 | 
			
		||||
@@ -464,8 +464,8 @@ var skills = map[int]*Skill{
 | 
			
		||||
		Hits: []interface{}{
 | 
			
		||||
			&FireballBullet{
 | 
			
		||||
				SpeciesId: int32(1),
 | 
			
		||||
				Speed:     int32(float64(8) * WORLD_TO_VIRTUAL_GRID_RATIO),
 | 
			
		||||
				Bullet: Bullet{
 | 
			
		||||
				Speed:     int32(float64(5) * WORLD_TO_VIRTUAL_GRID_RATIO),
 | 
			
		||||
				Bullet: &BulletConfig{
 | 
			
		||||
					StartupFrames:   int32(15),
 | 
			
		||||
					ActiveFrames:    MAX_INT32,
 | 
			
		||||
					HitStunFrames:   int32(15),
 | 
			
		||||
@@ -475,7 +475,7 @@ var skills = map[int]*Skill{
 | 
			
		||||
					SelfLockVelY:    NO_LOCK_VEL,
 | 
			
		||||
					PushbackVelX:    int32(float64(2) * WORLD_TO_VIRTUAL_GRID_RATIO),
 | 
			
		||||
					PushbackVelY:    int32(0),
 | 
			
		||||
					HitboxOffsetX:   int32(float64(32) * WORLD_TO_VIRTUAL_GRID_RATIO),
 | 
			
		||||
					HitboxOffsetX:   int32(float64(18) * WORLD_TO_VIRTUAL_GRID_RATIO),
 | 
			
		||||
					HitboxOffsetY:   int32(float64(5) * WORLD_TO_VIRTUAL_GRID_RATIO),
 | 
			
		||||
					HitboxSizeX:     int32(float64(48) * WORLD_TO_VIRTUAL_GRID_RATIO),
 | 
			
		||||
					HitboxSizeY:     int32(float64(32) * WORLD_TO_VIRTUAL_GRID_RATIO),
 | 
			
		||||
@@ -491,7 +491,7 @@ var skills = map[int]*Skill{
 | 
			
		||||
		BoundChState:          ATK_CHARACTER_STATE_ATK5,
 | 
			
		||||
		Hits: []interface{}{
 | 
			
		||||
			&MeleeBullet{
 | 
			
		||||
				Bullet: Bullet{
 | 
			
		||||
				Bullet: &BulletConfig{
 | 
			
		||||
					StartupFrames:   int32(3),
 | 
			
		||||
					ActiveFrames:    int32(25),
 | 
			
		||||
					HitStunFrames:   MAX_INT32,
 | 
			
		||||
@@ -518,7 +518,7 @@ var skills = map[int]*Skill{
 | 
			
		||||
		BoundChState:          ATK_CHARACTER_STATE_INAIR_ATK1,
 | 
			
		||||
		Hits: []interface{}{
 | 
			
		||||
			&MeleeBullet{
 | 
			
		||||
				Bullet: Bullet{
 | 
			
		||||
				Bullet: &BulletConfig{
 | 
			
		||||
					StartupFrames:   int32(3),
 | 
			
		||||
					ActiveFrames:    int32(20),
 | 
			
		||||
					HitStunFrames:   int32(18),
 | 
			
		||||
@@ -544,7 +544,7 @@ var skills = map[int]*Skill{
 | 
			
		||||
		BoundChState:          ATK_CHARACTER_STATE_INAIR_ATK1,
 | 
			
		||||
		Hits: []interface{}{
 | 
			
		||||
			&MeleeBullet{
 | 
			
		||||
				Bullet: Bullet{
 | 
			
		||||
				Bullet: &BulletConfig{
 | 
			
		||||
					StartupFrames:   int32(3),
 | 
			
		||||
					ActiveFrames:    int32(10),
 | 
			
		||||
					HitStunFrames:   int32(15),
 | 
			
		||||
@@ -570,7 +570,7 @@ var skills = map[int]*Skill{
 | 
			
		||||
		BoundChState:          ATK_CHARACTER_STATE_INAIR_ATK1,
 | 
			
		||||
		Hits: []interface{}{
 | 
			
		||||
			&MeleeBullet{
 | 
			
		||||
				Bullet: Bullet{
 | 
			
		||||
				Bullet: &BulletConfig{
 | 
			
		||||
					StartupFrames:   int32(3),
 | 
			
		||||
					ActiveFrames:    int32(20),
 | 
			
		||||
					HitStunFrames:   int32(18),
 | 
			
		||||
 
 | 
			
		||||
@@ -34,6 +34,8 @@ type PlayerDownsync struct {
 | 
			
		||||
	CharacterState    int32
 | 
			
		||||
	InAir             bool
 | 
			
		||||
	OnWall            bool
 | 
			
		||||
	OnWallNormX       int32
 | 
			
		||||
	OnWallNormY       int32
 | 
			
		||||
 | 
			
		||||
	ActiveSkillId  int32
 | 
			
		||||
	ActiveSkillHit int32
 | 
			
		||||
@@ -42,9 +44,6 @@ type PlayerDownsync struct {
 | 
			
		||||
 | 
			
		||||
	BulletTeamId      int32
 | 
			
		||||
	ChCollisionTeamId int32 // not the same as "BulletTeamId", because even in the same team, we should allow inter-character collisions
 | 
			
		||||
 | 
			
		||||
    OnWallNormX int32
 | 
			
		||||
    OnWallNormY int32
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type InputFrameDecoded struct {
 | 
			
		||||
@@ -63,7 +62,7 @@ type Barrier struct {
 | 
			
		||||
	Boundary *Polygon2D
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type Bullet struct {
 | 
			
		||||
type BulletConfig struct {
 | 
			
		||||
	BulletLocalId int32 // for referencing cached nodes in frontend rendering
 | 
			
		||||
 | 
			
		||||
	// for offender
 | 
			
		||||
@@ -97,7 +96,7 @@ type Bullet struct {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type MeleeBullet struct {
 | 
			
		||||
	Bullet
 | 
			
		||||
	Bullet *BulletConfig
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type FireballBullet struct {
 | 
			
		||||
@@ -109,7 +108,7 @@ type FireballBullet struct {
 | 
			
		||||
	VelY         int32
 | 
			
		||||
	Speed        int32
 | 
			
		||||
	SpeciesId    int32
 | 
			
		||||
	Bullet
 | 
			
		||||
	Bullet       *BulletConfig
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type Skill struct {
 | 
			
		||||
 
 | 
			
		||||
@@ -65,85 +65,19 @@ func NewPlayerDownsyncJs(id, virtualGridX, virtualGridY, dirX, dirY, velX, velY,
 | 
			
		||||
		ColliderRadius:    colliderRadius,
 | 
			
		||||
		InAir:             inAir,
 | 
			
		||||
		OnWall:            onWall,
 | 
			
		||||
        OnWallNormX:       onWallNormX,
 | 
			
		||||
        OnWallNormY:       onWallNormY,
 | 
			
		||||
		OnWallNormX:       onWallNormX,
 | 
			
		||||
		OnWallNormY:       onWallNormY,
 | 
			
		||||
		BulletTeamId:      bulletTeamId,
 | 
			
		||||
		ChCollisionTeamId: chCollisionTeamId,
 | 
			
		||||
	})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func NewMeleeBulletJs(bulletLocalId, originatedRenderFrameId, offenderJoinIndex, startupFrames, cancellableStFrame, cancellableEdFrame, activeFrames, hitStunFrames, blockStunFrames, pushbackVelX, pushbackVelY, damage, selfLockVelX, selfLockVelY, hitboxOffsetX, hitboxOffsetY, hitboxSizeX, hitboxSizeY int32, blowUp bool, teamId int32) *js.Object {
 | 
			
		||||
	return js.MakeWrapper(&MeleeBullet{
 | 
			
		||||
		Bullet: Bullet{
 | 
			
		||||
			BulletLocalId:           bulletLocalId,
 | 
			
		||||
			OriginatedRenderFrameId: originatedRenderFrameId,
 | 
			
		||||
			OffenderJoinIndex:       offenderJoinIndex,
 | 
			
		||||
 | 
			
		||||
			StartupFrames:      startupFrames,
 | 
			
		||||
			CancellableStFrame: cancellableStFrame,
 | 
			
		||||
			CancellableEdFrame: cancellableEdFrame,
 | 
			
		||||
			ActiveFrames:       activeFrames,
 | 
			
		||||
 | 
			
		||||
			HitStunFrames:   hitStunFrames,
 | 
			
		||||
			BlockStunFrames: blockStunFrames,
 | 
			
		||||
			PushbackVelX:    pushbackVelX,
 | 
			
		||||
			PushbackVelY:    pushbackVelY,
 | 
			
		||||
			Damage:          damage,
 | 
			
		||||
 | 
			
		||||
			SelfLockVelX: selfLockVelX,
 | 
			
		||||
			SelfLockVelY: selfLockVelY,
 | 
			
		||||
 | 
			
		||||
			HitboxOffsetX: hitboxOffsetX,
 | 
			
		||||
			HitboxOffsetY: hitboxOffsetY,
 | 
			
		||||
			HitboxSizeX:   hitboxSizeX,
 | 
			
		||||
			HitboxSizeY:   hitboxSizeY,
 | 
			
		||||
 | 
			
		||||
			BlowUp: blowUp,
 | 
			
		||||
 | 
			
		||||
			TeamId: teamId,
 | 
			
		||||
		},
 | 
			
		||||
	})
 | 
			
		||||
	return js.MakeWrapper(NewMeleeBullet(bulletLocalId, originatedRenderFrameId, offenderJoinIndex, startupFrames, cancellableStFrame, cancellableEdFrame, activeFrames, hitStunFrames, blockStunFrames, pushbackVelX, pushbackVelY, damage, selfLockVelX, selfLockVelY, hitboxOffsetX, hitboxOffsetY, hitboxSizeX, hitboxSizeY, blowUp, teamId))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func NewFireballBulletJs(bulletLocalId, originatedRenderFrameId, offenderJoinIndex, startupFrames, cancellableStFrame, cancellableEdFrame, activeFrames, hitStunFrames, blockStunFrames, pushbackVelX, pushbackVelY, damage, selfLockVelX, selfLockVelY, hitboxOffsetX, hitboxOffsetY, hitboxSizeX, hitboxSizeY int32, blowUp bool, teamId int32, virtualGridX, virtualGridY, dirX, dirY, velX, velY, speed, speciesId int32) *js.Object {
 | 
			
		||||
	return js.MakeWrapper(&FireballBullet{
 | 
			
		||||
		VirtualGridX: virtualGridX,
 | 
			
		||||
		VirtualGridY: virtualGridY,
 | 
			
		||||
		DirX:         dirX,
 | 
			
		||||
		DirY:         dirY,
 | 
			
		||||
		VelX:         velX,
 | 
			
		||||
		VelY:         velY,
 | 
			
		||||
		Speed:        speed,
 | 
			
		||||
		SpeciesId:    speciesId,
 | 
			
		||||
		Bullet: Bullet{
 | 
			
		||||
			BulletLocalId:           bulletLocalId,
 | 
			
		||||
			OriginatedRenderFrameId: originatedRenderFrameId,
 | 
			
		||||
			OffenderJoinIndex:       offenderJoinIndex,
 | 
			
		||||
 | 
			
		||||
			StartupFrames:      startupFrames,
 | 
			
		||||
			CancellableStFrame: cancellableStFrame,
 | 
			
		||||
			CancellableEdFrame: cancellableEdFrame,
 | 
			
		||||
			ActiveFrames:       activeFrames,
 | 
			
		||||
 | 
			
		||||
			HitStunFrames:   hitStunFrames,
 | 
			
		||||
			BlockStunFrames: blockStunFrames,
 | 
			
		||||
			PushbackVelX:    pushbackVelX,
 | 
			
		||||
			PushbackVelY:    pushbackVelY,
 | 
			
		||||
			Damage:          damage,
 | 
			
		||||
 | 
			
		||||
			SelfLockVelX: selfLockVelX,
 | 
			
		||||
			SelfLockVelY: selfLockVelY,
 | 
			
		||||
 | 
			
		||||
			HitboxOffsetX: hitboxOffsetX,
 | 
			
		||||
			HitboxOffsetY: hitboxOffsetY,
 | 
			
		||||
			HitboxSizeX:   hitboxSizeX,
 | 
			
		||||
			HitboxSizeY:   hitboxSizeY,
 | 
			
		||||
 | 
			
		||||
			BlowUp: blowUp,
 | 
			
		||||
 | 
			
		||||
			TeamId: teamId,
 | 
			
		||||
		},
 | 
			
		||||
	})
 | 
			
		||||
	return js.MakeWrapper(NewFireballBullet(bulletLocalId, originatedRenderFrameId, offenderJoinIndex, startupFrames, cancellableStFrame, cancellableEdFrame, activeFrames, hitStunFrames, blockStunFrames, pushbackVelX, pushbackVelY, damage, selfLockVelX, selfLockVelY, hitboxOffsetX, hitboxOffsetY, hitboxSizeX, hitboxSizeY, blowUp, teamId, virtualGridX, virtualGridY, dirX, dirY, velX, velY, speed, speciesId))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func NewNpcPatrolCue(flAct, frAct uint64, x, y float64) *js.Object {
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user