using System; using System.Collections.Generic; using System.Linq; using System.Threading; using System.Threading.Tasks; using asap.core; using cfg; using EventPartnerGather; using game; using GameCore; using Newtonsoft.Json; using UniRx; using UnityEngine; using EEventBuildBuildState = EventPartnerGatherRequests.EEventBuildBuildState; using EventPartnerGatherDetailResp = EventPartnerGatherRequests.EventPartnerGatherDetailResp; using EventBuildBotInfo = EventPartnerData.EventBuildBotInfo; using EEventBuildState = EventPartnerGatherRequests.EEventBuildState; using AutoMatchStatusResponse = EventPartnerGatherRequests.AutoMatchStatusResponse; using AutoMatchToggleResponse = EventPartnerGatherRequests.AutoMatchToggleResponse; namespace DataCenter { public enum ESpinState { Normal, Freespin, Morespin } // EventPartnerGather 游戏数据结构 public class EventPartnerGatherData { public bool FirstOpen = true; public int EventId; public DateTime StartTime; public DateTime EndTime; // UI数据 和实际数据可能不同,进入游戏的时候,可以进入下一步,保存的情况下执行开发 // public int CurrentUIPoints = 0; // 核心游戏数据 public int CurrentPoints = 0; // 当前积分 public int CurrentMiningProgress = 0; // 挖矿进度 // 同意需要多个 // public readonly List CompletedNodes = new(); // 已完成的节点 // 好友系统数据(第二阶段) // public List Friends = new(); public int Token = 0; public int InvitedCount = 0; // 代币数量, Detail的时候会获得吗 public int TicketCount; // 公用的 public List> CompletedNodesList; // public List<(int myScore, int FriendScore)> BuilderPartnerScore; // 获取当前的 More状态 // public List<(float BonusPercent, DateTime BonusExpireUtc)> BuilderPartnerBonus; // public List BuildFreeSpinNum; public (float BonusPercent, DateTime BonusExpireUtc) PartnerBonus; public int FreeSpinNum; // 获取机器人 public Dictionary Bots { get; set; } // 是否领取最终奖励 public bool IsRecvFinalReward = false; // UI 上显示是否完成, public List UIFinishedPartners; // 当前活动 是否结束 public bool IsFinished = false; // 默认显示 // public ESpinState SpinState = ESpinState.Normal; //MoreSpin // public float BonusPercent = 0f; // public DateTime BonusExpireUtc = DateTime.MinValue; //FreeSpin // public int freeSpinNum = 0; public ESpinState SpinState; public float InflationRate; // 自动匹配状态 public bool IsEnableAutoMatch = false; // 自动匹配开关 public DateTime LastAutoMatchTime = DateTime.MinValue; // 上次匹配时间 } // EventPartnerGather 管理器 public class EventPartnerGatherManager : IAutoMatchManagerContext { private const bool EnableTraceLogs = false; public const string SaveKey = "EventPartnerGatherData"; [Inject] public Tables _tables { get; set; } [Inject] public EventPartnerService EventPartnerService { get; set; } private FishingEvent _fishingEvent; // 检查数据是否改变过 private bool _isEventDataChanged = false; // 管理数值 public PartnerInfoManager PartnerInfoManager { get; private set; } = new(); public EventPartnerGatherRobotInvitationSystem RobotInvitationSystem { get; private set; } = new(); // public EventPartnerGatherData GatherData { get; private set; } // 数据 public EventPartnerMineMain EventPartnerMineMainData { get; private set; } // 抽奖数据 public List EventPartnerMineSpinPointList { get; private set; } // 模型数据,目前开来需要构造模型从表里读取 public EventPartnerMineInit EventPartnerMineInitData { get; private set; } // 累分状态 private readonly List _nodeCumulativeSteps = new(); // 状态积分,最后需要去除一个 private readonly List _nodeSteps = new(); // public List NodeSteps => _nodeSteps; public List NodeCumulativeSteps => _nodeCumulativeSteps; public int MaxSteps => _nodeCumulativeSteps.Count - 1; public int EventId => GatherData?.EventId ?? -1; public bool ShouldCloseAutoMatch => PartnerInfoManager.IsFullyMatched; public bool IsAutoMatchEnabled => GatherData?.IsEnableAutoMatch ?? false; public int AutoMatchSlotCount => PartnerInfoManager.BuildPartners.Length; public DateTime StartTime; // 结束时间 public DateTime EndTime; private readonly List _stageDropList = new(); private CompositeDisposable _disposables; public bool IsFirstOpen { get => GatherData?.FirstOpen ?? false; set { if (GatherData != null && GatherData.FirstOpen) { GatherData.FirstOpen = false; SaveData(); } } } public void UpdateEventData(FishingEvent fishingEvent) { Trace($"UpdateEventData eventId={fishingEvent?.ID}"); _fishingEvent = fishingEvent; if (GatherData != null && _fishingEvent != null && GatherData.EventId != _fishingEvent.ID) { ClearData(); } if (!_isInit || GatherData == null) { _isInit = false; Init(); } else { // { UpdateLocalData(); UpdateTime(); SetRedPoint(); // } } } #region 红点相关 public void SetRedPoint() { if (GatherData != null) { RedPointManager.Instance.SetRedPointState(RedPointKey, GatherData.TicketCount > 0, GatherData.TicketCount); } else { RedPointManager.Instance.SetRedPointState(RedPointKey, false); } } public string RedPointKey { get; private set; } = RedPointName.Home_Parther; #endregion // 有可能改时间的,改时间不可清空保存的值 // public void ProcessLastChallenge() // { // UpdateTime(); // if (GatherData != null) // { // if (!IsTimeFinished()) // { // ClearData(); // } // } // } // private bool _isInit = false; public void Init() { if (_fishingEvent == null) return; if (_isInit) return; Trace($"Init eventId={EventId}"); if (GatherData == null) { CreateNewData(); } InitManager(); UpdateLocalData(); UpdateTime(); SetRedPoint(); _isInit = true; } public void ReInit() { _isInit = false; Init(); } private void CreateNewData() { var limitedTime = (LimitedTime)_fishingEvent.TimeDefinition; var startTime = GlobalUtils.TryParseDateTime(limitedTime.StartTime, ZZTimeHelper.UtcNow()); var endTime = GlobalUtils.TryParseDateTime(limitedTime.EndTime, ZZTimeHelper.UtcNow()); var ticketCount = GContext.container.Resolve().GetInitWelcomeGift(_fishingEvent.ID); GatherData = new EventPartnerGatherData { FirstOpen = true, EventId = _fishingEvent.ID, StartTime = startTime, EndTime = endTime, CurrentPoints = 0, CurrentMiningProgress = 0, TicketCount = ticketCount, BuilderPartnerScore = new List<(int myScore, int partnerScore)>(), // { // (0,0),(0,0),(0,0) // }, PartnerBonus = (0F, DateTime.MinValue), CompletedNodesList = new List>(), // { // new(), // new(), // new(), // }, // BuildFreeSpinNum = new List{0,0,0}, FreeSpinNum = 0, Bots = new Dictionary(), // IsGetFinalReward = new List // { // false,false,false, // }, IsRecvFinalReward = false, UIFinishedPartners = new List(), // { false, false, false }, InflationRate = GContext.container.Resolve().InflationRate, }; for (var i = 0; i < PartnerInfoManager.BuilderCapacity; ++i) { GatherData.BuilderPartnerScore.Add( (myScore:0,FriendScore:0)); GatherData.CompletedNodesList.Add(new List()); GatherData.UIFinishedPartners.Add(false); } StartTime = startTime; EndTime = endTime; GContext.container.Resolve().SaveTransitionData(EventId, GatherData.TicketCount); SaveData(); } private void InitManager() { InitPartnerInfoManager(); InitRobotInvitationSystem(); } private void InitPartnerInfoManager() { PartnerInfoManager.Init(); PartnerInfoManager.EventId = EventId; } private void InitRobotInvitationSystem() { RobotInvitationSystem.Init(); } private void UpdateLocalData() { Trace($"UpdateLocalData eventId={EventId}"); if (_fishingEvent == null) return; var redirectId = _fishingEvent.RedirectID; var eventPartnerDataList = _tables.TbEventPartnerMineMain.DataList; EventPartnerMineMainData = eventPartnerDataList.FirstOrDefault(data => data.ID == redirectId); // var allSpinPoints = _tables.TbEventPartnerMineSpinPoint.DataList; var ids = EventPartnerMineMainData?.SpinPoint; if (ids is { Count: > 0 }) { var filtered = new List(); foreach (var id in ids) { var sp = _tables.TbEventPartnerMineSpinPoint.GetOrDefault(id); if (sp != null) filtered.Add(sp); } EventPartnerMineSpinPointList = filtered; } else { EventPartnerMineSpinPointList = new List(); } _nodeCumulativeSteps.Clear(); _nodeSteps.Clear(); if (EventPartnerMineMainData?.StagePoint is { Count: > 0 }) { var sum = 0; foreach (var stagePoint in EventPartnerMineMainData.StagePoint) { _nodeSteps.Add(stagePoint); sum += stagePoint; _nodeCumulativeSteps.Add(sum); } } var eventPartnerMineInitDataList = _tables.TbEventPartnerMineInit.DataList; EventPartnerMineInitData = eventPartnerMineInitDataList.FirstOrDefault(data => data.ID == redirectId); // if (EventPartnerMineMainData != null) // GContext.container.Resolve().EventRefresh(_fishingEvent, EventPartnerMineMainData.RushId, RushType.OneDataKey); } private void UpdateTime() { if (_fishingEvent != null) { var limitedTime = (LimitedTime)_fishingEvent.TimeDefinition; var startTime = GlobalUtils.TryParseDateTime(limitedTime.StartTime, ZZTimeHelper.UtcNow()); var endTime = GlobalUtils.TryParseDateTime(limitedTime.EndTime, ZZTimeHelper.UtcNow().AddDays(-1)); if (GatherData != null) { if (GatherData.EventId == _fishingEvent.ID) { GatherData.StartTime = startTime; GatherData.EndTime = endTime; } else { GatherData.EndTime = DateTime.Now.AddDays(-1); } } EndTime = endTime; StartTime = startTime; } } private void ValidateAndUpdateData() { if (GatherData == null) return; // 验证时间有效性 // if (!IsTimeFinished()) // { // HandleEventEnd(); // } } public bool IsTimeFinished() { var now = ZZTimeHelper.UtcNow(); Trace($"CheckTimeFinished now={now} endTime={EndTime}"); return now > EndTime; } public bool IsTimeValid() { if (GatherData == null) return false; var now = ZZTimeHelper.UtcNow(); return now >= StartTime && now <= EndTime; } public TimeSpan GetRemainingTime() { if (GatherData == null) return TimeSpan.Zero; return EndTime - ZZTimeHelper.UtcNow(); } public int GetFreeSpin(int slotId) { if (GatherData == null) return 0; // var freeSpinNum = GatherData.BuildFreeSpinNum[slotId]; // return freeSpinNum; return GatherData.FreeSpinNum; } public TimeSpan GetMoreTimeSpan(int slotId) { if (GatherData == null) return TimeSpan.Zero; if (!IsBonusActive(slotId)) return TimeSpan.Zero; var builderPartnerBonus = GatherData.PartnerBonus; // var timespan = DateTime.MinValue - ZZTimeHelper.UtcNow(); // Log($"builderPartnerBonus = {builderPartnerBonus.BonusExpireUtc},{builderPartnerBonus.BonusPercent}"); return builderPartnerBonus.BonusExpireUtc - ZZTimeHelper.UtcNow(); } public bool CheckOpen() { if (GatherData == null) return false; if (!IsTimeValid()) return false; if (GatherData.IsFinished) return false; return true; } public static T WeightedRandom(Dictionary weightTable) { // 1. 计算总权重 var totalWeight = weightTable.Values.Sum(); // 2. 生成随机数 var randomValue = UnityEngine.Random.Range(0, totalWeight); // 3. 线性遍历选择 var currentWeight = 0; foreach (var (key, value) in weightTable) { currentWeight += value; if (randomValue < currentWeight) return key; } return default; // 理论上不会执行到这里 } // 核心游戏逻辑 // 抽奖数据 public EventPartnerMineSpinPoint GetSpinPointElem(bool noCheating = false) { if (EventPartnerMineSpinPointList is { Count: > 0 }) { // 根据权重,生成字典 var dict = EventPartnerMineSpinPointList.ToDictionary(elem => elem, elem => elem.Weight); var elem = WeightedRandom(dict); Trace($"GetSpinPointElem spinId={elem.ID}"); if (_userGm && _spinId != -1) { var elem1 = EventPartnerMineSpinPointList.Find(ele => ele.ID == _spinId); if (elem1 != null) { Trace($"GetSpinPointElemGM spinId={elem1.ID}"); return elem1; } } return elem; } return null; } public void AddPoints(int points) { if (GatherData == null) { LogWarning($"AddPointsInvalid eventId={EventId} reason=GatherDataNull"); return; } if (GatherData.IsFinished) { LogWarning($"AddPointsInvalid eventId={EventId} reason=EventFinished"); return; } if (points <= 0) { LogWarning($"AddPointsInvalid eventId={EventId} reason=InvalidValue points={points}"); return; } GatherData.CurrentPoints += points; // var currentMiningProgress = CalcMiningProgressOld(GatherData.CurrentPoints); // GatherData.CurrentMiningProgress = currentMiningProgress; CurrentBuildPartner.AddSelfScore(points); SavePartnerInfo(CurrentBuildPartner.SlotId); Trace($"AddPoints eventId={EventId} delta={points} totalPoints={GatherData.CurrentPoints}"); UpdatePointAdd(); } // 挖完了的时候,Step算在后面 public int CalcMiningProgressNew(int points) { var maxStep = _nodeCumulativeSteps.Count; for (var i = 0; i < maxStep; ++i) { if (points < _nodeCumulativeSteps[i]) { return i; } } return maxStep - 1; } // 计算落在阶段,考虑机器人用,别出意外 public int CalcMiningProgressOld(int points) { var maxStep = _nodeCumulativeSteps.Count; for (var i = 0; i < maxStep; ++i) { if (points <= _nodeCumulativeSteps[i]) { return i; } } return maxStep - 1; } private void UpdatePointAdd() { GContext.Publish(new EventPartnerGatherPointsUpdate { Points = GatherData.CurrentPoints }); // 检查是否需要触发自动挖矿 // CheckAutoMining(); } public bool CompleteStage(int stageIndex) { Trace($"CompleteStage eventId={EventId} stageIndex={stageIndex} slotId={CurrentBuildPartner?.SlotId}"); var stageReward = EventPartnerMineMainData.StageReward; var slotId = CurrentBuildPartner.SlotId; var completedNodes = GatherData.CompletedNodesList[slotId]; if (stageIndex >= 0 && stageIndex < stageReward.Count) { if (!completedNodes.Contains(stageIndex)) { completedNodes.Add(stageIndex); _stageDropList.Add(EventPartnerMineMainData.StageReward[stageIndex]); // 发放节点奖励 // DistributeStageReward(nodeIndex); if (CurrentBuildPartner.IsPartnerRobot) { CurrentBuildPartner.ScheduleBotScoreAction(); } SaveData(); return true; } } return false; } public bool IsCompleteStageAll(int slotId) { var completedNodes = GatherData.CompletedNodesList[slotId]; var stageReward = EventPartnerMineMainData.StageReward; var finalStaqe = stageReward.Count - 1; return completedNodes.Contains(finalStaqe); } private void DistributeStageReward(int stageIndex) { Trace($"DistributeStageReward stageIndex={stageIndex}"); var stageReward = EventPartnerMineMainData.StageReward; var playerItemData = GContext.container.Resolve(); if (stageIndex >= 0 && stageIndex < stageReward.Count - 1) { var dropId = stageReward[stageIndex]; ChangeSource changeSource = new ChangeSource(GatherData.EventId, "Event_Minigame4", "Event_Minigame4_Partner", "StageReward"); playerItemData.AddItemByDropListLureInflation(GatherData.InflationRate, new List { dropId, }, changeSource); GContext.Publish(new ShowData()); } } private int GetNodeRewardDropId(int nodeIndex) { return EventPartnerMineMainData.StageReward[nodeIndex]; } private void DistributeFinalReward() { var dropId = GetFinalRewardDropId(); if (dropId > 0) { var playerItemData = GContext.container.Resolve(); var rewards = playerItemData.GetItemDataByDropIdLureInflation(GatherData.InflationRate, dropId); ChangeSource changeSource = new ChangeSource(GatherData.EventId, "Event_Minigame4", "Event_Minigame4_Partner", "GrandReward"); playerItemData.AddItem(rewards, changeSource); GContext.Publish(new ShowData(rewards)); } } private int GetFinalRewardDropId() { // 最终奖励掉落ID(后续从配置表读取) return EventPartnerMineMainData.Reward; } // private void HandleEventEnd() // { // if (GatherData == null || GatherData.IsFinished) return; // // Log("Event Ended"); // // // 结算未领取的奖励 // if (GatherData.CurrentMiningProgress >= MaxSteps && !GatherData.IsFinished) // { // HandleGameComplete(); // } // // ClearData(); // } public int ResolvePointsWithBuffer(int points) { var rtPoints = points; var slotId = CurrentBuildPartner?.SlotId ?? -1; if (IsBonusActive(slotId)) { // rtPoints *= (1 + GatherData.BonusPercent); // var slotId = CurrentBuildPartner.SlotId; // var (bonusPercent, bonusExpireUtc) = GatherData.BuilderPartnerBonus[slotId]; var (bonusPercent, bonusExpireUtc) = GatherData.PartnerBonus; rtPoints = (int)(rtPoints * (1 + bonusPercent)); } return Mathf.Max(0, rtPoints); } // public int ResolveSpinPoints(EventPartnerMineSpinPoint p) // { // if (p?.SpinPointParam == null) return 0; // return p.SpinPointParam switch // { // NormalSpinPoint n => ResolvePointsWithBuffer(n.SpinPoints), // FreeSpinPoint f => ResolvePointsWithBuffer(f.SpinPoints), // MoreSpinPoint m => ResolvePointsWithBuffer(m.SpinPoints), // _ => 0 // }; // } // // public ESpinState GetSpinState(EventPartnerMineSpinPoint p) // { // var spinState = p.SpinPointParam switch // { // NormalSpinPoint n => ESpinState.Normal, // FreeSpinPoint f => ESpinState.Freespin, // MoreSpinPoint m => ESpinState.Morespin, // _ => ESpinState.Normal // }; // return spinState; // } public void DoEventWithSpinState(EventPartnerMineSpinPoint p) { switch (p.SpinPointParam) { case NormalSpinPoint n: FinFreeSpin(); break; case FreeSpinPoint f: ApplyFreeSpin(1); break; case MoreSpinPoint m: FinFreeSpin(); ResolveBonus(m.BonusPercent, m.BonusDuration); break; } } public int ResolveSpinAndGetPoints(EventPartnerMineSpinPoint p, out ESpinState spinState) { spinState = ESpinState.Normal; if (p?.SpinPointParam == null) return 0; switch (p.SpinPointParam) { case NormalSpinPoint n: spinState = ESpinState.Normal; return ResolvePointsWithBuffer(n.SpinPoints); case FreeSpinPoint f: spinState = ESpinState.Freespin; return ResolvePointsWithBuffer(f.SpinPoints); case MoreSpinPoint m: spinState = ESpinState.Morespin; return ResolvePointsWithBuffer(m.SpinPoints); } return 0; } public void CleanSpinState() { if (GatherData == null) return; // GatherData.freeSpinNum = 0; GatherData.SpinState = ESpinState.Normal; } // 添加FreeSpin 状态 private void ApplyFreeSpin(int freeSpinNum) { if (GatherData == null) return; var slot = CurrentBuildPartner.SlotId; // GatherData.BuildFreeSpinNum[slot] = freeSpinNum; GatherData.FreeSpinNum = freeSpinNum; } public void FinFreeSpin() { if (GatherData == null) return; // var slot = CurrentBuildPartner.SlotId; // GatherData.BuildFreeSpinNum[slot] = 0; GatherData.FreeSpinNum = 0; } // 临时保存 public float BonusPercent; public int BonusExpireDuration; // public int spinPoint; // // public int SavePoint(int point) // { // spinPoint = point; // return point; // } public void ResolveBonus(float percent, int durationSec) { BonusPercent = percent; BonusExpireDuration = Mathf.Max(0, durationSec); Trace($"ResolveBonus percent={BonusPercent} durationSec={BonusExpireDuration}"); } private void ApplyBonus(float percent, int durationSec) { if (GatherData == null) return; if (CurrentBuildPartner == null) return; // var buildSlot = CurrentBuildPartner.SlotId; // if (GatherData.BuilderPartnerBonus != null) // { // GatherData.BuilderPartnerBonus[buildSlot] = (Mathf.Max(0f, percent), // ZZTimeHelper.UtcNow().AddSeconds(Mathf.Max(0, durationSec))); // } GatherData.PartnerBonus = (Mathf.Max(0f, percent), ZZTimeHelper.UtcNow().AddSeconds(Mathf.Max(0, durationSec) + 0.5f)); SaveData(); } // 确定是否过期?? public void EnsureBonusValid() { if (GatherData == null) return; if (CurrentBuildPartner == null) return; // var slotId = CurrentBuildPartner.SlotId; // var (_, bonusExpireUtc) = GatherData.BuilderPartnerBonus[slotId]; var (_, bonusExpireUtc) = GatherData.PartnerBonus; if (ZZTimeHelper.UtcNow() > bonusExpireUtc) { // GatherData.BonusPercent = 0f; // GatherData.BonusExpireUtc = DateTime.MinValue; // GatherData.BuilderPartnerBonus[slotId] = (0f, DateTime.MinValue); GatherData.PartnerBonus = (0f, DateTime.MinValue); SaveData(); GContext.Publish(new EventPartnerGatherBonusEndEvent()); } // if (CurrentBuildPartner == null) return; // if (CurrentBuildPartner.BonusPercent > 0f && ZZTimeHelper.UtcNow() > CurrentBuildPartner.BonusExpireUtc) // { // GatherData.BonusPercent = 0f; // GatherData.BonusExpireUtc = DateTime.MinValue; // SaveData(); // GContext.Publish(new EventPartnerGatherRefreshBonus()); // } } public bool IsBonusActive(int slotId) { if (GatherData == null) return false; if (slotId == -1) return false; // var (bonusPercent, bonusExpireUtc) = GatherData.BuilderPartnerBonus[slotId]; var (bonusPercent, bonusExpireUtc) = GatherData.PartnerBonus; return bonusPercent > 0f && ZZTimeHelper.UtcNow() < bonusExpireUtc; } public void SaveData() { // Log("SaveData()-> "); #if UNITY_EDITOR Trace($"SaveData eventId={EventId}"); #endif { PlayFabMgr.Instance.UpdateUserDataValue(SaveKey, JsonConvert.SerializeObject(GatherData)); } } public void LoadData(string dataValue) { Trace($"LoadData eventId={EventId} hasData={!string.IsNullOrEmpty(dataValue)}"); if (!string.IsNullOrEmpty(dataValue)) { GatherData = JsonConvert.DeserializeObject(dataValue); } } public void ClearData() { GatherData = null; CurrentBuildPartner?.ClearData(); // CurrentBuildPartner.ClearReceivedCalls(); SaveData(); } // 进入游戏场景 public void EnterGatherScene() { Trace($"EnterGatherScene eventId={EventId}"); IsFirstOpen = false; // var list = EventPartnerMineMainData.Addressable; GContext.Publish(new UnloadActToNextAct("EventPartnerGatherAct", UITypes.CloudTransitionPanel, 0.5f)); } public BuildPartner CurrentBuildPartner { get; set; } // 开始采矿 public void StartBuildProgress(BuildPartner buildPartner) { CurrentBuildPartner = buildPartner; // 更新点数 var points = buildPartner.ScorePartnerActual + buildPartner.ScoreSelfActual; GatherData.CurrentPoints = points; // GatherData.CurrentMiningProgress = CalcMiningProgressNew(points); Trace($"StartBuildProgress eventId={EventId} slotId={CurrentBuildPartner?.SlotId} partnerId={CurrentBuildPartner?.PartnerId}"); ((EventPartnerGatherAct)_act)?.OnEnterGather(); } public void FinishMining() { Trace($"FinishMining eventId={EventId}"); ((EventPartnerGatherAct)_act)?.OnLeaveGather(); } private static void LogError(object t) { Debug.LogError($"EventPartnerGatherManager-> {t} "); } private static void ELog(object t) { #if UNITY_EDITOR Debug.Log($"EventPartnerGatherManager-> {t} "); #endif } [System.Diagnostics.Conditional("UNITY_EDITOR")] private static void Trace(object t) { if (!EnableTraceLogs) return; ELog(t); } //- public static void Log(string message) { Debug.Log($"EventPartnerGatherManager-> {message} "); } public static void LogWarning(string message) { Debug.LogWarning($"EventPartnerGatherManager-> {message} "); } private AGameAct _act; public void SetGameAct(AGameAct act) { _act = act; } public AGameAct GetGameAct() { return _act; } /// /// 通知所有绑定组件刷新数据显示 /// public void RefreshAllUI() { if (GatherData == null) return; // 刷新抽奖面板 // if (_drawPanel) // { // _drawPanel?.SetMiningProgress(GatherData.CurrentMiningProgress); // } Trace($"RefreshAllUI eventId={EventId}"); } // 有可能会激活,不应该在OnEnable 中调用Init(); // public bool CheckForOnEnable() // { // // return false // } // 获取挖矿的数量 public int CalcWaNum(int point, out int leftPoint) { var minePoint = EventPartnerMineMainData.MinePoint; var num = Mathf.Clamp(point / minePoint, 0, point); leftPoint = (point - num * minePoint); return num; } // 获取当前Spin的Cost public int CalcSpinCost(int multipleValue) { return multipleValue * EventPartnerMineMainData.SpinRequire; } // public int GetCumulativeStepPoint(int prevStep) { if (prevStep < 0) return 0; prevStep = Mathf.Clamp(0, prevStep, _nodeCumulativeSteps.Count - 1); return _nodeCumulativeSteps[prevStep]; } public int GetStageReward(int step) { return EventPartnerMineMainData.StageReward[step]; } // public int GetStepPoints(int step) { return step < _nodeSteps.Count ? _nodeSteps[step] : 0; } public int GetGoColumn(int waPoint, int waProgress) { var num = CalcWaNum(waPoint, out var leftPoint); var column = 0; // if (leftPoint > 0) // { // num += 1; // } var mps = EventPartnerMineMainData.MinePointShow[waProgress]; if (EventPartnerMineMainData != null) { column = num / mps; // column2 = num2 / mps; } Trace($"GetGoColumn waPoint={waPoint} num={num} curProgress={waProgress} mps={mps} column={column}"); return column; } // private readonly int maxCol = 3; // 每次挖矿最多消耗的点数,根据排数进行计算 public int GetConsumePoint(int curProgress) { var maxCol = EventPartnerMineMainData.MaxColumn; var oreScore = EventPartnerMineMainData.MinePoint; var mps = EventPartnerMineMainData.MinePointShow[curProgress]; return mps * maxCol * oreScore; } // 根据点数获得进度 // AGG 使用 public int GetMiningProgress() { // return GatherData.CurrentMiningProgress; var currentProgress = CalcMiningProgressNew(CurrentBuildPartner.ScorePartnerActual + CurrentBuildPartner.ScoreSelfActual); return currentProgress; } // // public int NextMiningProgress() // { // Log($"NextMiningProgress()-> {GatherData.CurrentMiningProgress}"); // GatherData.CurrentMiningProgress += 1; // // var curLeftPoint = GetStepPoints(GatherData.CurrentMiningProgress); // // SetLeftPoint(curLeftPoint); // Log($"NextMiningProgress()-> End: {GatherData.CurrentMiningProgress}"); // SaveData(); // return GatherData.CurrentMiningProgress; // } // // public int GetFullStep() // { // var stagePoint = EventPartnerMineMainData.StagePoint; // return stagePoint.Count; // } public bool PlayGetStageReward() { Trace($"PlayGetStageReward currentProgress={GatherData?.CurrentMiningProgress} dropCount={_stageDropList.Count}"); var playerItemData = GContext.container.Resolve(); if (_stageDropList.Count > 0) { ChangeSource changeSource = new ChangeSource(GatherData.EventId, "Event_Minigame4", "Event_Minigame4_Partner", "StageReward"); var itemDatas = playerItemData.AddItemByDropListLureInflation(GatherData.InflationRate, _stageDropList, changeSource); GContext.Publish(new ShowData()); _stageDropList.Clear(); var partnerId = CurrentBuildPartner?.PartnerId ?? ""; if (CurrentBuildPartner is { IsPartnerRobot: true }) { partnerId = "robot"; } Trace($"PlayGetFinalReward partnerId={partnerId}"); #if AGG using (var e = GEvent.GameEvent("event_partners_gather_reward")) { var hookCount = 0; if (itemDatas is { Count: > 0 }) { for (int i = 0; i < itemDatas.Count; i++) { if (itemDatas[i].id == 1001) { hookCount += itemDatas[i].count; } } } e.AddContent("teammate_id", partnerId) .AddContent("position_id", CurrentBuildPartner.SlotId) .AddContent("stage_id", GetMiningProgress()) .AddContent("reward_hook", hookCount); } #endif SaveData(); return true; } return false; } // 某一个stage获得奖励 public bool PlayGetStageReward(int stageIndex) { var stageReward = EventPartnerMineMainData.StageReward; var playerItemData = GContext.container.Resolve(); if (stageIndex >= 0 && stageIndex < stageReward.Count - 1) { var dropId = stageReward[stageIndex]; ChangeSource changeSource = new ChangeSource(GatherData.EventId, "Event_Minigame4", "Event_Minigame4_Partner", "StageReward"); playerItemData.AddItemByDropListLureInflation(GatherData.InflationRate, new List { dropId, }, changeSource); GContext.Publish(new ShowData()); } // CompleteTrainNode(stageIndex); return false; } private int GetFinalReward() { return EventPartnerMineMainData.Reward; } public void PlayGetFinalReward() { // var slotId = CurrentBuildPartner.SlotId; if (GatherData.IsRecvFinalReward) { return; } GatherData.IsRecvFinalReward = true; // SaveData(); var playerItemData = GContext.container.Resolve(); var dropId = GetFinalReward(); ChangeSource changeSource = new ChangeSource(GatherData.EventId, "Event_Minigame4", "Event_Minigame4_Partner", "GrandReward"); var itemDatas = playerItemData.AddItemByDropListLureInflation(GatherData.InflationRate, new List { dropId, }, changeSource); var partnerId = CurrentBuildPartner?.PartnerId ?? ""; if (CurrentBuildPartner is { IsPartnerRobot: true }) { partnerId = "robot"; } Trace($"PlayGetFinalReward partnerId={partnerId}"); #if AGG using (var e = GEvent.GameEvent("event_partners_gather_reward")) { var hookCount = 0; if (itemDatas is { Count: > 0 }) { for (int i = 0; i < itemDatas.Count; i++) { if (itemDatas[i].id == 1001) { hookCount += itemDatas[i].count; } } } e.AddContent("teammate_id", partnerId) .AddContent("position_id", 99) .AddContent("stage_id", 99) .AddContent("reward_hook", hookCount); } #endif GContext.Publish(new ShowData()); SaveData(); } // public int CollectPointAndCalcValid(int step, int points) { // var step = GatherData.CurrentMiningProgress; // return AddCollectPoints(points) return points - GetCumulativeStepPoint(step - 1); } private int _spinId = -1; private bool _userGm = false; public void GmSetSpinId(int spinId) { _spinId = spinId; if (spinId > 0) { _userGm = true; } } public void CloseGM() { _userGm = false; _spinId = -1; } public List GetSpinMag() { return EventPartnerMineMainData.SpinMag; } public bool IsStepFinished(int slotId, int step) { // return GatherData?.CompletedNodes?.Contains(step) ?? false; // var slotId = CurrentBuildPartner.SlotId; var completeNode = GatherData?.CompletedNodesList[slotId] ?? new List(); return completeNode?.Contains(step) ?? false; } public void LoadPlayFabData() { // PartnerInfoManager.LoadPlayFabData(); // 之前是为了适配,后面看看是否可删除 var buildScore = GatherData.BuilderPartnerScore; if (buildScore == null) { //buildScore = new List<(int myScore, int FriendScore)> { (0, 0), (0, 0), (0, 0) }; buildScore = new List<(int myScore, int FriendScore)>(); for (var i = 0; i < PartnerInfoManager.BuilderCapacity; ++i) { buildScore.Add((0, 0)); } GatherData.BuilderPartnerScore = buildScore; // 很少会调用 SaveData(); } // var builderPartnerBonus = GatherData.BuilderPartnerBonus; // var builderPartnerBonus = GatherData.PartnerBonus; // if (builderPartnerBonus == null) { // builderPartnerBonus = new List<(float BonusPercent, DateTime BonusExpireUtc)> // { // (0F, DateTime.MinValue), (0F, DateTime.MinValue), (0F, DateTime.MinValue) // }; // GatherData.PartnerBonus = (0F, DateTime.MinValue); // SaveData(); } var completedNodesList = GatherData.CompletedNodesList; if (completedNodesList == null) { completedNodesList = new List>(); // { // new(), new(), new(), // }; for (var i = 0; i < PartnerInfoManager.BuilderCapacity; ++i) { completedNodesList.Add(new List()); } GatherData.CompletedNodesList = completedNodesList; SaveData(); } var bots = GatherData.Bots; if (bots == null) { bots = new Dictionary(); GatherData.Bots = bots; SaveData(); } var fi = GatherData.UIFinishedPartners; if (fi == null) { fi = new List { false, false, false }; GatherData.UIFinishedPartners = fi; SaveData(); } // var bf = GatherData.BuildFreeSpinNum; // if (bf == null) // { // bf = new List { 0, 0, 0 }; // GatherData.BuildFreeSpinNum = bf; // SaveData(); // } PartnerInfoManager.SyncGatherData(this); } // private readonly int _timeCount = 60 * 2; // 定时 private DateTime _dateTimeForGetEvent = DateTime.MinValue; // 是否允许请求 private bool CheckGetDetailEnable() { var timeSpan = ZZTimeHelper.UtcNow() - _dateTimeForGetEvent; return timeSpan.TotalSeconds > _timeCount; } public async Task GetEventDetail(bool force = false) { if (!force && !CheckGetDetailEnable()) return false; _dateTimeForGetEvent = ZZTimeHelper.UtcNow(); var resp = await EventPartnerService.GetEventDetail(EventId); switch (resp?.State) { case EEventBuildState.Success: LoadBuilderData(resp); return true; case EEventBuildState.Failed: case EEventBuildState.InviteNotFound: case EEventBuildState.AlreadyExist: case EEventBuildState.NoActiveEvent: case EEventBuildState.Full: default: return false; } } public void LoadBuilderData(EventPartnerGatherDetailResp resp) { Trace($"LoadBuilderData eventId={EventId} state={resp?.State}"); PartnerInfoManager.LoadBuildInfo(resp.Detail, GatherData.Bots); PartnerInfoManager.LoadScore(resp.Detail.Scores, GatherData.BuilderPartnerScore); } public async Task BuildWorks(int score, int cost) { if (GatherData.TicketCount < cost) { // Log("当前的代币不够用了"); // ToastPanel.Show(LocalizationMgr.GetText("UI_ToastPanel_21")); GContext.Publish(new EventPartnerGatherShowPack()); return false; } if (score <= 0 && cost > 0) { return false; } var partnerId = CurrentBuildPartner.PartnerId; var newScore = CurrentBuildPartner.ScoreSelfActual + score; // CurrentBuildPartner.ScoreSelfActual = newScore; Trace($"BuildWorks eventId={EventId} partnerId={partnerId} newScore={newScore} cost={cost}"); if (partnerId.EndsWith(EventPartnerBot.RobotIdentifier)) { // CurrentBuildPartner.SyncRobotScore(); // DoNothing(); } else { var response = await EventPartnerService.BuildWorks(partnerId, newScore, EventId); if (response.State != EEventBuildBuildState.Success) { LogWarning($"BuildRequestFailed eventId={EventId} partnerId={partnerId} state={response.State} reason={response.Message}"); return false; } } AddTicket(-cost); return true; } public void AddTicketPure(int costValue) { if (GatherData != null) { GatherData.TicketCount += costValue; if (GatherData.TicketCount < 0) { GatherData.TicketCount = 0; } GContext.container.Resolve().SaveTransitionData(EventId, GatherData.TicketCount); GContext.Publish(new EventPartnerGatherUpdateTicketCount()); SaveData(); } } public void AddTicket(int costValue) { if (GatherData != null) { GatherData.TicketCount += costValue; if (GatherData.TicketCount < 0) { GatherData.TicketCount = 0; } GContext.container.Resolve().SaveTransitionData(EventId, GatherData.TicketCount); GContext.Publish(new EventPartnerGatherUpdateTicketCount()); SaveData(); } SetRedPoint(); } public int GetLeftPoint(int usedPoints, int progress) { // var progress = CalcMiningProgressOld(usedPoints); if (progress >= _nodeCumulativeSteps.Count) { return 0; } return NodeCumulativeSteps[progress] - usedPoints; } public void AddInvitationCount() { GatherData.InvitedCount++; } public void ResetBuildPartner(int slotId) { Trace($"ResetBuildPartner eventId={EventId} slotId={slotId}"); // if (GatherData.UIFinishedPartners != null && slotId < GatherData.UIFinishedPartners.Count) { GatherData.UIFinishedPartners[slotId] = false; } if (GatherData.BuilderPartnerScore != null && slotId < GatherData.BuilderPartnerScore.Count) { GatherData.BuilderPartnerScore[slotId] = (0, 0); } if (GatherData.CompletedNodesList != null && slotId < GatherData.CompletedNodesList.Count) { GatherData.CompletedNodesList[slotId] = new List(); } } public void SavePartnerInfo(int slotId) { Trace($"SavePartnerInfo eventId={EventId} slotId={slotId}"); var partners = PartnerInfoManager.BuildPartners; if (slotId > partners.Length - 1) { Trace($"SyncFromPartnerInfoFailed eventId={EventId} slotId={slotId} partnerCount={partners.Length}"); return; } var partner = partners[slotId]; partner.ScorePartnerDisplay = partner.ScorePartnerActual; partner.ScoreSelfDisplay = partner.ScoreSelfActual; GatherData.BuilderPartnerScore[slotId] = (partner.ScoreSelfActual, partner.ScorePartnerActual); SaveData(); } public void SyncPartnerInfoToSave() { Trace($"SyncFromPartnerInfo eventId={EventId}"); var partners = PartnerInfoManager.BuildPartners; for (var i = 0; i < partners.Length; ++i) { var partner = partners[i]; GatherData.BuilderPartnerScore[i] = (partner.ScoreSelfActual, partner.ScorePartnerActual); } SaveData(); } public int GetProgressBySlotId(int slotId) { if (slotId > GatherData.BuilderPartnerScore.Count) { return -1; } var (myScore, partnerScore) = GatherData.BuilderPartnerScore[slotId]; var buildScore = myScore + partnerScore; return CalcMiningProgressOld(buildScore); } public void SaveBotInfo(int idx, EventBuildBotInfo botInfo) { // throw new NotImplementedException(); var ret = GatherData.Bots.TryAdd(idx, botInfo); if (ret) { // dosmt } } public int startTime; public int delta; public void GMRobotStartTIme(int timeSeconds) { startTime = timeSeconds; } public void GMRobotDelta(int timeSeconds) { delta = timeSeconds; } public bool GetDoRecommendBot() { // if (true) // { // return true; // } return !PartnerInfoManager.IsFullyMatched && (GatherData.InvitedCount >= _tables.TbEventPartnerConfig.RobotRecommendTriggerCount); } public static void EnsureTokenExchangeContextRegistered(EventPartnerGatherManager manager) { var context = GContext.container.Resolve(); if (context == null) { GContext.container.Unregister(); GContext.container.Register().AsSingleton(); context = GContext.container.Resolve(); } if (context is EventPartnerGatherExchangeContext tokenExchangeContext) tokenExchangeContext.SetManager(manager); // EventPartnerTokenExchangeContextRegistry.Context = context; } // 结算 public void OnSettle() { Trace($"OnSettle eventId={EventId}"); FinishMining(); } // 一轮完成的时候调用 public void UpdateSpinData() { if (BonusExpireDuration != 0 && BonusPercent > 0) { ApplyBonus(BonusPercent, BonusExpireDuration); } BonusPercent = 0; BonusExpireDuration = 0; } #region 自动匹配相关方法 public async Task FetchAndProcessAutoMatchStatus(bool force = true) { var response = await GetAutoMatchStatusResponseAsync(force); if (response?.State == EEventBuildState.Success) ProcessAutoMatchStatusSuccess(response); return response; } private void ProcessAutoMatchStatusSuccess(AutoMatchStatusResponse response) { if (response?.Partners is { Count: > 0 } && !ShouldCloseAutoMatch) { for (var i = 0; i < response.Partners.Count; ++i) { if (i >= AutoMatchSlotCount) break; TryAddAutoMatchedPartner(i, response.Partners[i]); } } } public bool TryAddAutoMatchedPartner(int slotIndex, PlayerInfo partner) { if (partner == null || slotIndex < 0 || slotIndex >= PartnerInfoManager.BuildPartners.Length) return false; if (partner.PlayFabId.EndsWith(BuildPartner.RobotIdentifier)) return false; var buildPartner = PartnerInfoManager.BuildPartners[slotIndex]; if (buildPartner != null && buildPartner.IsValid()) return false; var builder = new BuildPartner { SlotId = slotIndex, PartnerId = partner.PlayFabId, AvatarUrl = partner.AvatarUrl, DisplayName = partner.DisplayName, Cache = PartnerInfoManager.Cache, }; var ret = PartnerInfoManager.AddBuildPartner(builder); if (ret) { GContext.Publish(new EventPartnerGatherAddPartner { SlotId = slotIndex, }); } #if AGG if (ret) { using (var e = GEvent.GameEvent("event_partners_gather_invite")) { e.AddContent("teammate_id", partner.PlayFabId) .AddContent("position_id", slotIndex) .AddContent("inviter_id", "auto"); } } #endif return ret; } /// 自动匹配 status 请求最短间隔(秒),仅对 force=false 生效 private const int AutoMatchStatusMinIntervalSeconds = 3; private readonly object _autoMatchStatusGate = new object(); private Task _autoMatchStatusInFlight; private DateTime _lastAutoMatchStatusUtc = DateTime.MinValue; private AutoMatchStatusResponse _cachedAutoMatchStatus; // 设置自动匹配状态 public void SetAutoMatchEnabled(bool enabled) { GatherData.IsEnableAutoMatch = enabled; SaveData(); } public async Task ControlAutoMatch(bool enable) { return await EventPartnerService.ControlAutoMatch(EventId, enable); } public void NotifyAutoMatchStopped() { GContext.Publish(new EventForAutoMatchChangeStage { State = AutoMatchState.AutoMatchOff }); } public void NotifyAutoMatchFailed(string reason) { GContext.Publish(new EventAutoMatchFailed { Reason = reason }); } // 检查是否可以开启自动匹配 public bool CanStopAutoMatch(out int leftTimes) { leftTimes = -1; // 检查是否已满员 if (PartnerInfoManager.IsFullyMatched) return false; // 检查冷却时间 var config = _tables.TbEventPartnerConfig; var cooldownSeconds = config.AutoMatchCD; var elapsed = (DateTime.UtcNow - GatherData.LastAutoMatchTime).TotalSeconds; leftTimes = (int)Math.Floor(cooldownSeconds - elapsed); leftTimes = leftTimes > 0 ? leftTimes : 0; Trace($"CanStopAutoMatch eventId={EventId} elapsedSec={elapsed:F2} leftSec={leftTimes}"); return elapsed >= cooldownSeconds; } /// /// 拉取自动匹配完整状态(合并并发请求、可选短节流)。业务侧应优先通过本方法访问,勿直接调 EventPartnerService.GetAutoMatchResult。 /// /// true:忽略短节流(轮询、关键操作后刷新等仍会与进行中的请求合并为一次 HTTP) public async Task GetAutoMatchStatusResponseAsync(bool force = false) { if (GatherData == null || EventId < 0) { return new AutoMatchStatusResponse { State = EEventBuildState.Failed, Message = "No active gather data or event", IsInMatching = false }; } if (!force && _cachedAutoMatchStatus != null) { var elapsed = (DateTime.UtcNow - _lastAutoMatchStatusUtc).TotalSeconds; if (elapsed < AutoMatchStatusMinIntervalSeconds) return _cachedAutoMatchStatus; } Task taskToAwait; lock (_autoMatchStatusGate) { if (_autoMatchStatusInFlight != null) taskToAwait = _autoMatchStatusInFlight; else taskToAwait = _autoMatchStatusInFlight = FetchAutoMatchStatusFromServerAsync(); } try { return await taskToAwait; } finally { lock (_autoMatchStatusGate) { if (ReferenceEquals(_autoMatchStatusInFlight, taskToAwait)) _autoMatchStatusInFlight = null; } } } private async Task FetchAutoMatchStatusFromServerAsync() { try { var response = await EventPartnerService.GetAutoMatchResult(EventId); lock (_autoMatchStatusGate) { _cachedAutoMatchStatus = response; _lastAutoMatchStatusUtc = DateTime.UtcNow; } return response; } catch (Exception e) { LogError($"AutoMatchFetchStatusFailed eventId={EventId} error={e.Message}"); return new AutoMatchStatusResponse { State = EEventBuildState.Failed, Message = e.Message, IsInMatching = false }; } } #endregion #region Token Exchange public int GetTicketItemId() { return EventPartnerMineMainData?.ItemID ?? 0; } public int GetTokenExchangeId() { return EventPartnerMineMainData?.TokenExchangeId ?? 0; } public int GetTokenExchangeItemId() { var tokenExchangeId = GetTokenExchangeId(); return _tables.TbEventPartnerTokenExchange[tokenExchangeId].TokenItem; } #endregion } }