using asap.core; using cfg; using game; using GameCore; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using UniRx; using UnityEngine; public partial class FishingDuelManager { public const string SoloDataKey = "SoloDataKey"; public const string EventPkDuel = "FishingDuelAct"; private cfg.Tables _tables; private ICustomServerMgr customServerMgr; FishingEventData eventData; public DuelData duelData { get { return eventData.duelData; } } int magnificationIndex { get { return duelData.multiple; } set { duelData.multiple = value; eventData.SaveDuelData(); } } public int magnification { get; set; } DuelModel selfModel = new DuelModel(); DuelModel enemyModel = new DuelModel(); IDuelChatModel selfChatModel = new DuelChatModel(); IDuelChatModel enemyChatModel = new DuelChatModel(); public IDuelPlayerModel SelfModel { get { return selfModel; } } public IDuelPlayerModel EnemyModel { get { return enemyModel; } } public IDuelChatModel SelfChatModel { get { return selfChatModel; } } public IDuelChatModel EnemyChatModel { get { return enemyChatModel; } } public bool CurrentFail => selfModel.CurrentFailNum == 0; //当前失败次数 public int CurrentRetryNum => eventSoloConfig.MaxFailCount - selfModel.CurrentFailNum; public float FillAmount { get { int pts = selfModel.CurrentPTS + enemyModel.CurrentPTS; if (pts <= 0) { return 0; } return (float)selfModel.CurrentPTS / pts; } } //当前倍率 public EventSolomain eventSolomain { get; private set; } public EventSolomain beforeSolomain { get; private set; } public EventSolomain enemySolomain { get; set; } public TbEventSoloConfig eventSoloConfig { get; private set; } //======================机器人信息========================== public EventSoloRobot eventSoloRobot { get; set; } public List robotInfos { get; private set; } public DuelRodData robotRod { get; private set; } //======================机器人信息========================== public int MapId => duelData.mapId; int index = 0; public List FishItemIDs; public int curProgress; public bool isSettle = false; public UIType OnStopShowUIType = UITypes.EventFishingDuelPanel; public ReactiveProperty GameOver = new ReactiveProperty(); public List TraitID = new List(); public bool selfIsLastFish = false; public FishingDuelManager() { _tables = GContext.container.Resolve(); eventSoloConfig = _tables.TbEventSoloConfig; customServerMgr = GContext.container.Resolve(); eventData = GContext.container.Resolve(); SetCurRankData(); } #region 活动数据 void SetCurRankData() { eventSolomain = GetEventSolomain(duelData.progress); magnification = eventSolomain.MagList[magnificationIndex]; //获取机器人配置 List RobotAccountList = eventSolomain.RobotAccountList; var robotAccount = RobotAccountList[UnityEngine.Random.Range(0, RobotAccountList.Count)]; eventSoloRobot = _tables.TbEventSoloRobot.DataMap[robotAccount]; } public bool SetProgress() { bool add = selfModel.CurrentPTS > enemyModel.CurrentPTS; if (add) { duelData.progress += eventSolomain.WinTrophy * magnification; } else { duelData.progress -= eventSolomain.LoseTrophy * magnification; } if (duelData.progress < _tables.TbEventSoloConfig.MinTrophy) { duelData.progress = _tables.TbEventSoloConfig.MinTrophy; } eventData.duelData.preProgress = duelData.progress; beforeSolomain = eventSolomain; var data = _tables.TbEventSolomain.DataList; for (int i = data.Count - 1; i >= 0; i--) { if (duelData.progress >= data[i].TrophyRange[0]) { beforeSolomain = data[i]; break; } } eventData.SaveDuelData(); //往服务器传数据 SendDuelFishingData(); return add; } public void SetMagnification() { magnificationIndex++; if (magnificationIndex >= eventSolomain.MagList.Count) { magnificationIndex = 0; GContext.Publish(new VibrationData(HapticTypes.LightImpact)); } else { GContext.Publish(new VibrationData(HapticTypes.Selection)); } magnification = eventSolomain.MagList[magnificationIndex]; } public void SetMaxMagnification() { for (int i = magnificationIndex - 1; i >= 0; i--) { if (duelData.tickets >= eventSolomain.MagList[i]) { magnificationIndex = i; magnification = eventSolomain.MagList[magnificationIndex]; break; } } } public bool IsMaxMagnification() { return magnificationIndex == eventSolomain.MagList.Count - 1; } public void OnJoin() { //扣除体力 //GContext.container.Resolve().ReduceEnergy(eventSolomain.MatchCost * magnification); //增加参与过的次数 curProgress = duelData.progress; duelData.progress -= eventSolomain.LoseTrophy * magnification; if (duelData.progress < _tables.TbEventSoloConfig.MinTrophy) { duelData.progress = _tables.TbEventSoloConfig.MinTrophy; } eventData.AddDuelTickets(-magnification); } public void SetMapId(int id) { duelData.mapId = id; eventData.SaveDuelData(); } public List GetReward() { List itemDatas = GContext.container.Resolve(). GetItemDataByDropId(eventSolomain.WinReward); return itemDatas; } public EventSolomain GetEventSolomain(int progress) { var data = _tables.TbEventSolomain.DataList; for (int i = data.Count - 1; i >= 0; i--) { if (progress >= data[i].TrophyRange[0]) { return data[i]; } } return data[0]; } public Item GetFishItem() { int id; Item item; if (index >= FishItemIDs.Count) { id = FishItemIDs[index - 1]; item = _tables.TbItem.GetOrDefault(id); return item; } id = FishItemIDs[index]; index++; item = _tables.TbItem.GetOrDefault(id); return item; } public bool IsFishItemFull() { return index >= FishItemIDs.Count; } public bool IsFishItemEmpty() { return index >= FishItemIDs.Count; } public void GetFishItemID(System.Random random) { //对比 eventSolomain 和 enemySolomain 取段位低的 FishItemIDs = new List(); var mapData = _tables.TbMapData.DataMap[MapId]; var solomain = eventSolomain; if (enemySolomain.RankTier < eventSolomain.RankTier) { solomain = enemySolomain; } //同一ID 不能超过总数的这个比例 float duplicateFishRat = eventSoloConfig.DuplicateFishRat / 100f; int fishCount = solomain.FishSequenceCount; int fishConfig = solomain.FishSequenceConfig; List dropFishList = mapData.DropFishList; List> fishList = new List>(); for (int i = 0; i < dropFishList.Count; i++) { List fishs = new List(dropFishList[i].FishIDList); fishList.Add(fishs); } // 辅助字典:记录每个ID已出现的次数 Dictionary idCountDict = new Dictionary(); int itemID; for (int i = 0; i < fishConfig; i++) { itemID = GetValidFishID(fishList, random, 2, 5, fishCount, duplicateFishRat, idCountDict); FishItemIDs.Add(itemID); UpdateIDCountDict(idCountDict, itemID); } for (int i = fishConfig; i < fishCount; i++) { itemID = GetValidFishID(fishList, random, 0, 5, fishCount, duplicateFishRat, idCountDict); FishItemIDs.Add(itemID); UpdateIDCountDict(idCountDict, itemID); } //随机打乱 for (int i = 0; i < FishItemIDs.Count; i++) { int j = random.Next(i, FishItemIDs.Count); int temp = FishItemIDs[i]; FishItemIDs[i] = FishItemIDs[j]; FishItemIDs[j] = temp; } Debug.Log($"本局鱼ID:{string.Join(',', FishItemIDs)}"); } // 辅助方法:获取符合比例限制的鱼ID private int GetValidFishID(List> dropFishList, System.Random random, int minQuality, int maxQuality, int currentTotal, float maxRatio, Dictionary idCountDict) { int isInvalid = 10; int itemID = 0; do { // 随机选择品质等级([minQuality, maxQuality)) int qualityIndex = random.Next(minQuality, maxQuality); // 从该品质的鱼ID列表中随机选一个 var fishIDList = dropFishList[qualityIndex]; if (fishIDList.Count > 0) { itemID = fishIDList[random.Next(fishIDList.Count)]; if (qualityIndex >= 4) { hasRedFish = true; } // 计算当前ID如果添加后是否超过比例限制 int newCount = idCountDict.TryGetValue(itemID, out int count) ? count + 1 : 1; // 检查是否超过最大比例(允许有1e-6的浮点误差) if (newCount > 1 && currentTotal > 0 && (float)newCount / currentTotal > maxRatio - 1e-6) { fishIDList.Remove(itemID); // 从列表中移除该ID,避免重复选择 isInvalid--; Debug.Log($"剔除鱼ID:{itemID} 当前数量:{newCount} 总数量:{currentTotal} 比例:{(float)newCount / currentTotal} 最大比例:{maxRatio}"); } else { isInvalid = 0; } } } // 如果无效则重新选择(直到找到符合条件的ID) while (isInvalid > 0); if (itemID == 0) { // 理论上不会到这里 Debug.LogError("配表冲突"); itemID = dropFishList[0][0]; } return itemID; } // 辅助方法:更新ID计数字典 private void UpdateIDCountDict(Dictionary dict, int id) { if (dict.ContainsKey(id)) { dict[id]++; } else { dict[id] = 1; } } #endregion 活动数据 #region 机器人相关 void PlayerRobot(DuelFishingDataSave dataSave) { enemyModel = new DuelPlayerModel(); enemyModel.Init(dataSave.playerID, dataSave.playerName, dataSave.playerAvatar, dataSave.rankScore, dataSave.mag, eventSoloConfig.MaxFailCount); ReceiveEnemyFishCardLevel(dataSave.carLevels); enemyChatModel = new DuelRobotChatModel(); //设置机器人数据列表 robotInfos = dataSave.selfDuelFishingDatas; robotRod = new DuelRodData { rodId = dataSave.rodId, rodLevel = dataSave.rodLevel, rodStar = dataSave.rodStar }; } void MachRobot() { var RobotList = eventSoloRobot.RobotList; int robotId = RobotList[UnityEngine.Random.Range(0, RobotList.Count)]; var robotData = _tables.TbRobot.DataMap[robotId]; //获得鱼卡等级 List carLv = new List(); MapData _curMapData = _tables.TbMapData.DataMap[MapId]; var CardLevelList = eventSoloRobot.CardLevel; int mapIndex = (_curMapData.ID - 1) % 100; for (int i = 0; i < FishItemIDs.Count; i++) { Item item = _tables.TbItem.DataMap[FishItemIDs[i]]; var fishData = _tables.TbFishData.DataMap[item.RedirectID]; int cardIndex = _curMapData.FishList.IndexOf(fishData.ID); cardIndex %= CardLevelList.Count; var CardLevel = CardLevelList[cardIndex]; mapIndex = mapIndex % CardLevel.Count; int cardLevel = CardLevel[mapIndex]; carLv.Add(cardLevel); } var MagList = enemySolomain.MagList; int magIndex = UnityEngine.Random.Range(0, MagList.Count); enemyModel = new DuelPlayerModel(); enemyModel.Init(robotData.ID.ToString("X"), LocalizationMgr.GetText(robotData.Name_l10n_key), robotData.Avatar, eventSoloRobot.Trophy, MagList[magIndex], eventSoloConfig.MaxFailCount); ReceiveEnemyFishCardLevel(carLv); enemyChatModel = new DuelRobotChatModel(); //设置机器人数据列表 robotInfos = new List(); int rodIndex = UnityEngine.Random.Range(0, eventSoloRobot.RodId.Count); var rodData = _tables.TbRodData.GetOrDefault(eventSoloRobot.RodId[rodIndex]); int rodLevel = eventSoloRobot.RodLevel[rodIndex]; int star = eventSoloRobot.RodStar[rodIndex]; if (star < 0) { star = 0; } robotRod = new DuelRodData { rodId = rodData.ID, rodLevel = rodLevel, rodStar = star }; for (int i = 0; i < FishItemIDs.Count; i++) { GetRobotInfo(FishItemIDs[i], _curMapData, rodData.ID, star); } //打印 机器人id、杯数、鱼竿id,鱼竿等级、鱼竿星级、鱼卡等级 Debug.Log($"机器人ID:{robotId} 杯数:{eventSoloRobot.Trophy} 鱼杆id:{rodData.ID} 鱼杆等级:{rodLevel} 鱼杆星级:{star} 鱼卡等级:{string.Join(',', carLv)}"); } List SetSelfModel() { List carLv = new List(); PlayerFishData playerFishData = GContext.container.Resolve(); for (int i = 0; i < FishItemIDs.Count; i++) { Item item = _tables.TbItem.DataMap[FishItemIDs[i]]; carLv.Add(playerFishData.GetDataLevel(item.RedirectID)); } IUserService user = GContext.container.Resolve(); selfModel = new DuelPlayerModel(); selfModel.Init(user.UserId, user.DisplayName, user.AvatarUrl, duelData.progress, magnification, eventSoloConfig.MaxFailCount); selfChatModel = new DuelPlayerChatModel(); return carLv; } async Task> GetRandmMap() { //int curMapId = duelData.mapId; var mapDatas = _tables.TbMapData.DataList; var lastMapID = GContext.container.Resolve().lastMapId; int maxMapID = _tables.TbMapData.DataList[^1].ID + eventSolomain.MaxMapId; if (lastMapID < maxMapID) { maxMapID = lastMapID; } if (maxMapID < mapDatas[0].ID) { maxMapID = mapDatas[0].ID; } //非当前地图 var mapIDs = new List(); for (int i = 0; i < mapDatas.Count; i++) { if (mapDatas[i].ID <= maxMapID) { //if (mapDatas[i].ID != curMapId) //{ mapIDs.Add(mapDatas[i].ID); //} } else { break; } } List ints = new List(); if (mapIDs.Count <= 1) { ints.Add(maxMapID); } else { ILoadResourceService loadResourceService = GContext.container.Resolve(); for (int i = 0; i < mapIDs.Count; i++) { int id = mapIDs[i]; var mapData = _tables.TbMapData.DataMap[id]; bool isCanEnter = await loadResourceService.CheckResource(mapData.EnvName); if (isCanEnter) { ints.Add(id); } } } return ints; } void GetRobotInfo(int itemId, MapData _curMapData, int rodId, int star) { //获得积分 DuelFishingData robotInfo; Item item = _tables.TbItem.DataMap[itemId]; if (item.Quality == 4 || item.Quality == 5) { float prob = item.Quality == 4 ? enemySolomain.HugeFishLoseProb : enemySolomain.GiantFishLoseProb; int probCount = eventSoloConfig.MaxFailCount; for (int i = 0; i < probCount; i++) { float fishLoseProb = UnityEngine.Random.Range(0, 1f); ///失败区间内 if (fishLoseProb < prob) { robotInfo = RobotInfo(itemId); robotInfos.Add(robotInfo); } else { break; } if (i == probCount - 1) { ///如果是最后一次,彻底失败 return; } } } robotInfo = RobotInfo(itemId); robotInfos.Add(robotInfo); RobotInfoSucceed(robotInfo, _curMapData, rodId, star); } DuelFishingData RobotInfo(int itemId) { DuelFishingData robotInfo = new DuelFishingData(); robotInfo.fishItemId = itemId; Item item = _tables.TbItem.DataMap[itemId]; var slienceTime = enemySolomain.SlienceTime; robotInfo.slienceTime = UnityEngine.Random.Range(slienceTime[0], slienceTime[1]); float fishingTime = enemySolomain.FishTime[0]; if (item.Quality < enemySolomain.FishTime.Count) { fishingTime = enemySolomain.FishTime[item.Quality - 1]; } fishingTime = fishingTime * (1 + UnityEngine.Random.Range(-enemySolomain.FishTimeRandomRange, enemySolomain.FishTimeRandomRange)); robotInfo.fishingTime = (int)fishingTime; return robotInfo; } void RobotInfoSucceed(DuelFishingData robotInfo, MapData _curMapData, int rodId, int star) { robotInfo.isWin = true; Item item = _tables.TbItem.DataMap[robotInfo.fishItemId]; var fishData = _tables.TbFishData.DataMap[item.RedirectID]; DropPackageList dropPackageList = _tables.TbDrop[fishData.DropID].DropList; float proficiency = dropPackageList.DropCountList[0]; proficiency *= fishData.PointExtraParam * UnityEngine.Random.Range(1 - fishData.WeightRandom, 1 + fishData.WeightRandom); float cardBonus = 0; int cardIndex = _curMapData.FishList.IndexOf(fishData.ID); if (cardIndex >= 0) { var CardLevelList = eventSoloRobot.CardLevel; cardIndex %= CardLevelList.Count; var CardLevel = CardLevelList[cardIndex]; int mapIndex = (_curMapData.ID - 1) % 100; mapIndex = mapIndex % CardLevel.Count; int cardLevel = CardLevel[mapIndex]; if (cardLevel > 0) { cfg.FishCard fishCard = _tables.TbFishCard[fishData.FishCardID]; cardBonus = fishCard.WeightMagList[cardLevel - 1]; //重量加成参数 = 针对特定鱼的重量加成 + 基于基础重量的重量加成 + 总体重量加成,初始为0 } } PlayerFishData playerFishData = GContext.container.Resolve(); Dictionary RodPerkDic = playerFishData.GetRodPerk(rodId, star); float rodBonus = GetCurRodBuff(RodPerkDic, fishData.Quality); float bonus = (1 + cardBonus) * (1 + rodBonus); robotInfo.fishCardAdd = cardBonus; robotInfo.fishRodAdd = rodBonus; robotInfo.point = (int)(proficiency * bonus); } #endregion 机器人相关 #region 设置玩家和对手数据 public void Start() { selfModel.Start(); enemyModel.Start(); } public void StartLastRecordedTime() { selfModel.StartLastRecordedTime(); enemyModel.StartLastRecordedTime(); } public void SetRodData(int id) { PlayerFishData playerFishData = GContext.container.Resolve(); DuelRodData duelRod = new DuelRodData { rodId = id, rodLevel = playerFishData.GetRodLevel(id), rodStar = playerFishData.GetRodPiece(id) }; SendRodData(duelRod); } public void StopFishing(int fishID, int point, float fishCardAdd, float fishRodAdd) { isSettle = false; FishInfoData data = enemyModel.GetDuelFishingData(selfModel.index, fishID, point, fishCardAdd, fishRodAdd); bool gameOver = selfModel.StopFishing(data); enemyChatModel.SendMessage((int)RobotDuelChatType.OpponetFishingSuccess); if (gameOver) { SetFishingOver(1); } //刷新view AwaitSettle(); } public void OnFishingChangeStateEvent(FishingChangeState fishState, int id) { IDuelPlayerModel model = selfModel; switch (fishState) { case FishingChangeState.Cast: model.CastRod(id); break; //case FishingChangeState.StartFishing: // model.StartFishing(); // break; case FishingChangeState.FishingSuccess: break; case FishingChangeState.FishingFail: bool gameOver = model.StopFishing(new FishInfoData()); enemyChatModel.SendMessage((int)RobotDuelChatType.OpponetFishingFail); if (gameOver) { SetFishingOver(1); } break; } //view刷新 } async void AwaitSettle() { await Awaiters.Seconds(3); isSettle = true; } /// /// /// /// == 1 自己 == 2钓友 void SetFishingOver(int over) { GameOverState++; Debug.Log("SetFishingOver:" + over); //倒计时五秒结束 GameOver.Value = over; } #endregion /// /// PVP 鱼竿词条加成 /// /// /// public float GetCurRodBuff(Dictionary rodPerkDic, int quality) { MapData mapData = _tables.TbMapData.DataMap[MapId]; if (mapData == null) { return 0; } float add = 0; PlayerFishData playerFishData = GContext.container.Resolve(); //水域类型加成 switch (mapData.WaterType) { case SceneWaterType.ShallowSea: case SceneWaterType.DeepSea: add += playerFishData.GetRodPerkValue(RodPerkType.ExtraDuelPointsWaterType_2, rodPerkDic); break; case SceneWaterType.Lake: case SceneWaterType.River: add += playerFishData.GetRodPerkValue(RodPerkType.ExtraDuelPointsWaterType_1, rodPerkDic); break; default: break; } //天气类型加成 if (WeatherType == 0) { add += playerFishData.GetRodPerkValue(RodPerkType.ExtraDuelPointsWeatherType_1, rodPerkDic); } else { add += playerFishData.GetRodPerkValue(RodPerkType.ExtraDuelPointsWeatherType_2, rodPerkDic); } //鱼品质加成 switch (quality) { case 1: add += playerFishData.GetRodPerkValue(RodPerkType.ExtraDuelPointsFishQuality_1, rodPerkDic); break; case 2: add += playerFishData.GetRodPerkValue(RodPerkType.ExtraDuelPointsFishQuality_2, rodPerkDic); break; case 3: add += playerFishData.GetRodPerkValue(RodPerkType.ExtraDuelPointsFishQuality_3, rodPerkDic); break; case 4: add += playerFishData.GetRodPerkValue(RodPerkType.ExtraDuelPointsFishQuality_4, rodPerkDic); break; case 5: add += playerFishData.GetRodPerkValue(RodPerkType.ExtraDuelPointsFishQuality_5, rodPerkDic); break; default: break; } return add; } /* RodData.DuelPower:对应每个鱼竿,每一级的基础战斗力 RodAscendPerk.DuelPowerParam:对应属性的属性的对决战斗力参数 总战力 = 鱼竿当前等级的基础战力 *(1 + 生效特性1 * 该属性的对决战斗力参数 + 生效特性2 * 该属性的对决战斗力参数) */ public void GetDuelPerk(RodItemData rodItemData) { MapData mapData = _tables.TbMapData.DataMap[MapId]; var playerFishData = GContext.container.Resolve(); Dictionary RodPerkList = playerFishData.GetRodDuelPerk(rodItemData.config.ID, rodItemData.star); rodItemData.SetRodPower(); float param = 1; foreach (var perk in RodPerkList) { var rodEngancePerk = _tables.TbRodAscendPerk.GetOrDefault(perk.Key); bool hasValue = false; switch (rodEngancePerk.PerkType) { case RodPerkType.ExtraDuelPointsWaterType_1: hasValue = mapData.WaterType == SceneWaterType.Lake || mapData.WaterType == SceneWaterType.River; break; case RodPerkType.ExtraDuelPointsWaterType_2: hasValue = mapData.WaterType == SceneWaterType.ShallowSea || mapData.WaterType == SceneWaterType.DeepSea; break; case RodPerkType.ExtraDuelPointsWeatherType_1: hasValue = WeatherType == 0; break; case RodPerkType.ExtraDuelPointsWeatherType_2: hasValue = WeatherType == 1; break; case RodPerkType.ExtraDuelPointsFishQuality_5: hasValue = hasRedFish; break; default: hasValue = true; break; } rodItemData.traitIDs.Add(perk.Key); rodItemData.noactions.Add(hasValue); if (hasValue) { string value = perk.Value; if (!string.IsNullOrEmpty(value) && !value.Contains('|')) { param += float.Parse(value) * _tables.TbRodAscendPerk.GetOrDefault(perk.Key)?.DuelPowerParam ?? 0; } } } rodItemData.RodPower = (int)(rodItemData.RodPower * param); } void SetTraitID() { TraitID = new List(); MapData mapData = _tables.TbMapData.DataMap[MapId]; switch (mapData.WaterType) { case SceneWaterType.ShallowSea: case SceneWaterType.DeepSea: //RodPerkType.ExtraDuelPointsWaterType_2 TraitID.Add(5002); break; case SceneWaterType.Lake: case SceneWaterType.River: //RodPerkType.ExtraDuelPointsWaterType_1 TraitID.Add(5001); break; default: break; } //天气类型加成 if (WeatherType == 0) { //RodPerkType.ExtraDuelPointsWeatherType_1 TraitID.Add(5011); } else { //RodPerkType.ExtraDuelPointsWeatherType_2 TraitID.Add(5012); } //鱼王,可能也会有小卡拉米 if (hasRedFish) { TraitID.Add(5025); } } } public class DuelData { public int eventID; public int rewardEventID; public int preProgress; public int progress; public int tickets; public int soloTimes; public int mapId; public int multiple; public int rePVPToken; public int pvpShopTimeDay = -1; public Dictionary buyItem = new Dictionary(); }