diff --git a/OshimaModules/Models/FunGameConstant.cs b/OshimaModules/Models/FunGameConstant.cs index 189f4d1..f255210 100644 --- a/OshimaModules/Models/FunGameConstant.cs +++ b/OshimaModules/Models/FunGameConstant.cs @@ -18,6 +18,7 @@ namespace Oshima.FunGame.OshimaModules.Models public const int DrawCardReduce = 1000; public const int DrawCardReduce_Material = 5; public const int SemaphoreSlimTimeout = 5000; + public const int RoomExpireTime = 10; public static List Characters { get; } = []; public static List Skills { get; } = []; public static List PassiveSkills { get; } = []; @@ -40,6 +41,12 @@ namespace Oshima.FunGame.OshimaModules.Models public static Dictionary UsersInRoom { get; set; } = []; public static ItemType[] ItemCanUsed => [ItemType.Consumable, ItemType.MagicCard, ItemType.SpecialItem, ItemType.GiftBox, ItemType.Others]; public static ItemType[] ItemCanNotDrawCard => [ItemType.Collectible, ItemType.QuestItem, ItemType.GiftBox, ItemType.Others]; + public static Dictionary UserHorseRacingRanking { get; } = []; + public static Dictionary UserCreditsRanking { get; } = []; + public static Dictionary UserMaterialsRanking { get; } = []; + public static Dictionary UserEXPRanking { get; } = []; + public static Dictionary UserSkillRanking { get; } = []; + public static DateTime RankingUpdateTime { get; set; } = DateTime.Now; public static char[] SplitChars => [',', ' ', ',', ';', ';']; public static Dictionary> LevelBreakNeedyList { get; } = new() diff --git a/OshimaServers/Model/Cooperative.cs b/OshimaServers/Model/Cooperative.cs new file mode 100644 index 0000000..a31913f --- /dev/null +++ b/OshimaServers/Model/Cooperative.cs @@ -0,0 +1,189 @@ +using System.Text; +using Milimoe.FunGame.Core.Api.Utility; +using Milimoe.FunGame.Core.Entity; +using Milimoe.FunGame.Core.Library.Constant; +using Oshima.FunGame.OshimaModules.Models; +using Oshima.FunGame.OshimaModules.Regions; +using Oshima.FunGame.OshimaServers.Service; + +namespace Oshima.FunGame.OshimaServers.Model +{ + public class Cooperative + { + private const int MaxRounds = 9999; + private static readonly Random _random = new(); + + public static async Task RunCooperativeGame(List msgs, Room room) + { + long[] userIds = [.. room.UserAndIsReady.Keys.Select(user => user.Id)]; + try + { + Dictionary characters = []; + Dictionary pcs = []; + try + { + foreach (User user in room.UserAndIsReady.Keys) + { + PluginConfig pc = FunGameService.GetUserConfig(user.Id, out bool isTimeout); + if (isTimeout) + { + throw new Exception($"获取 {user.Username} 的库存信息超时。"); + } + if (pc.Count > 0) + { + User userTemp = FunGameService.GetUser(pc); + pcs[userTemp] = pc; + characters[user] = user.Inventory.MainCharacter; + } + else + { + throw new Exception($"获取 {user.Username} 的库存信息失败。"); + } + } + } + catch (Exception e) + { + msgs.Add($"发生错误:{e.Message}"); + return; + } + + StringBuilder builder = new(); + builder.AppendLine("☆--- 共斗模式玩家列表 ---☆"); + builder.AppendLine($"房间号:{room.Roomid}"); + + foreach (User user in characters.Keys) + { + builder.AppendLine($"[ {user} ] 出战角色:{characters[user].ToStringWithLevelWithOutUser()}"); + } + + msgs.Add(builder.ToString().Trim()); + + // 生成敌人 + OshimaRegion region = FunGameConstant.Regions.OrderBy(o => _random.Next()).First(); + List enemys = []; + Character? enemy = null; + bool isUnit = Random.Shared.Next(2) != 0; + if (!isUnit) + { + enemy = region.Characters.OrderBy(o => Random.Shared.Next()).FirstOrDefault(); + if (enemy != null) + { + enemy = enemy.Copy(); + enemy.ExHPPercentage += 0.2; + enemys.Add(enemy); + } + } + else + { + for (int i = 0; i < Math.Max(1, characters.Count); i++) + { + enemy = region.Units.OrderBy(o => Random.Shared.Next()).FirstOrDefault(); + if (enemy != null) + { + enemy = enemy.Copy(); + int dcount = enemys.Count(e => e.Name == enemy.Name); + if (dcount > 0 && FunGameConstant.GreekAlphabet.Length > dcount) enemy.Name += FunGameConstant.GreekAlphabet[dcount - 1]; + enemys.Add(enemy); + } + } + } + if (enemys.Count == 0) + { + msgs.Add("没有可用的敌人,返回游戏房间。"); + } + else + { + Item[] weapons = [.. FunGameConstant.Equipment.Where(i => i.Id.ToString().StartsWith("11") && (int)i.QualityType == 5)]; + Item[] armors = [.. FunGameConstant.Equipment.Where(i => i.Id.ToString().StartsWith("12") && (int)i.QualityType == 5)]; + Item[] shoes = [.. FunGameConstant.Equipment.Where(i => i.Id.ToString().StartsWith("13") && (int)i.QualityType == 5)]; + Item[] accessory = [.. FunGameConstant.Equipment.Where(i => i.Id.ToString().StartsWith("14") && (int)i.QualityType == 5)]; + Item[] consumables = [.. FunGameConstant.AllItems.Where(i => i.ItemType == ItemType.Consumable && i.IsInGameItem)]; + // 敌人为动态难度,根据玩家的等级生成 + int cLevel = (int)characters.Values.Average(c => c.Level) + 5; + int sLevel = cLevel / 7; + int mLevel = cLevel / 9; + int naLevel = mLevel; + foreach (Character enemy_loop in enemys) + { + FunGameService.EnhanceBoss(enemy_loop, weapons, armors, shoes, accessory, consumables, cLevel, sLevel, mLevel, naLevel, false, false, isUnit); + } + // 开始战斗 + Team team1 = new($"房间{room.Roomid}", characters.Values); + Team team2 = new($"{region.Name}", enemys); + FunGameActionQueue actionQueue = new(); + List gameMsgs = await actionQueue.StartTeamGame([team1, team2], showAllRound: true); + if (gameMsgs.Count > 15) + { + gameMsgs = gameMsgs[^15..]; + } + msgs.AddRange(gameMsgs); + + // 结算奖励 + Character? mvp = actionQueue.GamingQueue.CharacterStatistics.OrderByDescending(d => d.Value.Rating).Select(d => d.Key).FirstOrDefault(); + int multiplication = 1; + int award = multiplication * enemys.Count; + if (enemys.All(e => e.HP == 0)) + { + multiplication = 15; + award = multiplication * enemys.Count; + builder.Clear(); + builder.AppendLine($"☆--- 战斗胜利奖励 ---☆"); + builder.AppendLine($"击杀了全部敌人,所有玩家获得奖励:{award} {General.GameplayEquilibriumConstant.InGameMaterial}"); + } + else + { + builder.AppendLine($"☆--- 战斗失败奖励 ---☆"); + int count = enemys.Count(e => e.HP == 0); + award = multiplication * count; + builder.AppendLine($"击杀了 {count} 个敌人,所有玩家获得奖励:{award} {General.GameplayEquilibriumConstant.InGameMaterial}"); + } + foreach (User user in characters.Keys) + { + user.Inventory.Materials += award; + } + if (mvp != null) + { + builder.AppendLine($"MVP 玩家额外获得奖励:{award} {General.GameplayEquilibriumConstant.InGameMaterial}"); + if (characters.Keys.FirstOrDefault(u => u.Id == mvp.User.Id) is User mvpUser) + { + mvpUser.Inventory.Materials += award; + } + } + builder.AppendLine($"☆--- 战斗数据 ---☆"); + int index = 1; + foreach (Character character in actionQueue.GamingQueue.CharacterStatistics.Where(kv => characters.ContainsValue(kv.Key)).Select(kv => kv.Key)) + { + CharacterStatistics stats = actionQueue.GamingQueue.CharacterStatistics[character]; + builder.AppendLine($"{index + ". "}[ {character.ToStringWithLevel()} ]"); + builder.AppendLine($"技术得分:{stats.Rating:0.0#} / 击杀数:{stats.Kills} / 助攻数:{stats.Assists} / 死亡数:{stats.Deaths}"); + builder.AppendLine($"存活时长:{stats.LiveTime:0.##} / 存活回合数:{stats.LiveRound} / 行动回合数:{stats.ActionTurn}"); + builder.AppendLine($"控制时长:{stats.ControlTime:0.##} / 总计治疗:{stats.TotalHeal:0.##} / 护盾抵消:{stats.TotalShield:0.##}"); + builder.AppendLine($"总计伤害:{stats.TotalDamage:0.##} / 总计物理伤害:{stats.TotalPhysicalDamage:0.##} / 总计魔法伤害:{stats.TotalMagicDamage:0.##}"); + builder.AppendLine($"总承受伤害:{stats.TotalTakenDamage:0.##} / 总承受物理伤害:{stats.TotalTakenPhysicalDamage:0.##} / 总承受魔法伤害:{stats.TotalTakenMagicDamage:0.##}"); + if (stats.TotalTrueDamage > 0 || stats.TotalTakenTrueDamage > 0) builder.AppendLine($"总计真实伤害:{stats.TotalTrueDamage:0.##} / 总承受真实伤害:{stats.TotalTakenTrueDamage:0.##}"); + builder.AppendLine($"每秒伤害:{stats.DamagePerSecond:0.##} / 每回合伤害:{stats.DamagePerTurn:0.##}"); + index++; + } + msgs.Add(builder.ToString().Trim()); + } + + // 存档 + foreach (User user in pcs.Keys) + { + FunGameService.SetUserConfigButNotRelease(user.Id, pcs[user], user); + } + } + catch (Exception e) + { + msgs.Add($"发生错误:{e.Message}"); + } + finally + { + foreach (long id in userIds) + { + FunGameService.ReleaseUserSemaphoreSlim(id); + } + } + } + } +} diff --git a/OshimaServers/Model/HorseRacing.cs b/OshimaServers/Model/HorseRacing.cs index 5e8c30f..b813772 100644 --- a/OshimaServers/Model/HorseRacing.cs +++ b/OshimaServers/Model/HorseRacing.cs @@ -4,7 +4,7 @@ using Oshima.FunGame.OshimaModules.Models; namespace Oshima.FunGame.OshimaServers.Model { - public static class HorseRacing + public class HorseRacing { private const int MaxTurns = 100; private static readonly Random _random = new(); @@ -26,10 +26,10 @@ namespace Oshima.FunGame.OshimaServers.Model Horse horse = new(user); AssignRandomSkills(horse); horses.Add(horse); - builder.AppendLine($"[ {horse}({horse.HP}) ] 已准备就绪!初始步数: {horse.Step}, 生命值: {horse.HP}, 每回合恢复生命值: {horse.HPRecovery}"); + builder.AppendLine($"[ {horse} ] 已准备就绪!初始步数: {horse.Step}, 生命值: {horse.HP}, 每回合恢复生命值: {horse.HPRecovery}"); if (horse.Skills.Count != 0) { - builder.AppendLine($"[ {horse}({horse.HP}) ] 拥有技能: {string.Join(",", horse.Skills.Select(s => $"{s.Name}(持续 {s.Duration} 回合)"))}"); + builder.AppendLine($"[ {horse} ] 拥有技能: {string.Join(",", horse.Skills.Select(s => $"{s.Name}(持续 {s.Duration} 回合)"))}"); } } @@ -49,6 +49,7 @@ namespace Oshima.FunGame.OshimaServers.Model foreach (Horse horse in horses) { turnSkills.TryAdd(horse, []); + if (horse.HP == 0) continue; // 触发永久技能 foreach (HorseSkill skill in horse.Skills) { @@ -101,7 +102,8 @@ namespace Oshima.FunGame.OshimaServers.Model turnSteps[horse] += skill.ChangePosition; Horse? source = skill.Horse; - if (source != null && source != horse) turnEvents.Add($"💥 受到了 [ {skill.Name}(来自:{source})] 的影响,{skill}"); + if (source != null && source != horse) turnEvents.Add($"💥 受到了 [ {skill.Name}(来自:{source})] 的影响,{skill}(剩余 {activeEffect.RemainDuration} 回合)"); + else turnEvents.Add($"💥 受到了 [ {skill.Name} ] 的影响,{skill}(剩余 {activeEffect.RemainDuration} 回合)"); activeEffect.RemainDuration--; if (activeEffect.RemainDuration <= 0) @@ -116,7 +118,7 @@ namespace Oshima.FunGame.OshimaServers.Model } // 随机事件 - if (_random.NextDouble() < 0.5) + if (_random.NextDouble() < 0.3) { HorseSkill eventSkill = GenerateRandomEventSkill(); // 随机事件技能也可能持续多回合 @@ -140,11 +142,16 @@ namespace Oshima.FunGame.OshimaServers.Model horse.HP += effectiveHPRecovery; if (hp != horse.HP) { - builder.AppendLine($"[ {horse}({horse.HP}) ] ❤️ 生命值恢复至 {horse.HP} 点(+{effectiveHPRecovery})。"); + turnEvents.Add($"❤️ 生命值恢复至 {horse.HP} 点(+{effectiveHPRecovery})。"); } if (horse.HP <= 0) { + turnSteps[horse] = 0; + if (turnEvents.Count != 0) + { + builder.AppendLine($"[ {horse} ] {string.Join(";", turnEvents)}"); + } continue; } @@ -153,15 +160,15 @@ namespace Oshima.FunGame.OshimaServers.Model horse.CurrentPosition += effectiveStep; // 移动 turnSteps[horse] += effectiveStep; - //if (effectiveStep > 1) builder.AppendLine($"[ {horse}({horse.HP}) ] 移动了 {effectiveStep} 步!"); + //if (effectiveStep > 1) builder.AppendLine($"[ {horse} ] 移动了 {effectiveStep} 步!"); if (turnEvents.Count != 0) { - builder.AppendLine($"[ {horse}({horse.HP}) ] {string.Join(";", turnEvents)}"); + builder.AppendLine($"[ {horse} ] {string.Join(";", turnEvents)}"); } if (horse.CurrentPosition >= maxLength) { - builder.AppendLine($"\r\n🎯 恭喜 [ {horse}({horse.HP}) ] 冲过终点线!它赢得了比赛!\r\n"); + builder.AppendLine($"\r\n🎯 恭喜 [ {horse} ] 冲过终点线!它赢得了比赛!\r\n"); raceFinished = true; break; } @@ -185,13 +192,36 @@ namespace Oshima.FunGame.OshimaServers.Model builder.Clear(); builder.AppendLine("☆--- 比赛结果 ---☆"); List finalRanking = [.. horses.OrderByDescending(h => h.CurrentPosition)]; - int points = 10; + int rank = 1; + int horsesAtCurrentRankCount = 0; // 当前名次有多少匹马 + int lastPosition = -1; // 上一匹马的位置 for (int i = 0; i < finalRanking.Count; i++) { - userPoints[finalRanking[i].Id] = points; - builder.AppendLine($"第 {i + 1} 名:{finalRanking[i].Name}(获得 {points} 点赛马积分)"); - points = (int)(points * 0.8); - if (points == 0) points = 1; + Horse currentHorse = finalRanking[i]; + + if (i == 0) + { + rank = 1; + lastPosition = currentHorse.CurrentPosition; + horsesAtCurrentRankCount = 1; + } + else if (currentHorse.CurrentPosition == lastPosition) // 与前一匹马平局 + { + horsesAtCurrentRankCount++; + } + else + { + // 新的名次是当前名次加上之前平局的马匹数量 + rank += horsesAtCurrentRankCount; + lastPosition = currentHorse.CurrentPosition; + horsesAtCurrentRankCount = 1; + } + + // 根据实际名次计算积分 + int points = CalculatePointsForRank(rank); + + userPoints[currentHorse.Id] = points; + builder.AppendLine($"{(horsesAtCurrentRankCount > 1 ? "并列" : "")}第 {rank} 名:{currentHorse.Name}(获得 {points} 点赛马积分)"); } builder.AppendLine("\r\n比赛结束,奖励将在稍后发放!"); @@ -200,6 +230,20 @@ namespace Oshima.FunGame.OshimaServers.Model return userPoints; } + private static int CalculatePointsForRank(int rank) + { + if (rank <= 0) return 0; + if (rank == 1) return 10; + + int points = 10; + for (int r = 2; r <= rank; r++) + { + points = (int)(points * 0.8); + if (points == 0) points = 1; + } + return points; + } + private static void AssignRandomSkills(Horse horse) { // 技能池 @@ -277,7 +321,7 @@ namespace Oshima.FunGame.OshimaServers.Model int dashesAfterHorse = Math.Max(0, maxLength - horse.CurrentPosition); builder.Append(new string('=', dashesAfterHorse)); - string horseMarker = $"<{horse}>"; + string horseMarker = $"<{horse}({horse.HP})>"; if (horse.ActiveEffects.Count > 0 || horse.HP == 0) { if (horse.HP == 0) @@ -293,7 +337,7 @@ namespace Oshima.FunGame.OshimaServers.Model builder.Append(horseMarker); builder.Append(new string('=', dashesBeforeHorse)); - int turnStep = 1; + int turnStep = 0; if (turnSteps.TryGetValue(horse, out int step)) { turnStep = step; diff --git a/OshimaServers/Service/FunGameOrderList.cs b/OshimaServers/Service/FunGameOrderList.cs index 1de5fe7..66406e4 100644 --- a/OshimaServers/Service/FunGameOrderList.cs +++ b/OshimaServers/Service/FunGameOrderList.cs @@ -75,6 +75,10 @@ {"练级信息", "查看练级进度"}, {"生命之泉", "使用金币回复角色状态"}, {"酒馆", "使用钻石使角色获得满能量"}, + {"排行榜/养成排行榜", "查看全服角色养成排行榜"}, + {"金币排行榜", "查看全服金币数量排行榜"}, + {"钻石排行榜", "查看全服钻石数量排行榜"}, + {"赛马排行榜", "查看全服赛马积分排行榜"}, }; public static Dictionary PlayHelp { get; } = new() { @@ -101,12 +105,12 @@ {"创建房间 <类型>", "类型:mix/team/cooperative/horseracing对应混战/团队/共斗/赛马" }, {"加入房间 <房间号>", "加入多人游戏房间" }, {"退出房间", "退出多人游戏房间" }, + {"创建赛马", "快速创建赛马房间" }, {"开始游戏", "开始多人游戏" }, + {"加入赛马", "快速加入赛马房间" }, + {"创建共斗", "快速创建共斗房间" }, {"创建混战", "快速创建混战房间" }, {"创建团战", "快速创建团队死斗房间" }, - {"创建共斗", "快速创建共斗房间" }, - {"创建赛马", "快速创建赛马房间" }, - {"加入赛马", "快速加入赛马房间" }, }; public static Dictionary ClubHelp { get; } = new() { diff --git a/OshimaServers/Service/FunGameService.cs b/OshimaServers/Service/FunGameService.cs index d2ecb0b..9ad8faa 100644 --- a/OshimaServers/Service/FunGameService.cs +++ b/OshimaServers/Service/FunGameService.cs @@ -1288,7 +1288,7 @@ namespace Oshima.FunGame.OshimaServers.Service for (int i = 0; i < cardBox.Count; i++) { Item newItem = GenerateMagicCard(item.QualityType); - AddItemToUserInventory(user, newItem, false); + AddItemToUserInventory(user, newItem, false, true); cards.Add($"[{ItemSet.GetQualityTypeName(item.QualityType)}|魔法卡] {newItem.Name}\r\n{newItem.ToStringInventory(false)}"); } msg = "打开礼包成功!获得了以下物品:\r\n" + string.Join("\r\n", cards); @@ -1564,7 +1564,7 @@ namespace Oshima.FunGame.OshimaServers.Service } } - private static void EnhanceBoss(Character boss, Item[] weapons, Item[] armors, Item[] shoes, Item[] accessory, Item[] consumables, + public static void EnhanceBoss(Character boss, Item[] weapons, Item[] armors, Item[] shoes, Item[] accessory, Item[] consumables, int cLevel, int sLevel, int mLevel, int naLevel, bool enhanceHPMP = true, bool enhanceCRCRD = true, bool isUnit = false) { boss.Level = cLevel; @@ -4570,9 +4570,19 @@ namespace Oshima.FunGame.OshimaServers.Service { FunGameConstant.UserLastVisitStore.Remove(user.Id); } + // 排行榜更新 + FunGameConstant.UserCreditsRanking[user.Id] = user.Inventory.Credits; + FunGameConstant.UserMaterialsRanking[user.Id] = user.Inventory.Materials; + FunGameConstant.UserEXPRanking[user.Id] = user.Inventory.Characters.Select(c => FunGameConstant.PrecomputeTotalExperience[c.Level] + c.EXP).Sum(); + FunGameConstant.UserSkillRanking[user.Id] = user.Inventory.Characters.Select(c => (c.NormalAttack.Level - 1) * 50000 + c.Skills.Select(s => s.Level * 40000).Sum()).Sum(); + if (pc.TryGetValue("horseRacingPoints", out object? value3) && int.TryParse(value3.ToString(), out int horseRacingPoints)) + { + FunGameConstant.UserHorseRacingRanking[user.Id] = horseRacingPoints; + } } ReleaseUserSemaphoreSlim(fileName); } + FunGameConstant.RankingUpdateTime = DateTime.Now; } } diff --git a/OshimaServers/Service/OnlineService.cs b/OshimaServers/Service/OnlineService.cs index a25af7b..108d817 100644 --- a/OshimaServers/Service/OnlineService.cs +++ b/OshimaServers/Service/OnlineService.cs @@ -10,6 +10,7 @@ namespace Oshima.FunGame.OshimaServers.Service { public static SemaphoreSlim RoomSemaphoreSlim { get; } = new(1, 1); public static SemaphoreSlim HorseRacingSettleSemaphoreSlim { get; } = new(1, 1); + public static SemaphoreSlim CooperativeSettleSemaphoreSlim { get; } = new(1, 1); public static Dictionary GroupsHasHorseRacing { get; } = []; public static void GetRoomSemaphoreSlim() @@ -38,6 +39,19 @@ namespace Oshima.FunGame.OshimaServers.Service } } + public static void GetCooperativeSettleSemaphoreSlim() + { + CooperativeSettleSemaphoreSlim.Wait(FunGameConstant.SemaphoreSlimTimeout); + } + + public static void ReleaseCooperativeSettleSemaphoreSlim() + { + if (CooperativeSettleSemaphoreSlim.CurrentCount == 0) + { + CooperativeSettleSemaphoreSlim.Release(); + } + } + public static Room CreateRoom(User user, string roomType, string password, string groupId, out string msg) { try @@ -57,12 +71,13 @@ namespace Oshima.FunGame.OshimaServers.Service case "horseracing": if (GroupsHasHorseRacing.TryGetValue(groupId, out bool has) && has) { - msg = "本群已经存在一个赛马房间!空闲房间会在 6 分钟后自动解散,请先等待该房间完成比赛或自动解散。"; + msg = $"本群已经存在一个赛马房间!空闲房间会在 {FunGameConstant.RoomExpireTime} 分钟后自动解散,请先等待该房间完成比赛或自动解散。"; } else { room.RoomType = RoomType.Custom; room.Name = "赛马房间"; + room.GameModule = "horseracing"; room.GameMap = groupId; room.MaxUsers = 8; } @@ -70,16 +85,22 @@ namespace Oshima.FunGame.OshimaServers.Service case "mix": room.RoomType = RoomType.Mix; room.Name = "混战房间"; + room.GameModule = "mix"; + room.GameMap = groupId; room.MaxUsers = 10; break; case "team": room.RoomType = RoomType.Team; room.Name = "团队死斗房间"; + room.GameModule = "team"; + room.GameMap = groupId; room.MaxUsers = 8; break; case "cooperative": room.RoomType = RoomType.Custom; room.Name = "共斗房间"; + room.GameModule = "cooperative"; + room.GameMap = groupId; room.MaxUsers = 4; break; default: @@ -98,14 +119,14 @@ namespace Oshima.FunGame.OshimaServers.Service } room.Roomid = Verification.CreateVerifyCode(VerifyCodeType.MixVerifyCode, 7); } - msg = $"房间创建成功,房间号为:{room.Roomid}\r\n注意:房间若在 6 分钟后仍处于空闲状态,将自动解散。"; + msg = $"房间创建成功,房间号为:{room.Roomid}\r\n注意:房间若在 {FunGameConstant.RoomExpireTime} 分钟后仍处于空闲状态,将自动解散。"; room.RoomMaster = user; room.CreateTime = DateTime.Now; } if (room.Roomid != "-1") { FunGameConstant.Rooms[room.Roomid] = room; - if (room.Name == "赛马房间") + if (room.GameModule == "horseracing") { GroupsHasHorseRacing[room.GameMap] = true; } @@ -203,7 +224,7 @@ namespace Oshima.FunGame.OshimaServers.Service { FunGameConstant.Rooms.Remove(room.Roomid); msg += ",该房间人数为零,已解散该房间。"; - if (room.Name == "赛马房间") + if (room.GameModule == "horseracing") { GroupsHasHorseRacing[room.GameMap] = false; } @@ -238,6 +259,34 @@ namespace Oshima.FunGame.OshimaServers.Service ReleaseRoomSemaphoreSlim(); } } + + public static string RoomInfo(User user) + { + string msg = ""; + if (FunGameConstant.UsersInRoom.TryGetValue(user.Id, out Room? room) && room != null) + { + string username = ""; + if (FunGameConstant.UserIdAndUsername.TryGetValue(room.RoomMaster.Id, out User? value) && value != null) + { + username = value.Username; + } + List users = []; + foreach (long uid in room.UserAndIsReady.Keys.Select(u => u.Id)) + { + if (FunGameConstant.UserIdAndUsername.TryGetValue(uid, out value) && value != null) + { + users.Add(value.Username); + } + } + msg += $"☆--- [ {room.Roomid} ] ---☆\r\n房间类型:{room.Name}\r\n创建时间:{room.CreateTime.ToString(General.GeneralDateTimeFormatChinese)}\r\n房主:{username}\r\n" + + $"人数:{room.UserAndIsReady.Count} / {room.MaxUsers}\r\n在线玩家:{string.Join("、", users)}\r\n该房间将于 {room.CreateTime.AddMinutes(FunGameConstant.RoomExpireTime).ToString(General.GeneralDateTimeFormatChinese)} 后自动解散,请尽快开局"; + } + else + { + msg = "你当前不在任何房间中。"; + } + return msg; + } public static void RoomsAutoDisband() { @@ -247,14 +296,14 @@ namespace Oshima.FunGame.OshimaServers.Service Room[] rooms = [.. FunGameConstant.Rooms.Values]; foreach (Room room in rooms) { - if (room.RoomState == RoomState.Created && room.CreateTime.AddMinutes(6) < DateTime.Now) + if (room.RoomState == RoomState.Created && room.CreateTime.AddMinutes(FunGameConstant.RoomExpireTime) < DateTime.Now) { foreach (User user in room.UserAndIsReady.Keys) { FunGameConstant.UsersInRoom.Remove(user.Id); } FunGameConstant.Rooms.Remove(room.Roomid); - if (room.Name == "赛马房间") + if (room.GameModule == "horseracing") { GroupsHasHorseRacing[room.GameMap] = false; } @@ -308,7 +357,7 @@ namespace Oshima.FunGame.OshimaServers.Service FunGameConstant.UsersInRoom.Remove(userTemp.Id); } FunGameConstant.Rooms.Remove(room.Roomid); - if (room.Name == "赛马房间") + if (room.GameModule == "horseracing") { GroupsHasHorseRacing[room.GameMap] = false; } @@ -320,9 +369,9 @@ namespace Oshima.FunGame.OshimaServers.Service else { room.RoomState = RoomState.Gaming; - switch (room.Name) + switch (room.GameModule) { - case "赛马房间": + case "horseracing": try { GetHorseRacingSettleSemaphoreSlim(); @@ -354,6 +403,22 @@ namespace Oshima.FunGame.OshimaServers.Service ReleaseHorseRacingSettleSemaphoreSlim(); } break; + case "cooperative": + try + { + GetCooperativeSettleSemaphoreSlim(); + await Cooperative.RunCooperativeGame(msgs, room); + } + catch (Exception e2) + { + msgs.Add("Error: " + e2.Message); + } + finally + { + ReleaseCooperativeSettleSemaphoreSlim(); + } + room.CreateTime = DateTime.Now; + break; default: msgs.Add("游戏已开始!"); await Task.Delay(5000); diff --git a/OshimaWebAPI/Controllers/FunGameController.cs b/OshimaWebAPI/Controllers/FunGameController.cs index 1441e3b..1f513d4 100644 --- a/OshimaWebAPI/Controllers/FunGameController.cs +++ b/OshimaWebAPI/Controllers/FunGameController.cs @@ -4471,7 +4471,7 @@ namespace Oshima.FunGame.WebAPI.Controllers pc.Add("days", days + 1); pc.Add("lastTime", newLastTime); FunGameService.SetUserConfigAndReleaseSemaphoreSlim(userid, pc, user); - return msg + "\r\n>>> 请发送【帮助】来获取更多玩法指令!<<<"; + return msg + "\r\n提示:发送【帮助】来获取更多玩法指令!"; } else { @@ -8108,6 +8108,43 @@ namespace Oshima.FunGame.WebAPI.Controllers } } + [HttpPost("roominfo")] + public string RoomInfo([FromQuery] long uid = -1) + { + try + { + PluginConfig pc = FunGameService.GetUserConfig(uid, out bool isTimeout); + if (isTimeout) + { + return busy; + } + + string msg = ""; + if (pc.Count > 0) + { + User user = FunGameService.GetUser(pc); + + msg = OnlineService.RoomInfo(user); + + FunGameService.SetUserConfigButNotRelease(uid, pc, user); + return msg; + } + else + { + return noSaved; + } + } + catch (Exception e) + { + Logger.LogError(e, "Error: {e}", e); + return busy; + } + finally + { + FunGameService.ReleaseUserSemaphoreSlim(uid); + } + } + [HttpPost("rungame")] public async Task<(Room, List)> RunGame([FromQuery] long uid = -1) { @@ -8124,10 +8161,10 @@ namespace Oshima.FunGame.WebAPI.Controllers if (pc.Count > 0) { User user = FunGameService.GetUser(pc); + FunGameService.SetUserConfigButNotRelease(uid, pc, user); (room, msgs) = await OnlineService.RunGameAsync(user); - FunGameService.SetUserConfigButNotRelease(uid, pc, user); return (room, msgs); } else @@ -8146,6 +8183,116 @@ namespace Oshima.FunGame.WebAPI.Controllers } } + [HttpGet("getranking")] + public string GetRanking([FromQuery] long uid = -1, [FromQuery] int type = -1) + { + try + { + PluginConfig pc = FunGameService.GetUserConfig(uid, out bool isTimeout); + if (isTimeout) + { + return busy; + } + + string msg = ""; + int currentTop = -1; + int showTop = 20; + if (pc.Count > 0) + { + User user = FunGameService.GetUser(pc); + + msg = type switch + { + 0 => $"【{General.GameplayEquilibriumConstant.InGameCurrency}排行榜】\r\n" + + $"数据每分钟更新一次,上次更新:{FunGameConstant.RankingUpdateTime.ToString(General.GeneralDateTimeFormatChinese)}\r\n" + + $"\r\n{(FunGameConstant.UserCreditsRanking.Count > 0 ? string.Join("\r\n", FunGameConstant.UserCreditsRanking. + OrderByDescending(kv => kv.Value).Take(showTop).Select((kv, index) => + { + string username = "Unknown"; + if (FunGameConstant.UserIdAndUsername.TryGetValue(kv.Key, out User? value) && value != null) + { + username = value.Username; + } + if (kv.Key == user.Id) + { + currentTop = index + 1; + } + return $"{index + 1}. UID:{kv.Key},昵称:{username},{General.GameplayEquilibriumConstant.InGameCurrency}:{kv.Value:0.##}"; + })) : "暂无任何数据。")}\r\n\r\n仅显示前 {showTop} 位{(currentTop > 0 ? $",你目前排在第 {currentTop} 位。" : "")}", + 1 => $"【{General.GameplayEquilibriumConstant.InGameMaterial}排行榜】\r\n" + + $"数据每分钟更新一次,上次更新:{FunGameConstant.RankingUpdateTime.ToString(General.GeneralDateTimeFormatChinese)}\r\n" + + $"\r\n{(FunGameConstant.UserMaterialsRanking.Count > 0 ? string.Join("\r\n", FunGameConstant.UserMaterialsRanking. + OrderByDescending(kv => kv.Value).Take(showTop).Select((kv, index) => + { + string username = "Unknown"; + if (FunGameConstant.UserIdAndUsername.TryGetValue(kv.Key, out User? value) && value != null) + { + username = value.Username; + } + if (kv.Key == user.Id) + { + currentTop = index + 1; + } + return $"{index + 1}. UID:{kv.Key},昵称:{username},{General.GameplayEquilibriumConstant.InGameMaterial}:{kv.Value:0.##}"; + })) : "暂无任何数据。")}\r\n\r\n仅显示前 {showTop} 位{(currentTop > 0 ? $",你目前排在第 {currentTop} 位。" : "")}", + 2 => $"【角色养成排行榜】\r\n" + + $"数据每分钟更新一次,上次更新:{FunGameConstant.RankingUpdateTime.ToString(General.GeneralDateTimeFormatChinese)}\r\n" + + $"\r\n{(FunGameConstant.UserEXPRanking.Count > 0 ? string.Join("\r\n", FunGameConstant.UserEXPRanking. + OrderByDescending(kv => kv.Value).Take(showTop).Select((kv, index) => + { + string username = "Unknown"; + if (FunGameConstant.UserIdAndUsername.TryGetValue(kv.Key, out User? value) && value != null) + { + username = value.Username; + } + if (kv.Key == user.Id) + { + currentTop = index + 1; + } + double score = kv.Value; + if (FunGameConstant.UserSkillRanking.TryGetValue(kv.Key, out double value2)) + { + score += value2; + } + return $"{index + 1}. UID:{kv.Key},昵称:{username},总得分:{score:0.##}"; + })) : "暂无任何数据。")}\r\n\r\n本榜单统计角色的经验值总额,并根据其普通和技能等级计算总得分。\r\n仅显示前 {showTop} 位{(currentTop > 0 ? $",你目前排在第 {currentTop} 位。" : "")}", + 3 => $"【赛马积分排行榜】\r\n" + + $"数据每分钟更新一次,上次更新:{FunGameConstant.RankingUpdateTime.ToString(General.GeneralDateTimeFormatChinese)}\r\n" + + $"\r\n{(FunGameConstant.UserHorseRacingRanking.Count > 0 ? string.Join("\r\n", FunGameConstant.UserHorseRacingRanking. + OrderByDescending(kv => kv.Value).Take(showTop).Select((kv, index) => + { + string username = "Unknown"; + if (FunGameConstant.UserIdAndUsername.TryGetValue(kv.Key, out User? value) && value != null) + { + username = value.Username; + } + if (kv.Key == user.Id) + { + currentTop = index + 1; + } + return $"{index + 1}. UID:{kv.Key},昵称:{username},赛马积分:{kv.Value}"; + })) : "暂无任何数据。")}\r\n\r\n仅显示前 {showTop} 位{(currentTop > 0 ? $",你目前排在第 {currentTop} 位。" : "")}", + _ => "不支持的查询。", + }; + FunGameService.SetUserConfigButNotRelease(uid, pc, user); + return msg; + } + else + { + return noSaved; + } + } + catch (Exception e) + { + Logger.LogError(e, "Error: {e}", e); + return busy; + } + finally + { + FunGameService.ReleaseUserSemaphoreSlim(uid); + } + } + [HttpPost("template")] public string Template([FromQuery] long uid = -1) { diff --git a/OshimaWebAPI/Services/RainBOTService.cs b/OshimaWebAPI/Services/RainBOTService.cs index 0e8a6ae..c32deed 100644 --- a/OshimaWebAPI/Services/RainBOTService.cs +++ b/OshimaWebAPI/Services/RainBOTService.cs @@ -3314,6 +3314,84 @@ namespace Oshima.FunGame.WebAPI.Services return result; } + if (e.Detail == "创建共斗") + { + string groupId = ""; + if (e.IsGroup && e is GroupAtMessage groupAtMessage && groupAtMessage.GroupOpenId != "") + { + groupId = groupAtMessage.GroupOpenId; + } + else if (e.IsGroup && e is ThirdPartyMessage thirdPartyMessage && thirdPartyMessage.GroupOpenId != "") + { + groupId = thirdPartyMessage.GroupOpenId; + } + if (groupId != "") + { + string msg = Controller.CreateRoom(uid, "cooperative", "", groupId); + if (msg.Trim() != "") + { + await SendAsync(e, "房间", msg); + } + } + else + { + await SendAsync(e, "房间", "请在群聊中进行多人游戏。"); + } + return result; + } + + if (e.Detail == "创建混战") + { + string groupId = ""; + if (e.IsGroup && e is GroupAtMessage groupAtMessage && groupAtMessage.GroupOpenId != "") + { + groupId = groupAtMessage.GroupOpenId; + } + else if (e.IsGroup && e is ThirdPartyMessage thirdPartyMessage && thirdPartyMessage.GroupOpenId != "") + { + groupId = thirdPartyMessage.GroupOpenId; + } + if (groupId != "") + { + string msg = Controller.CreateRoom(uid, "mix", "", groupId); + if (msg.Trim() != "") + { + await SendAsync(e, "房间", msg); + } + } + else + { + await SendAsync(e, "房间", "请在群聊中进行多人游戏。"); + } + return result; + } + + if (e.Detail == "创建团战") + { + string groupId = ""; + if (e.IsGroup && e is GroupAtMessage groupAtMessage && groupAtMessage.GroupOpenId != "") + { + groupId = groupAtMessage.GroupOpenId; + } + else if (e.IsGroup && e is ThirdPartyMessage thirdPartyMessage && thirdPartyMessage.GroupOpenId != "") + { + groupId = thirdPartyMessage.GroupOpenId; + } + if (groupId != "") + { + string msg = Controller.CreateRoom(uid, "team", "", groupId); + if (msg.Trim() != "") + { + await SendAsync(e, "房间", msg); + } + } + else + { + await SendAsync(e, "房间", "请在群聊中进行多人游戏。"); + } + return result; + } + if (e.Detail == "加入赛马") { string groupId = ""; @@ -3436,11 +3514,7 @@ namespace Oshima.FunGame.WebAPI.Services } if (groupId != "") { - if (e.Detail == "开始赛马" && FunGameConstant.UsersInRoom.TryGetValue(uid, out Room? value) && value != null && value.Name == "赛马房间") - { - // do nothing - } - else + if (e.Detail == "开始赛马" && (!FunGameConstant.UsersInRoom.TryGetValue(uid, out Room? value) || value is null || value.Name != "赛马房间")) { await SendAsync(e, "房间", "你不在房间中或者所在的房间不是赛马房间,请使用【开始游戏】指令。注意:只有房主才可以开始游戏。"); return result; @@ -3520,6 +3594,56 @@ namespace Oshima.FunGame.WebAPI.Services } return result; } + + if (e.Detail == "房间信息") + { + string msg = Controller.RoomInfo(uid); + if (msg.Trim() != "") + { + await SendAsync(e, "房间", msg); + } + return result; + } + + if (e.Detail == "排行榜" || e.Detail == "养成排行榜") + { + string msg = Controller.GetRanking(uid, 2); + if (msg.Trim() != "") + { + await SendAsync(e, "排行榜", msg); + } + return result; + } + + if (e.Detail == $"{General.GameplayEquilibriumConstant.InGameCurrency}排行榜") + { + string msg = Controller.GetRanking(uid, 0); + if (msg.Trim() != "") + { + await SendAsync(e, "排行榜", msg); + } + return result; + } + + if (e.Detail == $"{General.GameplayEquilibriumConstant.InGameMaterial}排行榜") + { + string msg = Controller.GetRanking(uid, 1); + if (msg.Trim() != "") + { + await SendAsync(e, "排行榜", msg); + } + return result; + } + + if (e.Detail == $"赛马排行榜") + { + string msg = Controller.GetRanking(uid, 3); + if (msg.Trim() != "") + { + await SendAsync(e, "排行榜", msg); + } + return result; + } if (uid == GeneralSettings.Master && e.Detail.StartsWith("重载FunGame", StringComparison.CurrentCultureIgnoreCase)) {