mirror of
https://github.com/genxium/DelayNoMore
synced 2024-12-26 03:39:00 +00:00
Fixed fireball rollback sync.
This commit is contained in:
parent
dd9c03404e
commit
45380d170f
@ -55,9 +55,10 @@ func toPbRoomDownsyncFrame(rdf *battle.RoomDownsyncFrame) *pb.RoomDownsyncFrame
|
|||||||
|
|
||||||
for i, last := range rdf.MeleeBullets {
|
for i, last := range rdf.MeleeBullets {
|
||||||
pbBullet := &pb.MeleeBullet{
|
pbBullet := &pb.MeleeBullet{
|
||||||
BulletLocalId: last.Bullet.BulletLocalId,
|
BulletLocalId: last.BattleAttr.BulletLocalId,
|
||||||
OriginatedRenderFrameId: last.Bullet.OriginatedRenderFrameId,
|
OriginatedRenderFrameId: last.BattleAttr.OriginatedRenderFrameId,
|
||||||
OffenderJoinIndex: last.Bullet.OffenderJoinIndex,
|
OffenderJoinIndex: last.BattleAttr.OffenderJoinIndex,
|
||||||
|
TeamId: last.BattleAttr.TeamId,
|
||||||
|
|
||||||
StartupFrames: last.Bullet.StartupFrames,
|
StartupFrames: last.Bullet.StartupFrames,
|
||||||
CancellableStFrame: last.Bullet.CancellableStFrame,
|
CancellableStFrame: last.Bullet.CancellableStFrame,
|
||||||
@ -79,16 +80,16 @@ func toPbRoomDownsyncFrame(rdf *battle.RoomDownsyncFrame) *pb.RoomDownsyncFrame
|
|||||||
HitboxSizeY: last.Bullet.HitboxSizeY,
|
HitboxSizeY: last.Bullet.HitboxSizeY,
|
||||||
|
|
||||||
BlowUp: last.Bullet.BlowUp,
|
BlowUp: last.Bullet.BlowUp,
|
||||||
TeamId: last.Bullet.TeamId,
|
|
||||||
}
|
}
|
||||||
ret.MeleeBullets[i] = pbBullet
|
ret.MeleeBullets[i] = pbBullet
|
||||||
}
|
}
|
||||||
|
|
||||||
for i, last := range rdf.FireballBullets {
|
for i, last := range rdf.FireballBullets {
|
||||||
pbBullet := &pb.FireballBullet{
|
pbBullet := &pb.FireballBullet{
|
||||||
BulletLocalId: last.Bullet.BulletLocalId,
|
BulletLocalId: last.BattleAttr.BulletLocalId,
|
||||||
OriginatedRenderFrameId: last.Bullet.OriginatedRenderFrameId,
|
OriginatedRenderFrameId: last.BattleAttr.OriginatedRenderFrameId,
|
||||||
OffenderJoinIndex: last.Bullet.OffenderJoinIndex,
|
OffenderJoinIndex: last.BattleAttr.OffenderJoinIndex,
|
||||||
|
TeamId: last.BattleAttr.TeamId,
|
||||||
|
|
||||||
StartupFrames: last.Bullet.StartupFrames,
|
StartupFrames: last.Bullet.StartupFrames,
|
||||||
CancellableStFrame: last.Bullet.CancellableStFrame,
|
CancellableStFrame: last.Bullet.CancellableStFrame,
|
||||||
@ -110,7 +111,6 @@ func toPbRoomDownsyncFrame(rdf *battle.RoomDownsyncFrame) *pb.RoomDownsyncFrame
|
|||||||
HitboxSizeY: last.Bullet.HitboxSizeY,
|
HitboxSizeY: last.Bullet.HitboxSizeY,
|
||||||
|
|
||||||
BlowUp: last.Bullet.BlowUp,
|
BlowUp: last.Bullet.BlowUp,
|
||||||
TeamId: last.Bullet.TeamId,
|
|
||||||
|
|
||||||
VirtualGridX: last.VirtualGridX,
|
VirtualGridX: last.VirtualGridX,
|
||||||
VirtualGridY: last.VirtualGridY,
|
VirtualGridY: last.VirtualGridY,
|
||||||
|
@ -348,7 +348,7 @@ func (pR *Room) fireballDownsyncStr(fireball *battle.FireballBullet) string {
|
|||||||
if nil == fireball {
|
if nil == fireball {
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
s := fmt.Sprintf("{%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d}", fireball.Bullet.BulletLocalId, fireball.Bullet.OriginatedRenderFrameId, fireball.Bullet.OffenderJoinIndex, fireball.VirtualGridX, fireball.VirtualGridY, fireball.VelX, fireball.VelY, fireball.DirX, fireball.DirY, fireball.Bullet.HitboxSizeX, fireball.Bullet.HitboxSizeY)
|
s := fmt.Sprintf("{%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d}", fireball.BattleAttr.BulletLocalId, fireball.BattleAttr.OriginatedRenderFrameId, fireball.BattleAttr.OffenderJoinIndex, fireball.VirtualGridX, fireball.VirtualGridY, fireball.VelX, fireball.VelY, fireball.DirX, fireball.DirY, fireball.Bullet.HitboxSizeX, fireball.Bullet.HitboxSizeY)
|
||||||
|
|
||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
|
File diff suppressed because one or more lines are too long
@ -24,11 +24,11 @@
|
|||||||
"_active": true,
|
"_active": true,
|
||||||
"_components": [
|
"_components": [
|
||||||
{
|
{
|
||||||
"__id__": 7
|
"__id__": 8
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"_prefab": {
|
"_prefab": {
|
||||||
"__id__": 8
|
"__id__": 9
|
||||||
},
|
},
|
||||||
"_opacity": 255,
|
"_opacity": 255,
|
||||||
"_color": {
|
"_color": {
|
||||||
@ -92,7 +92,7 @@
|
|||||||
"_active": true,
|
"_active": true,
|
||||||
"_components": [],
|
"_components": [],
|
||||||
"_prefab": {
|
"_prefab": {
|
||||||
"__id__": 6
|
"__id__": 7
|
||||||
},
|
},
|
||||||
"_opacity": 255,
|
"_opacity": 255,
|
||||||
"_color": {
|
"_color": {
|
||||||
@ -149,14 +149,17 @@
|
|||||||
"__id__": 2
|
"__id__": 2
|
||||||
},
|
},
|
||||||
"_children": [],
|
"_children": [],
|
||||||
"_active": true,
|
"_active": false,
|
||||||
"_components": [
|
"_components": [
|
||||||
{
|
{
|
||||||
"__id__": 4
|
"__id__": 4
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"__id__": 5
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"_prefab": {
|
"_prefab": {
|
||||||
"__id__": 5
|
"__id__": 6
|
||||||
},
|
},
|
||||||
"_opacity": 255,
|
"_opacity": 255,
|
||||||
"_color": {
|
"_color": {
|
||||||
@ -220,9 +223,7 @@
|
|||||||
],
|
],
|
||||||
"_srcBlendFactor": 770,
|
"_srcBlendFactor": 770,
|
||||||
"_dstBlendFactor": 771,
|
"_dstBlendFactor": 771,
|
||||||
"_spriteFrame": {
|
"_spriteFrame": null,
|
||||||
"__uuid__": "e92702d5-d5fd-49e6-ab6b-2296b43fa6d6"
|
|
||||||
},
|
|
||||||
"_type": 0,
|
"_type": 0,
|
||||||
"_sizeMode": 1,
|
"_sizeMode": 1,
|
||||||
"_fillType": 0,
|
"_fillType": 0,
|
||||||
@ -239,6 +240,25 @@
|
|||||||
},
|
},
|
||||||
"_id": ""
|
"_id": ""
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"__type__": "cc.Animation",
|
||||||
|
"_name": "",
|
||||||
|
"_objFlags": 0,
|
||||||
|
"node": {
|
||||||
|
"__id__": 3
|
||||||
|
},
|
||||||
|
"_enabled": true,
|
||||||
|
"_defaultClip": {
|
||||||
|
"__uuid__": "ba12416b-eec3-4260-8402-7fc25b125624"
|
||||||
|
},
|
||||||
|
"_clips": [
|
||||||
|
{
|
||||||
|
"__uuid__": "ba12416b-eec3-4260-8402-7fc25b125624"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"playOnLoad": false,
|
||||||
|
"_id": ""
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"__type__": "cc.PrefabInfo",
|
"__type__": "cc.PrefabInfo",
|
||||||
"root": {
|
"root": {
|
||||||
|
@ -457,7 +457,7 @@
|
|||||||
"array": [
|
"array": [
|
||||||
0,
|
0,
|
||||||
0,
|
0,
|
||||||
216.50635094610968,
|
209.57814771583418,
|
||||||
0,
|
0,
|
||||||
0,
|
0,
|
||||||
0,
|
0,
|
||||||
|
@ -440,7 +440,7 @@
|
|||||||
"array": [
|
"array": [
|
||||||
0,
|
0,
|
||||||
0,
|
0,
|
||||||
215.94663282292512,
|
209.57814771583418,
|
||||||
0,
|
0,
|
||||||
0,
|
0,
|
||||||
0,
|
0,
|
||||||
|
@ -191,8 +191,8 @@
|
|||||||
0,
|
0,
|
||||||
0,
|
0,
|
||||||
1,
|
1,
|
||||||
0.8,
|
1.2,
|
||||||
0.8,
|
1.2,
|
||||||
1
|
1
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
@ -464,7 +464,7 @@
|
|||||||
"array": [
|
"array": [
|
||||||
0,
|
0,
|
||||||
0,
|
0,
|
||||||
215.64032554232523,
|
209.57814771583418,
|
||||||
0,
|
0,
|
||||||
0,
|
0,
|
||||||
0,
|
0,
|
||||||
|
@ -27,7 +27,7 @@ cc.Class({
|
|||||||
this.speciesName = speciesName;
|
this.speciesName = speciesName;
|
||||||
this.effAnimNode = this.animNode.getChildByName(this.speciesName);
|
this.effAnimNode = this.animNode.getChildByName(this.speciesName);
|
||||||
this.effAnimNode.active = true;
|
this.effAnimNode.active = true;
|
||||||
//this.updateAnim(speciesName, fireballBullet, rdf);
|
this.updateAnim(speciesName, fireballBullet, rdf);
|
||||||
},
|
},
|
||||||
|
|
||||||
onLoad() {},
|
onLoad() {},
|
||||||
|
@ -742,9 +742,9 @@ cc.Class({
|
|||||||
if (null == lhs || null == rhs) return false;
|
if (null == lhs || null == rhs) return false;
|
||||||
if (null == lhs && null != rhs) return false;
|
if (null == lhs && null != rhs) return false;
|
||||||
if (null != lhs && null == rhs) return false;
|
if (null != lhs && null == rhs) return false;
|
||||||
if (lhs.Bullet.BulletLocalId != rhs.Bullet.BulletLocalId) return false;
|
if (lhs.BattleAttr.BulletLocalId != rhs.BattleAttr.BulletLocalId) return false;
|
||||||
if (lhs.Bullet.OffenderJoinIndex != rhs.Bullet.OffenderJoinIndex) return false;
|
if (lhs.BattleAttr.OffenderJoinIndex != rhs.BattleAttr.OffenderJoinIndex) return false;
|
||||||
if (lhs.Bullet.OriginatedRenderFrameId != rhs.Bullet.OriginatedRenderFrameId) return false;
|
if (lhs.BattleAttr.OriginatedRenderFrameId != rhs.BattleAttr.OriginatedRenderFrameId) return false;
|
||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -752,31 +752,32 @@ cc.Class({
|
|||||||
if (null == lhs || null == rhs) return false;
|
if (null == lhs || null == rhs) return false;
|
||||||
if (null == lhs && null != rhs) return false;
|
if (null == lhs && null != rhs) return false;
|
||||||
if (null != lhs && null == rhs) return false;
|
if (null != lhs && null == rhs) return false;
|
||||||
if (lhs.Bullet.BulletLocalId != rhs.Bullet.BulletLocalId) return false;
|
if (lhs.BattleAttr.BulletLocalId != rhs.BattleAttr.BulletLocalId) return false;
|
||||||
if (lhs.Bullet.OffenderJoinIndex != rhs.Bullet.OffenderJoinIndex) return false;
|
if (lhs.BattleAttr.OffenderJoinIndex != rhs.BattleAttr.OffenderJoinIndex) return false;
|
||||||
if (lhs.Bullet.OriginatedRenderFrameId != rhs.Bullet.OriginatedRenderFrameId) return false;
|
if (lhs.BattleAttr.OriginatedRenderFrameId != rhs.BattleAttr.OriginatedRenderFrameId) return false;
|
||||||
|
|
||||||
|
if (lhs.VirtualGridX != rhs.Bullet.VirtualGridX) return false;
|
||||||
|
if (lhs.VirtualGridY != rhs.Bullet.VirtualGridY) return false;
|
||||||
|
if (lhs.DirX != rhs.DirX) return false;
|
||||||
|
if (lhs.DirY != rhs.DirY) return false;
|
||||||
|
if (lhs.VelX != rhs.VelX) return false;
|
||||||
|
if (lhs.VelY != rhs.VelY) return false;
|
||||||
|
if (lhs.Speed != rhs.Speed) return false;
|
||||||
|
if (lhs.SpeciesId != rhs.SpeciesId) return false;
|
||||||
|
|
||||||
if (lhs.Bullet.VirtualGridX != rhs.Bullet.VirtualGridX) return false;
|
|
||||||
if (lhs.Bullet.VirtualGridY != rhs.Bullet.VirtualGridY) return false;
|
|
||||||
if (lhs.Bullet.DirX != rhs.Bullet.DirX) return false;
|
|
||||||
if (lhs.Bullet.DirY != rhs.Bullet.DirY) return false;
|
|
||||||
if (lhs.Bullet.VelX != rhs.Bullet.VelX) return false;
|
|
||||||
if (lhs.Bullet.VelY != rhs.Bullet.VelY) return false;
|
|
||||||
if (lhs.Bullet.Speed != rhs.Bullet.Speed) return false;
|
|
||||||
if (lhs.Bullet.SpeciesId != rhs.Bullet.SpeciesId) return false;
|
|
||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
|
|
||||||
equalRoomDownsyncFrames(lhs, rhs) {
|
equalRoomDownsyncFrames(lhs, rhs) {
|
||||||
if (null == lhs || null == rhs) return false;
|
if (null == lhs || null == rhs) return false;
|
||||||
for (let k in lhs.players) {
|
for (let k in lhs.PlayersArr) {
|
||||||
if (!this.equalPlayers(lhs.players[k], rhs.players[k])) return false;
|
if (!this.equalPlayers(lhs.PlayersArr[k], rhs.PlayersArr[k])) return false;
|
||||||
}
|
}
|
||||||
for (let k in lhs.meleeBullets) {
|
for (let k in lhs.MeleeBullets) {
|
||||||
if (!this.equalMeleeBullets(lhs.meleeBullets[k], rhs.meleeBullets[k])) return false;
|
if (!this.equalMeleeBullets(lhs.MeleeBullets[k], rhs.MeleeBullets[k])) return false;
|
||||||
}
|
}
|
||||||
for (let k in lhs.fireballBullet) {
|
for (let k in lhs.fireballBullet) {
|
||||||
if (!this.equalMeleeBullets(lhs.meleeBullets[k], rhs.meleeBullets[k])) return false;
|
if (!this.equalFireballBullets(lhs.FireballBullets[k], rhs.FireballBullets[k])) return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
@ -1093,10 +1094,6 @@ othersForcedDownsyncRenderFrame=${JSON.stringify(othersForcedDownsyncRenderFrame
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
_renderFireballBullet(fireballBullet, rdf) {
|
|
||||||
const self = this;
|
|
||||||
},
|
|
||||||
|
|
||||||
applyRoomDownsyncFrameDynamics(rdf, prevRdf) {
|
applyRoomDownsyncFrameDynamics(rdf, prevRdf) {
|
||||||
const self = this;
|
const self = this;
|
||||||
const playersArr = rdf.PlayersArr;
|
const playersArr = rdf.PlayersArr;
|
||||||
@ -1117,31 +1114,32 @@ othersForcedDownsyncRenderFrame=${JSON.stringify(othersForcedDownsyncRenderFrame
|
|||||||
const fireball = pqNode.value;
|
const fireball = pqNode.value;
|
||||||
fireball.node.setPosition(cc.v2(Number.MAX_VALUE, Number.MAX_VALUE));
|
fireball.node.setPosition(cc.v2(Number.MAX_VALUE, Number.MAX_VALUE));
|
||||||
}
|
}
|
||||||
const fireballBullets = rdf.FireballBullets;
|
for (let k in rdf.FireballBullets) {
|
||||||
for (let k in fireballBullets) {
|
const fireballBullet = rdf.FireballBullets[k];
|
||||||
const fireballBullet = fireballBullets[k];
|
|
||||||
if (
|
if (
|
||||||
fireballBullet.Bullet.OriginatedRenderFrameId + fireballBullet.Bullet.StartupFrames <= rdf.Id
|
fireballBullet.BattleAttr.OriginatedRenderFrameId + fireballBullet.Bullet.StartupFrames <= rdf.Id
|
||||||
&&
|
&&
|
||||||
fireballBullet.Bullet.OriginatedRenderFrameId + fireballBullet.Bullet.StartupFrames + fireballBullet.Bullet.ActiveFrames > rdf.Id
|
fireballBullet.BattleAttr.OriginatedRenderFrameId + fireballBullet.Bullet.StartupFrames + fireballBullet.Bullet.ActiveFrames > rdf.Id
|
||||||
) {
|
) {
|
||||||
let pqNode = self.cachedFireballs.popAny(fireballBullet.Bullet.BulletLocalId);
|
let pqNode = self.cachedFireballs.popAny(fireballBullet.BattleAttr.BulletLocalId);
|
||||||
const speciesName = `Fireball${fireballBullet.SpeciesId}`;
|
const speciesName = `Fireball${fireballBullet.SpeciesId}`;
|
||||||
const [wx, wy] = gopkgs.VirtualGridToWorldPos(fireballBullet.VirtualGridX, fireballBullet.VirtualGridY);
|
const [wx, wy] = gopkgs.VirtualGridToWorldPos(fireballBullet.VirtualGridX, fireballBullet.VirtualGridY);
|
||||||
|
|
||||||
if (null == pqNode) {
|
if (null == pqNode) {
|
||||||
pqNode = self.cachedFireballs.pop();
|
pqNode = self.cachedFireballs.pop();
|
||||||
console.log(`@rdf.Id=${rdf.Id}, origRdfId=${fireballBullet.Bullet.OriginatedRenderFrameId}, startupFrames=${fireballBullet.Bullet.StartupFrames}, activeFrames=${fireballBullet.Bullet.ActiveFrames}, using a new fireball node for rendering for bulletLocalId=${fireballBullet.Bullet.BulletLocalId} at wpos=(${wx},${wy})`);
|
//console.log(`@rdf.Id=${rdf.Id}, origRdfId=${fireballBullet.BattleAttr.OriginatedRenderFrameId}, startupFrames=${fireballBullet.Bullet.StartupFrames}, using a new fireball node for rendering for bulletLocalId=${fireballBullet.BattleAttr.BulletLocalId} at wpos=(${wx},${wy})`);
|
||||||
} else {
|
} else {
|
||||||
console.log(`@rdf.Id=${rdf.Id}, origRdfId=${fireballBullet.Bullet.OriginatedRenderFrameId}, startupFrames=${fireballBullet.Bullet.StartupFrames}, activeFrames=${fireballBullet.Bullet.ActiveFrames}, using a cached fireball node for rendering for bulletLocalId=${fireballBullet.Bullet.BulletLocalId} at wpos=(${wx},${wy})`);
|
//console.log(`@rdf.Id=${rdf.Id}, origRdfId=${fireballBullet.BattleAttr.OriginatedRenderFrameId}, startupFrames=${fireballBullet.Bullet.StartupFrames}, using a cached fireball node for rendering for bulletLocalId=${fireballBullet.BattleAttr.BulletLocalId} at wpos=(${wx},${wy})`);
|
||||||
}
|
}
|
||||||
const cachedFireball = pqNode.value;
|
const cachedFireball = pqNode.value;
|
||||||
cachedFireball.setSpecies(speciesName, fireballBullet, rdf);
|
cachedFireball.setSpecies(speciesName, fireballBullet, rdf);
|
||||||
cachedFireball.lastUsed = self.renderFrameId;
|
cachedFireball.lastUsed = self.renderFrameId;
|
||||||
cachedFireball.bulletLocalId = fireballBullet.Bullet.BulletLocalId;
|
cachedFireball.bulletLocalId = fireballBullet.BattleAttr.BulletLocalId;
|
||||||
cachedFireball.node.setPosition(cc.v2(wx, wy));
|
cachedFireball.node.setPosition(cc.v2(wx, wy));
|
||||||
|
|
||||||
self.cachedFireballs.push(cachedFireball.lastUsed, cachedFireball, fireballBullet.Bullet.BulletLocalId);
|
self.cachedFireballs.push(cachedFireball.lastUsed, cachedFireball, fireballBullet.BattleAttr.BulletLocalId);
|
||||||
|
} else {
|
||||||
|
//console.log(`@rdf.Id=${rdf.Id}, origRdfId=${fireballBullet.BattleAttr.OriginatedRenderFrameId}, startupFrames=${fireballBullet.Bullet.StartupFrames}, activeFrames=${fireballBullet.Bullet.ActiveFrames}, not rendering fireball node for bulletLocalId=${fireballBullet.BattleAttr.BulletLocalId}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1271,7 +1269,7 @@ othersForcedDownsyncRenderFrame=${JSON.stringify(othersForcedDownsyncRenderFrame
|
|||||||
|
|
||||||
fireballDownsyncStr(fireball) {
|
fireballDownsyncStr(fireball) {
|
||||||
if (null == fireball) return "";
|
if (null == fireball) return "";
|
||||||
return `{${fireball.Bullet.BulletLocalId},${fireball.Bullet.OriginatedRenderFrameId},${fireball.Bullet.OffenderJoinIndex},${fireball.VirtualGridX},${fireball.VirtualGridY},${fireball.VelX},${fireball.VelY},${fireball.DirX},${fireball.DirY},${fireball.Bullet.HitboxSizeX},${fireball.Bullet.HitboxSizeY}}`;
|
return `{${fireball.BattleAttr.BulletLocalId},${fireball.BattleAttr.OriginatedRenderFrameId},${fireball.BattleAttr.OffenderJoinIndex},${fireball.VirtualGridX},${fireball.VirtualGridY},${fireball.VelX},${fireball.VelY},${fireball.DirX},${fireball.DirY},${fireball.Bullet.HitboxSizeX},${fireball.Bullet.HitboxSizeY}}`;
|
||||||
},
|
},
|
||||||
|
|
||||||
inputFrameDownsyncStr(inputFrameDownsync) {
|
inputFrameDownsyncStr(inputFrameDownsync) {
|
||||||
@ -1366,11 +1364,11 @@ actuallyUsedinputList:{${self.inputFrameDownsyncStr(actuallyUsedInputClone)}}`);
|
|||||||
for (let k in rdf.MeleeBullets) {
|
for (let k in rdf.MeleeBullets) {
|
||||||
const meleeBullet = rdf.MeleeBullets[k];
|
const meleeBullet = rdf.MeleeBullets[k];
|
||||||
if (
|
if (
|
||||||
meleeBullet.Bullet.OriginatedRenderFrameId + meleeBullet.Bullet.StartupFrames <= rdf.Id
|
meleeBullet.BattleAttr.OriginatedRenderFrameId + meleeBullet.Bullet.StartupFrames <= rdf.Id
|
||||||
&&
|
&&
|
||||||
meleeBullet.Bullet.OriginatedRenderFrameId + meleeBullet.Bullet.StartupFrames + meleeBullet.Bullet.ActiveFrames > rdf.Id
|
meleeBullet.BattleAttr.OriginatedRenderFrameId + meleeBullet.Bullet.StartupFrames + meleeBullet.Bullet.ActiveFrames > rdf.Id
|
||||||
) {
|
) {
|
||||||
const offender = rdf.PlayersArr[meleeBullet.Bullet.OffenderJoinIndex - 1];
|
const offender = rdf.PlayersArr[meleeBullet.BattleAttr.OffenderJoinIndex - 1];
|
||||||
if (1 == offender.JoinIndex) {
|
if (1 == offender.JoinIndex) {
|
||||||
g2.strokeColor = cc.Color.BLUE;
|
g2.strokeColor = cc.Color.BLUE;
|
||||||
} else {
|
} else {
|
||||||
@ -1398,11 +1396,11 @@ actuallyUsedinputList:{${self.inputFrameDownsyncStr(actuallyUsedInputClone)}}`);
|
|||||||
for (let k in rdf.FireballBullets) {
|
for (let k in rdf.FireballBullets) {
|
||||||
const fireballBullet = rdf.FireballBullets[k];
|
const fireballBullet = rdf.FireballBullets[k];
|
||||||
if (
|
if (
|
||||||
fireballBullet.Bullet.OriginatedRenderFrameId + fireballBullet.Bullet.StartupFrames <= rdf.Id
|
fireballBullet.BattleAttr.OriginatedRenderFrameId + fireballBullet.Bullet.StartupFrames <= rdf.Id
|
||||||
&&
|
&&
|
||||||
fireballBullet.Bullet.OriginatedRenderFrameId + fireballBullet.Bullet.StartupFrames + fireballBullet.Bullet.ActiveFrames > rdf.Id
|
fireballBullet.BattleAttr.OriginatedRenderFrameId + fireballBullet.Bullet.StartupFrames + fireballBullet.Bullet.ActiveFrames > rdf.Id
|
||||||
) {
|
) {
|
||||||
const offender = rdf.PlayersArr[fireballBullet.Bullet.OffenderJoinIndex - 1];
|
const offender = rdf.PlayersArr[fireballBullet.BattleAttr.OffenderJoinIndex - 1];
|
||||||
if (1 == offender.JoinIndex) {
|
if (1 == offender.JoinIndex) {
|
||||||
g2.strokeColor = cc.Color.BLUE;
|
g2.strokeColor = cc.Color.BLUE;
|
||||||
} else {
|
} else {
|
||||||
|
@ -541,10 +541,13 @@ func ApplyInputFrameDownsyncDynamicsOnSingleRenderFrame(inputsBuffer *RingBuffer
|
|||||||
switch v := skillConfig.Hits[thatPlayerInNextFrame.ActiveSkillHit].(type) {
|
switch v := skillConfig.Hits[thatPlayerInNextFrame.ActiveSkillHit].(type) {
|
||||||
case *MeleeBullet:
|
case *MeleeBullet:
|
||||||
var newBullet MeleeBullet = *v // Copied primitive fields into an onstack variable
|
var newBullet MeleeBullet = *v // Copied primitive fields into an onstack variable
|
||||||
newBullet.Bullet.BulletLocalId = bulletLocalId
|
newBullet.BattleAttr = &BulletBattleAttr{
|
||||||
|
BulletLocalId: bulletLocalId,
|
||||||
|
OriginatedRenderFrameId: currRenderFrame.Id,
|
||||||
|
OffenderJoinIndex: joinIndex,
|
||||||
|
TeamId: currPlayerDownsync.BulletTeamId,
|
||||||
|
}
|
||||||
bulletLocalId++
|
bulletLocalId++
|
||||||
newBullet.Bullet.OriginatedRenderFrameId = currRenderFrame.Id
|
|
||||||
newBullet.Bullet.OffenderJoinIndex = joinIndex
|
|
||||||
nextRenderFrameMeleeBullets = append(nextRenderFrameMeleeBullets, &newBullet)
|
nextRenderFrameMeleeBullets = append(nextRenderFrameMeleeBullets, &newBullet)
|
||||||
if NO_LOCK_VEL != v.Bullet.SelfLockVelX {
|
if NO_LOCK_VEL != v.Bullet.SelfLockVelX {
|
||||||
hasLockVel = true
|
hasLockVel = true
|
||||||
@ -556,10 +559,13 @@ func ApplyInputFrameDownsyncDynamicsOnSingleRenderFrame(inputsBuffer *RingBuffer
|
|||||||
}
|
}
|
||||||
case *FireballBullet:
|
case *FireballBullet:
|
||||||
var newBullet FireballBullet = *v // Copied primitive fields into an onstack variable
|
var newBullet FireballBullet = *v // Copied primitive fields into an onstack variable
|
||||||
newBullet.Bullet.BulletLocalId = bulletLocalId
|
newBullet.BattleAttr = &BulletBattleAttr{
|
||||||
|
BulletLocalId: bulletLocalId,
|
||||||
|
OriginatedRenderFrameId: currRenderFrame.Id,
|
||||||
|
OffenderJoinIndex: joinIndex,
|
||||||
|
TeamId: currPlayerDownsync.BulletTeamId,
|
||||||
|
}
|
||||||
bulletLocalId++
|
bulletLocalId++
|
||||||
newBullet.Bullet.OriginatedRenderFrameId = currRenderFrame.Id
|
|
||||||
newBullet.Bullet.OffenderJoinIndex = joinIndex
|
|
||||||
newBullet.VirtualGridX, newBullet.VirtualGridY = currPlayerDownsync.VirtualGridX+xfac*newBullet.Bullet.HitboxOffsetX, currPlayerDownsync.VirtualGridY+newBullet.Bullet.HitboxOffsetY
|
newBullet.VirtualGridX, newBullet.VirtualGridY = currPlayerDownsync.VirtualGridX+xfac*newBullet.Bullet.HitboxOffsetX, currPlayerDownsync.VirtualGridY+newBullet.Bullet.HitboxOffsetY
|
||||||
newBullet.DirX = xfac
|
newBullet.DirX = xfac
|
||||||
newBullet.DirY = 0
|
newBullet.DirY = 0
|
||||||
@ -669,8 +675,8 @@ func ApplyInputFrameDownsyncDynamicsOnSingleRenderFrame(inputsBuffer *RingBuffer
|
|||||||
// 3. Add bullet colliders into collision system
|
// 3. Add bullet colliders into collision system
|
||||||
bulletColliders := make([]*resolv.Object, 0, len(currRenderFrame.MeleeBullets)) // Will all be removed at the end of this function due to the need for being rollback-compatible
|
bulletColliders := make([]*resolv.Object, 0, len(currRenderFrame.MeleeBullets)) // Will all be removed at the end of this function due to the need for being rollback-compatible
|
||||||
for _, meleeBullet := range currRenderFrame.MeleeBullets {
|
for _, meleeBullet := range currRenderFrame.MeleeBullets {
|
||||||
if (meleeBullet.Bullet.OriginatedRenderFrameId+meleeBullet.Bullet.StartupFrames <= currRenderFrame.Id) && (meleeBullet.Bullet.OriginatedRenderFrameId+meleeBullet.Bullet.StartupFrames+meleeBullet.Bullet.ActiveFrames > currRenderFrame.Id) {
|
if (meleeBullet.BattleAttr.OriginatedRenderFrameId+meleeBullet.Bullet.StartupFrames <= currRenderFrame.Id) && (meleeBullet.BattleAttr.OriginatedRenderFrameId+meleeBullet.Bullet.StartupFrames+meleeBullet.Bullet.ActiveFrames > currRenderFrame.Id) {
|
||||||
offender := currRenderFrame.PlayersArr[meleeBullet.Bullet.OffenderJoinIndex-1]
|
offender := currRenderFrame.PlayersArr[meleeBullet.BattleAttr.OffenderJoinIndex-1]
|
||||||
|
|
||||||
xfac := int32(1) // By now, straight Punch offset doesn't respect "y-axis"
|
xfac := int32(1) // By now, straight Punch offset doesn't respect "y-axis"
|
||||||
if 0 > offender.DirX {
|
if 0 > offender.DirX {
|
||||||
@ -681,7 +687,7 @@ func ApplyInputFrameDownsyncDynamicsOnSingleRenderFrame(inputsBuffer *RingBuffer
|
|||||||
newBulletCollider := GenerateRectCollider(bulletWx, bulletWy, hitboxSizeWx, hitboxSizeWy, SNAP_INTO_PLATFORM_OVERLAP, SNAP_INTO_PLATFORM_OVERLAP, SNAP_INTO_PLATFORM_OVERLAP, SNAP_INTO_PLATFORM_OVERLAP, collisionSpaceOffsetX, collisionSpaceOffsetY, meleeBullet, "MeleeBullet")
|
newBulletCollider := GenerateRectCollider(bulletWx, bulletWy, hitboxSizeWx, hitboxSizeWy, SNAP_INTO_PLATFORM_OVERLAP, SNAP_INTO_PLATFORM_OVERLAP, SNAP_INTO_PLATFORM_OVERLAP, SNAP_INTO_PLATFORM_OVERLAP, collisionSpaceOffsetX, collisionSpaceOffsetY, meleeBullet, "MeleeBullet")
|
||||||
collisionSys.Add(newBulletCollider)
|
collisionSys.Add(newBulletCollider)
|
||||||
bulletColliders = append(bulletColliders, newBulletCollider)
|
bulletColliders = append(bulletColliders, newBulletCollider)
|
||||||
} else if meleeBullet.Bullet.OriginatedRenderFrameId+meleeBullet.Bullet.StartupFrames+meleeBullet.Bullet.ActiveFrames > currRenderFrame.Id {
|
} else if meleeBullet.BattleAttr.OriginatedRenderFrameId+meleeBullet.Bullet.StartupFrames+meleeBullet.Bullet.ActiveFrames > currRenderFrame.Id {
|
||||||
nextRenderFrameMeleeBullets = append(nextRenderFrameMeleeBullets, meleeBullet)
|
nextRenderFrameMeleeBullets = append(nextRenderFrameMeleeBullets, meleeBullet)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -697,15 +703,16 @@ func ApplyInputFrameDownsyncDynamicsOnSingleRenderFrame(inputsBuffer *RingBuffer
|
|||||||
Speed: prevFireball.Speed,
|
Speed: prevFireball.Speed,
|
||||||
SpeciesId: prevFireball.SpeciesId,
|
SpeciesId: prevFireball.SpeciesId,
|
||||||
Bullet: prevFireball.Bullet,
|
Bullet: prevFireball.Bullet,
|
||||||
|
BattleAttr: prevFireball.BattleAttr,
|
||||||
}
|
}
|
||||||
if (fireballBullet.Bullet.OriginatedRenderFrameId+fireballBullet.Bullet.StartupFrames < currRenderFrame.Id) && (fireballBullet.Bullet.OriginatedRenderFrameId+fireballBullet.Bullet.StartupFrames+fireballBullet.Bullet.ActiveFrames > currRenderFrame.Id) {
|
if (fireballBullet.BattleAttr.OriginatedRenderFrameId+fireballBullet.Bullet.StartupFrames < currRenderFrame.Id) && (fireballBullet.BattleAttr.OriginatedRenderFrameId+fireballBullet.Bullet.StartupFrames+fireballBullet.Bullet.ActiveFrames > currRenderFrame.Id) {
|
||||||
bulletWx, bulletWy := VirtualGridToWorldPos(fireballBullet.VirtualGridX, fireballBullet.VirtualGridY)
|
bulletWx, bulletWy := VirtualGridToWorldPos(fireballBullet.VirtualGridX, fireballBullet.VirtualGridY)
|
||||||
hitboxSizeWx, hitboxSizeWy := VirtualGridToWorldPos(fireballBullet.Bullet.HitboxSizeX, fireballBullet.Bullet.HitboxSizeY)
|
hitboxSizeWx, hitboxSizeWy := VirtualGridToWorldPos(fireballBullet.Bullet.HitboxSizeX, fireballBullet.Bullet.HitboxSizeY)
|
||||||
newBulletCollider := GenerateRectCollider(bulletWx, bulletWy, hitboxSizeWx, hitboxSizeWy, SNAP_INTO_PLATFORM_OVERLAP, SNAP_INTO_PLATFORM_OVERLAP, SNAP_INTO_PLATFORM_OVERLAP, SNAP_INTO_PLATFORM_OVERLAP, collisionSpaceOffsetX, collisionSpaceOffsetY, fireballBullet, "FireballBullet")
|
newBulletCollider := GenerateRectCollider(bulletWx, bulletWy, hitboxSizeWx, hitboxSizeWy, SNAP_INTO_PLATFORM_OVERLAP, SNAP_INTO_PLATFORM_OVERLAP, SNAP_INTO_PLATFORM_OVERLAP, SNAP_INTO_PLATFORM_OVERLAP, collisionSpaceOffsetX, collisionSpaceOffsetY, fireballBullet, "FireballBullet")
|
||||||
collisionSys.Add(newBulletCollider)
|
collisionSys.Add(newBulletCollider)
|
||||||
bulletColliders = append(bulletColliders, newBulletCollider)
|
bulletColliders = append(bulletColliders, newBulletCollider)
|
||||||
} else if fireballBullet.Bullet.OriginatedRenderFrameId+fireballBullet.Bullet.StartupFrames+fireballBullet.Bullet.ActiveFrames > currRenderFrame.Id {
|
} else if fireballBullet.BattleAttr.OriginatedRenderFrameId+fireballBullet.Bullet.StartupFrames+fireballBullet.Bullet.ActiveFrames > currRenderFrame.Id {
|
||||||
// fmt.Printf("Pushing static fireball to next frame @currRenderFrame.Id=%d, bulletLocalId=%d, virtualGridX=%d, virtualGridY=%d\n", currRenderFrame.Id, fireballBullet.BulletLocalId, fireballBullet.VirtualGridX, fireballBullet.VirtualGridY)
|
// fmt.Printf("Pushing static fireball to next frame @currRenderFrame.Id=%d, bulletLocalId=%d, virtualGridX=%d, virtualGridY=%d\n", currRenderFrame.Id, fireballBullet.BattleAttr.BulletLocalId, fireballBullet.VirtualGridX, fireballBullet.VirtualGridY)
|
||||||
nextRenderFrameFireballBullets = append(nextRenderFrameFireballBullets, fireballBullet)
|
nextRenderFrameFireballBullets = append(nextRenderFrameFireballBullets, fireballBullet)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -843,12 +850,12 @@ func ApplyInputFrameDownsyncDynamicsOnSingleRenderFrame(inputsBuffer *RingBuffer
|
|||||||
switch v := bulletCollider.Data.(type) {
|
switch v := bulletCollider.Data.(type) {
|
||||||
case *MeleeBullet:
|
case *MeleeBullet:
|
||||||
bulletShape := bulletCollider.Shape.(*resolv.ConvexPolygon)
|
bulletShape := bulletCollider.Shape.(*resolv.ConvexPolygon)
|
||||||
offender := currRenderFrame.PlayersArr[v.Bullet.OffenderJoinIndex-1]
|
offender := currRenderFrame.PlayersArr[v.BattleAttr.OffenderJoinIndex-1]
|
||||||
for _, obj := range collision.Objects {
|
for _, obj := range collision.Objects {
|
||||||
defenderShape := obj.Shape.(*resolv.ConvexPolygon)
|
defenderShape := obj.Shape.(*resolv.ConvexPolygon)
|
||||||
switch t := obj.Data.(type) {
|
switch t := obj.Data.(type) {
|
||||||
case *PlayerDownsync:
|
case *PlayerDownsync:
|
||||||
if v.Bullet.OffenderJoinIndex == t.JoinIndex {
|
if v.BattleAttr.OffenderJoinIndex == t.JoinIndex {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
overlapped, _, _, _ := calcPushbacks(0, 0, bulletShape, defenderShape)
|
overlapped, _, _, _ := calcPushbacks(0, 0, bulletShape, defenderShape)
|
||||||
@ -885,12 +892,12 @@ func ApplyInputFrameDownsyncDynamicsOnSingleRenderFrame(inputsBuffer *RingBuffer
|
|||||||
}
|
}
|
||||||
case *FireballBullet:
|
case *FireballBullet:
|
||||||
bulletShape := bulletCollider.Shape.(*resolv.ConvexPolygon)
|
bulletShape := bulletCollider.Shape.(*resolv.ConvexPolygon)
|
||||||
offender := currRenderFrame.PlayersArr[v.Bullet.OffenderJoinIndex-1]
|
offender := currRenderFrame.PlayersArr[v.BattleAttr.OffenderJoinIndex-1]
|
||||||
for _, obj := range collision.Objects {
|
for _, obj := range collision.Objects {
|
||||||
defenderShape := obj.Shape.(*resolv.ConvexPolygon)
|
defenderShape := obj.Shape.(*resolv.ConvexPolygon)
|
||||||
switch t := obj.Data.(type) {
|
switch t := obj.Data.(type) {
|
||||||
case *PlayerDownsync:
|
case *PlayerDownsync:
|
||||||
if v.Bullet.OffenderJoinIndex == t.JoinIndex {
|
if v.BattleAttr.OffenderJoinIndex == t.JoinIndex {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
overlapped, _, _, _ := calcPushbacks(0, 0, bulletShape, defenderShape)
|
overlapped, _, _, _ := calcPushbacks(0, 0, bulletShape, defenderShape)
|
||||||
@ -1071,11 +1078,13 @@ func AlignPolygon2DToBoundingBox(input *Polygon2D) *Polygon2D {
|
|||||||
|
|
||||||
func NewMeleeBullet(bulletLocalId, originatedRenderFrameId, offenderJoinIndex, startupFrames, cancellableStFrame, cancellableEdFrame, activeFrames, hitStunFrames, blockStunFrames, pushbackVelX, pushbackVelY, damage, selfLockVelX, selfLockVelY, hitboxOffsetX, hitboxOffsetY, hitboxSizeX, hitboxSizeY int32, blowUp bool, teamId int32) *MeleeBullet {
|
func NewMeleeBullet(bulletLocalId, originatedRenderFrameId, offenderJoinIndex, startupFrames, cancellableStFrame, cancellableEdFrame, activeFrames, hitStunFrames, blockStunFrames, pushbackVelX, pushbackVelY, damage, selfLockVelX, selfLockVelY, hitboxOffsetX, hitboxOffsetY, hitboxSizeX, hitboxSizeY int32, blowUp bool, teamId int32) *MeleeBullet {
|
||||||
return &MeleeBullet{
|
return &MeleeBullet{
|
||||||
Bullet: &BulletConfig{
|
BattleAttr: &BulletBattleAttr{
|
||||||
BulletLocalId: bulletLocalId,
|
BulletLocalId: bulletLocalId,
|
||||||
OriginatedRenderFrameId: originatedRenderFrameId,
|
OriginatedRenderFrameId: originatedRenderFrameId,
|
||||||
OffenderJoinIndex: offenderJoinIndex,
|
OffenderJoinIndex: offenderJoinIndex,
|
||||||
|
TeamId: teamId,
|
||||||
|
},
|
||||||
|
Bullet: &BulletConfig{
|
||||||
StartupFrames: startupFrames,
|
StartupFrames: startupFrames,
|
||||||
CancellableStFrame: cancellableStFrame,
|
CancellableStFrame: cancellableStFrame,
|
||||||
CancellableEdFrame: cancellableEdFrame,
|
CancellableEdFrame: cancellableEdFrame,
|
||||||
@ -1096,8 +1105,6 @@ func NewMeleeBullet(bulletLocalId, originatedRenderFrameId, offenderJoinIndex, s
|
|||||||
HitboxSizeY: hitboxSizeY,
|
HitboxSizeY: hitboxSizeY,
|
||||||
|
|
||||||
BlowUp: blowUp,
|
BlowUp: blowUp,
|
||||||
|
|
||||||
TeamId: teamId,
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1112,11 +1119,13 @@ func NewFireballBullet(bulletLocalId, originatedRenderFrameId, offenderJoinIndex
|
|||||||
VelY: velY,
|
VelY: velY,
|
||||||
Speed: speed,
|
Speed: speed,
|
||||||
SpeciesId: speciesId,
|
SpeciesId: speciesId,
|
||||||
Bullet: &BulletConfig{
|
BattleAttr: &BulletBattleAttr{
|
||||||
BulletLocalId: bulletLocalId,
|
BulletLocalId: bulletLocalId,
|
||||||
OriginatedRenderFrameId: originatedRenderFrameId,
|
OriginatedRenderFrameId: originatedRenderFrameId,
|
||||||
OffenderJoinIndex: offenderJoinIndex,
|
OffenderJoinIndex: offenderJoinIndex,
|
||||||
|
TeamId: teamId,
|
||||||
|
},
|
||||||
|
Bullet: &BulletConfig{
|
||||||
StartupFrames: startupFrames,
|
StartupFrames: startupFrames,
|
||||||
CancellableStFrame: cancellableStFrame,
|
CancellableStFrame: cancellableStFrame,
|
||||||
CancellableEdFrame: cancellableEdFrame,
|
CancellableEdFrame: cancellableEdFrame,
|
||||||
@ -1137,8 +1146,6 @@ func NewFireballBullet(bulletLocalId, originatedRenderFrameId, offenderJoinIndex
|
|||||||
HitboxSizeY: hitboxSizeY,
|
HitboxSizeY: hitboxSizeY,
|
||||||
|
|
||||||
BlowUp: blowUp,
|
BlowUp: blowUp,
|
||||||
|
|
||||||
TeamId: teamId,
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -63,15 +63,10 @@ type Barrier struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type BulletConfig struct {
|
type BulletConfig struct {
|
||||||
BulletLocalId int32 // for referencing cached nodes in frontend rendering
|
StartupFrames int32 // from "OriginatedRenderFrameId"
|
||||||
|
CancellableStFrame int32 // from "OriginatedRenderFrameId"
|
||||||
// for offender
|
CancellableEdFrame int32 // from "OriginatedRenderFrameId"
|
||||||
OriginatedRenderFrameId int32 // Copied from the first bullet for all subsequent bullets
|
ActiveFrames int32
|
||||||
OffenderJoinIndex int32 // Copied to favor collision handling of the dispatched bullet
|
|
||||||
StartupFrames int32 // from "OriginatedRenderFrameId"
|
|
||||||
CancellableStFrame int32 // from "OriginatedRenderFrameId"
|
|
||||||
CancellableEdFrame int32 // from "OriginatedRenderFrameId"
|
|
||||||
ActiveFrames int32
|
|
||||||
|
|
||||||
// for defender
|
// for defender
|
||||||
HitStunFrames int32
|
HitStunFrames int32
|
||||||
@ -91,12 +86,20 @@ type BulletConfig struct {
|
|||||||
BlowUp bool
|
BlowUp bool
|
||||||
|
|
||||||
CancelTransit map[int]int
|
CancelTransit map[int]int
|
||||||
|
}
|
||||||
|
|
||||||
TeamId int32
|
type BulletBattleAttr struct {
|
||||||
|
BulletLocalId int32 // for referencing cached nodes in frontend rendering
|
||||||
|
|
||||||
|
// for offender
|
||||||
|
OriginatedRenderFrameId int32 // Copied from the first bullet for all subsequent bullets
|
||||||
|
OffenderJoinIndex int32 // Copied to favor collision handling of the dispatched bullet
|
||||||
|
TeamId int32
|
||||||
}
|
}
|
||||||
|
|
||||||
type MeleeBullet struct {
|
type MeleeBullet struct {
|
||||||
Bullet *BulletConfig
|
BattleAttr *BulletBattleAttr
|
||||||
|
Bullet *BulletConfig
|
||||||
}
|
}
|
||||||
|
|
||||||
type FireballBullet struct {
|
type FireballBullet struct {
|
||||||
@ -108,6 +111,7 @@ type FireballBullet struct {
|
|||||||
VelY int32
|
VelY int32
|
||||||
Speed int32
|
Speed int32
|
||||||
SpeciesId int32
|
SpeciesId int32
|
||||||
|
BattleAttr *BulletBattleAttr
|
||||||
Bullet *BulletConfig
|
Bullet *BulletConfig
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user