OshimaGameModule/OshimaServers/FastAutoServer.cs

379 lines
18 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using System.Collections.Concurrent;
using System.Text;
using Milimoe.FunGame.Core.Api.Transmittal;
using Milimoe.FunGame.Core.Api.Utility;
using Milimoe.FunGame.Core.Entity;
using Milimoe.FunGame.Core.Interface.Base;
using Milimoe.FunGame.Core.Library.Common.Addon;
using Milimoe.FunGame.Core.Library.Constant;
using Milimoe.FunGame.Core.Model;
using Oshima.Core.Constant;
using Oshima.FunGame.OshimaModules.Items;
using Oshima.FunGame.OshimaModules.Skills;
using Oshima.FunGame.OshimaServers.Service;
namespace Oshima.FunGame.OshimaServers
{
public class FastAutoServer : GameModuleServer
{
public override string Name => OshimaGameModuleConstant.FastAuto;
public override string Description => OshimaGameModuleConstant.Description;
public override string Version => OshimaGameModuleConstant.Version;
public override string Author => OshimaGameModuleConstant.Author;
public override string DefaultMap => OshimaGameModuleConstant.FastAutoMap;
public override GameModuleDepend GameModuleDepend => OshimaGameModuleConstant.GameModuleDepend;
public static List<Character> Characters { get; } = [];
public static Dictionary<Character, CharacterStatistics> CharacterStatistics { get; } = [];
public static PluginConfig StatsConfig { get; } = new(OshimaGameModuleConstant.FastAuto, nameof(CharacterStatistics));
public static GameModuleLoader? GameModuleLoader { get; set; } = null;
public override async Task<Dictionary<string, object>> GamingMessageHandler(IServerModel model, GamingType type, Dictionary<string, object> data)
{
Dictionary<string, object> result = [];
// 获取model所在的房间工作类
ModuleServerWorker worker = Workers[model.InRoom.Roomid];
switch (type)
{
case GamingType.Connect:
string un = (DataRequest.GetDictionaryJsonObject<string>(data, "un") ?? "").Trim();
if (un != "" && !worker.ConnectedUser.Any(u => u.Username == un))
{
worker.ConnectedUser.Add(model.User);
Controller.WriteLine(un + " 已连接至房间。");
}
break;
case GamingType.Disconnect:
break;
case GamingType.Reconnect:
break;
case GamingType.BanCharacter:
break;
case GamingType.PickCharacter:
break;
case GamingType.Random:
break;
case GamingType.Round:
break;
case GamingType.LevelUp:
break;
case GamingType.Move:
break;
case GamingType.Attack:
break;
case GamingType.Skill:
break;
case GamingType.Item:
break;
case GamingType.Magic:
break;
case GamingType.Buy:
break;
case GamingType.SuperSkill:
break;
case GamingType.Pause:
break;
case GamingType.Unpause:
break;
case GamingType.Surrender:
break;
case GamingType.UpdateInfo:
break;
case GamingType.Punish:
break;
case GamingType.None:
default:
await Task.Delay(1);
break;
}
return result;
}
private readonly struct ModuleServerWorker(GamingObject obj)
{
public GamingObject GamingObject { get; } = obj;
public List<User> ConnectedUser { get; } = [];
public Dictionary<string, Dictionary<string, object>> UserData { get; } = [];
public Dictionary<User, Character> PickCharacters { get; } = [];
}
private ConcurrentDictionary<string, ModuleServerWorker> Workers { get; } = [];
public override bool StartServer(GamingObject obj, params object[] args)
{
// 因为模组是单例的,需要为这个房间创建一个工作类接收参数,不能直接用本地变量处理
ModuleServerWorker worker = new(obj);
Workers[obj.Room.Roomid] = worker;
TaskUtility.NewTask(async () => await StartGame(obj, worker)).OnError(Controller.Error);
return true;
}
private async Task StartGame(GamingObject obj, ModuleServerWorker worker)
{
try
{
while (true)
{
if (worker.ConnectedUser.Count == obj.All.Count)
{
break;
}
await Task.Delay(500);
}
Dictionary<User, Character> characters = [];
List<Character> characterPickeds = [];
Dictionary<string, object> data = [];
// 抽取角色
foreach (User user in obj.Users)
{
List<Character> list = [.. Characters.Where(c => !characterPickeds.Contains(c))];
Character cr = list[Random.Shared.Next(list.Count - 1)];
string msg = $"{user.Username} 抽到了 [ {cr} ]";
characterPickeds.Add(cr);
characters.Add(user, cr.Copy());
Controller.WriteLine(msg);
SendAllGamingMessage(obj, data, msg);
}
int clevel = 60;
int slevel = 6;
int mlevel = 8;
List<Character> inGameCharacters = [.. characters.Values];
// 升级和赋能
foreach (Character c in inGameCharacters)
{
c.Level = clevel;
c.NormalAttack.Level = mlevel;
Skill = new (c)
{
Level = mlevel
};
c.Skills.Add();
Skill = new (c)
{
Level = slevel
};
c.Skills.Add();
FunGameService.AddCharacterSkills(c, 1, slevel, slevel);
SendAllGamingMessage(obj, data, c.GetInfo());
}
ActionQueue actionQueue = new(inGameCharacters, false, (str) =>
{
SendAllGamingMessage(obj, data, str);
});
actionQueue.SetCharactersToAIControl(false, inGameCharacters);
// 总游戏时长
double totalTime = 0;
// 总死亡数
int deaths = 0;
// 开始空投
(actionQueue, totalTime);
// 总回合数
int i = 1;
while (i < 999)
{
if (i == 998)
{
SendAllGamingMessage(obj, data, $"=== 终局审判 ===");
Dictionary<Character, double> = [];
foreach (Character c in inGameCharacters)
{
.TryAdd(c, Calculation.Round4Digits(c.HP / c.MaxHP));
}
double max = .Values.Max();
Character winner = .Keys.Where(c => [c] == max).First();
SendAllGamingMessage(obj, data, "[ " + winner + " ] 成为了天选之人!!");
foreach (Character c in inGameCharacters.Where(c => c != winner && c.HP > 0))
{
SendAllGamingMessage(obj, data, "[ " + winner + " ] 对 [ " + c + " ] 造成了 99999999999 点真实伤害。");
await actionQueue.DeathCalculationAsync(winner, c);
}
await actionQueue.EndGameInfo(winner);
break;
}
// 检查是否有角色可以行动
Character? characterToAct = await actionQueue.NextCharacterAsync();
// 处理回合
if (characterToAct != null)
{
SendAllGamingMessage(obj, data, $"=== Round {i++} ===");
SendAllGamingMessage(obj, data, "现在是 [ " + characterToAct + " ] 的回合!");
if (actionQueue.Queue.Count == 0)
{
break;
}
bool isGameEnd = await actionQueue.ProcessTurnAsync(characterToAct);
if (isGameEnd)
{
break;
}
actionQueue.DisplayQueue();
}
// 模拟时间流逝
totalTime += await actionQueue.TimeLapse();
if (actionQueue.Eliminated.Count > deaths)
{
deaths = actionQueue.Eliminated.Count;
}
}
SendAllGamingMessage(obj, data, "--- End ---");
SendAllGamingMessage(obj, data, "总游戏时长:" + Calculation.Round2Digits(totalTime));
// 赛后统计
SendAllGamingMessage(obj, data, "=== 伤害排行榜 ===");
int top = inGameCharacters.Count;
int count = 1;
foreach (Character character in actionQueue.CharacterStatistics.OrderByDescending(d => d.Value.TotalDamage).Select(d => d.Key))
{
StringBuilder builder = new();
CharacterStatistics stats = actionQueue.CharacterStatistics[character];
builder.AppendLine($"{count++}. [ {character.ToStringWithLevel()} ] {stats.Kills} / {stats.Assists}");
builder.AppendLine($"存活时长:{stats.LiveTime} / 存活回合数:{stats.LiveRound} / 行动回合数:{stats.ActionTurn}");
builder.AppendLine($"总计伤害:{stats.TotalDamage} / 总计物理伤害:{stats.TotalPhysicalDamage} / 总计魔法伤害:{stats.TotalMagicDamage}");
builder.AppendLine($"总承受伤害:{stats.TotalTakenDamage} / 总承受物理伤害:{stats.TotalTakenPhysicalDamage} / 总承受魔法伤害:{stats.TotalTakenMagicDamage}");
builder.Append($"每秒伤害:{stats.DamagePerSecond} / 每回合伤害:{stats.DamagePerTurn}");
SendAllGamingMessage(obj, data, builder.ToString());
CharacterStatistics? totalStats = CharacterStatistics.Where(kv => kv.Key.GetName() == character.GetName()).Select(kv => kv.Value).FirstOrDefault();
if (totalStats != null)
{
// 统计此角色的所有数据
totalStats.TotalDamage = Calculation.Round2Digits(totalStats.TotalDamage + stats.TotalDamage);
totalStats.TotalPhysicalDamage = Calculation.Round2Digits(totalStats.TotalPhysicalDamage + stats.TotalPhysicalDamage);
totalStats.TotalMagicDamage = Calculation.Round2Digits(totalStats.TotalMagicDamage + stats.TotalMagicDamage);
totalStats.TotalRealDamage = Calculation.Round2Digits(totalStats.TotalRealDamage + stats.TotalRealDamage);
totalStats.TotalTakenDamage = Calculation.Round2Digits(totalStats.TotalTakenDamage + stats.TotalTakenDamage);
totalStats.TotalTakenPhysicalDamage = Calculation.Round2Digits(totalStats.TotalTakenPhysicalDamage + stats.TotalTakenPhysicalDamage);
totalStats.TotalTakenMagicDamage = Calculation.Round2Digits(totalStats.TotalTakenMagicDamage + stats.TotalTakenMagicDamage);
totalStats.TotalTakenRealDamage = Calculation.Round2Digits(totalStats.TotalTakenRealDamage + stats.TotalTakenRealDamage);
totalStats.LiveRound += stats.LiveRound;
totalStats.ActionTurn += stats.ActionTurn;
totalStats.LiveTime = Calculation.Round2Digits(totalStats.LiveTime + stats.LiveTime);
totalStats.TotalEarnedMoney += stats.TotalEarnedMoney;
totalStats.Kills += stats.Kills;
totalStats.Deaths += stats.Deaths;
totalStats.Assists += stats.Assists;
totalStats.LastRank = stats.LastRank;
double totalRank = totalStats.AvgRank * totalStats.Plays + totalStats.LastRank;
totalStats.Plays += stats.Plays;
if (totalStats.Plays != 0) totalStats.AvgRank = Calculation.Round2Digits(totalRank / totalStats.Plays);
totalStats.Wins += stats.Wins;
totalStats.Top3s += stats.Top3s;
totalStats.Loses += stats.Loses;
if (totalStats.Plays != 0)
{
totalStats.AvgDamage = Calculation.Round2Digits(totalStats.TotalDamage / totalStats.Plays);
totalStats.AvgPhysicalDamage = Calculation.Round2Digits(totalStats.TotalPhysicalDamage / totalStats.Plays);
totalStats.AvgMagicDamage = Calculation.Round2Digits(totalStats.TotalMagicDamage / totalStats.Plays);
totalStats.AvgRealDamage = Calculation.Round2Digits(totalStats.TotalRealDamage / totalStats.Plays);
totalStats.AvgTakenDamage = Calculation.Round2Digits(totalStats.TotalTakenDamage / totalStats.Plays);
totalStats.AvgTakenPhysicalDamage = Calculation.Round2Digits(totalStats.TotalTakenPhysicalDamage / totalStats.Plays);
totalStats.AvgTakenMagicDamage = Calculation.Round2Digits(totalStats.TotalTakenMagicDamage / totalStats.Plays);
totalStats.AvgTakenRealDamage = Calculation.Round2Digits(totalStats.TotalTakenRealDamage / totalStats.Plays);
totalStats.AvgLiveRound = totalStats.LiveRound / totalStats.Plays;
totalStats.AvgActionTurn = totalStats.ActionTurn / totalStats.Plays;
totalStats.AvgLiveTime = Calculation.Round2Digits(totalStats.LiveTime / totalStats.Plays);
totalStats.AvgEarnedMoney = totalStats.TotalEarnedMoney / totalStats.Plays;
totalStats.Winrates = Calculation.Round4Digits(Convert.ToDouble(totalStats.Wins) / Convert.ToDouble(totalStats.Plays));
totalStats.Top3rates = Calculation.Round4Digits(Convert.ToDouble(totalStats.Top3s) / Convert.ToDouble(totalStats.Plays));
}
if (totalStats.LiveRound != 0) totalStats.DamagePerRound = Calculation.Round2Digits(totalStats.TotalDamage / totalStats.LiveRound);
if (totalStats.ActionTurn != 0) totalStats.DamagePerTurn = Calculation.Round2Digits(totalStats.TotalDamage / totalStats.ActionTurn);
if (totalStats.LiveTime != 0) totalStats.DamagePerSecond = Calculation.Round2Digits(totalStats.TotalDamage / totalStats.LiveTime);
}
}
// 显示每个角色的信息
for (i = actionQueue.Eliminated.Count - 1; i >= 0; i--)
{
Character character = actionQueue.Eliminated[i];
SendAllGamingMessage(obj, data, $"=== 角色 [ {character} ] ===\r\n{character.GetInfo()}");
}
lock (StatsConfig)
{
foreach (Character c in CharacterStatistics.Keys)
{
StatsConfig.Add(c.ToStringWithOutUser(), CharacterStatistics[c]);
}
StatsConfig.SaveConfig();
}
// 结束
SendEndGame(obj);
worker.ConnectedUser.Clear();
Workers.Remove(obj.Room.Roomid, out _);
}
catch (Exception e)
{
TXTHelper.AppendErrorLog(e.ToString());
}
}
public static void (ActionQueue queue, double totalTime)
{
Item[] ;
if (totalTime == 0)
{
foreach (Character character in queue.Queue)
{
= [new 35()];
foreach (Item item in )
{
queue.Equip(character, EquipSlotType.Accessory1, item, out _);
}
}
}
}
private void SendAllGamingMessage(GamingObject obj, Dictionary<string, object> data, string str, bool showmessage = false)
{
data.Clear();
data.Add("msg", str);
data.Add("showmessage", showmessage);
_ = SendGamingMessage(obj.All.Values, GamingType.UpdateInfo, data);
}
public override void AfterLoad(params object[] args)
{
foreach (Character c in GameModuleDepend.Characters.Values)
{
Character character = c.Copy();
Characters.Add(character);
CharacterStatistics.Add(character, new());
}
StatsConfig.LoadConfig();
foreach (Character character in CharacterStatistics.Keys)
{
if (StatsConfig.ContainsKey(character.ToStringWithOutUser()))
{
CharacterStatistics[character] = StatsConfig.Get<CharacterStatistics>(character.ToStringWithOutUser()) ?? CharacterStatistics[character];
}
}
}
}
}