Files
ft/Client/Assets/Scripts/UI/PartnerStatue/EventPartnerRobot.cs
2026-06-29 21:18:33 +08:00

300 lines
12 KiB
C#

using cfg;
using asap.core;
using game;
using System.Globalization;
using GameCore;
using System;
using System.Collections.Generic;
using System.Linq;
using EventPartnerGatherRequests;
using Castle.Core.Internal;
namespace EventPartner
{
public class Robot
{
public string Id;
public string Avatar;
public string DisplayName;
public DateTime WakeStartTime;
public float WakingSeconds;
public int Grade;
public int TargetScore;
public Queue<BotAddScoreAction> ActionQ;
private IEventPartnerArchitecture _arc;
public Robot(string id, string avatar, string displayName, DateTime wakeStartTime, float wakingSeconds, int grade, IEventPartnerArchitecture arc)
{
Id = id;
Avatar = avatar;
DisplayName = displayName;
WakeStartTime = wakeStartTime;
WakingSeconds = wakingSeconds;
Grade = grade;
_arc = arc;
Schedule(0, 0);
}
public Robot(RobotPlayerPreferenceData data, IEventPartnerArchitecture arc)
{
Id = data.Id;
WakeStartTime = data.WakeStartTime;
WakingSeconds = data.WakingSeconds;
Grade = data.Grade;
TargetScore = data.TargetScore;
ActionQ = data.ActionQ;
_arc = arc;
}
/// <summary>
/// Schedule a new action queue for the robot.
/// </summary>
/// <param name="botScore">Current score added by bot.</param>
/// <param name="totalScore">Current total score.</param>
public void Schedule(int botScore, int totalScore)
{
var tableConfig = GContext.container.Resolve<Tables>().TbEventPartnerConfig;
ActionQ = new Queue<BotAddScoreAction>();
var currentProgress = _arc.Ctx.GetStageFromTotalScore(totalScore);
var robotData = GContext.container.Resolve<Tables>().TbEventPartnerRobot.DataList[Grade];
var percentage = UnityEngine.Random.Range(robotData.PointsRange[0], robotData.PointsRange[1]) / 100;
int newBotTargetScore = (int)(percentage * _arc.Ctx.GetTargetScoreByStage(currentProgress));
if (newBotTargetScore < TargetScore)
{
UnityEngine.Debug.Log($"<color=#c191ff>[EventPartner]New bot target {newBotTargetScore} is smaller than old target {TargetScore}</color>");
return;
}
var startTime = WakeStartTime;
startTime += TimeSpan.FromDays(ZZTimeHelper.UtcNow().Day - startTime.Day);
startTime += TimeSpan.FromSeconds(UnityEngine.Random.Range(0, WakingSeconds));
var scheduleDuration = UnityEngine.Random.Range(tableConfig.RobotSingleAddPointsTimeWindow[0], tableConfig.RobotSingleAddPointsTimeWindow[1]);
var scheduleAmount = UnityEngine.Random.Range(tableConfig.RobotSingleAddPointsOperations[0], tableConfig.RobotSingleAddPointsOperations[1]);
var timeList = Enumerable.Range(1, scheduleDuration)
.OrderBy(_ => UnityEngine.Random.value)
.Take(scheduleAmount)
.OrderBy(t => t)
.ToList();
var gap = newBotTargetScore - botScore;
if (!FtUtils.RandomSplit(gap, scheduleAmount, out var scoreList))
UnityEngine.Debug.Log($"<color=#c191ff>[EventPartnerStatue]Split {gap} into {scheduleAmount}.</color>");
UnityEngine.Debug.Log($"<color=#c191ff>[EventPartnerStatue]WakeTime:{WakeStartTime}, StartTime: {startTime}, Duration: {scheduleDuration}s, Add {gap} points in {scheduleAmount} times.</color>");
UnityEngine.Debug.Log($"<color=#c191ff>[EventPartnerStatue]Activation Delay: {startTime - WakeStartTime}, BotAddScoreStrengthGrade: {Grade}</color>");
int minWheelNumber = _arc.Ctx.GetMinSpinPoint();
for (int i = 0; i < scoreList.Count; i++)
{
FtUtils.ScaleByBase(scoreList[i], minWheelNumber, out var score);
ActionQ.Enqueue(new BotAddScoreAction
{
Score = score,
DueTime = startTime + TimeSpan.FromSeconds(timeList[i])
});
}
_arc.SavePlayerPreference();
foreach (var a in ActionQ)
UnityEngine.Debug.Log($"<color=#c191ff>[EventPartnerStatue] Add {a.Score} @ {a.DueTime}.</color>");
}
public RobotPlayerPreferenceData ToPpData()
{
return new RobotPlayerPreferenceData
{
Id = Id,
WakeStartTime = WakeStartTime,
WakingSeconds = WakingSeconds,
Grade = Grade,
TargetScore = TargetScore,
ActionQ = ActionQ
};
}
}
public class BotAddScoreAction
{
public DateTime DueTime { get; set; }
public int Score { get; set; }
}
public class RobotInvitation
{
public string Id { get; set; }
public DateTime DueTime { get; set; }
private (int from, int to) GetInvitationWindow()
{
var configTable = GContext.container.Resolve<Tables>().TbEventPartnerConfig;
return (configTable.RobotAgreeTime[0], configTable.RobotAgreeTime[1]);
}
public RobotInvitation(string id)
{
Id = id;
var (from, to) = GetInvitationWindow();
DueTime = ZZTimeHelper.UtcNow() + TimeSpan.FromSeconds(UnityEngine.Random.Range(from, to));
}
}
public class RobotDataManager
{
public Dictionary<string, Robot> RobotList = new Dictionary<string, Robot>();
public Queue<RobotInvitation> RobotInvitationBuffer = new Queue<RobotInvitation>();
private bool _isLoopActive = false;
private TimeSpan _loopInterval = TimeSpan.FromSeconds(10);
public const char RobotIdentifier = 'R';
private static readonly IUserService UserService = GContext.container.Resolve<IUserService>();
private static readonly TbRobot RobotTable = GContext.container.Resolve<Tables>().TbRobot;
private readonly IEventPartnerArchitecture _arc;
public RobotDataManager(IEventPartnerArchitecture arc)
{
_arc = arc;
}
public static string Idx2Id(int idx)
{
return idx.ToString("X5") + RobotIdentifier;
}
public static int Id2Idx(string id)
{
return int.Parse(id.Trim(RobotIdentifier), NumberStyles.HexNumber);
}
public static void GetBotDisplayInfo(string id, out string avatar, out string displayName)
{
int idx = Id2Idx(id);
var res = RobotTable.DataMap.TryGetValue(idx, out var r);
if (!res)
{
avatar = "";
displayName = UserService.GetDefaultName(id);
return;
}
avatar = r.Avatar;
displayName = LocalizationMgr.GetText(r.Name_l10n_key);
}
public static bool IsRobot(string id)
{
return id.EndsWith(RobotIdentifier);
}
public void PushToBuffer(string robotId)
{
RobotInvitationBuffer.Enqueue(new RobotInvitation(robotId));
_arc.SavePlayerPreference();
}
public async System.Threading.Tasks.Task TryBotInvitation()
{
while (!_arc.IsFull() && RobotInvitationBuffer.Count > 0 && RobotInvitationBuffer.Peek().DueTime < ZZTimeHelper.UtcNow())
{
var invitation = RobotInvitationBuffer.Dequeue();
_arc.SavePlayerPreference();
UnityEngine.Debug.Log($"<color=cyan>[EventPartnerStatue]Adding bot {invitation.Id}, with due time {invitation.DueTime}</color>");
var resp = await _arc.Requests.Accept(invitation.Id);
if (resp == null || resp.State != EEventBuildState.Success)
{
OnBotInvitationFail(invitation);
UnityEngine.Debug.Log($"<color=cyan>[EventPartnerStatue] Fail: {resp.State}.</color>");
continue;
}
OnBotInvitationSuccess(invitation.Id);
UnityEngine.Debug.Log($"<color=cyan>[EventPartnerStatue] Success: {resp.State}.</color>");
}
}
public void TryAddScore()
{
var arc = GContext.container.Resolve<EventPartnerStatueArc>();
foreach (var robot in RobotList.Values)
{
if (!robot.ActionQ.TryPeek(out var addScoreAction) || ZZTimeHelper.UtcNow() < addScoreAction.DueTime)
continue;
var c = arc.Model.ComponentList.FirstOrDefault(c => c.PartnerId == robot.Id);
if (c == null)
{
UnityEngine.Debug.LogWarning($"[EventPartnerStatue]Robot {robot.Id} not found in component list. SUS.");
continue;
}
if (c.ScoreTotal >= arc.TableContext.GetMaxScore())
{
robot.ActionQ.Clear();
continue;
}
while (!robot.ActionQ.IsNullOrEmpty() && ZZTimeHelper.UtcNow() >= robot.ActionQ.Peek().DueTime)
{
var action = robot.ActionQ.Dequeue();
c.AddPartnerScore(action.Score);
UnityEngine.Debug.Log($"[EventPartnerStatue] Add robot score to {c.PartnerId} with {action.Score} at {action.DueTime}");
arc.SavePlayfab();// To save score;
arc.SavePlayerPreference(); // To update action queue.
}
}
}
private void OnBotInvitationSuccess(string botId)
{
_arc.InvitationData.OnAccept(botId);
CreateBotDataToRobotList(botId);
_arc.AddPartner(botId);
_arc.EventAggregator.Publish(new EventBotAdd());
}
public void CreateBotDataToRobotList(string botId)
{
var tableRobot = GContext.container.Resolve<Tables>().TbEventPartnerRobot;
var tableConfig = GContext.container.Resolve<Tables>().TbEventPartnerConfig;
var grade = FtUtils.GetRandomIdxFromWeightList(tableRobot.DataList.Select(r => r.Weight));
var wakeStartTime = ZZTimeHelper.UtcNow();
int wakingSeconds = UnityEngine.Random.Range(tableConfig.RobotDailyActiveTimeWindow[0],
tableConfig.RobotDailyActiveTimeWindow[1]);
GetBotDisplayInfo(botId, out string avatar, out string displayName);
RobotList.Add(botId, new Robot(botId, avatar, displayName, wakeStartTime, wakingSeconds, grade, _arc));
}
private void OnBotInvitationFail(RobotInvitation invitation)
{
_arc.InvitationData.OnRefuse(invitation.Id);
}
public async void StartRobotLoop()
{
try
{
if (_isLoopActive)
return;
_isLoopActive = true;
while (_isLoopActive)
{
await TryBotInvitation();
TryAddScore();
await System.Threading.Tasks.Task.Delay(_loopInterval);
}
}
catch (Exception e)
{
_isLoopActive = false;
UnityEngine.Debug.LogError(e);
// await System.Threading.Tasks.Task.Delay(_loopInterval);
// StartRobotLoop();
}
}
public void CutRobotLoop()
{
_isLoopActive = false;
}
}
public class RobotPlayerPreferenceData
{
public string Id { get; set; }
public DateTime WakeStartTime { get; set; }
public float WakingSeconds { get; set; }
public int Grade { get; set; }
public int TargetScore { get; set; }
public Queue<BotAddScoreAction> ActionQ { get; set; }
}
public class EventBotAdd { public int SlotId { get; set; } }
}