1
This commit is contained in:
@@ -66,6 +66,19 @@ namespace Assets.MyGame.Scripts.Net.WebResponses
|
||||
public List<string> bulletIds;
|
||||
public SyncGameData extra;
|
||||
public bool isBot;
|
||||
/// <summary>服务端权威:每发子弹扣费明细(bulletId + gold)</summary>
|
||||
public List<BulletGold> bullets;
|
||||
/// <summary>服务端时间戳</summary>
|
||||
public long timestamp;
|
||||
}
|
||||
/// <summary>
|
||||
/// 单发子弹扣费明细:服务端在 FireCannon 广播里返回的权威扣费金额
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
public class BulletGold
|
||||
{
|
||||
public string bulletId;
|
||||
public long gold;
|
||||
}
|
||||
/// <summary>
|
||||
/// 同步游戏数据
|
||||
|
||||
@@ -445,6 +445,9 @@ namespace Assets.MyGame.Scripts.Net
|
||||
case "SyncPlayerGold":
|
||||
WebSocketReponseList.SyncPlayerGold(msg);
|
||||
break;
|
||||
case "ChangeGunMultipleNotice":
|
||||
WebSocketReponseList.ChangeGunMultipleNotice(msg);
|
||||
break;
|
||||
case "GetRoomInfoResult":
|
||||
WebSocketReponseList.GetRoomInfoResult(msg);
|
||||
|
||||
@@ -579,14 +582,9 @@ namespace Assets.MyGame.Scripts.Net
|
||||
}
|
||||
break;
|
||||
|
||||
case "ChangeGunMultipleNotice":
|
||||
{
|
||||
var data = JsonConvert.DeserializeObject<WebSocketResponseBase<Dictionary<string, object>>>(msg).arguments[0];
|
||||
// { userId, multiple }
|
||||
if (data != null)
|
||||
Debug.Log($"[FishGameHub] 炮倍变更: userId={data["userId"]}, multiple={data["multiple"]}");
|
||||
}
|
||||
break;
|
||||
//case "ChangeGunMultipleNotice":
|
||||
// WebSocketReponseList.ChangeGunMultipleNotice(msg);
|
||||
// break;
|
||||
|
||||
case "UsePropNotice":
|
||||
{
|
||||
|
||||
@@ -220,6 +220,22 @@ namespace Assets.MyGame.Scripts.Net
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 炮倍变更通知:服务端确认玩家炮倍切换后广播给房间内所有玩家。
|
||||
/// 客户端收到后才应用新炮倍到 consumeGoldNum 和 UI,避免开火扣费不一致。
|
||||
/// </summary>
|
||||
public static void ChangeGunMultipleNotice(string msg)
|
||||
{
|
||||
var data = JsonConvert.DeserializeObject<WebSocketResponseBase<ChangeGunMultipleNotice>>(msg).arguments[0];
|
||||
var panel = UIManager.Instance.Get<GameMainPanel_FishBase>(UIPanel.MainGamePanelDefault)
|
||||
?? UIManager.Instance.Get<GameMainPanel_FishBase>(UIPanel.MainGamePanelFLZY)
|
||||
?? UIManager.Instance.Get<GameMainPanel_FishBase>(UIPanel.MainGamePanelYYSC)
|
||||
?? UIManager.Instance.Get<GameMainPanel_FishBase>(UIPanel.MainGamePanelXJMH)
|
||||
?? UIManager.Instance.Get<GameMainPanel_FishBase>(UIPanel.MainGamePanelHHWK);
|
||||
if (panel == null) return;
|
||||
panel.ApplyCannonMultiple(data.userId, data.multiple);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 后端推送 SyncPlayerGold 时同步本地玩家金币(底层值 + UI 显示)。
|
||||
/// 之前只更新 goldNum 不刷新 tMP_GoldNum.text,导致 UI 显示和后端 authoritative 值不同步,
|
||||
|
||||
@@ -597,15 +597,18 @@ public class SitPosData : MonoBehaviour
|
||||
|
||||
// ==================== 金币消耗 ====================
|
||||
|
||||
public bool ConsumeGold(int mul = 1)
|
||||
public bool ConsumeGold(int mul = 1, long fireConsumeGold = 0)
|
||||
{
|
||||
long temp = goldNum - consumeGoldNum * mul;
|
||||
// fireConsumeGold>0: 用服务端返回的该发子弹权威扣费金额
|
||||
// fireConsumeGold=0: 回退到本地 consumeGoldNum * mul
|
||||
long useConsume = fireConsumeGold > 0 ? fireConsumeGold : consumeGoldNum * mul;
|
||||
long temp = goldNum - useConsume;
|
||||
if (temp < 0) return false;
|
||||
long oldGold = goldNum;
|
||||
goldNum = temp;
|
||||
tMP_GoldNum.text = goldNum.ToString();
|
||||
// ⭐ 记录金币流水:已扣钱(开火消耗,本地已执行)
|
||||
GameManager.LogGoldFlowAppliedCost("Fire", $"开火消耗 原有金币={oldGold} 消耗金币=-{consumeGoldNum * mul} 剩余金币={goldNum} 座位={posID} 房间={GameManager.Instance.gameData?.currRoomData?.roomId}");
|
||||
GameManager.LogGoldFlowAppliedCost("Fire", $"开火消耗 原有金币={oldGold} 消耗金币=-{useConsume} 剩余金币={goldNum} 座位={posID} 房间={GameManager.Instance.gameData?.currRoomData?.roomId}");
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -631,9 +634,12 @@ public class SitPosData : MonoBehaviour
|
||||
indexConsume = 0;
|
||||
AudioManager.Instance.PlayerFX(62011);
|
||||
|
||||
consumeGoldNum = CreateFishManager.Instance.currGameType.paoBeiList[indexConsume];
|
||||
tMP_TurretConsumeNum.text = consumeGoldNum.ToString();
|
||||
CreateFishManager.Instance.currGameType?.SyncCannonMultiple((int)consumeGoldNum);
|
||||
// ⭐ 不立即修改 consumeGoldNum 和 UI,等服务端 ChangeGunMultipleNotice 确认后再应用
|
||||
// 避免:客户端先切换炮倍 → 开火按新炮倍扣费 → 服务端还没确认按旧炮倍扣费 → 差值
|
||||
int newConsume = CreateFishManager.Instance.currGameType.paoBeiList[indexConsume];
|
||||
string roomId = GameManager.Instance.gameData?.currRoomData?.roomId;
|
||||
if (!string.IsNullOrEmpty(roomId))
|
||||
WebSocketSendList.ChangeGunMultiple(roomId, user, newConsume);
|
||||
}
|
||||
|
||||
public void ChangeTurretMinusConsumeNum()
|
||||
@@ -644,9 +650,12 @@ public class SitPosData : MonoBehaviour
|
||||
indexConsume = CreateFishManager.Instance.currGameType.paoBeiList.Count - 1;
|
||||
AudioManager.Instance.PlayerFX(62010);
|
||||
|
||||
consumeGoldNum = CreateFishManager.Instance.currGameType.paoBeiList[indexConsume];
|
||||
tMP_TurretConsumeNum.text = consumeGoldNum.ToString();
|
||||
CreateFishManager.Instance.currGameType?.SyncCannonMultiple((int)consumeGoldNum);
|
||||
// ⭐ 不立即修改 consumeGoldNum 和 UI,等服务端 ChangeGunMultipleNotice 确认后再应用
|
||||
// 避免:客户端先切换炮倍 → 开火按新炮倍扣费 → 服务端还没确认按旧炮倍扣费 → 差值
|
||||
int newConsume = CreateFishManager.Instance.currGameType.paoBeiList[indexConsume];
|
||||
string roomId = GameManager.Instance.gameData?.currRoomData?.roomId;
|
||||
if (!string.IsNullOrEmpty(roomId))
|
||||
WebSocketSendList.ChangeGunMultiple(roomId, user, newConsume);
|
||||
}
|
||||
|
||||
// ==================== 锁定系统 ====================
|
||||
|
||||
@@ -44,23 +44,50 @@ MonoBehaviour:
|
||||
m_Script: {fileID: 11500000, guid: 38ccfa89023796c4a94893b7e9b4914c, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
tMP_CoinCount: {fileID: 0}
|
||||
gold: {fileID: 0}
|
||||
silver: {fileID: 0}
|
||||
endPoint: {x: 0, y: 0, z: 0}
|
||||
isBoss: 0
|
||||
coinCount: 0
|
||||
userID:
|
||||
sitID: 0
|
||||
startPoint: {x: 0, y: 0, z: 0}
|
||||
radius: 0
|
||||
count: 0
|
||||
bjEndUI: {fileID: 0}
|
||||
useScorePanel: 1
|
||||
tMP_Score: {fileID: 0}
|
||||
scorePanelPrefab: {fileID: 0}
|
||||
currScore: 0
|
||||
maxScore: 0
|
||||
scoreCount: 0
|
||||
posID: 0
|
||||
paoBei: 0
|
||||
time: 0.5
|
||||
baoJiangEffectPrefab: {fileID: 0}
|
||||
fishScore: 0
|
||||
bJbeishu: 0
|
||||
showDelay: 3
|
||||
moveDuration: 0.5
|
||||
moveEase: 6
|
||||
startOffset: {x: 0, y: 0, z: -100}
|
||||
bjEndUIOffsetY: 150
|
||||
coinFlyDuration: 0.5
|
||||
coinArrivalInterval: 0.1
|
||||
isSelf: 0
|
||||
autoFinishOnScoreFull: 1
|
||||
totalBJCount: 0
|
||||
currBJCount: 0
|
||||
isEffEnd: 0
|
||||
isScoreStart: 0
|
||||
isScoreEnd: 0
|
||||
isGoldEffectStart: 0
|
||||
baoJiangMode: 0
|
||||
endUIType: 1
|
||||
endUIEntries: []
|
||||
monster: {fileID: 0}
|
||||
startPoint: {x: 0, y: 0, z: 0}
|
||||
coinCount: 0
|
||||
isLocalCoin: 0
|
||||
baoJiangEffectPrefab: {fileID: 136624701376015062, guid: 8d6e13708cafa5d448d52c8fd7f00f1a,
|
||||
type: 3}
|
||||
parentTransform: {fileID: 0}
|
||||
offsetPosition: {x: 0, y: 2, z: 0}
|
||||
effectType: 5
|
||||
onTimeObjs: []
|
||||
hitTimes: []
|
||||
time: 0.5
|
||||
dieEffectTesyNum: 0
|
||||
minBJCount: 10
|
||||
maxBJCount: 15
|
||||
lightningMaterial: {fileID: 2100000, guid: a023113e8eb8bf245b8ba10c3e9788d1, type: 2}
|
||||
lightningColor: {r: 1, g: 0.92156863, b: 0.015686275, a: 1}
|
||||
lightningWidth: 0.08
|
||||
@@ -69,7 +96,6 @@ MonoBehaviour:
|
||||
explosionPrefab: {fileID: 1709254077376921, guid: 08be47e4b1d3e1c4a895269c2c08653c,
|
||||
type: 3}
|
||||
explosionRadius: 10
|
||||
startPoint: {x: 0, y: 0, z: 0}
|
||||
lightningCount: 5
|
||||
lightningRange: 5
|
||||
lightningWait: 0.5
|
||||
|
||||
@@ -1371,10 +1371,18 @@ public class GameMainPanel_FishBase : GameMainPanel_Base
|
||||
SyncFire(angle, isSanShe, isDouble, isLock);
|
||||
}
|
||||
private void FireBullet(SitPosData sit, float angle, bool isSanShe, bool isDouble, bool isLock, List<string> bullitIDs,
|
||||
List<string> fireTargetMonsterIDs = null)
|
||||
List<string> fireTargetMonsterIDs = null, List<long> bulletGolds = null)
|
||||
{
|
||||
Vector2 startPos = sit.GetBulletStartPos();
|
||||
|
||||
// ⭐ 按索引取该发子弹的服务端权威扣费金额(bullets[i].gold),没有则回退到0(ConsumeGold内部用consumeGoldNum)
|
||||
long GetBulletGold(int i)
|
||||
{
|
||||
if (bulletGolds != null && i < bulletGolds.Count)
|
||||
return bulletGolds[i];
|
||||
return 0;
|
||||
}
|
||||
|
||||
// ⭐ 三发 + 锁定:每发子弹优先瞄准对应目标,目标不足或失效则按默认角度发射
|
||||
// 必须保证三发子弹都创建,否则金币消耗和后端对不上
|
||||
if (isSanShe && isLock)
|
||||
@@ -1406,24 +1414,24 @@ public class GameMainPanel_FishBase : GameMainPanel_Base
|
||||
Vector3 targetWorldPos = targetHitPos.position;
|
||||
Vector2 dir = ((Vector2)targetWorldPos - startPos).normalized;
|
||||
float targetAngle = Mathf.Atan2(dir.y, dir.x) * Mathf.Rad2Deg - 90f;
|
||||
CreateBullet(sit, startPos, targetAngle, isDouble, true, bulletId, targetMonster, targetHitPos);
|
||||
CreateBullet(sit, startPos, targetAngle, isDouble, true, bulletId, targetMonster, targetHitPos, GetBulletGold(i));
|
||||
}
|
||||
else
|
||||
{
|
||||
// 目标不足或失效 → 按默认角度发射(不锁定)
|
||||
CreateBullet(sit, startPos, defaultAngles[i], isDouble, isLock, bulletId);
|
||||
CreateBullet(sit, startPos, defaultAngles[i], isDouble, isLock, bulletId, null, null, GetBulletGold(i));
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (isSanShe)
|
||||
{
|
||||
CreateBullet(sit, startPos, angle - 20, isDouble, isLock, bullitIDs[0]);
|
||||
CreateBullet(sit, startPos, angle, isDouble, isLock, bullitIDs[1]);
|
||||
CreateBullet(sit, startPos, angle + 20, isDouble, isLock, bullitIDs[2]);
|
||||
CreateBullet(sit, startPos, angle - 20, isDouble, isLock, bullitIDs[0], null, null, GetBulletGold(0));
|
||||
CreateBullet(sit, startPos, angle, isDouble, isLock, bullitIDs[1], null, null, GetBulletGold(1));
|
||||
CreateBullet(sit, startPos, angle + 20, isDouble, isLock, bullitIDs[2], null, null, GetBulletGold(2));
|
||||
}
|
||||
else
|
||||
{
|
||||
CreateBullet(sit, startPos, angle, isDouble, isLock, bullitIDs[0]);
|
||||
CreateBullet(sit, startPos, angle, isDouble, isLock, bullitIDs[0], null, null, GetBulletGold(0));
|
||||
}
|
||||
if (sit.posID==currSitData.posID)
|
||||
{
|
||||
@@ -2001,7 +2009,7 @@ public class GameMainPanel_FishBase : GameMainPanel_Base
|
||||
}
|
||||
|
||||
private BullitMove CreateBullet(SitPosData sit, Vector2 pos, float angle, bool isDouble, bool isLock, string id,
|
||||
MonsterContral lockTarget = null, Transform lockHitPos = null)
|
||||
MonsterContral lockTarget = null, Transform lockHitPos = null, long fireConsumeGold = 0)
|
||||
{
|
||||
// ⭐ 远程座位炮台未加载时补加载
|
||||
if (sit.currentTurret == null)
|
||||
@@ -2025,7 +2033,7 @@ public class GameMainPanel_FishBase : GameMainPanel_Base
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!sit.ConsumeGold(isDouble ? 2 : 1))
|
||||
if (!sit.ConsumeGold(isDouble ? 2 : 1, fireConsumeGold))
|
||||
return null;
|
||||
|
||||
// ⭐ 从对象池取子弹(池空时内部会 Instantiate)
|
||||
@@ -2211,6 +2219,24 @@ public class GameMainPanel_FishBase : GameMainPanel_Base
|
||||
// 但如果服务器不回广播给开火者,本地玩家需要在 SyncFire 里直接 FireBullet)
|
||||
// 实际设计:本地玩家也通过广播回来创建子弹,所以不跳过
|
||||
|
||||
// ⭐ 从服务端权威数据提取每发子弹扣费金额
|
||||
// 1. 优先用 bullets 字段(type:3 调用返回值,包含每发子弹的扣费金额)
|
||||
// 2. fallback 用 sync.gold(RoomFireNotice 广播里的炮倍,服务端权威)
|
||||
List<long> bulletGolds = null;
|
||||
if (sync.bullets != null && sync.bullets.Count > 0)
|
||||
{
|
||||
bulletGolds = new List<long>(sync.bullets.Count);
|
||||
foreach (var bg in sync.bullets)
|
||||
bulletGolds.Add(bg.gold);
|
||||
}
|
||||
else if (sync.gold > 0 && sync.bulletIds != null && sync.bulletIds.Count > 0)
|
||||
{
|
||||
// RoomFireNotice 没有 bullets 字段,用 sync.gold(服务端权威炮倍)作为每发扣费金额
|
||||
bulletGolds = new List<long>(sync.bulletIds.Count);
|
||||
for (int i = 0; i < sync.bulletIds.Count; i++)
|
||||
bulletGolds.Add(sync.gold);
|
||||
}
|
||||
|
||||
// extra 为空:从 botLastFireData 或默认参数还原(bot 或真人玩家都可能走到这里)
|
||||
if (sync.extra == null || sync.extra.frameData == null)
|
||||
{
|
||||
@@ -2226,17 +2252,18 @@ public class GameMainPanel_FishBase : GameMainPanel_Base
|
||||
if (isBot && botLastFireData.TryGetValue(sync.userId, out var fireData))
|
||||
{
|
||||
FireBullet(sit, angle, fireData.isSanShe, fireData.twoRTruuect,
|
||||
fireData.isSuoDing, sync.bulletIds, fireData.monsterIDs);
|
||||
fireData.isSuoDing, sync.bulletIds, fireData.monsterIDs, bulletGolds);
|
||||
}
|
||||
else
|
||||
{
|
||||
FireBullet(sit, angle, false, false, false, sync.bulletIds, null);
|
||||
FireBullet(sit, angle, false, false, false, sync.bulletIds, null, bulletGolds);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
var data = FrameDataParser.Parse<FrameData_FireBullet>(sync.extra.frameData);
|
||||
FireBullet(list_Sits[sync.extra.sitPos], data.angle, data.isSanShe, data.twoRTruuect, data.isSuoDing, sync.bulletIds, data.monsterIDs);
|
||||
var targetSit = list_Sits[sync.extra.sitPos];
|
||||
FireBullet(targetSit, data.angle, data.isSanShe, data.twoRTruuect, data.isSuoDing, sync.bulletIds, data.monsterIDs, bulletGolds);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -2333,6 +2360,22 @@ public class GameMainPanel_FishBase : GameMainPanel_Base
|
||||
sit.tMP_TurretConsumeNum.text = data.cannonMultiple.ToString();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 应用服务端确认的炮倍变更(ChangeGunMultipleNotice)。
|
||||
/// 本地玩家切换炮倍后,等服务端广播此通知才更新 consumeGoldNum 和 UI,
|
||||
/// 确保开火扣费时本地炮倍与服务端一致。
|
||||
/// </summary>
|
||||
public void ApplyCannonMultiple(string userId, int multiple)
|
||||
{
|
||||
var sit = list_Sits.Find(s => s.user == userId);
|
||||
if (sit == null) return;
|
||||
|
||||
sit.consumeGoldNum = multiple;
|
||||
sit.SyncIndexByConsume(multiple);
|
||||
if (sit.tMP_TurretConsumeNum != null)
|
||||
sit.tMP_TurretConsumeNum.text = multiple.ToString();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 爆奖结束帧同步(baoJiangEnd):
|
||||
/// 其他玩家爆奖流程完成,从 pending 取出对应 not-self 爆奖,
|
||||
|
||||
Reference in New Issue
Block a user