mirror of
https://github.com/genxium/DelayNoMore
synced 2024-12-26 03:39:00 +00:00
More preparations on backend to sync jumping.
This commit is contained in:
parent
7985a242fd
commit
5c06cfdbac
@ -754,7 +754,7 @@ func (pR *Room) OnDismissed() {
|
|||||||
pR.InputFrameUpsyncDelayTolerance = 2
|
pR.InputFrameUpsyncDelayTolerance = 2
|
||||||
pR.MaxChasingRenderFramesPerUpdate = 8
|
pR.MaxChasingRenderFramesPerUpdate = 8
|
||||||
|
|
||||||
pR.BackendDynamicsEnabled = true // [WARNING] When "false", recovery upon reconnection wouldn't work!
|
pR.BackendDynamicsEnabled = false // [WARNING] When "false", recovery upon reconnection wouldn't work!
|
||||||
punchSkillId := int32(1)
|
punchSkillId := int32(1)
|
||||||
pR.MeleeSkillConfig = make(map[int32]*MeleeBullet, 0)
|
pR.MeleeSkillConfig = make(map[int32]*MeleeBullet, 0)
|
||||||
pR.MeleeSkillConfig[punchSkillId] = &MeleeBullet{
|
pR.MeleeSkillConfig[punchSkillId] = &MeleeBullet{
|
||||||
@ -782,6 +782,12 @@ func (pR *Room) OnDismissed() {
|
|||||||
Damage: int32(5),
|
Damage: int32(5),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pR.SnapIntoPlatformOverlap = float64(0.1)
|
||||||
|
pR.SnapIntoPlatformThreshold = float64(0.5)
|
||||||
|
pR.JumpingInitVelY = int32(float64(6) * pR.WorldToVirtualGridRatio)
|
||||||
|
pR.GravityX = 0
|
||||||
|
pR.GravityY = -(4*pR.JumpingInitVelY + (pR.ServerFps - 1)) / pR.ServerFps // i.e. -Math.ceil(4*jumpingInitVelY / serverFps)
|
||||||
|
|
||||||
pR.ChooseStage()
|
pR.ChooseStage()
|
||||||
pR.EffectivePlayerCount = 0
|
pR.EffectivePlayerCount = 0
|
||||||
|
|
||||||
@ -1502,10 +1508,12 @@ func (pR *Room) applyInputFrameDownsyncDynamicsOnSingleRenderFrame(delayedInputF
|
|||||||
func (pR *Room) decodeInput(encodedInput uint64) *InputFrameDecoded {
|
func (pR *Room) decodeInput(encodedInput uint64) *InputFrameDecoded {
|
||||||
encodedDirection := (encodedInput & uint64(15))
|
encodedDirection := (encodedInput & uint64(15))
|
||||||
btnALevel := int32((encodedInput >> 4) & 1)
|
btnALevel := int32((encodedInput >> 4) & 1)
|
||||||
|
btnBLevel := int32((encodedInput >> 5) & 1)
|
||||||
return &InputFrameDecoded{
|
return &InputFrameDecoded{
|
||||||
Dx: DIRECTION_DECODER[encodedDirection][0],
|
Dx: DIRECTION_DECODER[encodedDirection][0],
|
||||||
Dy: DIRECTION_DECODER[encodedDirection][1],
|
Dy: DIRECTION_DECODER[encodedDirection][1],
|
||||||
BtnALevel: btnALevel,
|
BtnALevel: btnALevel,
|
||||||
|
BtnBLevel: btnBLevel,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -265,9 +265,14 @@ func Serve(c *gin.Context) {
|
|||||||
WorldToVirtualGridRatio: pRoom.WorldToVirtualGridRatio,
|
WorldToVirtualGridRatio: pRoom.WorldToVirtualGridRatio,
|
||||||
VirtualGridToWorldRatio: pRoom.VirtualGridToWorldRatio,
|
VirtualGridToWorldRatio: pRoom.VirtualGridToWorldRatio,
|
||||||
|
|
||||||
SpAtkLookupFrames: pRoom.SpAtkLookupFrames,
|
SpAtkLookupFrames: pRoom.SpAtkLookupFrames,
|
||||||
RenderCacheSize: pRoom.RenderCacheSize,
|
RenderCacheSize: pRoom.RenderCacheSize,
|
||||||
MeleeSkillConfig: pRoom.MeleeSkillConfig,
|
MeleeSkillConfig: pRoom.MeleeSkillConfig,
|
||||||
|
SnapIntoPlatformOverlap: pRoom.SnapIntoPlatformOverlap,
|
||||||
|
SnapIntoPlatformThreshold: pRoom.SnapIntoPlatformThreshold,
|
||||||
|
JumpingInitVelY: pRoom.JumpingInitVelY,
|
||||||
|
GravityX: pRoom.GravityX,
|
||||||
|
GravityY: pRoom.GravityY,
|
||||||
}
|
}
|
||||||
|
|
||||||
resp := &pb.WsResp{
|
resp := &pb.WsResp{
|
||||||
|
@ -1,18 +1,18 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<map version="1.2" tiledversion="1.2.3" orientation="orthogonal" renderorder="right-down" width="128" height="128" tilewidth="16" tileheight="16" infinite="0" nextlayerid="3" nextobjectid="73">
|
<map version="1.2" tiledversion="1.2.3" orientation="orthogonal" renderorder="right-down" width="128" height="128" tilewidth="16" tileheight="16" infinite="0" nextlayerid="3" nextobjectid="79">
|
||||||
<tileset firstgid="1" source="tiles0.tsx"/>
|
<tileset firstgid="1" source="tiles0.tsx"/>
|
||||||
<tileset firstgid="65" source="tiles1.tsx"/>
|
<tileset firstgid="65" source="tiles1.tsx"/>
|
||||||
<tileset firstgid="129" source="tiles2.tsx"/>
|
<tileset firstgid="129" source="tiles2.tsx"/>
|
||||||
<layer id="2" name="Ground" width="128" height="128">
|
<layer id="2" name="Ground" width="128" height="128">
|
||||||
<data encoding="base64" compression="zlib">
|
<data encoding="base64" compression="zlib">
|
||||||
eJzt3D9v00AYwGErFUuHigqB1J0Bic+BhDqgTnRj6gdBYmdkZILviSNiFKwkTlz73rPfZ3gUKcmQ3O/sXP5crpqmuQIAAAAAAAAAAAAAAAAAgBGud6IfB+U87en6PyUU3SGy/2an6/+ldX/EZoX0/7//Mfqvz37/+4H+1xW00n++/ofov27b5/5659DY9M8J0a30L9s/A/31j+4Q2f/FCNHN9Ndff/31j+lfA/31119//fXXf1n9v595v9sd/dfV/1fr55Hbtt8//2590j+8/9gx7T5T/PZM+uuvv/7bx3LXo//6+9/1+jv+9dc/vv/LAVP236d/bP+h7of6/2h9vlDX3/qvnv7ntn9orP+X4tz+57bf6L8oQ/0PNX7fujnSvt+/7+FC+sf1P9T+Zk+/+xz937Te6l9N/1Pn/f3+U9O/3v5j1oiX0r98/3PmQH8sp+w7ZMr+mwpa1Nh/qjE+ZUx7/dfTP5r+ueeA/vrrr390i6j+0eMfLfv7v+jxj6Z/bvrnpn9u+uemf27656Z/bvrnpn9u+uemf27656Z/bvrnpn9u+o/3qie6pf7L7n87YI7fqOlfT3/Hf939+731XzbrP/1Lj/ex1/bI/psKWtTc/3HnY9N8/fD38t91j4Ht9C/Tv+ve182D6Ib6z9f/1N6gofdrkef1S/pHd6i1f9f53TPUvodM/3nVvo8we/+593zWvpc0e/9Sx3+nts+J9C/bv5sDl9K/XP8p1/Nj/ttH/3Uf/yX/30n/ZfYvNQ/0zy17f+I7AAAAAAAAAAAAAAAAAOX9ARz6kGc=
|
eJzt3DFv00AYgGErFUuHigqBVGYGJP4ECxLqgDrRjak/BImdkZGJ/k8cNUbBSmLnavu75HuGR5WaDum9Z+fi5HzRNM0FAAAAAAAAAAAAAAAAAAAUuNyIfh4s52FL1/8hoegOkf1XG13/b63bPVZnSP//+++j//nZ7n870P+yglb6z9d/F/3P2/p/f72xa2z654ToVvov2z8D/fWP7hDZ/0WB6Gb666+//vrH9K+B/vrrr7/++ut/Wv1/jvy76w39z6v/n9bvPY+tP39+bH3RP7x/6Zh21xR/PJP++uuv//q53PTof/79b3r9Hf/66x/f/+WAKftv0z+2/1D3Xf1/tb4eqetv/VdP/7Ht7xrr/1Mxtv/Y9iv9T8pQ/12NP7Su9rTv9++7O5L+cf13tb/a0u8+R/83rXf6V9P/0Hl/u//U9K+3f8ka8Vj6L99/zBzoj+WUfYdM2X9VQYsa+081xoeUtNf/fPpH0z/3HNBff/31j24R1T96/KNlf/8XPf7R9M9N/9z0z03/3PTPTf/c9M9N/9z0z03/3PTPTf/c9M9N/9z0z03/cq96olvqf9r9rwfM8R01/evp7/ivu3+/t/6nzfpP/6XHe99re2T/VQUtau5/v/G5ab5/evr573f3ge30X6Z/172vmwfRDfWfr/+hvUFD79ciz+vH9I/uUGv/rvP7Z6h9D5n+86p9H2H2/nPv+ax9L2n2/ksd/53arhPpv2z/bg4cS//l+k+5ni+5t4/+9Rz/z50Hpfd3Wup1Qv9xSufB28L+j83T9SX96+hfqu3/sbR/Z855kL0/8R0AAAAAAAAAAAAAAACA5f0FyTyU0g==
|
||||||
</data>
|
</data>
|
||||||
</layer>
|
</layer>
|
||||||
<objectgroup id="1" name="PlayerStartingPos">
|
<objectgroup id="1" name="PlayerStartingPos">
|
||||||
<object id="135" x="980" y="1588.67">
|
<object id="135" x="1170" y="1539.67">
|
||||||
<point/>
|
<point/>
|
||||||
</object>
|
</object>
|
||||||
<object id="137" x="1349" y="1580.5">
|
<object id="137" x="1254" y="1332">
|
||||||
<point/>
|
<point/>
|
||||||
</object>
|
</object>
|
||||||
</objectgroup>
|
</objectgroup>
|
||||||
@ -175,12 +175,12 @@
|
|||||||
<property name="boundary_type" value="barrier"/>
|
<property name="boundary_type" value="barrier"/>
|
||||||
</properties>
|
</properties>
|
||||||
</object>
|
</object>
|
||||||
<object id="54" x="656" y="1502.5" width="80.5" height="16.5">
|
<object id="54" x="656" y="1503.5" width="80.5" height="15.5">
|
||||||
<properties>
|
<properties>
|
||||||
<property name="boundary_type" value="barrier"/>
|
<property name="boundary_type" value="barrier"/>
|
||||||
</properties>
|
</properties>
|
||||||
</object>
|
</object>
|
||||||
<object id="55" x="735" y="1550" width="113.5" height="17.5">
|
<object id="55" x="735" y="1552" width="113.5" height="15.5">
|
||||||
<properties>
|
<properties>
|
||||||
<property name="boundary_type" value="barrier"/>
|
<property name="boundary_type" value="barrier"/>
|
||||||
</properties>
|
</properties>
|
||||||
@ -195,7 +195,7 @@
|
|||||||
<property name="boundary_type" value="barrier"/>
|
<property name="boundary_type" value="barrier"/>
|
||||||
</properties>
|
</properties>
|
||||||
</object>
|
</object>
|
||||||
<object id="59" x="1039.75" y="1568" width="224.25" height="15.5">
|
<object id="59" x="1039.75" y="1568" width="224.25" height="47.75">
|
||||||
<properties>
|
<properties>
|
||||||
<property name="boundary_type" value="barrier"/>
|
<property name="boundary_type" value="barrier"/>
|
||||||
</properties>
|
</properties>
|
||||||
@ -205,11 +205,6 @@
|
|||||||
<property name="boundary_type" value="barrier"/>
|
<property name="boundary_type" value="barrier"/>
|
||||||
</properties>
|
</properties>
|
||||||
</object>
|
</object>
|
||||||
<object id="61" x="1040" y="1583" width="256.5" height="15">
|
|
||||||
<properties>
|
|
||||||
<property name="boundary_type" value="barrier"/>
|
|
||||||
</properties>
|
|
||||||
</object>
|
|
||||||
<object id="62" x="1040.13" y="1552.5" width="208" height="15.5">
|
<object id="62" x="1040.13" y="1552.5" width="208" height="15.5">
|
||||||
<properties>
|
<properties>
|
||||||
<property name="boundary_type" value="barrier"/>
|
<property name="boundary_type" value="barrier"/>
|
||||||
@ -269,5 +264,37 @@
|
|||||||
</properties>
|
</properties>
|
||||||
<polyline points="0,0 -0.333333,16.9105 15,17.3334"/>
|
<polyline points="0,0 -0.333333,16.9105 15,17.3334"/>
|
||||||
</object>
|
</object>
|
||||||
|
<object id="73" x="783.75" y="1567.5" width="96.25" height="15.5">
|
||||||
|
<properties>
|
||||||
|
<property name="boundary_type" value="barrier"/>
|
||||||
|
</properties>
|
||||||
|
</object>
|
||||||
|
<object id="74" x="815.75" y="1583.75" width="96.25" height="15.5">
|
||||||
|
<properties>
|
||||||
|
<property name="boundary_type" value="barrier"/>
|
||||||
|
</properties>
|
||||||
|
</object>
|
||||||
|
<object id="75" x="1264.25" y="1599" width="47.75" height="17.25">
|
||||||
|
<properties>
|
||||||
|
<property name="boundary_type" value="barrier"/>
|
||||||
|
</properties>
|
||||||
|
</object>
|
||||||
|
<object id="76" x="1264.63" y="1583.63" width="15.5" height="15.25">
|
||||||
|
<properties>
|
||||||
|
<property name="boundary_type" value="barrier"/>
|
||||||
|
</properties>
|
||||||
|
</object>
|
||||||
|
<object id="77" x="1280.5" y="1583.75">
|
||||||
|
<properties>
|
||||||
|
<property name="boundary_type" value="barrier"/>
|
||||||
|
</properties>
|
||||||
|
<polyline points="0,0 0,15.25 15.75,15.25"/>
|
||||||
|
</object>
|
||||||
|
<object id="78" x="1312" y="1599.25">
|
||||||
|
<properties>
|
||||||
|
<property name="boundary_type" value="barrier"/>
|
||||||
|
</properties>
|
||||||
|
<polyline points="0,0 0,17 15.75,17"/>
|
||||||
|
</object>
|
||||||
</objectgroup>
|
</objectgroup>
|
||||||
</map>
|
</map>
|
||||||
|
@ -408,7 +408,7 @@
|
|||||||
"ctor": "Float64Array",
|
"ctor": "Float64Array",
|
||||||
"array": [
|
"array": [
|
||||||
3,
|
3,
|
||||||
20,
|
40,
|
||||||
0,
|
0,
|
||||||
0,
|
0,
|
||||||
0,
|
0,
|
||||||
|
@ -440,7 +440,7 @@
|
|||||||
"array": [
|
"array": [
|
||||||
0,
|
0,
|
||||||
0,
|
0,
|
||||||
209.73151519075364,
|
215.64032554232523,
|
||||||
0,
|
0,
|
||||||
0,
|
0,
|
||||||
0,
|
0,
|
||||||
|
@ -454,7 +454,7 @@
|
|||||||
"array": [
|
"array": [
|
||||||
0,
|
0,
|
||||||
0,
|
0,
|
||||||
209.73151519075364,
|
215.64032554232523,
|
||||||
0,
|
0,
|
||||||
0,
|
0,
|
||||||
0,
|
0,
|
||||||
|
@ -402,6 +402,7 @@ cc.Class({
|
|||||||
console.log(`Received parsedBattleColliderInfo via ws`);
|
console.log(`Received parsedBattleColliderInfo via ws`);
|
||||||
// TODO: Upon reconnection, the backend might have already been sending down data that'd trigger "onRoomDownsyncFrame & onInputFrameDownsyncBatch", but frontend could reject those data due to "battleState != PlayerBattleState.ACTIVE".
|
// TODO: Upon reconnection, the backend might have already been sending down data that'd trigger "onRoomDownsyncFrame & onInputFrameDownsyncBatch", but frontend could reject those data due to "battleState != PlayerBattleState.ACTIVE".
|
||||||
Object.assign(self, parsedBattleColliderInfo);
|
Object.assign(self, parsedBattleColliderInfo);
|
||||||
|
self.gravityX = parsedBattleColliderInfo.gravityX; // to avoid integer default value 0 accidentally becoming null in "Object.assign(...)"
|
||||||
self.tooFastDtIntervalMillis = 0.5 * self.rollbackEstimatedDtMillis;
|
self.tooFastDtIntervalMillis = 0.5 * self.rollbackEstimatedDtMillis;
|
||||||
|
|
||||||
const tiledMapIns = self.node.getComponent(cc.TiledMap);
|
const tiledMapIns = self.node.getComponent(cc.TiledMap);
|
||||||
@ -834,6 +835,7 @@ cc.Class({
|
|||||||
*/
|
*/
|
||||||
// [WARNING] Don't try to get "prevRdf(i.e. renderFrameId == latest-1)" by "self.recentRenderCache.getByFrameId(...)" here, as the cache might have been updated by asynchronous "onRoomDownsyncFrame(...)" calls!
|
// [WARNING] Don't try to get "prevRdf(i.e. renderFrameId == latest-1)" by "self.recentRenderCache.getByFrameId(...)" here, as the cache might have been updated by asynchronous "onRoomDownsyncFrame(...)" calls!
|
||||||
self.applyRoomDownsyncFrameDynamics(rdf, prevRdf);
|
self.applyRoomDownsyncFrameDynamics(rdf, prevRdf);
|
||||||
|
self.showDebugBoundaries();
|
||||||
++self.renderFrameId; // [WARNING] It's important to increment the renderFrameId AFTER all the operations above!!!
|
++self.renderFrameId; // [WARNING] It's important to increment the renderFrameId AFTER all the operations above!!!
|
||||||
self.lastRenderFrameIdTriggeredAt = performance.now();
|
self.lastRenderFrameIdTriggeredAt = performance.now();
|
||||||
let t3 = performance.now();
|
let t3 = performance.now();
|
||||||
@ -978,6 +980,73 @@ cc.Class({
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
showDebugBoundaries() {
|
||||||
|
const self = this;
|
||||||
|
if (self.showCriticalCoordinateLabels) {
|
||||||
|
let g = self.g;
|
||||||
|
g.clear();
|
||||||
|
|
||||||
|
for (let k in self.collisionSys._bvh._bodies) {
|
||||||
|
const body = self.collisionSys._bvh._bodies[k];
|
||||||
|
if (!body._polygon) continue;
|
||||||
|
if (null != body.data && null != body.data.joinIndex) {
|
||||||
|
// character
|
||||||
|
if (1 == body.data.joinIndex) {
|
||||||
|
g.strokeColor = cc.Color.BLUE;
|
||||||
|
} else {
|
||||||
|
g.strokeColor = cc.Color.RED;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// barrier
|
||||||
|
g.strokeColor = cc.Color.WHITE;
|
||||||
|
}
|
||||||
|
g.moveTo(body.x, body.y);
|
||||||
|
const cnt = body._coords.length;
|
||||||
|
for (let j = 0; j < cnt; j += 2) {
|
||||||
|
const x = body._coords[j],
|
||||||
|
y = body._coords[j + 1];
|
||||||
|
g.lineTo(x, y);
|
||||||
|
}
|
||||||
|
g.lineTo(body.x, body.y);
|
||||||
|
g.stroke();
|
||||||
|
}
|
||||||
|
// For convenience of recovery upon reconnection, active bullets are always created & immediately removed from "collisionSys" within "applyInputFrameDownsyncDynamicsOnSingleRenderFrame"
|
||||||
|
|
||||||
|
for (let k in rdf.meleeBullets) {
|
||||||
|
const meleeBullet = rdf.meleeBullets[k];
|
||||||
|
if (
|
||||||
|
meleeBullet.originatedRenderFrameId + meleeBullet.startupFrames <= rdf.id
|
||||||
|
&&
|
||||||
|
meleeBullet.originatedRenderFrameId + meleeBullet.startupFrames + meleeBullet.activeFrames > rdf.id
|
||||||
|
) {
|
||||||
|
const offender = rdf.players[meleeBullet.offenderPlayerId];
|
||||||
|
if (1 == offender.joinIndex) {
|
||||||
|
g.strokeColor = cc.Color.BLUE;
|
||||||
|
} else {
|
||||||
|
g.strokeColor = cc.Color.RED;
|
||||||
|
}
|
||||||
|
|
||||||
|
let xfac = 1; // By now, straight Punch offset doesn't respect "y-axis"
|
||||||
|
if (0 > offender.dirX) {
|
||||||
|
xfac = -1;
|
||||||
|
}
|
||||||
|
const [offenderWx, offenderWy] = self.virtualGridToWorldPos(offender.virtualGridX, offender.virtualGridY);
|
||||||
|
const bulletWx = offenderWx + xfac * meleeBullet.hitboxOffset;
|
||||||
|
const bulletWy = offenderWy + 0.5 * meleeBullet.hitboxSize.y;
|
||||||
|
const [bulletCx, bulletCy] = self.worldToPolygonColliderAnchorPos(bulletWx, bulletWy, meleeBullet.hitboxSize.x * 0.5, meleeBullet.hitboxSize.y * 0.5),
|
||||||
|
pts = [[0, 0], [meleeBullet.hitboxSize.x, 0], [meleeBullet.hitboxSize.x, meleeBullet.hitboxSize.y], [0, meleeBullet.hitboxSize.y]];
|
||||||
|
|
||||||
|
g.moveTo(bulletCx, bulletCy);
|
||||||
|
for (let j = 0; j < pts.length; j += 1) {
|
||||||
|
g.lineTo(pts[j][0] + bulletCx, pts[j][1] + bulletCy);
|
||||||
|
}
|
||||||
|
g.lineTo(bulletCx, bulletCy);
|
||||||
|
g.stroke();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
getCachedInputFrameDownsyncWithPrediction(inputFrameId) {
|
getCachedInputFrameDownsyncWithPrediction(inputFrameId) {
|
||||||
const self = this;
|
const self = this;
|
||||||
const inputFrameDownsync = self.recentInputCache.getByFrameId(inputFrameId);
|
const inputFrameDownsync = self.recentInputCache.getByFrameId(inputFrameId);
|
||||||
@ -1004,7 +1073,10 @@ cc.Class({
|
|||||||
virtualGridY: currPlayerDownsync.virtualGridY,
|
virtualGridY: currPlayerDownsync.virtualGridY,
|
||||||
dirX: currPlayerDownsync.dirX,
|
dirX: currPlayerDownsync.dirX,
|
||||||
dirY: currPlayerDownsync.dirY,
|
dirY: currPlayerDownsync.dirY,
|
||||||
|
velX: currPlayerDownsync.velX,
|
||||||
|
velY: currPlayerDownsync.velY,
|
||||||
characterState: currPlayerDownsync.characterState,
|
characterState: currPlayerDownsync.characterState,
|
||||||
|
inAir: true, // will be updated if collided with a barrier with "0 > pushbackY"
|
||||||
speed: currPlayerDownsync.speed,
|
speed: currPlayerDownsync.speed,
|
||||||
battleState: currPlayerDownsync.battleState,
|
battleState: currPlayerDownsync.battleState,
|
||||||
score: currPlayerDownsync.score,
|
score: currPlayerDownsync.score,
|
||||||
@ -1018,12 +1090,17 @@ cc.Class({
|
|||||||
|
|
||||||
const nextRenderFrameMeleeBullets = [];
|
const nextRenderFrameMeleeBullets = [];
|
||||||
|
|
||||||
const bulletPushbacks = new Array(self.playerRichInfoArr.length); // Guaranteed determinism regardless of traversal order
|
// Guaranteed determinism regardless of traversal order
|
||||||
const effPushbacks = new Array(self.playerRichInfoArr.length); // Guaranteed determinism regardless of traversal order
|
const jumpTriggered = new Array(self.playerRichInfoArr.length);
|
||||||
|
const movements = new Array(self.playerRichInfoArr.length);
|
||||||
|
const bulletPushbacks = new Array(self.playerRichInfoArr.length);
|
||||||
|
const effPushbacks = new Array(self.playerRichInfoArr.length);
|
||||||
|
|
||||||
// Reset playerCollider position from the "virtual grid position"
|
// Reset playerCollider position from the "virtual grid position"
|
||||||
for (let j in self.playerRichInfoArr) {
|
for (let j in self.playerRichInfoArr) {
|
||||||
const joinIndex = parseInt(j) + 1;
|
const joinIndex = parseInt(j) + 1;
|
||||||
|
jumpTriggered[joinIndex - 1] = false;
|
||||||
|
movements[joinIndex - 1] = [0.0, 0.0];
|
||||||
bulletPushbacks[joinIndex - 1] = [0.0, 0.0];
|
bulletPushbacks[joinIndex - 1] = [0.0, 0.0];
|
||||||
effPushbacks[joinIndex - 1] = [0.0, 0.0];
|
effPushbacks[joinIndex - 1] = [0.0, 0.0];
|
||||||
const playerRichInfo = self.playerRichInfoArr[j];
|
const playerRichInfo = self.playerRichInfoArr[j];
|
||||||
@ -1031,10 +1108,20 @@ cc.Class({
|
|||||||
const collisionPlayerIndex = self.collisionPlayerIndexPrefix + joinIndex;
|
const collisionPlayerIndex = self.collisionPlayerIndexPrefix + joinIndex;
|
||||||
const playerCollider = collisionSysMap.get(collisionPlayerIndex);
|
const playerCollider = collisionSysMap.get(collisionPlayerIndex);
|
||||||
const currPlayerDownsync = currRenderFrame.players[playerId];
|
const currPlayerDownsync = currRenderFrame.players[playerId];
|
||||||
|
const thatPlayerInNextFrame = nextRenderFramePlayers[playerId];
|
||||||
|
|
||||||
const newVx = currPlayerDownsync.virtualGridX;
|
const newVx = currPlayerDownsync.virtualGridX;
|
||||||
const newVy = currPlayerDownsync.virtualGridY;
|
const newVy = currPlayerDownsync.virtualGridY;
|
||||||
[playerCollider.x, playerCollider.y] = self.virtualGridToPolygonColliderAnchorPos(newVx, newVy, self.playerRichInfoArr[joinIndex - 1].colliderRadius, self.playerRichInfoArr[joinIndex - 1].colliderRadius);
|
[playerCollider.x, playerCollider.y] = self.virtualGridToPolygonColliderAnchorPos(newVx, newVy, self.playerRichInfoArr[joinIndex - 1].colliderRadius, self.playerRichInfoArr[joinIndex - 1].colliderRadius);
|
||||||
|
|
||||||
|
// Process gravity before anyother interaction, by now "currPlayerDownsync.velX & velY" are properly snapped to be parallel to the edge of its standing platform if necessary
|
||||||
|
[movements[joinIndex - 1][0], movements[joinIndex - 1][1]] = self.virtualGridToWorldPos(currPlayerDownsync.velX, currPlayerDownsync.velY);
|
||||||
|
playerCollider.x += movements[joinIndex - 1][0];
|
||||||
|
playerCollider.y += movements[joinIndex - 1][1];
|
||||||
|
if (currPlayerDownsync.inAir) {
|
||||||
|
thatPlayerInNextFrame.velX += self.gravityX;
|
||||||
|
thatPlayerInNextFrame.velY += self.gravityY;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check bullet-anything collisions first, because the pushbacks caused by bullets might later be reverted by player-barrier collision
|
// Check bullet-anything collisions first, because the pushbacks caused by bullets might later be reverted by player-barrier collision
|
||||||
@ -1087,8 +1174,12 @@ cc.Class({
|
|||||||
}
|
}
|
||||||
bulletPushbacks[joinIndex - 1][0] += xfac * bulletCollider.data.pushback; // Only for straight punch, there's no y-pushback
|
bulletPushbacks[joinIndex - 1][0] += xfac * bulletCollider.data.pushback; // Only for straight punch, there's no y-pushback
|
||||||
bulletPushbacks[joinIndex - 1][1] += 0;
|
bulletPushbacks[joinIndex - 1][1] += 0;
|
||||||
|
const thatAckedPlayerInCurFrame = currRenderFrame.players[potential.data.id];
|
||||||
const thatAckedPlayerInNextFrame = nextRenderFramePlayers[potential.data.id];
|
const thatAckedPlayerInNextFrame = nextRenderFramePlayers[potential.data.id];
|
||||||
thatAckedPlayerInNextFrame.characterState = window.ATK_CHARACTER_STATE.Atked1[0];
|
thatAckedPlayerInNextFrame.characterState = window.ATK_CHARACTER_STATE.Atked1[0];
|
||||||
|
if (thatAckedPlayerInCurFrame.inAir) {
|
||||||
|
thatAckedPlayerInNextFrame.characterState = window.toInAirConjugate(thatAckedPlayerInNextFrame.characterState);
|
||||||
|
}
|
||||||
const oldFramesToRecover = thatAckedPlayerInNextFrame.framesToRecover;
|
const oldFramesToRecover = thatAckedPlayerInNextFrame.framesToRecover;
|
||||||
thatAckedPlayerInNextFrame.framesToRecover = (oldFramesToRecover > bulletCollider.data.hitStunFrames ? oldFramesToRecover : bulletCollider.data.hitStunFrames); // In case the hit player is already stun, we extend it
|
thatAckedPlayerInNextFrame.framesToRecover = (oldFramesToRecover > bulletCollider.data.hitStunFrames ? oldFramesToRecover : bulletCollider.data.hitStunFrames); // In case the hit player is already stun, we extend it
|
||||||
}
|
}
|
||||||
@ -1136,9 +1227,25 @@ cc.Class({
|
|||||||
}
|
}
|
||||||
|
|
||||||
const decodedInput = self.ctrl.decodeInput(inputList[joinIndex - 1]);
|
const decodedInput = self.ctrl.decodeInput(inputList[joinIndex - 1]);
|
||||||
|
|
||||||
const prevDecodedInput = (null == delayedInputFrameForPrevRenderFrame ? null : self.ctrl.decodeInput(delayedInputFrameForPrevRenderFrame.inputList[joinIndex - 1]));
|
const prevDecodedInput = (null == delayedInputFrameForPrevRenderFrame ? null : self.ctrl.decodeInput(delayedInputFrameForPrevRenderFrame.inputList[joinIndex - 1]));
|
||||||
const prevBtnALevel = (null == prevDecodedInput ? 0 : prevDecodedInput.btnALevel);
|
const prevBtnALevel = (null == prevDecodedInput ? 0 : prevDecodedInput.btnALevel);
|
||||||
|
const prevBtnBLevel = (null == prevDecodedInput ? 0 : prevDecodedInput.btnBLevel);
|
||||||
|
/*
|
||||||
|
[WARNING] Player input alone WOULD NOT take "characterState" into any "ATK_CHARACTER_STATE_IN_AIR_SET", only after the calculation of "effPushbacks" do we know exactly whether or not a player is "inAir", the finalize the transition of "thatPlayerInNextFrame.characterState".
|
||||||
|
*/
|
||||||
|
if (1 == decodedInput.btnBLevel && 0 == prevBtnBLevel) {
|
||||||
|
const characStateAlreadyInAir = window.ATK_CHARACTER_STATE_IN_AIR_SET.has(thatPlayerInNextFrame.characterState);
|
||||||
|
const characStateIsInterruptWaivable = window.ATK_CHARACTER_STATE_INTERRUPT_WAIVE_SET.has(thatPlayerInNextFrame.characterState);
|
||||||
|
if (
|
||||||
|
!characStateAlreadyInAir
|
||||||
|
&&
|
||||||
|
characStateIsInterruptWaivable
|
||||||
|
) {
|
||||||
|
thatPlayerInNextFrame.velY = self.jumpingInitVelY;
|
||||||
|
jumpTriggered[joinIndex - 1] = true;
|
||||||
|
console.log(`playerId=${playerId}, joinIndex=${joinIndex} triggered a rising-edge of btnB at renderFrame.id=${currRenderFrame.id}, delayedInputFrame.id=${delayedInputFrame.inputFrameId}, nextVelY=${thatPlayerInNextFrame.velY}, characStateAlreadyInAir=${characStateAlreadyInAir}, characStateIsInterruptWaivable=${characStateIsInterruptWaivable}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (1 == decodedInput.btnALevel && 0 == prevBtnALevel) {
|
if (1 == decodedInput.btnALevel && 0 == prevBtnALevel) {
|
||||||
// console.log(`playerId=${playerId} triggered a rising-edge of btnA at renderFrame.id=${currRenderFrame.id}, delayedInputFrame.id=${delayedInputFrame.inputFrameId}`);
|
// console.log(`playerId=${playerId} triggered a rising-edge of btnA at renderFrame.id=${currRenderFrame.id}, delayedInputFrame.id=${delayedInputFrame.inputFrameId}`);
|
||||||
@ -1155,52 +1262,91 @@ cc.Class({
|
|||||||
// console.log(`A rising-edge of meleeBullet is created at renderFrame.id=${currRenderFrame.id}, delayedInputFrame.id=${delayedInputFrame.inputFrameId}`);
|
// console.log(`A rising-edge of meleeBullet is created at renderFrame.id=${currRenderFrame.id}, delayedInputFrame.id=${delayedInputFrame.inputFrameId}`);
|
||||||
|
|
||||||
thatPlayerInNextFrame.characterState = window.ATK_CHARACTER_STATE.Atk1[0];
|
thatPlayerInNextFrame.characterState = window.ATK_CHARACTER_STATE.Atk1[0];
|
||||||
|
if (false == currPlayerDownsync.inAir) {
|
||||||
|
thatPlayerInNextFrame.velX = 0; // prohibits simultaneous movement with Atk1 on the ground
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else if (0 == decodedInput.btnALevel && 1 == prevBtnALevel) {
|
} else if (0 == decodedInput.btnALevel && 1 == prevBtnALevel) {
|
||||||
// console.log(`playerId=${playerId} triggered a falling-edge of btnA at renderFrame.id=${currRenderFrame.id}, delayedInputFrame.id=${delayedInputFrame.inputFrameId}`);
|
// console.log(`playerId=${playerId} triggered a falling-edge of btnA at renderFrame.id=${currRenderFrame.id}, delayedInputFrame.id=${delayedInputFrame.inputFrameId}`);
|
||||||
} else {
|
} else {
|
||||||
// No bullet trigger, process movement inputs
|
// No bullet trigger, process joystick movement inputs (except for jumping).
|
||||||
if (0 != decodedInput.dx || 0 != decodedInput.dy) {
|
if (0 != decodedInput.dx || 0 != decodedInput.dy) {
|
||||||
// Update directions and thus would eventually update moving animation accordingly
|
// Update directions and thus would eventually update moving animation accordingly
|
||||||
thatPlayerInNextFrame.dirX = decodedInput.dx;
|
thatPlayerInNextFrame.dirX = decodedInput.dx;
|
||||||
thatPlayerInNextFrame.dirY = decodedInput.dy;
|
thatPlayerInNextFrame.dirY = decodedInput.dy;
|
||||||
|
thatPlayerInNextFrame.velX = decodedInput.dx * currPlayerDownsync.speed;
|
||||||
thatPlayerInNextFrame.characterState = window.ATK_CHARACTER_STATE.Walking[0];
|
thatPlayerInNextFrame.characterState = window.ATK_CHARACTER_STATE.Walking[0];
|
||||||
} else {
|
} else {
|
||||||
thatPlayerInNextFrame.characterState = window.ATK_CHARACTER_STATE.Idle1[0];
|
thatPlayerInNextFrame.characterState = window.ATK_CHARACTER_STATE.Idle1[0];
|
||||||
|
thatPlayerInNextFrame.velX = 0;
|
||||||
}
|
}
|
||||||
const [movementX, movementY] = self.virtualGridToWorldPos(decodedInput.dx + currPlayerDownsync.speed * decodedInput.dx, decodedInput.dy + currPlayerDownsync.speed * decodedInput.dy);
|
}
|
||||||
playerCollider.x += movementX;
|
if (currPlayerDownsync.inAir) {
|
||||||
playerCollider.y += movementY;
|
thatPlayerInNextFrame.characterState = window.toInAirConjugate(thatPlayerInNextFrame.characterState);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
collisionSys.update(); // by now all "bulletCollider"s are removed
|
collisionSys.update(); // by now all "bulletCollider"s are removed
|
||||||
const result2 = collisionSys.createResult(); // Can I reuse a "self.collisionSysResult" object throughout the whole battle?
|
const result2 = collisionSys.createResult(); // Can I reuse a "self.collisionSysResult" object throughout the whole battle?
|
||||||
|
|
||||||
for (let j in self.playerRichInfoArr) {
|
for (let j in self.playerRichInfoArr) {
|
||||||
const joinIndex = parseInt(j) + 1;
|
const joinIndex = parseInt(j) + 1;
|
||||||
const playerId = self.playerRichInfoArr[j].id;
|
const playerId = self.playerRichInfoArr[j].id;
|
||||||
const collisionPlayerIndex = self.collisionPlayerIndexPrefix + joinIndex;
|
const collisionPlayerIndex = self.collisionPlayerIndexPrefix + joinIndex;
|
||||||
const playerCollider = collisionSysMap.get(collisionPlayerIndex);
|
const playerCollider = collisionSysMap.get(collisionPlayerIndex);
|
||||||
const potentials = playerCollider.potentials();
|
const potentials = playerCollider.potentials();
|
||||||
for (const potential of potentials) {
|
const currPlayerDownsync = currRenderFrame.players[playerId];
|
||||||
// Test if the player collides with the wall
|
const thatPlayerInNextFrame = nextRenderFramePlayers[playerId];
|
||||||
if (!playerCollider.collides(potential, result2)) continue;
|
let fallStopping = false;
|
||||||
// Push the player out of the wall
|
let [snappedIntoPlatformEx, snappedIntoPlatformEy] = [null, null];
|
||||||
effPushbacks[joinIndex - 1][0] += result2.overlap * result2.overlap_x;
|
for (const potential of potentials) {
|
||||||
effPushbacks[joinIndex - 1][1] += result2.overlap * result2.overlap_y;
|
// Test if the player collides with the wall
|
||||||
|
if (!playerCollider.collides(potential, result2)) continue;
|
||||||
|
// Push the player out of the wall
|
||||||
|
let [pushbackX, pushbackY] = [result2.overlap * result2.overlap_x, result2.overlap * result2.overlap_y];
|
||||||
|
if (null == potential.data) {
|
||||||
|
// "null == potential.data" implies a barrier
|
||||||
|
const normAlignmentWithGravity = (result2.overlap_x * 0 + result2.overlap_y * (-1.0));
|
||||||
|
const flatEnough = (self.snapIntoPlatformThreshold < normAlignmentWithGravity); // prevents false snapping on the lateral sides
|
||||||
|
const remainsNotInAir = (!currPlayerDownsync.inAir && flatEnough);
|
||||||
|
const localFallStopping = (currPlayerDownsync.inAir && flatEnough);
|
||||||
|
if (remainsNotInAir || localFallStopping) {
|
||||||
|
fallStopping |= localFallStopping;
|
||||||
|
[pushbackX, pushbackY] = [(result2.overlap - self.snapIntoPlatformOverlap) * result2.overlap_x, (result2.overlap - self.snapIntoPlatformOverlap) * result2.overlap_y]
|
||||||
|
// [overlay_x, overlap_y] is the unit vector that points into the platform; FIXME: Should only assign to [snappedIntoPlatformEx, snappedIntoPlatformEy] at most once!
|
||||||
|
snappedIntoPlatformEx = -result2.overlap_y;
|
||||||
|
snappedIntoPlatformEy = result2.overlap_x;
|
||||||
|
if (snappedIntoPlatformEx * currPlayerDownsync.dirX + snappedIntoPlatformEy * currPlayerDownsync.dirY) {
|
||||||
|
[snappedIntoPlatformEx, snappedIntoPlatformEy] = [-snappedIntoPlatformEx, -snappedIntoPlatformEy];
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
// What if we're on the edge of 2 barriers? Would adding up make an unexpected bounce?
|
||||||
|
effPushbacks[joinIndex - 1][0] += pushbackX;
|
||||||
|
effPushbacks[joinIndex - 1][1] += pushbackY;
|
||||||
}
|
}
|
||||||
|
if (false == jumpTriggered[joinIndex - 1] && null != snappedIntoPlatformEx && null != snappedIntoPlatformEy) {
|
||||||
for (let j in self.playerRichInfoArr) {
|
thatPlayerInNextFrame.inAir = false;
|
||||||
const joinIndex = parseInt(j) + 1;
|
if (fallStopping) {
|
||||||
const playerId = self.playerRichInfoArr[j].id;
|
thatPlayerInNextFrame.velY = 0;
|
||||||
const collisionPlayerIndex = self.collisionPlayerIndexPrefix + joinIndex;
|
thatPlayerInNextFrame.velX = 0;
|
||||||
const playerCollider = collisionSysMap.get(collisionPlayerIndex);
|
thatPlayerInNextFrame.characterState = window.ATK_CHARACTER_STATE.Idle1[0];
|
||||||
const thatPlayerInNextFrame = nextRenderFramePlayers[playerId];
|
thatPlayerInNextFrame.framesToRecover = 0;
|
||||||
[thatPlayerInNextFrame.virtualGridX, thatPlayerInNextFrame.virtualGridY] = self.polygonColliderAnchorToVirtualGridPos(playerCollider.x - effPushbacks[joinIndex - 1][0], playerCollider.y - effPushbacks[joinIndex - 1][1], self.playerRichInfoArr[j].colliderRadius, self.playerRichInfoArr[j].colliderRadius);
|
}
|
||||||
|
const dotProd = thatPlayerInNextFrame.velX * snappedIntoPlatformEx + thatPlayerInNextFrame.velY * snappedIntoPlatformEy;
|
||||||
|
[thatPlayerInNextFrame.velX, thatPlayerInNextFrame.velY] = [dotProd * snappedIntoPlatformEx, dotProd * snappedIntoPlatformEy];
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get players out of stuck barriers if there's any
|
||||||
|
for (let j in self.playerRichInfoArr) {
|
||||||
|
const joinIndex = parseInt(j) + 1;
|
||||||
|
const playerId = self.playerRichInfoArr[j].id;
|
||||||
|
const collisionPlayerIndex = self.collisionPlayerIndexPrefix + joinIndex;
|
||||||
|
const playerCollider = collisionSysMap.get(collisionPlayerIndex);
|
||||||
|
const thatPlayerInNextFrame = nextRenderFramePlayers[playerId];
|
||||||
|
[thatPlayerInNextFrame.virtualGridX, thatPlayerInNextFrame.virtualGridY] = self.polygonColliderAnchorToVirtualGridPos(playerCollider.x - effPushbacks[joinIndex - 1][0], playerCollider.y - effPushbacks[joinIndex - 1][1], self.playerRichInfoArr[j].colliderRadius, self.playerRichInfoArr[j].colliderRadius);
|
||||||
}
|
}
|
||||||
|
|
||||||
return window.pb.protos.RoomDownsyncFrame.create({
|
return window.pb.protos.RoomDownsyncFrame.create({
|
||||||
|
@ -39,6 +39,7 @@ cc.Class({
|
|||||||
self.rollbackEstimatedDt = 0.016667;
|
self.rollbackEstimatedDt = 0.016667;
|
||||||
self.rollbackEstimatedDtMillis = 16.667;
|
self.rollbackEstimatedDtMillis = 16.667;
|
||||||
self.rollbackEstimatedDtNanos = 16666666;
|
self.rollbackEstimatedDtNanos = 16666666;
|
||||||
|
self.tooFastDtIntervalMillis = 0.5 * self.rollbackEstimatedDtMillis;
|
||||||
|
|
||||||
self.worldToVirtualGridRatio = 1000;
|
self.worldToVirtualGridRatio = 1000;
|
||||||
self.virtualGridToWorldRatio = 1.0 / self.worldToVirtualGridRatio;
|
self.virtualGridToWorldRatio = 1.0 / self.worldToVirtualGridRatio;
|
||||||
@ -206,7 +207,8 @@ cc.Class({
|
|||||||
const self = this;
|
const self = this;
|
||||||
if (ALL_BATTLE_STATES.IN_BATTLE == self.battleState) {
|
if (ALL_BATTLE_STATES.IN_BATTLE == self.battleState) {
|
||||||
const elapsedMillisSinceLastFrameIdTriggered = performance.now() - self.lastRenderFrameIdTriggeredAt;
|
const elapsedMillisSinceLastFrameIdTriggered = performance.now() - self.lastRenderFrameIdTriggeredAt;
|
||||||
if (elapsedMillisSinceLastFrameIdTriggered < (self.rollbackEstimatedDtMillis)) {
|
if (elapsedMillisSinceLastFrameIdTriggered < self.tooFastDtIntervalMillis) {
|
||||||
|
// [WARNING] We should avoid a frontend ticking too fast to prevent cheating, as well as ticking too slow to cause a "resync avalanche" that impacts user experience!
|
||||||
// console.debug("Avoiding too fast frame@renderFrameId=", self.renderFrameId, ": elapsedMillisSinceLastFrameIdTriggered=", elapsedMillisSinceLastFrameIdTriggered);
|
// console.debug("Avoiding too fast frame@renderFrameId=", self.renderFrameId, ": elapsedMillisSinceLastFrameIdTriggered=", elapsedMillisSinceLastFrameIdTriggered);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -223,373 +225,12 @@ cc.Class({
|
|||||||
|
|
||||||
const [prevRdf, rdf] = self.rollbackAndChase(self.renderFrameId, self.renderFrameId + 1, self.collisionSys, self.collisionSysMap, false);
|
const [prevRdf, rdf] = self.rollbackAndChase(self.renderFrameId, self.renderFrameId + 1, self.collisionSys, self.collisionSysMap, false);
|
||||||
self.applyRoomDownsyncFrameDynamics(rdf, prevRdf);
|
self.applyRoomDownsyncFrameDynamics(rdf, prevRdf);
|
||||||
|
self.showDebugBoundaries();
|
||||||
|
++self.renderFrameId;
|
||||||
|
self.lastRenderFrameIdTriggeredAt = performance.now();
|
||||||
let t3 = performance.now();
|
let t3 = performance.now();
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error("Error during Map.update", err);
|
console.error("Error during Map.update", err);
|
||||||
} finally {
|
|
||||||
++self.renderFrameId; // [WARNING] It's important to increment the renderFrameId AFTER all the operations above!!!
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
applyInputFrameDownsyncDynamicsOnSingleRenderFrame(delayedInputFrame, currRenderFrame, collisionSys, collisionSysMap) {
|
|
||||||
const self = this;
|
|
||||||
const nextRenderFramePlayers = {};
|
|
||||||
for (let playerId in currRenderFrame.players) {
|
|
||||||
const currPlayerDownsync = currRenderFrame.players[playerId];
|
|
||||||
nextRenderFramePlayers[playerId] = {
|
|
||||||
id: playerId,
|
|
||||||
virtualGridX: currPlayerDownsync.virtualGridX,
|
|
||||||
virtualGridY: currPlayerDownsync.virtualGridY,
|
|
||||||
dirX: currPlayerDownsync.dirX,
|
|
||||||
dirY: currPlayerDownsync.dirY,
|
|
||||||
velX: currPlayerDownsync.velX,
|
|
||||||
velY: currPlayerDownsync.velY,
|
|
||||||
characterState: currPlayerDownsync.characterState,
|
|
||||||
inAir: true, // will be updated if collided with a barrier with "0 > pushbackY"
|
|
||||||
speed: currPlayerDownsync.speed,
|
|
||||||
battleState: currPlayerDownsync.battleState,
|
|
||||||
score: currPlayerDownsync.score,
|
|
||||||
removed: currPlayerDownsync.removed,
|
|
||||||
joinIndex: currPlayerDownsync.joinIndex,
|
|
||||||
framesToRecover: (0 < currPlayerDownsync.framesToRecover ? currPlayerDownsync.framesToRecover - 1 : 0),
|
|
||||||
hp: currPlayerDownsync.hp,
|
|
||||||
maxHp: currPlayerDownsync.maxHp,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
const nextRenderFrameMeleeBullets = [];
|
|
||||||
|
|
||||||
// Guaranteed determinism regardless of traversal order
|
|
||||||
const jumpTriggered = new Array(self.playerRichInfoArr.length);
|
|
||||||
const movements = new Array(self.playerRichInfoArr.length);
|
|
||||||
const bulletPushbacks = new Array(self.playerRichInfoArr.length);
|
|
||||||
const effPushbacks = new Array(self.playerRichInfoArr.length);
|
|
||||||
|
|
||||||
// Reset playerCollider position from the "virtual grid position"
|
|
||||||
for (let j in self.playerRichInfoArr) {
|
|
||||||
const joinIndex = parseInt(j) + 1;
|
|
||||||
jumpTriggered[joinIndex - 1] = false;
|
|
||||||
movements[joinIndex - 1] = [0.0, 0.0];
|
|
||||||
bulletPushbacks[joinIndex - 1] = [0.0, 0.0];
|
|
||||||
effPushbacks[joinIndex - 1] = [0.0, 0.0];
|
|
||||||
const playerRichInfo = self.playerRichInfoArr[j];
|
|
||||||
const playerId = playerRichInfo.id;
|
|
||||||
const collisionPlayerIndex = self.collisionPlayerIndexPrefix + joinIndex;
|
|
||||||
const playerCollider = collisionSysMap.get(collisionPlayerIndex);
|
|
||||||
const currPlayerDownsync = currRenderFrame.players[playerId];
|
|
||||||
const thatPlayerInNextFrame = nextRenderFramePlayers[playerId];
|
|
||||||
|
|
||||||
const newVx = currPlayerDownsync.virtualGridX;
|
|
||||||
const newVy = currPlayerDownsync.virtualGridY;
|
|
||||||
[playerCollider.x, playerCollider.y] = self.virtualGridToPolygonColliderAnchorPos(newVx, newVy, self.playerRichInfoArr[joinIndex - 1].colliderRadius, self.playerRichInfoArr[joinIndex - 1].colliderRadius);
|
|
||||||
|
|
||||||
// Process gravity before anyother interaction, by now "currPlayerDownsync.velX & velY" are properly snapped to be parallel to the edge of its standing platform if necessary
|
|
||||||
[movements[joinIndex - 1][0], movements[joinIndex - 1][1]] = self.virtualGridToWorldPos(currPlayerDownsync.velX, currPlayerDownsync.velY);
|
|
||||||
playerCollider.x += movements[joinIndex - 1][0];
|
|
||||||
playerCollider.y += movements[joinIndex - 1][1];
|
|
||||||
if (currPlayerDownsync.inAir) {
|
|
||||||
thatPlayerInNextFrame.velX += self.gravityX;
|
|
||||||
thatPlayerInNextFrame.velY += self.gravityY;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check bullet-anything collisions first, because the pushbacks caused by bullets might later be reverted by player-barrier collision
|
|
||||||
const bulletColliders = new Map(); // Will all be removed at the end of `applyInputFrameDownsyncDynamicsOnSingleRenderFrame` due to the need for being rollback-compatible
|
|
||||||
const removedBulletsAtCurrFrame = new Set();
|
|
||||||
for (let k in currRenderFrame.meleeBullets) {
|
|
||||||
const meleeBullet = currRenderFrame.meleeBullets[k];
|
|
||||||
if (
|
|
||||||
meleeBullet.originatedRenderFrameId + meleeBullet.startupFrames <= currRenderFrame.id
|
|
||||||
&&
|
|
||||||
meleeBullet.originatedRenderFrameId + meleeBullet.startupFrames + meleeBullet.activeFrames > currRenderFrame.id
|
|
||||||
) {
|
|
||||||
const collisionBulletIndex = self.collisionBulletIndexPrefix + meleeBullet.battleLocalId;
|
|
||||||
const collisionOffenderIndex = self.collisionPlayerIndexPrefix + meleeBullet.offenderJoinIndex;
|
|
||||||
const offenderCollider = collisionSysMap.get(collisionOffenderIndex);
|
|
||||||
const offender = currRenderFrame.players[meleeBullet.offenderPlayerId];
|
|
||||||
|
|
||||||
let xfac = 1; // By now, straight Punch offset doesn't respect "y-axis"
|
|
||||||
if (0 > offender.dirX) {
|
|
||||||
xfac = -1;
|
|
||||||
}
|
|
||||||
const [offenderWx, offenderWy] = self.virtualGridToWorldPos(offender.virtualGridX, offender.virtualGridY);
|
|
||||||
const bulletWx = offenderWx + xfac * meleeBullet.hitboxOffset;
|
|
||||||
const bulletWy = offenderWy + 0.5 * meleeBullet.hitboxSize.y;
|
|
||||||
const [bulletCx, bulletCy] = self.worldToPolygonColliderAnchorPos(bulletWx, bulletWy, meleeBullet.hitboxSize.x * 0.5, meleeBullet.hitboxSize.y * 0.5),
|
|
||||||
pts = [[0, 0], [meleeBullet.hitboxSize.x, 0], [meleeBullet.hitboxSize.x, meleeBullet.hitboxSize.y], [0, meleeBullet.hitboxSize.y]];
|
|
||||||
const newBulletCollider = collisionSys.createPolygon(bulletCx, bulletCy, pts);
|
|
||||||
newBulletCollider.data = meleeBullet;
|
|
||||||
collisionSysMap.set(collisionBulletIndex, newBulletCollider);
|
|
||||||
bulletColliders.set(collisionBulletIndex, newBulletCollider);
|
|
||||||
// console.log(`A meleeBullet is added to collisionSys at currRenderFrame.id=${currRenderFrame.id} as start-up frames ended and active frame is not yet ended: ${JSON.stringify(meleeBullet)}`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
collisionSys.update();
|
|
||||||
const result1 = collisionSys.createResult(); // Can I reuse a "self.collisionSysResult" object throughout the whole battle?
|
|
||||||
|
|
||||||
bulletColliders.forEach((bulletCollider, collisionBulletIndex) => {
|
|
||||||
const potentials = bulletCollider.potentials();
|
|
||||||
const offender = currRenderFrame.players[bulletCollider.data.offenderPlayerId];
|
|
||||||
let shouldRemove = false;
|
|
||||||
for (const potential of potentials) {
|
|
||||||
if (null != potential.data && potential.data.joinIndex == bulletCollider.data.offenderJoinIndex) continue;
|
|
||||||
if (!bulletCollider.collides(potential, result1)) continue;
|
|
||||||
if (null != potential.data && null !== potential.data.joinIndex) {
|
|
||||||
const joinIndex = potential.data.joinIndex;
|
|
||||||
let xfac = 1;
|
|
||||||
if (0 > offender.dirX) {
|
|
||||||
xfac = -1;
|
|
||||||
}
|
|
||||||
bulletPushbacks[joinIndex - 1][0] += xfac * bulletCollider.data.pushback; // Only for straight punch, there's no y-pushback
|
|
||||||
bulletPushbacks[joinIndex - 1][1] += 0;
|
|
||||||
const thatAckedPlayerInCurFrame = currRenderFrame.players[potential.data.id];
|
|
||||||
const thatAckedPlayerInNextFrame = nextRenderFramePlayers[potential.data.id];
|
|
||||||
thatAckedPlayerInNextFrame.characterState = window.ATK_CHARACTER_STATE.Atked1[0];
|
|
||||||
if (thatAckedPlayerInCurFrame.inAir) {
|
|
||||||
thatAckedPlayerInNextFrame.characterState = window.toInAirConjugate(thatAckedPlayerInNextFrame.characterState);
|
|
||||||
}
|
|
||||||
const oldFramesToRecover = thatAckedPlayerInNextFrame.framesToRecover;
|
|
||||||
thatAckedPlayerInNextFrame.framesToRecover = (oldFramesToRecover > bulletCollider.data.hitStunFrames ? oldFramesToRecover : bulletCollider.data.hitStunFrames); // In case the hit player is already stun, we extend it
|
|
||||||
}
|
|
||||||
shouldRemove = true;
|
|
||||||
}
|
|
||||||
if (shouldRemove) {
|
|
||||||
removedBulletsAtCurrFrame.add(collisionBulletIndex);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// [WARNING] Remove bullets from collisionSys ANYWAY for the convenience of rollback
|
|
||||||
for (let k in currRenderFrame.meleeBullets) {
|
|
||||||
const meleeBullet = currRenderFrame.meleeBullets[k];
|
|
||||||
const collisionBulletIndex = self.collisionBulletIndexPrefix + meleeBullet.battleLocalId;
|
|
||||||
if (collisionSysMap.has(collisionBulletIndex)) {
|
|
||||||
const bulletCollider = collisionSysMap.get(collisionBulletIndex);
|
|
||||||
bulletCollider.remove();
|
|
||||||
collisionSysMap.delete(collisionBulletIndex);
|
|
||||||
}
|
|
||||||
if (removedBulletsAtCurrFrame.has(collisionBulletIndex)) continue;
|
|
||||||
nextRenderFrameMeleeBullets.push(meleeBullet);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Process player inputs
|
|
||||||
if (null != delayedInputFrame) {
|
|
||||||
const delayedInputFrameForPrevRenderFrame = self.getCachedInputFrameDownsyncWithPrediction(self._convertToInputFrameId(currRenderFrame.id - 1, self.inputDelayFrames));
|
|
||||||
const inputList = delayedInputFrame.inputList;
|
|
||||||
for (let j in self.playerRichInfoArr) {
|
|
||||||
const joinIndex = parseInt(j) + 1;
|
|
||||||
effPushbacks[joinIndex - 1] = [0.0, 0.0];
|
|
||||||
const playerRichInfo = self.playerRichInfoArr[j];
|
|
||||||
const playerId = playerRichInfo.id;
|
|
||||||
const collisionPlayerIndex = self.collisionPlayerIndexPrefix + joinIndex;
|
|
||||||
const playerCollider = collisionSysMap.get(collisionPlayerIndex);
|
|
||||||
const currPlayerDownsync = currRenderFrame.players[playerId];
|
|
||||||
const thatPlayerInNextFrame = nextRenderFramePlayers[playerId];
|
|
||||||
if (0 < thatPlayerInNextFrame.framesToRecover) {
|
|
||||||
// No need to process inputs for this player, but there might be bullet pushbacks on this player
|
|
||||||
playerCollider.x += bulletPushbacks[joinIndex - 1][0];
|
|
||||||
playerCollider.y += bulletPushbacks[joinIndex - 1][1];
|
|
||||||
if (0 != bulletPushbacks[joinIndex - 1][0] || 0 != bulletPushbacks[joinIndex - 1][1]) {
|
|
||||||
console.log(`playerId=${playerId}, joinIndex=${joinIndex} is pushbacked back by ${bulletPushbacks[joinIndex - 1]} by bullet impacts, now its framesToRecover is ${thatPlayerInNextFrame.framesToRecover}`);
|
|
||||||
}
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
const decodedInput = self.ctrl.decodeInput(inputList[joinIndex - 1]);
|
|
||||||
const prevDecodedInput = (null == delayedInputFrameForPrevRenderFrame ? null : self.ctrl.decodeInput(delayedInputFrameForPrevRenderFrame.inputList[joinIndex - 1]));
|
|
||||||
const prevBtnALevel = (null == prevDecodedInput ? 0 : prevDecodedInput.btnALevel);
|
|
||||||
const prevBtnBLevel = (null == prevDecodedInput ? 0 : prevDecodedInput.btnBLevel);
|
|
||||||
/*
|
|
||||||
[WARNING] Player input alone WOULD NOT take "characterState" into any "ATK_CHARACTER_STATE_IN_AIR_SET", only after the calculation of "effPushbacks" do we know exactly whether or not a player is "inAir", the finalize the transition of "thatPlayerInNextFrame.characterState".
|
|
||||||
*/
|
|
||||||
if (1 == decodedInput.btnBLevel && 0 == prevBtnBLevel) {
|
|
||||||
const characStateAlreadyInAir = window.ATK_CHARACTER_STATE_IN_AIR_SET.has(thatPlayerInNextFrame.characterState);
|
|
||||||
const characStateIsInterruptWaivable = window.ATK_CHARACTER_STATE_INTERRUPT_WAIVE_SET.has(thatPlayerInNextFrame.characterState);
|
|
||||||
if (
|
|
||||||
!characStateAlreadyInAir
|
|
||||||
&&
|
|
||||||
characStateIsInterruptWaivable
|
|
||||||
) {
|
|
||||||
thatPlayerInNextFrame.velY = self.jumpingInitVelY;
|
|
||||||
jumpTriggered[joinIndex - 1] = true;
|
|
||||||
console.log(`playerId=${playerId}, joinIndex=${joinIndex} triggered a rising-edge of btnB at renderFrame.id=${currRenderFrame.id}, delayedInputFrame.id=${delayedInputFrame.inputFrameId}, nextVelY=${thatPlayerInNextFrame.velY}, characStateAlreadyInAir=${characStateAlreadyInAir}, characStateIsInterruptWaivable=${characStateIsInterruptWaivable}`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (1 == decodedInput.btnALevel && 0 == prevBtnALevel) {
|
|
||||||
// console.log(`playerId=${playerId} triggered a rising-edge of btnA at renderFrame.id=${currRenderFrame.id}, delayedInputFrame.id=${delayedInputFrame.inputFrameId}`);
|
|
||||||
if (self.bulletTriggerEnabled) {
|
|
||||||
const punchSkillId = 1;
|
|
||||||
const punch = window.pb.protos.MeleeBullet.create(self.meleeSkillConfig[punchSkillId]);
|
|
||||||
thatPlayerInNextFrame.framesToRecover = punch.recoveryFrames;
|
|
||||||
punch.battleLocalId = self.bulletBattleLocalIdCounter++;
|
|
||||||
punch.offenderJoinIndex = joinIndex;
|
|
||||||
punch.offenderPlayerId = playerId;
|
|
||||||
punch.originatedRenderFrameId = currRenderFrame.id;
|
|
||||||
nextRenderFrameMeleeBullets.push(punch);
|
|
||||||
// console.log(`A rising-edge of meleeBullet is created at renderFrame.id=${currRenderFrame.id}, delayedInputFrame.id=${delayedInputFrame.inputFrameId}: ${self._stringifyRecentInputCache(true)}`);
|
|
||||||
// console.log(`A rising-edge of meleeBullet is created at renderFrame.id=${currRenderFrame.id}, delayedInputFrame.id=${delayedInputFrame.inputFrameId}`);
|
|
||||||
|
|
||||||
thatPlayerInNextFrame.characterState = window.ATK_CHARACTER_STATE.Atk1[0];
|
|
||||||
if (false == currPlayerDownsync.inAir) {
|
|
||||||
thatPlayerInNextFrame.velX = 0; // prohibits simultaneous movement with Atk1 on the ground
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if (0 == decodedInput.btnALevel && 1 == prevBtnALevel) {
|
|
||||||
// console.log(`playerId=${playerId} triggered a falling-edge of btnA at renderFrame.id=${currRenderFrame.id}, delayedInputFrame.id=${delayedInputFrame.inputFrameId}`);
|
|
||||||
} else {
|
|
||||||
// No bullet trigger, process joystick movement inputs (except for jumping).
|
|
||||||
if (0 != decodedInput.dx || 0 != decodedInput.dy) {
|
|
||||||
// Update directions and thus would eventually update moving animation accordingly
|
|
||||||
thatPlayerInNextFrame.dirX = decodedInput.dx;
|
|
||||||
thatPlayerInNextFrame.dirY = decodedInput.dy;
|
|
||||||
thatPlayerInNextFrame.velX = decodedInput.dx * currPlayerDownsync.speed;
|
|
||||||
thatPlayerInNextFrame.characterState = window.ATK_CHARACTER_STATE.Walking[0];
|
|
||||||
} else {
|
|
||||||
thatPlayerInNextFrame.characterState = window.ATK_CHARACTER_STATE.Idle1[0];
|
|
||||||
thatPlayerInNextFrame.velX = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (currPlayerDownsync.inAir) {
|
|
||||||
thatPlayerInNextFrame.characterState = window.toInAirConjugate(thatPlayerInNextFrame.characterState);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
collisionSys.update(); // by now all "bulletCollider"s are removed
|
|
||||||
const result2 = collisionSys.createResult(); // Can I reuse a "self.collisionSysResult" object throughout the whole battle?
|
|
||||||
|
|
||||||
for (let j in self.playerRichInfoArr) {
|
|
||||||
const joinIndex = parseInt(j) + 1;
|
|
||||||
const playerId = self.playerRichInfoArr[j].id;
|
|
||||||
const collisionPlayerIndex = self.collisionPlayerIndexPrefix + joinIndex;
|
|
||||||
const playerCollider = collisionSysMap.get(collisionPlayerIndex);
|
|
||||||
const potentials = playerCollider.potentials();
|
|
||||||
const currPlayerDownsync = currRenderFrame.players[playerId];
|
|
||||||
const thatPlayerInNextFrame = nextRenderFramePlayers[playerId];
|
|
||||||
let fallStopping = false;
|
|
||||||
let [snappedIntoPlatformEx, snappedIntoPlatformEy] = [null, null];
|
|
||||||
for (const potential of potentials) {
|
|
||||||
// Test if the player collides with the wall
|
|
||||||
if (!playerCollider.collides(potential, result2)) continue;
|
|
||||||
// Push the player out of the wall
|
|
||||||
let [pushbackX, pushbackY] = [result2.overlap * result2.overlap_x, result2.overlap * result2.overlap_y];
|
|
||||||
if (null == potential.data) {
|
|
||||||
// "null == potential.data" implies a barrier
|
|
||||||
const normAlignmentWithGravity = (result2.overlap_x * 0 + result2.overlap_y * (-1.0));
|
|
||||||
const flatEnough = (self.snapIntoPlatformThreshold < normAlignmentWithGravity); // prevents false snapping on the lateral sides
|
|
||||||
const remainsNotInAir = (!currPlayerDownsync.inAir && flatEnough);
|
|
||||||
const localFallStopping = (currPlayerDownsync.inAir && flatEnough);
|
|
||||||
if (remainsNotInAir || localFallStopping) {
|
|
||||||
fallStopping |= localFallStopping;
|
|
||||||
[pushbackX, pushbackY] = [(result2.overlap - self.snapIntoPlatformOverlap) * result2.overlap_x, (result2.overlap - self.snapIntoPlatformOverlap) * result2.overlap_y]
|
|
||||||
// [overlay_x, overlap_y] is the unit vector that points into the platform; FIXME: Should only assign to [snappedIntoPlatformEx, snappedIntoPlatformEy] at most once!
|
|
||||||
snappedIntoPlatformEx = -result2.overlap_y;
|
|
||||||
snappedIntoPlatformEy = result2.overlap_x;
|
|
||||||
if (snappedIntoPlatformEx * currPlayerDownsync.dirX + snappedIntoPlatformEy * currPlayerDownsync.dirY) {
|
|
||||||
[snappedIntoPlatformEx, snappedIntoPlatformEy] = [-snappedIntoPlatformEx, -snappedIntoPlatformEy];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// What if we're on the edge of 2 barriers? Would adding up make an unexpected bounce?
|
|
||||||
effPushbacks[joinIndex - 1][0] += pushbackX;
|
|
||||||
effPushbacks[joinIndex - 1][1] += pushbackY;
|
|
||||||
}
|
|
||||||
if (false == jumpTriggered[joinIndex - 1] && null != snappedIntoPlatformEx && null != snappedIntoPlatformEy) {
|
|
||||||
thatPlayerInNextFrame.inAir = false;
|
|
||||||
if (fallStopping) {
|
|
||||||
thatPlayerInNextFrame.velY = 0;
|
|
||||||
thatPlayerInNextFrame.velX = 0;
|
|
||||||
thatPlayerInNextFrame.characterState = window.ATK_CHARACTER_STATE.Idle1[0];
|
|
||||||
thatPlayerInNextFrame.framesToRecover = 0;
|
|
||||||
}
|
|
||||||
const dotProd = thatPlayerInNextFrame.velX * snappedIntoPlatformEx + thatPlayerInNextFrame.velY * snappedIntoPlatformEy;
|
|
||||||
[thatPlayerInNextFrame.velX, thatPlayerInNextFrame.velY] = [dotProd * snappedIntoPlatformEx, dotProd * snappedIntoPlatformEy];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get players out of stuck barriers if there's any
|
|
||||||
for (let j in self.playerRichInfoArr) {
|
|
||||||
const joinIndex = parseInt(j) + 1;
|
|
||||||
const playerId = self.playerRichInfoArr[j].id;
|
|
||||||
const collisionPlayerIndex = self.collisionPlayerIndexPrefix + joinIndex;
|
|
||||||
const playerCollider = collisionSysMap.get(collisionPlayerIndex);
|
|
||||||
const thatPlayerInNextFrame = nextRenderFramePlayers[playerId];
|
|
||||||
[thatPlayerInNextFrame.virtualGridX, thatPlayerInNextFrame.virtualGridY] = self.polygonColliderAnchorToVirtualGridPos(playerCollider.x - effPushbacks[joinIndex - 1][0], playerCollider.y - effPushbacks[joinIndex - 1][1], self.playerRichInfoArr[j].colliderRadius, self.playerRichInfoArr[j].colliderRadius);
|
|
||||||
}
|
|
||||||
|
|
||||||
return window.pb.protos.RoomDownsyncFrame.create({
|
|
||||||
id: currRenderFrame.id + 1,
|
|
||||||
players: nextRenderFramePlayers,
|
|
||||||
meleeBullets: nextRenderFrameMeleeBullets,
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
applyRoomDownsyncFrameDynamics(rdf, prevRdf) {
|
|
||||||
const self = this;
|
|
||||||
OnlineMap.prototype.applyRoomDownsyncFrameDynamics.call(self, rdf, prevRdf);
|
|
||||||
if (self.showCriticalCoordinateLabels) {
|
|
||||||
let g = self.g;
|
|
||||||
g.clear();
|
|
||||||
|
|
||||||
for (let k in self.collisionSys._bvh._bodies) {
|
|
||||||
const body = self.collisionSys._bvh._bodies[k];
|
|
||||||
if (!body._polygon) continue;
|
|
||||||
if (null != body.data && null != body.data.joinIndex) {
|
|
||||||
// character
|
|
||||||
if (1 == body.data.joinIndex) {
|
|
||||||
g.strokeColor = cc.Color.BLUE;
|
|
||||||
} else {
|
|
||||||
g.strokeColor = cc.Color.RED;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// barrier
|
|
||||||
g.strokeColor = cc.Color.WHITE;
|
|
||||||
}
|
|
||||||
g.moveTo(body.x, body.y);
|
|
||||||
const cnt = body._coords.length;
|
|
||||||
for (let j = 0; j < cnt; j += 2) {
|
|
||||||
const x = body._coords[j],
|
|
||||||
y = body._coords[j + 1];
|
|
||||||
g.lineTo(x, y);
|
|
||||||
}
|
|
||||||
g.lineTo(body.x, body.y);
|
|
||||||
g.stroke();
|
|
||||||
}
|
|
||||||
// For convenience of recovery upon reconnection, active bullets are always created & immediately removed from "collisionSys" within "applyInputFrameDownsyncDynamicsOnSingleRenderFrame"
|
|
||||||
|
|
||||||
for (let k in rdf.meleeBullets) {
|
|
||||||
const meleeBullet = rdf.meleeBullets[k];
|
|
||||||
if (
|
|
||||||
meleeBullet.originatedRenderFrameId + meleeBullet.startupFrames <= rdf.id
|
|
||||||
&&
|
|
||||||
meleeBullet.originatedRenderFrameId + meleeBullet.startupFrames + meleeBullet.activeFrames > rdf.id
|
|
||||||
) {
|
|
||||||
const offender = rdf.players[meleeBullet.offenderPlayerId];
|
|
||||||
if (1 == offender.joinIndex) {
|
|
||||||
g.strokeColor = cc.Color.BLUE;
|
|
||||||
} else {
|
|
||||||
g.strokeColor = cc.Color.RED;
|
|
||||||
}
|
|
||||||
|
|
||||||
let xfac = 1; // By now, straight Punch offset doesn't respect "y-axis"
|
|
||||||
if (0 > offender.dirX) {
|
|
||||||
xfac = -1;
|
|
||||||
}
|
|
||||||
const [offenderWx, offenderWy] = self.virtualGridToWorldPos(offender.virtualGridX, offender.virtualGridY);
|
|
||||||
const bulletWx = offenderWx + xfac * meleeBullet.hitboxOffset;
|
|
||||||
const bulletWy = offenderWy + 0.5 * meleeBullet.hitboxSize.y;
|
|
||||||
const [bulletCx, bulletCy] = self.worldToPolygonColliderAnchorPos(bulletWx, bulletWy, meleeBullet.hitboxSize.x * 0.5, meleeBullet.hitboxSize.y * 0.5),
|
|
||||||
pts = [[0, 0], [meleeBullet.hitboxSize.x, 0], [meleeBullet.hitboxSize.x, meleeBullet.hitboxSize.y], [0, meleeBullet.hitboxSize.y]];
|
|
||||||
|
|
||||||
g.moveTo(bulletCx, bulletCy);
|
|
||||||
for (let j = 0; j < pts.length; j += 1) {
|
|
||||||
g.lineTo(pts[j][0] + bulletCx, pts[j][1] + bulletCy);
|
|
||||||
}
|
|
||||||
g.lineTo(bulletCx, bulletCy);
|
|
||||||
g.stroke();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
Loading…
Reference in New Issue
Block a user