diff --git a/Assets/AddressableAssetsData/Windows/addressables_content_state.bin b/Assets/AddressableAssetsData/Windows/addressables_content_state.bin index 7a919b894..970442ac9 100644 Binary files a/Assets/AddressableAssetsData/Windows/addressables_content_state.bin and b/Assets/AddressableAssetsData/Windows/addressables_content_state.bin differ diff --git a/Assets/Framework/Event/ConstEventString.cs b/Assets/Framework/Event/ConstEventString.cs index 1904c68e2..2154f4127 100644 --- a/Assets/Framework/Event/ConstEventString.cs +++ b/Assets/Framework/Event/ConstEventString.cs @@ -8,5 +8,6 @@ public class ConstEventString public const string SyncGameData_RendromDieFish = "SyncGameData_RenderDieFish"; public const string SyncGameData_TurretState = "SyncGameData_TurretState"; public const string SyncGameData_ChangeTurret = "SyncGameData_ChangeTurret"; + public const string SyncGameData_FreezeFish = "SyncGameData_FreezeFish"; } diff --git a/Assets/MyGame/Scripts/Manager/CreateFishManager.cs b/Assets/MyGame/Scripts/Manager/CreateFishManager.cs index 73f936567..de8b2d5b1 100644 --- a/Assets/MyGame/Scripts/Manager/CreateFishManager.cs +++ b/Assets/MyGame/Scripts/Manager/CreateFishManager.cs @@ -96,6 +96,8 @@ public class CreateFishManager : MonoSingleton public Dictionary spawnDic; public bool isCreateFish; //是否可以创建鱼 + public bool isFrozen = false; //是否冰冻中(冻结出鱼+所有鱼定格) + private Dictionary frozenFishStates = new Dictionary(); /// 按鱼类型分类的生成配置(Boss / 普通 / 黄金…) private Dictionary> fishTypeConfig; @@ -240,7 +242,8 @@ public class CreateFishManager : MonoSingleton private void Update() { - isCreateFish = BroadCastCreateFish(); + if (!isFrozen) + isCreateFish = BroadCastCreateFish(); } @@ -902,6 +905,7 @@ public class CreateFishManager : MonoSingleton /// public void CreateFish(SyncGameData syncGameData) { + if (isFrozen) return; // 冰冻中不创建鱼 var rotateData = FrameDataParser.Parse(syncGameData.frameData); if (rotateData != null) SpawnFishMainThread(rotateData, syncGameData.time); @@ -1487,7 +1491,9 @@ public class CreateFishManager : MonoSingleton { if (item.user == killFish.userId) { - item.monCotl_Lock = null; + // 只有死的鱼正好是当前锁定目标时才清除 + if (item.monCotl_Lock != null && item.monCotl_Lock.guid == killFish.fishId) + item.monCotl_Lock = null; //item.lockFX.gameObject.SetActive(false); sitData = item; } @@ -1681,4 +1687,54 @@ public class CreateFishManager : MonoSingleton return null; } } + + #region 冰冻技能 + /// + /// 冰冻:记录所有鱼当前状态并强制切到 Idle(暂停移动+动画) + /// + public void FreezeAllFish() + { + isFrozen = true; + frozenFishStates.Clear(); + + // 只遍历全量字典 fishDatas_hashCodekey(已包含所有鱼),不重复遍历子字典 + if (fishDatas_hashCodekey != null) + { + foreach (var kv in fishDatas_hashCodekey) + { + MonsterContral m = kv.Value; + if (m == null || !m.gameObject.activeInHierarchy) continue; + + // 记录原状态 + frozenFishStates[m.guid] = m.currState; + // 定格 + m.isMove = false; + m.ChangeState(CharacterStateType.idle); + } + } + } + + /// + /// 解冻:恢复鱼到冰冻前状态 + /// + public void UnfreezeAllFish() + { + isFrozen = false; + + if (fishDatas_hashCodekey != null) + { + foreach (var kv in fishDatas_hashCodekey) + { + MonsterContral m = kv.Value; + if (m == null || !m.gameObject.activeInHierarchy) continue; + + m.isMove = true; + if (frozenFishStates.TryGetValue(m.guid, out var prevState)) + m.ChangeState(prevState); + } + } + + frozenFishStates.Clear(); + } + #endregion } diff --git a/Assets/MyGame/Scripts/Net/WebResponses/SyncGameData.cs b/Assets/MyGame/Scripts/Net/WebResponses/SyncGameData.cs index 0ed808586..a73c88d6a 100644 --- a/Assets/MyGame/Scripts/Net/WebResponses/SyncGameData.cs +++ b/Assets/MyGame/Scripts/Net/WebResponses/SyncGameData.cs @@ -48,7 +48,8 @@ namespace Assets.MyGame.Scripts.Net.WebResponses changeAutoTarget=4, randomDieFish=5, changeTurretState=6, - changeTurret=7 + changeTurret=7, + freezeFish=8 } [Serializable] @@ -93,6 +94,8 @@ namespace Assets.MyGame.Scripts.Net.WebResponses public bool twoRTruuect; public string monsterID; public bool isZuanTou; + /// 三发锁定时每个子弹对应的鱼 GUID(3个),按优先级排序 + public List monsterIDs; } [Serializable] public class FrameData_CreateFish : FrameData @@ -127,4 +130,9 @@ namespace Assets.MyGame.Scripts.Net.WebResponses { public int turretIndex; } + [Serializable] + public class FrameData_FreezeFish : FrameData + { + public bool isFreeze; // true=冰冻开始, false=解冻结束 + } } diff --git a/Assets/MyGame/Scripts/Net/WebSocketReponseList.cs b/Assets/MyGame/Scripts/Net/WebSocketReponseList.cs index ad363f6b2..503c55994 100644 --- a/Assets/MyGame/Scripts/Net/WebSocketReponseList.cs +++ b/Assets/MyGame/Scripts/Net/WebSocketReponseList.cs @@ -202,6 +202,9 @@ namespace Assets.MyGame.Scripts.Net case FrameDataType.randomDieFish: EventCenter.Instance.EventTrigger(ConstEventString.SyncGameData_RendromDieFish, syncGameData); break; + case FrameDataType.freezeFish: + EventCenter.Instance.EventTrigger(ConstEventString.SyncGameData_FreezeFish, syncGameData); + break; default: break; } diff --git a/Assets/MyGame/aa/UI/Game/GameMainPanel_FishBase.cs b/Assets/MyGame/aa/UI/Game/GameMainPanel_FishBase.cs index 2e9ed8279..dabfe9859 100644 --- a/Assets/MyGame/aa/UI/Game/GameMainPanel_FishBase.cs +++ b/Assets/MyGame/aa/UI/Game/GameMainPanel_FishBase.cs @@ -91,6 +91,7 @@ using Spine.Unity; using System; using System.Collections; using System.Collections.Generic; +using System.Linq; using System.Security.Cryptography; using System.Text; using System.Threading.Tasks; @@ -240,6 +241,7 @@ public class GameMainPanel_FishBase : GameMainPanel_Base EventCenter.Instance.AddEventListener(ConstEventString.SyncGameData_RendromDieFish, SyncGameData_RendromDieFish); EventCenter.Instance.AddEventListener(ConstEventString.SyncGameData_TurretState, SyncGameData_TurretState); EventCenter.Instance.AddEventListener(ConstEventString.SyncGameData_ChangeTurret, SyncGameData_ChangeTurret); + EventCenter.Instance.AddEventListener(ConstEventString.SyncGameData_FreezeFish, SyncGameData_FreezeFish); } @@ -251,6 +253,7 @@ public class GameMainPanel_FishBase : GameMainPanel_Base EventCenter.Instance.RemoveEventListener(ConstEventString.SyncGameData_RendromDieFish, SyncGameData_RendromDieFish); EventCenter.Instance.RemoveEventListener(ConstEventString.SyncGameData_TurretState, SyncGameData_TurretState); EventCenter.Instance.RemoveEventListener(ConstEventString.SyncGameData_ChangeTurret, SyncGameData_ChangeTurret); + EventCenter.Instance.RemoveEventListener(ConstEventString.SyncGameData_FreezeFish, SyncGameData_FreezeFish); @@ -290,6 +293,7 @@ public class GameMainPanel_FishBase : GameMainPanel_Base // UI 开关只是触发,但最终状态以 currGameType 为准 CreateFishManager.Instance.currGameType.isFrenzy = on; SetFrenzy(on); + if (on) currFrenzyTime = frenzyDuration; }); toggle_SkillAuto.onValueChanged.AddListener(on => { @@ -299,10 +303,23 @@ public class GameMainPanel_FishBase : GameMainPanel_Base currSitData.ChangeState(TurretState.Idle); } }); - toggle_BingFeng.onValueChanged.AddListener(on => { - - - + toggle_BingFeng.onValueChanged.AddListener(on => { + if (on) + { + // 开始冰冻 + currBingFengTime = bingFengDuration; + CreateFishManager.Instance.FreezeAllFish(); + SyncFreeze(true); + } + else + { + // 手动关闭 → 解冻 + if (CreateFishManager.Instance.isFrozen) + { + CreateFishManager.Instance.UnfreezeAllFish(); + SyncFreeze(false); + } + } }); @@ -357,6 +374,7 @@ public class GameMainPanel_FishBase : GameMainPanel_Base HandleAutoLock(); LoopLaser(); SettingClose(); + DaoJiShi(); } float sittingTime = 10; @@ -768,6 +786,7 @@ public class GameMainPanel_FishBase : GameMainPanel_Base { toggle_BingFeng.isOn = false; img_BingFeng.fillAmount = 1; + // 解冻由 toggle.onValueChanged(false) 触发,不在此重复 } } } @@ -789,10 +808,47 @@ public class GameMainPanel_FishBase : GameMainPanel_Base //FireBullet(currSitData, angle, isSanShe, isDouble, isLock); SyncFire(angle, isSanShe, isDouble, isLock); } - private void FireBullet(SitPosData sit, float angle, bool isSanShe, bool isDouble, bool isLock,List bullitIDs) + private void FireBullet(SitPosData sit, float angle, bool isSanShe, bool isDouble, bool isLock, List bullitIDs, + List fireTargetMonsterIDs = null) { Vector2 startPos = sit.GetBulletStartPos(); - if (isSanShe) + + // ⭐ 三发 + 锁定 + 有多目标:每发子弹瞄准不同的目标 + if (isSanShe && isLock && fireTargetMonsterIDs != null && fireTargetMonsterIDs.Count > 0) + { + for (int i = 0; i < 3 && i < fireTargetMonsterIDs.Count; i++) + { + // 按 GUID 查找目标 + MonsterContral targetMonster = null; + Transform targetHitPos = null; + if (CreateFishManager.Instance.fishDatas_hashCodekey.TryGetValue(fireTargetMonsterIDs[i], out var m) + && m != null && m.gameObject.activeInHierarchy) + { + targetMonster = m; + targetHitPos = m.transform; + // 优先用活跃 collider + if (m.colliderList != null) + { + foreach (var col in m.colliderList) + { + if (col.gameObject.activeInHierarchy) { targetHitPos = col; break; } + } + } + } + + if (targetMonster != null) + { + // 计算朝目标的角度 + Vector3 targetWorldPos = targetHitPos.position; + Vector2 dir = ((Vector2)targetWorldPos - startPos).normalized; + float targetAngle = Mathf.Atan2(dir.y, dir.x) * Mathf.Rad2Deg - 90f; + + string bulletId = i < bullitIDs.Count ? bullitIDs[i] : bullitIDs[0]; + CreateBullet(sit, startPos, targetAngle, isDouble, true, bulletId, targetMonster, targetHitPos); + } + } + } + else if (isSanShe) { CreateBullet(sit, startPos, angle - 20, isDouble, isLock, bullitIDs[0]); CreateBullet(sit, startPos, angle, isDouble, isLock, bullitIDs[1]); @@ -881,6 +937,83 @@ public class GameMainPanel_FishBase : GameMainPanel_Base + #endregion + + #region 多目标查找(TripleLock) + /// 鱼锁定优先级:特殊Boss(5) > Boss鱼(4) > 特殊鱼(3) > 黄金鱼(2) > 普通鱼(1) + private int GetFishLockPriority(MonsterContral monster) + { + switch (monster.fishenum) + { + case FishTypeEnum.特殊Boss: return 5; + case FishTypeEnum.Boss鱼: return 4; + case FishTypeEnum.特殊鱼: return 3; + case FishTypeEnum.黄金鱼: return 2; + case FishTypeEnum.普通鱼: return 1; + default: return 0; + } + } + + /// + /// 查找 fire 时的多目标(三发锁定用)。 + /// 第一个目标 = 主锁 monCotl_Lock(打哪锁哪),剩余从 Boss/特殊Boss/特殊鱼/黄金鱼 按优先级补满 + /// + private List<(MonsterContral monster, Transform hitPos)> FindFireTargets(int maxCount) + { + var result = new List<(MonsterContral monster, Transform hitPos)>(); + var selected = new HashSet(); + + if (CreateFishManager.Instance == null) return result; + + // ① 主锁目标排第一(打哪锁哪) + if (currSitData.isLock && currSitData.monCotl_Lock != null && + currSitData.monCotl_Lock.gameObject.activeInHierarchy) + { + Transform pos = currSitData.monCotl_Lock_Position != null && currSitData.monCotl_Lock_Position.gameObject.activeInHierarchy + ? currSitData.monCotl_Lock_Position + : currSitData.monCotl_Lock.transform; + result.Add((currSitData.monCotl_Lock, pos)); + selected.Add(currSitData.monCotl_Lock.guid); + if (result.Count >= maxCount) return result; + } + + // ② 按优先级从分类字典补满剩余位置 + var dicts = new Dictionary[] + { + CreateFishManager.Instance.fishDatas_hashCodekey_TS_Boss, + CreateFishManager.Instance.fishDatas_hashCodekey_Boss, + CreateFishManager.Instance.fishDatas_hashCodekey_TS, + CreateFishManager.Instance.fishDatas_hashCodekey_HJ, + }; + + foreach (var dict in dicts) + { + if (dict == null) continue; + + foreach (var kv in dict) + { + MonsterContral m = kv.Value; + if (m == null || !m.gameObject.activeInHierarchy) continue; + if (selected.Contains(m.guid)) continue; // 跳过已选中的 + + Transform pos = m.transform; + if (m.colliderList != null) + { + foreach (var col in m.colliderList) + { + if (col.gameObject.activeInHierarchy) { pos = col; break; } + } + } + + result.Add((m, pos)); + selected.Add(m.guid); + + if (result.Count >= maxCount) return result; + } + } + + return result; + } #endregion #region 锁定系统(LockModule) @@ -1056,7 +1189,8 @@ public class GameMainPanel_FishBase : GameMainPanel_Base #endregion #region BulletModule(子弹系统) - private BullitMove CreateBullet(SitPosData sit, Vector2 pos, float angle, bool isDouble, bool isLock,string id) + private BullitMove CreateBullet(SitPosData sit, Vector2 pos, float angle, bool isDouble, bool isLock, string id, + MonsterContral lockTarget = null, Transform lockHitPos = null) { GameObject prefab = isDouble ? sit.currentTurret.bulletPrefab_Two : sit.currentTurret.bulletPrefab; @@ -1071,7 +1205,12 @@ public class GameMainPanel_FishBase : GameMainPanel_Base bullet.bulletID = id; if (isLock) - bullet.Lock(sit.monCotl_Lock, currSitData.monCotl_Lock_Position); + { + if (lockTarget != null) + bullet.Lock(lockTarget, lockHitPos); + else + bullet.Lock(sit.monCotl_Lock, currSitData.monCotl_Lock_Position); + } return bullet; } @@ -1093,20 +1232,30 @@ public class GameMainPanel_FishBase : GameMainPanel_Base private void SyncFire(float angle, bool isSanShe, bool isDouble, bool isLock) { + var fireData = new FrameData_FireBullit + { + angle = angle, + gold = currSitData.goldNum, + isSanShe = isSanShe, + twoRTruuect = isDouble, + isSuoDing = isLock, + auto = toggle_SkillAuto.isOn + }; + + // ⭐ 三发 + 锁定:查找 3 个优先级目标,写入 monsterIDs + if (isSanShe && isLock) + { + var targets = FindFireTargets(3); + if (targets.Count > 0) + fireData.monsterIDs = targets.Select(t => t.monster.guid).ToList(); + } + SyncGameData sgd = new SyncGameData { sitPos = currSitData.posID, frameType = FrameDataType.fireBullet, time = BeijingTimeManager.Instance.CurrentTimestampMs, - frameData = new FrameData_FireBullit - { - angle = angle, - gold = currSitData.goldNum, - isSanShe = isSanShe, - twoRTruuect = isDouble, - isSuoDing = isLock, - auto = toggle_SkillAuto.isOn - } + frameData = fireData }; //WebSocketSendList.SyncGameData(sgd); var roomdata = GameManager.Instance.gameData.currRoomData; @@ -1173,6 +1322,18 @@ public class GameMainPanel_FishBase : GameMainPanel_Base WebSocketSendList.SyncGameData(sgd); } + private void SyncFreeze(bool isFreeze) + { + SyncGameData sgd = new SyncGameData + { + sitPos = currSitData.posID, + frameType = FrameDataType.freezeFish, + time = BeijingTimeManager.Instance.CurrentTimestampMs, + frameData = new FrameData_FreezeFish { isFreeze = isFreeze } + }; + WebSocketSendList.SyncGameData(sgd); + } + #endregion @@ -1190,7 +1351,7 @@ public class GameMainPanel_FishBase : GameMainPanel_Base //if (sync.sitPos == currSitData.posID) return; var data = FrameDataParser.Parse(sync.extra.frameData); - FireBullet(list_Sits[sync.extra.sitPos], data.angle, data.isSanShe, data.twoRTruuect, data.isSuoDing,sync.bulletIds); + FireBullet(list_Sits[sync.extra.sitPos], data.angle, data.isSanShe, data.twoRTruuect, data.isSuoDing, sync.bulletIds, data.monsterIDs); } public void SyncGameData_ChangeTargetMonster(SyncGameData sync) @@ -1242,6 +1403,17 @@ public class GameMainPanel_FishBase : GameMainPanel_Base list_Sits[sync.sitPos].ChangeTurret(data.turretIndex); } + public void SyncGameData_FreezeFish(SyncGameData sync) + { + if (sync.sitPos == currSitData.posID) return; // 自己发的不管 + + var data = FrameDataParser.Parse(sync.frameData); + if (data.isFreeze) + CreateFishManager.Instance.FreezeAllFish(); + else + CreateFishManager.Instance.UnfreezeAllFish(); + } + #endregion diff --git a/Assets/MyGame/aa/UI/Game/HHWK/GameMainPanel_HHWK.prefab b/Assets/MyGame/aa/UI/Game/HHWK/GameMainPanel_HHWK.prefab index e4f0b0686..faff4b1c5 100644 --- a/Assets/MyGame/aa/UI/Game/HHWK/GameMainPanel_HHWK.prefab +++ b/Assets/MyGame/aa/UI/Game/HHWK/GameMainPanel_HHWK.prefab @@ -3521,6 +3521,13 @@ MonoBehaviour: toggle_Three: {fileID: 748668496574007314} toggle_Shuang: {fileID: 870607161084201748} toggle_BaoZou: {fileID: 6820470193323630788} + img_BaoZou: {fileID: 4519855428832475769} + frenzyDuration: 5 + currFrenzyTime: 0 + toggle_BingFeng: {fileID: 7126682788597616111} + img_BingFeng: {fileID: 734296607713292731} + bingFengDuration: 5 + currBingFengTime: 0 list_Sits: - {fileID: 1736561711096904313} - {fileID: 9022000259197046427} @@ -4483,7 +4490,7 @@ MonoBehaviour: m_PersistentCalls: m_Calls: [] m_Sprite: {fileID: 21300000, guid: 698df028996d4c14dace6f08d1aa8452, type: 3} - m_Type: 0 + m_Type: 3 m_PreserveAspect: 0 m_FillCenter: 1 m_FillMethod: 4