A broken commit during backend bullet adaptation.

This commit is contained in:
genxium
2022-11-23 22:11:28 +08:00
parent becc56f672
commit fdc296531a
13 changed files with 1373 additions and 1081 deletions

View File

@@ -1,17 +1,18 @@
<?xml version="1.0" encoding="UTF-8"?>
<map version="1.2" tiledversion="1.2.3" orientation="orthogonal" renderorder="right-down" width="64" height="64" tilewidth="16" tileheight="16" infinite="0" nextlayerid="2" nextobjectid="12">
<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="39">
<tileset firstgid="1" source="tiles0.tsx"/>
<tileset firstgid="65" source="tiles1.tsx"/>
<layer id="1" name="Ground" width="64" height="64">
<tileset firstgid="129" source="tiles2.tsx"/>
<layer id="2" name="Ground" width="128" height="128">
<data encoding="base64" compression="zlib">
eJzt0jEOwjAUREGLkiLmALRU3In7HwMKkFCUCCwiNrKnmCbVvh8fSikHAAAA/qruYEOr84Zd+vM9re0v+vXr1z9S/7f0l3KZ9dXZ91r67QeAXxyf0jv0Z/tvO9iS7F+T3ucG+Xuk96RvkN4CMJLaIL0VttLy7r19euHdA+zX6YP0vlR3z/3ax2tfarw+TAO0L/VPb3ruXvv3o7z7lv70zuQN0vtgz+6DDSsZ
eJzt2q1u22AYhuEo1UhBtYFWKh/bmRRMRYNFPZPxwcKi7jznSInkWk7teG5ex88FLhQD570d5/PP1WazuQIAAAAAAAAAAAAAAAAAgAmu96r3g/N5bjn0fw5U3aGy/3bv0P+p8XDEdoX0f9//GP3Xp93/YaD/9QJa6f95/fvov26773671zeb7jmhupX+5+2fQH/9qztU9v8yQXUz/fXXX3/9a/ovgf7666+//vrrf1n9/4zc7tue/uvq/9Z4PfLZ7vnz38ZP/cv7T53p4Z7i7/+kv/7667/bl/sO/dff/77T3+9ff/3r+38dMGf/Nv1r+w917+v/0vh1okN/67/l9B/b/nFj/X8pxvYf236r/0UZ6t/X+Efj5kj7bv+uxxPpX9e/r/1NS7f7Z/S/a3zXfzH9Pzrvt/vPTf/l9p+yRjyV/ufvP+YY6M5yzr5D5uy/XUCLJfafa8YfmdJe//X0r6Z/9jGgv/7661/doqp/9fyrpV//Vc+/mv7Z9M+mfzb9s+mfTf9s+mfTP5v+2fTPpn82/bPpn03/bPpn0z+b/tn0z6Z/Nv2z6Z/N+9/Z9M+mfzb//9n0z6Z/Nv2z6Z9N/2z6Z9M/m/7Z0vtT3wEAAAAAAAAAAAAAAAA4v39IY4NC
</data>
</layer>
<objectgroup id="1" name="PlayerStartingPos">
<object id="135" x="512" y="512">
<object id="135" x="1090" y="833.667">
<point/>
</object>
<object id="137" x="640" y="640">
<object id="137" x="1215" y="830.5">
<point/>
</object>
</objectgroup>
@@ -19,71 +20,149 @@
<properties>
<property name="type" value="barrier_and_shelter"/>
</properties>
<object id="1" x="400" y="224.5">
<properties>
<property name="boundary_type" value="barrier"/>
</properties>
<polyline points="0,0 0,141.5 17,141.5 17,-1"/>
</object>
<object id="2" x="559.5" y="207.5">
<properties>
<property name="boundary_type" value="barrier"/>
</properties>
<polyline points="0,0 1,158.5 17,158.5 17,0"/>
</object>
<object id="3" x="448.5" y="353">
<properties>
<property name="boundary_type" value="barrier"/>
</properties>
<polyline points="0,0 0,14.5 -49,14.5 -49,-1.5"/>
</object>
<object id="4" x="577.5" y="351.5">
<properties>
<property name="boundary_type" value="barrier"/>
</properties>
<polyline points="0,0 -98,1 -98,16 -1,15.5"/>
</object>
<object id="5" x="449.333" y="654.667">
<properties>
<property name="boundary_type" value="barrier"/>
</properties>
<polyline points="0,0 -178.667,0.666667 -178.667,17.3333 -0.666667,17.3333"/>
</object>
<object id="6" x="432.5" y="703.5">
<properties>
<property name="boundary_type" value="barrier"/>
</properties>
<polyline points="0,0 -191.667,0.666667 -191.667,17.3333 -0.715174,17.3333"/>
</object>
<object id="7" x="400" y="751">
<properties>
<property name="boundary_type" value="barrier"/>
</properties>
<polyline points="0,0 -191.667,0.666667 -191.667,17.3333 -0.715174,17.3333"/>
</object>
<object id="8" x="-9.33333" y="-13.3333">
<object id="8" x="648.242" y="480.606">
<properties>
<property name="boundary_type" value="barrier"/>
</properties>
<polyline points="0,0 0,18.6667 1041.33,21.3333 1041.33,-1.33333"/>
</object>
<object id="9" x="-9.33333" y="1014.67">
<object id="9" x="650.667" y="1604.67">
<properties>
<property name="boundary_type" value="barrier"/>
</properties>
<polyline points="0,0 0,18.6667 1041.33,21.3333 1041.33,-1.33333"/>
</object>
<object id="10" x="-14" y="-40">
<object id="10" x="634.485" y="505.455">
<properties>
<property name="boundary_type" value="barrier"/>
</properties>
<polyline points="0,0 4,1110 24,1110 24,-8"/>
</object>
<object id="11" x="1014" y="-42">
<object id="11" x="1677.64" y="501.333">
<properties>
<property name="boundary_type" value="barrier"/>
</properties>
<polyline points="0,0 4,1110 24,1110 24,-8"/>
</object>
<object id="14" x="688.667" y="464">
<properties>
<property name="boundary_type" value="barrier"/>
</properties>
<polyline points="0,0 -0.666667,78 33.3333,78 32,-0.666667"/>
</object>
<object id="15" x="833.333" y="495.333">
<properties>
<property name="boundary_type" value="barrier"/>
</properties>
<polyline points="0,0 -112,1.33333 -111.333,44.6667 -1.33333,44.6667"/>
</object>
<object id="17" x="832" y="574">
<properties>
<property name="boundary_type" value="barrier"/>
</properties>
<polyline points="0,0 -67.3333,0 -67.3333,-76.6667 0.666667,-76"/>
</object>
<object id="18" x="865.333" y="606.667">
<properties>
<property name="boundary_type" value="barrier"/>
</properties>
<polyline points="0,0 -210,-0.666667 -210,143.333 0,142.667"/>
</object>
<object id="19" x="754.667" y="1055.33">
<properties>
<property name="boundary_type" value="barrier"/>
</properties>
<polyline points="-2,1.33333 -100,0.666667 -97.3333,-454 -9.33333,-451.333"/>
</object>
<object id="20" x="769.333" y="747.333">
<properties>
<property name="boundary_type" value="barrier"/>
</properties>
<polyline points="0,0 -115.333,0.666667 -114.667,160.667 -2,162"/>
</object>
<object id="21" x="768" y="960.667">
<properties>
<property name="boundary_type" value="barrier"/>
</properties>
<polyline points="0,0 -18,0.666667 -18.6667,95.3333 0,95.3333"/>
</object>
<object id="23" x="786" y="1058.67">
<properties>
<property name="boundary_type" value="barrier"/>
</properties>
<polyline points="0,0 0,-52.6667 -20,-52 -19.3333,-1.33333"/>
</object>
<object id="24" x="1118.67" y="749.333">
<properties>
<property name="boundary_type" value="barrier"/>
</properties>
<polyline points="0,0 -0.666667,-94 -254,-93.3333 -256.667,1.33333"/>
</object>
<object id="25" x="1168" y="975.333">
<properties>
<property name="boundary_type" value="barrier"/>
</properties>
<polyline points="0,0 0,16.6667 224.667,17.3333 225.333,-2"/>
</object>
<object id="28" x="1394.67" y="958">
<properties>
<property name="boundary_type" value="barrier"/>
</properties>
<polyline points="0,0 -210.667,1.33333 -210.667,17.3333 -2,18"/>
</object>
<object id="29" x="1119" y="654.5">
<properties>
<property name="boundary_type" value="barrier"/>
</properties>
<polyline points="0,0 0,63.5 272.5,65 273,-1"/>
</object>
<object id="30" x="1136.5" y="717.5">
<properties>
<property name="boundary_type" value="barrier"/>
</properties>
<polyline points="0,0 -0.5,17 255,17.5 255,1.5"/>
</object>
<object id="31" x="1152" y="735.667">
<properties>
<property name="boundary_type" value="barrier"/>
</properties>
<polyline points="0,0 0,15 80.3333,15.3333 80.3333,-2"/>
</object>
<object id="32" x="1280.67" y="734.667">
<properties>
<property name="boundary_type" value="barrier"/>
</properties>
<polyline points="0,0 -0.666667,64.3333 48,65 48,0"/>
</object>
<object id="34" x="1329" y="783">
<properties>
<property name="boundary_type" value="barrier"/>
</properties>
<polyline points="0,0 63.3333,0.333333 63,-48.3333 -0.666667,-48.3333"/>
</object>
<object id="35" x="1296.67" y="799">
<properties>
<property name="boundary_type" value="barrier"/>
</properties>
<polyline points="0,0 -0.666667,31.3333 31.3333,31.6667 31.3333,-0.333333"/>
</object>
<object id="36" x="1280" y="848">
<properties>
<property name="boundary_type" value="barrier"/>
</properties>
<polyline points="0,0 0.333333,62 112,63 111.667,-1.33333"/>
</object>
<object id="37" x="1392.33" y="911.333">
<properties>
<property name="boundary_type" value="barrier"/>
</properties>
<polyline points="0,0 -81.3333,-0.666667 -81,45 -0.666667,46.3333"/>
</object>
<object id="38" x="1344.33" y="800.667">
<properties>
<property name="boundary_type" value="barrier"/>
</properties>
<polyline points="0,0 0,46.3333 47,46.3333 47,-1"/>
</object>
</objectgroup>
</map>

View File

@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="UTF-8"?>
<tileset version="1.2" tiledversion="1.2.3" name="tiles2" tilewidth="16" tileheight="16" tilecount="64" columns="16">
<image source="watabou_pixel_dungeon_orig_files/tiles2.png" width="256" height="64"/>
</tileset>

View File

@@ -0,0 +1,5 @@
{
"ver": "2.0.0",
"uuid": "2e53500f-f9c8-4b5b-b74c-f2adbc2ec34d",
"subMetas": {}
}

View File

@@ -38,22 +38,23 @@ message PlayerDownsync {
int32 id = 1;
int32 virtualGridX = 2;
int32 virtualGridY = 3;
sharedprotos.Direction dir = 4;
int32 speed = 5; // in terms of virtual grid units
int32 battleState = 6;
int32 joinIndex = 7;
double colliderRadius = 8;
bool removed = 9;
int32 score = 10;
int32 lastMoveGmtMillis = 11;
int32 framesToRecover = 12;
int32 hp = 13;
int32 maxHp = 14;
int32 characterState = 15;
int32 dirX = 4;
int32 dirY = 5;
int32 speed = 6; // in terms of virtual grid units
int32 battleState = 7;
int32 joinIndex = 8;
double colliderRadius = 9;
bool removed = 10;
int32 score = 11;
int32 lastMoveGmtMillis = 12;
int32 framesToRecover = 13;
int32 hp = 14;
int32 maxHp = 15;
int32 characterState = 16;
string name = 16;
string displayName = 17;
string avatar = 18;
string name = 17;
string displayName = 18;
string avatar = 19;
}
message InputFrameDecoded {
@@ -123,8 +124,6 @@ message MeleeBullet {
int32 offenderJoinIndex = 16;
int32 offenderPlayerId = 17;
int32 offenderPlayerId = 17;
}
message RoomDownsyncFrame {

View File

@@ -277,6 +277,7 @@
"forceBigEndianFloatingNumDecoding": false,
"renderFrameIdLagTolerance": 4,
"jigglingEps1D": 0.001,
"bulletTriggerEnabled": true,
"_id": "4b+kZ46VhC0LCBixXEK2dk"
},
{

View File

@@ -95,6 +95,9 @@ cc.Class({
type: cc.Float,
default: 1e-3
},
bulletTriggerEnabled: {
default: false
},
},
_inputFrameIdDebuggable(inputFrameId) {
@@ -321,10 +324,10 @@ cc.Class({
self.battleState = ALL_BATTLE_STATES.WAITING;
self.countdownNanos = null;
if (self.countdownLabel) {
self.countdownLabel.string = "";
}
self.countdownNanos = null;
if (self.findingPlayerNode) {
const findingPlayerScriptIns = self.findingPlayerNode.getComponent("FindingPlayer");
findingPlayerScriptIns.init();
@@ -741,17 +744,15 @@ cc.Class({
self.playersInfoNode.getComponent("PlayersInfo").clearInfo();
},
spawnPlayerNode(joinIndex, vx, vy, playerRichInfo) {
spawnPlayerNode(joinIndex, vx, vy, playerDownsyncInfo) {
const self = this;
const newPlayerNode = cc.instantiate(self.controlledCharacterPrefab)
const playerScriptIns = newPlayerNode.getComponent("ControlledCharacter");
playerScriptIns.joinIndex = joinIndex;
if (1 == joinIndex) {
playerScriptIns.setSpecies("SoldierWaterGhost");
} else if (2 == joinIndex) {
playerScriptIns.setSpecies("SoldierFireGhost");
if (0 == playerRichInfo.dir.dx && 0 == playerRichInfo.dir.dy) {
if (0 == playerDownsyncInfo.dirX && 0 == playerDownsyncInfo.dirY) {
playerScriptIns.animComp.node.scaleX = (-1.0);
}
}
@@ -760,16 +761,15 @@ cc.Class({
newPlayerNode.setPosition(cc.v2(wpos[0], wpos[1]));
playerScriptIns.mapNode = self.node;
const cpos = self.virtualGridToPlayerColliderPos(vx, vy, playerRichInfo);
const d = playerRichInfo.colliderRadius * 2,
const cpos = self.virtualGridToPlayerColliderPos(vx, vy, playerDownsyncInfo);
const d = playerDownsyncInfo.colliderRadius * 2,
x0 = cpos[0],
y0 = cpos[1];
let pts = [[0, 0], [d, 0], [d, d], [0, d]];
const newPlayerCollider = self.collisionSys.createPolygon(x0, y0, pts);
const collisionPlayerIndex = self.collisionPlayerIndexPrefix + joinIndex;
newPlayerCollider.collisionPlayerIndex = parseInt(collisionPlayerIndex);
newPlayerCollider.playerId = parseInt(playerRichInfo.id);
newPlayerCollider.data = playerDownsyncInfo;
self.collisionSysMap.set(collisionPlayerIndex, newPlayerCollider);
safelyAddChild(self.node, newPlayerNode);
@@ -777,9 +777,9 @@ cc.Class({
newPlayerNode.active = true;
playerScriptIns.updateCharacterAnim({
dx: playerRichInfo.dir.dx,
dy: playerRichInfo.dir.dy
}, playerRichInfo, true);
dx: playerDownsyncInfo.dirX,
dy: playerDownsyncInfo.dirY,
}, playerDownsyncInfo, true);
return [newPlayerNode, playerScriptIns];
},
@@ -973,8 +973,6 @@ cc.Class({
const dy = (wpos[1] - playerRichInfo.node.y);
//const justJiggling = (self.jigglingEps1D >= Math.abs(dx) && self.jigglingEps1D >= Math.abs(dy));
playerRichInfo.node.setPosition(wpos[0], wpos[1]);
playerRichInfo.virtualGridX = immediatePlayerInfo.virtualGridX;
playerRichInfo.virtualGridY = immediatePlayerInfo.virtualGridY;
// TODO: check "rdf.players[playerId].characterState" instead, might have to play Atk/Atked anim!
if (null != delayedInputFrameForPrevRenderFrame) {
const decodedInput = self.ctrl.decodeInput(delayedInputFrameForPrevRenderFrame.inputList[playerRichInfo.joinIndex - 1]);
@@ -1003,17 +1001,15 @@ cc.Class({
// TODO: Write unit-test for this function to compare with its backend counter part
applyInputFrameDownsyncDynamicsOnSingleRenderFrame(delayedInputFrame, currRenderFrame, collisionSys, collisionSysMap) {
const self = this;
const nextRenderFramePlayers = {}
const nextRenderFramePlayers = {};
for (let playerId in currRenderFrame.players) {
const currPlayerDownsync = currRenderFrame.players[playerId];
nextRenderFramePlayers[playerId] = {
id: playerId,
virtualGridX: currPlayerDownsync.virtualGridX,
virtualGridY: currPlayerDownsync.virtualGridY,
dir: {
dx: currPlayerDownsync.dir.dx,
dy: currPlayerDownsync.dir.dy,
},
dirX: currPlayerDownsync.dirX,
dirY: currPlayerDownsync.dirY,
characterState: currPlayerDownsync.characterState,
speed: currPlayerDownsync.speed,
battleState: currPlayerDownsync.battleState,
@@ -1034,6 +1030,8 @@ cc.Class({
const bulletPushbacks = new Array(self.playerRichInfoArr.length); // Guaranteed determinism regardless of traversal order
const effPushbacks = new Array(self.playerRichInfoArr.length); // Guaranteed determinism regardless of traversal order
// Reset playerCollider position from the "virtual grid position"
for (let j in self.playerRichInfoArr) {
const joinIndex = parseInt(j) + 1;
bulletPushbacks[joinIndex - 1] = [0.0, 0.0];
@@ -1042,17 +1040,17 @@ cc.Class({
const playerId = playerRichInfo.id;
const collisionPlayerIndex = self.collisionPlayerIndexPrefix + joinIndex;
const playerCollider = collisionSysMap.get(collisionPlayerIndex);
const player = currRenderFrame.players[playerId];
const currPlayerDownsync = currRenderFrame.players[playerId];
const newVx = player.virtualGridX;
const newVy = player.virtualGridY;
const newVx = currPlayerDownsync.virtualGridX;
const newVy = currPlayerDownsync.virtualGridY;
const newCpos = self.virtualGridToPlayerColliderPos(newVx, newVy, self.playerRichInfoArr[joinIndex - 1]);
playerCollider.x = newCpos[0];
playerCollider.y = newCpos[1];
}
// Check bullet-anything collisions first, because the pushbacks caused by bullets might later be reverted by player-barrier collision
const colliderBullets = new Map(); // Will all be removed at the end of `applyInputFrameDownsyncDynamicsOnSingleRenderFrame` due to the need for being rollback-compatible
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];
@@ -1066,39 +1064,39 @@ cc.Class({
const offenderCollider = collisionSysMap.get(collisionOffenderIndex);
const offender = currRenderFrame.players[meleeBullet.offenderPlayerId];
const xfac = Math.sign(offender.dir.dx),
yfac = 0; // By now, straight Punch offset doesn't respect "y-axis"
let xfac = 1,
yfac = 0; // By now, straight Punch offset doesn't respect "y-axis"
if (0 > offender.dirX) {
xfac = -1;
}
const x0 = offenderCollider.x + xfac * meleeBullet.hitboxOffset,
y0 = offenderCollider.y + yfac * meleeBullet.hitboxOffset;
const pts = [[0, 0], [xfac * meleeBullet.hitboxSize.x, 0], [xfac * meleeBullet.hitboxSize.x, meleeBullet.hitboxSize.y], [0, meleeBullet.hitboxSize.y]];
const newBulletCollider = collisionSys.createPolygon(x0, y0, pts);
newBulletCollider.collisionBulletIndex = collisionBulletIndex;
newBulletCollider.offenderPlayerId = meleeBullet.offenderPlayerId;
newBulletCollider.pushback = meleeBullet.pushback;
newBulletCollider.hitStunFrames = meleeBullet.hitStunFrames;
newBulletCollider.data = meleeBullet;
collisionSysMap.set(collisionBulletIndex, newBulletCollider);
colliderBullets.set(collisionBulletIndex, newBulletCollider);
console.log(`A meleeBullet=${JSON.stringify(meleeBullet)} is added to collisionSys at renderFrame.id=${currRenderFrame.id} as start-up frames ended and active frame is not yet ended`);
bulletColliders.set(collisionBulletIndex, newBulletCollider);
// console.log(`A meleeBullet is added to collisionSys at renderFrame.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?
colliderBullets.forEach((bulletCollider, collisionBulletIndex) => {
bulletColliders.forEach((bulletCollider, collisionBulletIndex) => {
const potentials = bulletCollider.potentials();
let shouldRemove = false;
for (const potential of potentials) {
if (null != potential.playerId && potential.playerId == bulletCollider.offenderPlayerId) continue;
if (null != potential.data && potential.data.joinIndex == bulletCollider.data.offenderJoinIndex) continue;
if (!bulletCollider.collides(potential, result1)) continue;
if (null != potential.playerId) {
const joinIndex = potential.collisionPlayerIndex - self.collisionPlayerIndexPrefix;
bulletPushbacks[joinIndex - 1][0] += result1.overlap_x * bulletCollider.pushback;
bulletPushbacks[joinIndex - 1][1] += result1.overlap_y * bulletCollider.pushback;
const thatAckedPlayerInNextFrame = nextRenderFramePlayers[potential.playerId];
if (null != potential.data && null !== potential.data.joinIndex) {
const joinIndex = potential.data.joinIndex;
bulletPushbacks[joinIndex - 1][0] += bulletCollider.data.pushback; // Only for straight punch, there's no y-pushback
bulletPushbacks[joinIndex - 1][1] += 0;
const thatAckedPlayerInNextFrame = nextRenderFramePlayers[potential.data.id];
thatAckedPlayerInNextFrame.characterState = window.ATK_CHARACTER_STATE.Atked1[0];
const oldFrameToRecover = thatAckedPlayerInNextFrame.framesToRecover;
thatAckedPlayerInNextFrame.framesToRecover = oldFrameToRecover > bulletCollider.hitStunFrames ? oldFrameToRecover : bulletCollider.hitStunFrames; // In case the hit player is already stun, we take the larger "hitStunFrames"
thatAckedPlayerInNextFrame.framesToRecover = (oldFrameToRecover > bulletCollider.data.hitStunFrames ? oldFrameToRecover : bulletCollider.data.hitStunFrames); // In case the hit player is already stun, we extend it
}
shouldRemove = true;
}
@@ -1107,9 +1105,9 @@ cc.Class({
}
});
// [WARNING] Remove bullets from collisionSys ANYWAY for the convenience of rollback
for (let k in currRenderFrame.meleeBullets) {
const meleeBullet = currRenderFrame.meleeBullets[k];
// [WARNING] remove from collisionSys ANYWAY for the convenience of rollback
const collisionBulletIndex = self.collisionBulletIndexPrefix + meleeBullet.battleLocalId;
if (collisionSysMap.has(collisionBulletIndex)) {
const bulletCollider = collisionSysMap.get(collisionBulletIndex);
@@ -1120,10 +1118,10 @@ cc.Class({
toRet.meleeBullets.push(meleeBullet);
}
// Process player inputs
if (null != delayedInputFrame) {
const delayedInputFrameForPrevRenderFrame = self.getCachedInputFrameDownsyncWithPrediction(self._convertToInputFrameId(currRenderFrame.id - 1, self.inputDelayFrames));
const inputList = delayedInputFrame.inputList;
// Process player inputs
for (let j in self.playerRichInfoArr) {
const joinIndex = parseInt(j) + 1;
effPushbacks[joinIndex - 1] = [0.0, 0.0];
@@ -1131,13 +1129,14 @@ cc.Class({
const playerId = playerRichInfo.id;
const collisionPlayerIndex = self.collisionPlayerIndexPrefix + joinIndex;
const playerCollider = collisionSysMap.get(collisionPlayerIndex);
const player = currRenderFrame.players[playerId];
if (0 < nextRenderFramePlayers[playerId].framesToRecover) {
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]) {
playerCollider.x += bulletPushbacks[joinIndex - 1][0];
playerCollider.y += bulletPushbacks[joinIndex - 1][1];
console.log(`playerId=${playerId}, joinIndex=${joinIndex} is pushbacked back by ${bulletPushbacks[joinIndex - 1]} by bullet impacts, now its framesToRecover is ${player.framesToRecover}`);
console.log(`playerId=${playerId}, joinIndex=${joinIndex} is pushbacked back by ${bulletPushbacks[joinIndex - 1]} by bullet impacts, now its framesToRecover is ${currPlayerDownsync.framesToRecover}`);
}
continue;
}
@@ -1148,33 +1147,32 @@ cc.Class({
const prevBtnALevel = (null == prevDecodedInput ? 0 : prevDecodedInput.btnALevel);
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}`);
// The online map is not yet ready for bullet shooting!
/*
nextRenderFramePlayers[playerId].framesToRecover = PunchAtkConfig.recoveryFrames;
const punch = window.pb.protos.MeleeBullet.create(PunchAtkConfig);
punch.battleLocalId = self.bulletBattleLocalIdCounter++;
punch.offenderJoinIndex = joinIndex;
punch.offenderPlayerId = playerId;
punch.originatedRenderFrameId = currRenderFrame.id;
toRet.meleeBullets.push(punch);
console.log(`A rising-edge of meleeBullet=${JSON.stringify(punch)} is created 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}`);
if (self.bulletTriggerEnabled) {
thatPlayerInNextFrame.framesToRecover = window.PunchAtkConfig.recoveryFrames;
const punch = window.pb.protos.MeleeBullet.create(window.PunchAtkConfig);
punch.battleLocalId = self.bulletBattleLocalIdCounter++;
punch.offenderJoinIndex = joinIndex;
punch.offenderPlayerId = playerId;
punch.originatedRenderFrameId = currRenderFrame.id;
toRet.meleeBullets.push(punch);
console.log(`A rising-edge of meleeBullet is created at renderFrame.id=${currRenderFrame.id}, delayedInputFrame.id=${delayedInputFrame.inputFrameId}: ${JSON.stringify(punch)}`);
nextRenderFramePlayers[playerId].characterState = window.ATK_CHARACTER_STATE.Atk1[0];
*/
thatPlayerInNextFrame.characterState = window.ATK_CHARACTER_STATE.Atk1[0];
}
} 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 {
// No trigger, process movement inputs
if (0 != decodedInput.dx || 0 != decodedInput.dy) {
// Update directions and thus would eventually update moving animation accordingly
nextRenderFramePlayers[playerId].dir.dx = decodedInput.dx;
nextRenderFramePlayers[playerId].dir.dy = decodedInput.dy;
nextRenderFramePlayers[playerId].characterState = window.ATK_CHARACTER_STATE.Walking[0];
thatPlayerInNextFrame.dirX = decodedInput.dx;
thatPlayerInNextFrame.dirY = decodedInput.dy;
thatPlayerInNextFrame.characterState = window.ATK_CHARACTER_STATE.Walking[0];
} else {
nextRenderFramePlayers[playerId].characterState = window.ATK_CHARACTER_STATE.Idle1[0];
thatPlayerInNextFrame.characterState = window.ATK_CHARACTER_STATE.Idle1[0];
}
const movement = self.virtualGridToWorldPos(decodedInput.dx + player.speed * decodedInput.dx, decodedInput.dy + player.speed * decodedInput.dy);
const movement = self.virtualGridToWorldPos(decodedInput.dx + currPlayerDownsync.speed * decodedInput.dx, decodedInput.dy + currPlayerDownsync.speed * decodedInput.dy);
playerCollider.x += movement[0];
playerCollider.y += movement[1];
}
@@ -1204,8 +1202,9 @@ cc.Class({
const collisionPlayerIndex = self.collisionPlayerIndexPrefix + joinIndex;
const playerCollider = collisionSysMap.get(collisionPlayerIndex);
const newVpos = self.playerColliderAnchorToVirtualGridPos(playerCollider.x - effPushbacks[joinIndex - 1][0], playerCollider.y - effPushbacks[joinIndex - 1][1], self.playerRichInfoArr[j]);
nextRenderFramePlayers[playerId].virtualGridX = newVpos[0];
nextRenderFramePlayers[playerId].virtualGridY = newVpos[1];
const thatPlayerInNextFrame = nextRenderFramePlayers[playerId];
thatPlayerInNextFrame.virtualGridX = newVpos[0];
thatPlayerInNextFrame.virtualGridY = newVpos[1];
}
}

View File

@@ -3,27 +3,27 @@ i18n.init(window.language); // languageID should be equal to the one we input in
const OnlineMap = require('./Map');
const PunchAtkConfig = {
window.PunchAtkConfig = {
// for offender
startupFrames: 18,
activeFrames: 42,
recoveryFrames: 60, // usually but not always "startupFrames+activeFrames"
recoveryFramesOnBlock: 60, // usually but not always the same as "recoveryFrames"
recoveryFramesOnHit: 60, // usually but not always the same as "recoveryFrames"
recoveryFrames: 61, // usually but not always "startupFrames+activeFrames", I hereby set it to be 1 frame more than the actual animation to avoid critical transition, i.e. when the animation is 1 frame from ending but "rdfPlayer.framesToRecover" is already counted 0 and the player triggers an other same attack, making an effective bullet trigger but no animation is played due to same animName is still playing
recoveryFramesOnBlock: 61,
recoveryFramesOnHit: 61,
moveforward: {
x: 0,
y: 0,
},
hitboxOffset: 12.0, // should be about the radius of the PlayerCollider
hitboxSize: {
x: 32.0,
x: 45.0,
y: 32.0,
},
// for defender
hitStunFrames: 18,
blockStunFrames: 9,
pushback: 11.0,
pushback: 22.0,
releaseTriggerType: 1, // 1: rising-edge, 2: falling-edge
damage: 5
};
@@ -35,45 +35,6 @@ cc.Class({
console.warn("+++++++ Map onDestroy()");
},
spawnPlayerNode(joinIndex, vx, vy, playerRichInfo) {
const self = this;
const newPlayerNode = cc.instantiate(self.controlledCharacterPrefab)
const playerScriptIns = newPlayerNode.getComponent("ControlledCharacter");
if (1 == joinIndex) {
playerScriptIns.setSpecies("SoldierWaterGhost");
} else if (2 == joinIndex) {
playerScriptIns.setSpecies("SoldierFireGhost");
playerScriptIns.animComp.node.scaleX = (-1.0);
}
const wpos = self.virtualGridToWorldPos(vx, vy);
newPlayerNode.setPosition(cc.v2(wpos[0], wpos[1]));
playerScriptIns.mapNode = self.node;
const cpos = self.virtualGridToPlayerColliderPos(vx, vy, playerRichInfo);
const d = playerRichInfo.colliderRadius * 2,
x0 = cpos[0],
y0 = cpos[1];
let pts = [[0, 0], [d, 0], [d, d], [0, d]];
const newPlayerCollider = self.collisionSys.createPolygon(x0, y0, pts);
const collisionPlayerIndex = self.collisionPlayerIndexPrefix + joinIndex;
newPlayerCollider.collisionPlayerIndex = parseInt(collisionPlayerIndex);
newPlayerCollider.playerId = parseInt(playerRichInfo.id);
self.collisionSysMap.set(collisionPlayerIndex, newPlayerCollider);
safelyAddChild(self.node, newPlayerNode);
setLocalZOrder(newPlayerNode, 5);
newPlayerNode.active = true;
playerScriptIns.updateCharacterAnim({
dx: playerRichInfo.dir.dx,
dy: playerRichInfo.dir.dy
}, playerRichInfo, true);
return [newPlayerNode, playerScriptIns];
},
onLoad() {
const self = this;
window.mapIns = self;
@@ -182,10 +143,8 @@ cc.Class({
colliderRadius: 12,
characterState: window.ATK_CHARACTER_STATE.Idle1[0],
framesToRecover: 0,
dir: {
dx: 0,
dy: 0
}
dirX: 0,
dirY: 0,
},
11: {
id: 11,
@@ -196,10 +155,8 @@ cc.Class({
colliderRadius: 12,
characterState: window.ATK_CHARACTER_STATE.Idle1[0],
framesToRecover: 0,
dir: {
dx: 0,
dy: 0
}
dirX: 0,
dirY: 0,
},
}
};
@@ -243,214 +200,4 @@ cc.Class({
}
}
},
// Overriding this function to test experimental dynamics
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,
dir: {
dx: currPlayerDownsync.dir.dx,
dy: currPlayerDownsync.dir.dy,
},
characterState: currPlayerDownsync.characterState,
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 toRet = {
id: currRenderFrame.id + 1,
players: nextRenderFramePlayers,
meleeBullets: []
};
const bulletPushbacks = new Array(self.playerRichInfoArr.length); // Guaranteed determinism regardless of traversal order
const effPushbacks = new Array(self.playerRichInfoArr.length); // Guaranteed determinism regardless of traversal order
for (let j in self.playerRichInfoArr) {
const joinIndex = parseInt(j) + 1;
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 player = currRenderFrame.players[playerId];
const newVx = player.virtualGridX;
const newVy = player.virtualGridY;
const newCpos = self.virtualGridToPlayerColliderPos(newVx, newVy, self.playerRichInfoArr[joinIndex - 1]);
playerCollider.x = newCpos[0];
playerCollider.y = newCpos[1];
}
// Check bullet-anything collisions first, because the pushbacks caused by bullets might later be reverted by player-barrier collision
const colliderBullets = 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];
const xfac = Math.sign(offender.dir.dx),
yfac = 0; // By now, straight Punch offset doesn't respect "y-axis"
const x0 = offenderCollider.x + xfac * meleeBullet.hitboxOffset,
y0 = offenderCollider.y + yfac * meleeBullet.hitboxOffset;
const pts = [[0, 0], [xfac * meleeBullet.hitboxSize.x, 0], [xfac * meleeBullet.hitboxSize.x, meleeBullet.hitboxSize.y], [0, meleeBullet.hitboxSize.y]];
const newBulletCollider = collisionSys.createPolygon(x0, y0, pts);
newBulletCollider.collisionBulletIndex = collisionBulletIndex;
newBulletCollider.offenderPlayerId = meleeBullet.offenderPlayerId;
newBulletCollider.pushback = meleeBullet.pushback;
newBulletCollider.hitStunFrames = meleeBullet.hitStunFrames;
collisionSysMap.set(collisionBulletIndex, newBulletCollider);
colliderBullets.set(collisionBulletIndex, newBulletCollider);
console.log(`A meleeBullet=${JSON.stringify(meleeBullet)} is added to collisionSys at renderFrame.id=${currRenderFrame.id} as start-up frames ended and active frame is not yet ended`);
}
}
collisionSys.update();
const result1 = collisionSys.createResult(); // Can I reuse a "self.collisionSysResult" object throughout the whole battle?
colliderBullets.forEach((bulletCollider, collisionBulletIndex) => {
const potentials = bulletCollider.potentials();
let shouldRemove = false;
for (const potential of potentials) {
if (null != potential.playerId && potential.playerId == bulletCollider.offenderPlayerId) continue;
if (!bulletCollider.collides(potential, result1)) continue;
if (null != potential.playerId) {
const joinIndex = potential.collisionPlayerIndex - self.collisionPlayerIndexPrefix;
bulletPushbacks[joinIndex - 1][0] += result1.overlap_x * bulletCollider.pushback;
bulletPushbacks[joinIndex - 1][1] += result1.overlap_y * bulletCollider.pushback;
const thatAckedPlayerInNextFrame = nextRenderFramePlayers[potential.playerId];
thatAckedPlayerInNextFrame.characterState = window.ATK_CHARACTER_STATE.Atked1[0];
const oldFrameToRecover = thatAckedPlayerInNextFrame.framesToRecover;
thatAckedPlayerInNextFrame.framesToRecover = oldFrameToRecover > bulletCollider.hitStunFrames ? oldFrameToRecover : bulletCollider.hitStunFrames; // In case the hit player is already stun, we take the larger "hitStunFrames"
}
shouldRemove = true;
}
if (shouldRemove) {
removedBulletsAtCurrFrame.add(collisionBulletIndex);
}
});
for (let k in currRenderFrame.meleeBullets) {
const meleeBullet = currRenderFrame.meleeBullets[k];
// [WARNING] remove from collisionSys ANYWAY for the convenience of rollback
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;
toRet.meleeBullets.push(meleeBullet);
}
if (null != delayedInputFrame) {
const delayedInputFrameForPrevRenderFrame = self.getCachedInputFrameDownsyncWithPrediction(self._convertToInputFrameId(currRenderFrame.id - 1, self.inputDelayFrames));
const inputList = delayedInputFrame.inputList;
// Process player inputs
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 player = currRenderFrame.players[playerId];
if (0 < nextRenderFramePlayers[playerId].framesToRecover) {
// No need to process inputs for this player, but there might be bullet pushbacks on this player
if (0 != bulletPushbacks[joinIndex - 1][0] || 0 != bulletPushbacks[joinIndex - 1][1]) {
playerCollider.x += bulletPushbacks[joinIndex - 1][0];
playerCollider.y += bulletPushbacks[joinIndex - 1][1];
console.log(`playerId=${playerId}, joinIndex=${joinIndex} is pushbacked back by ${bulletPushbacks[joinIndex - 1]} by bullet impacts, now its framesToRecover is ${player.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);
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}`);
nextRenderFramePlayers[playerId].framesToRecover = PunchAtkConfig.recoveryFrames;
const punch = window.pb.protos.MeleeBullet.create(PunchAtkConfig);
punch.battleLocalId = self.bulletBattleLocalIdCounter++;
punch.offenderJoinIndex = joinIndex;
punch.offenderPlayerId = playerId;
punch.originatedRenderFrameId = currRenderFrame.id;
toRet.meleeBullets.push(punch);
console.log(`A rising-edge of meleeBullet=${JSON.stringify(punch)} is created at renderFrame.id=${currRenderFrame.id}, delayedInputFrame.id=${delayedInputFrame.inputFrameId}`);
nextRenderFramePlayers[playerId].characterState = window.ATK_CHARACTER_STATE.Atk1[0];
} 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 trigger, process movement inputs
if (0 != decodedInput.dx || 0 != decodedInput.dy) {
// Update directions and thus would eventually update moving animation accordingly
nextRenderFramePlayers[playerId].dir.dx = decodedInput.dx;
nextRenderFramePlayers[playerId].dir.dy = decodedInput.dy;
nextRenderFramePlayers[playerId].characterState = window.ATK_CHARACTER_STATE.Walking[0];
} else {
nextRenderFramePlayers[playerId].characterState = window.ATK_CHARACTER_STATE.Idle1[0];
}
const movement = self.virtualGridToWorldPos(decodedInput.dx + player.speed * decodedInput.dx, decodedInput.dy + player.speed * decodedInput.dy);
playerCollider.x += movement[0];
playerCollider.y += movement[1];
}
}
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();
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
effPushbacks[joinIndex - 1][0] += result2.overlap * result2.overlap_x;
effPushbacks[joinIndex - 1][1] += result2.overlap * result2.overlap_y;
}
}
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 newVpos = self.playerColliderAnchorToVirtualGridPos(playerCollider.x - effPushbacks[joinIndex - 1][0], playerCollider.y - effPushbacks[joinIndex - 1][1], self.playerRichInfoArr[j]);
nextRenderFramePlayers[playerId].virtualGridX = newVpos[0];
nextRenderFramePlayers[playerId].virtualGridY = newVpos[1];
}
}
return toRet;
},
});