From a23802bdb01d3881afe06560b34d0d530b415cd2 Mon Sep 17 00:00:00 2001 From: milimoe Date: Wed, 30 Jul 2025 23:24:37 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E5=A4=9A=E4=BA=BA=E6=B8=B8?= =?UTF-8?q?=E6=88=8F=E6=88=BF=E9=97=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- OshimaModules/Items/ItemID.cs | 1 + OshimaModules/Items/SpecialItem/钻石.cs | 13 + OshimaModules/Models/FunGameConstant.cs | 2 + OshimaModules/Modules/ItemModule.cs | 1 + OshimaModules/Regions/Players.cs | 46 +++ OshimaServers/AnonymousServer.cs | 2 + OshimaServers/Model/Horse.cs | 132 ++++++ OshimaServers/Model/HorseRacing.cs | 306 ++++++++++++++ OshimaServers/Service/FunGameOrderList.cs | 12 +- OshimaServers/Service/FunGameService.cs | 81 +++- OshimaServers/Service/OnlineService.cs | 381 ++++++++++++++++++ OshimaWebAPI/Controllers/FunGameController.cs | 188 ++++++++- OshimaWebAPI/Models/QQBot.cs | 3 + OshimaWebAPI/OshimaWebAPI.cs | 4 +- OshimaWebAPI/Services/RainBOTService.cs | 318 ++++++++++++++- 15 files changed, 1465 insertions(+), 25 deletions(-) create mode 100644 OshimaModules/Items/SpecialItem/钻石.cs create mode 100644 OshimaServers/Model/Horse.cs create mode 100644 OshimaServers/Model/HorseRacing.cs create mode 100644 OshimaServers/Service/OnlineService.cs diff --git a/OshimaModules/Items/ItemID.cs b/OshimaModules/Items/ItemID.cs index 93aeeda..f7ac9fc 100644 --- a/OshimaModules/Items/ItemID.cs +++ b/OshimaModules/Items/ItemID.cs @@ -47,6 +47,7 @@ 创生之印 = 18012, 法则精粹 = 18013, 大师锻造券 = 18014, + 钻石 = 18998, 探索许可 = 18999 } diff --git a/OshimaModules/Items/SpecialItem/钻石.cs b/OshimaModules/Items/SpecialItem/钻石.cs new file mode 100644 index 0000000..ee50b57 --- /dev/null +++ b/OshimaModules/Items/SpecialItem/钻石.cs @@ -0,0 +1,13 @@ +using Milimoe.FunGame.Core.Entity; +using Milimoe.FunGame.Core.Library.Constant; + +namespace Oshima.FunGame.OshimaModules.Items +{ + public class 钻石() : Item(ItemType.SpecialItem) + { + public override long Id => (long)SpecialItemID.钻石; + public override string Name => "钻石"; + public override string Description => "钻石是筽祀牻大陆通用的稀有矿物,是第二流通货币。钻石可以以稳定的 1:200 兑换率兑换大陆第一流通货币金币。"; + public override QualityType QualityType => QualityType.White; + } +} diff --git a/OshimaModules/Models/FunGameConstant.cs b/OshimaModules/Models/FunGameConstant.cs index db888fa..189f4d1 100644 --- a/OshimaModules/Models/FunGameConstant.cs +++ b/OshimaModules/Models/FunGameConstant.cs @@ -36,6 +36,8 @@ namespace Oshima.FunGame.OshimaModules.Models public static Dictionary UserLastVisitStore { get; } = []; public static ConcurrentDictionary UserSemaphoreSlims { get; } = []; public static SemaphoreSlim MarketSemaphoreSlim { get; } = new(1, 1); + public static Dictionary Rooms { get; set; } = []; + 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 char[] SplitChars => [',', ' ', ',', ';', ';']; diff --git a/OshimaModules/Modules/ItemModule.cs b/OshimaModules/Modules/ItemModule.cs index 01020eb..73bbe70 100644 --- a/OshimaModules/Modules/ItemModule.cs +++ b/OshimaModules/Modules/ItemModule.cs @@ -56,6 +56,7 @@ namespace Oshima.FunGame.OshimaModules (long)SpecialItemID.创生之印 => new 创生之印(), (long)SpecialItemID.法则精粹 => new 法则精粹(), (long)SpecialItemID.大师锻造券 => new 大师锻造券(), + (long)SpecialItemID.钻石 => new 钻石(), (long)SpecialItemID.探索许可 => new 探索许可(), (long)ConsumableID.小回复药 => new 小回复药(), (long)ConsumableID.中回复药 => new 中回复药(), diff --git a/OshimaModules/Regions/Players.cs b/OshimaModules/Regions/Players.cs index 277d8e4..dabcc8d 100644 --- a/OshimaModules/Regions/Players.cs +++ b/OshimaModules/Regions/Players.cs @@ -36,6 +36,10 @@ namespace Oshima.FunGame.OshimaModules.Regions { template = CreateNewForgeStore(); } + else if (storeName == "dokyo_horseracing") + { + template = CreateNewHorseRacingStore(); + } else return null; } @@ -109,6 +113,15 @@ namespace Oshima.FunGame.OshimaModules.Regions } return $"现有锻造积分:{forgePoints:0.##}"; } + else if (storeName == "dokyo_horseracing") + { + double horseRacingPoints = 0; + if (pc.TryGetValue("horseRacingPoints", out object? value) && double.TryParse(value.ToString(), out double points)) + { + horseRacingPoints = points; + } + return $"现有赛马积分:{horseRacingPoints:0.##}"; + } return ""; } @@ -124,6 +137,7 @@ namespace Oshima.FunGame.OshimaModules.Regions { EntityModuleConfig storeTemplate = new("stores", "dokyo"); storeTemplate.LoadConfig(); + Store? store = storeTemplate.Get("dokyo_forge"); if (store is null) { @@ -136,6 +150,20 @@ namespace Oshima.FunGame.OshimaModules.Regions store.CopyGoodsToNextRefreshGoods(newStore.Goods); } storeTemplate.Add("dokyo_forge", store); + + store = storeTemplate.Get("dokyo_horseracing"); + if (store is null) + { + store = CreateNewHorseRacingStore(); + } + else + { + Store newStore = CreateNewHorseRacingStore(); + store.NextRefreshGoods.Clear(); + store.CopyGoodsToNextRefreshGoods(newStore.Goods); + } + storeTemplate.Add("dokyo_horseracing", store); + storeTemplate.SaveConfig(); } @@ -143,6 +171,7 @@ namespace Oshima.FunGame.OshimaModules.Regions { Store store = new("锻造积分商店") { + GetNewerGoodsOnVisiting = true, AutoRefresh = true, RefreshInterval = 3, NextRefreshDate = DateTime.Today.AddHours(4), @@ -161,5 +190,22 @@ namespace Oshima.FunGame.OshimaModules.Regions } return store; } + + private static Store CreateNewHorseRacingStore() + { + Store store = new("赛马积分商店") + { + GetNewerGoodsOnVisiting = true, + AutoRefresh = true, + RefreshInterval = 1, + NextRefreshDate = DateTime.Today.AddHours(4), + GlobalStock = true, + }; + Item item = new 钻石(); + store.AddItem(item, -1); + store.SetPrice(1, "赛马积分", 5); + store.Goods[1].Quota = 300; + return store; + } } } diff --git a/OshimaServers/AnonymousServer.cs b/OshimaServers/AnonymousServer.cs index a54cf54..ab3d9d6 100644 --- a/OshimaServers/AnonymousServer.cs +++ b/OshimaServers/AnonymousServer.cs @@ -136,6 +136,8 @@ namespace Oshima.FunGame.OshimaServers { FunGameService.RefreshSavedCache(); Controller.WriteLine("读取 FunGame 存档缓存", LogLevel.Debug); + OnlineService.RoomsAutoDisband(); + Controller.WriteLine("清除空闲房间", LogLevel.Debug); }, true); TaskScheduler.Shared.AddTask("刷新每日任务", new TimeSpan(4, 0, 0), () => { diff --git a/OshimaServers/Model/Horse.cs b/OshimaServers/Model/Horse.cs new file mode 100644 index 0000000..ee927f0 --- /dev/null +++ b/OshimaServers/Model/Horse.cs @@ -0,0 +1,132 @@ +using System.Text; +using Milimoe.FunGame.Core.Entity; + +namespace Oshima.FunGame.OshimaServers.Model +{ + public class Horse(User user) + { + public long Id => user.Id; + public string Name => user.Username; + + // 基础属性,技能会在此基础上进行增减 + private int _step = 1; + private int _hr = 1; + private int _hp = 3; + + public int MaxHP { get; set; } = 3; + + /// + /// 每回合行动的步数 + /// + public int Step + { + get + { + return _step; + } + set + { + _step = Math.Max(0, value); + } + } + + /// + /// 当前生命值 + /// + public int HP + { + get + { + return _hp; + } + set + { + _hp = Math.Min(MaxHP, Math.Max(0, value)); + } + } + + /// + /// 每回合恢复的HP值 + /// + public int HPRecovery + { + get + { + return _hr; + } + set + { + _hr = Math.Max(0, value); + } + } + + /// + /// 马匹当前在赛道上的位置 + /// + public int CurrentPosition { get; set; } = 0; + + /// + /// 马匹拥有的永久技能 + /// + public HashSet Skills { get; set; } = []; + + /// + /// 马匹当前正在生效的技能效果列表 + /// + public List ActiveEffects { get; set; } = []; + + public override string ToString() + { + return Name; + } + } + + /// + /// 技能定义 + /// + public class HorseSkill(Horse? horse = null) + { + public Horse? Horse { get; set; } = horse; + public string Name { get; set; } = ""; + public bool ToEnemy { get; set; } = false; + public int AddStep { get; set; } = 0; + public int ReduceStep { get; set; } = 0; + public int AddHP { get; set; } = 0; + public int ReduceHP { get; set; } = 0; + public int AddHR { get; set; } = 0; + public int ReduceHR { get; set; } = 0; + public int ChangePosition { get; set; } = 0; + /// + /// 技能发动概率,1表示每回合都发动 + /// + public double CastProbability { get; set; } = 1; + /// + /// 技能持续回合数,默认1回合(即立即生效并结束) + /// + public int Duration { get; set; } = 1; + + public override string ToString() + { + StringBuilder builder = new(); + + if (AddStep > 0) builder.Append($"每回合将额外移动 {AddStep} 步!"); + if (ReduceStep > 0) builder.Append($"每回合将少移动 {ReduceStep} 步!"); + if (AddHP > 0) builder.AppendLine($"恢复了 {AddHP} 点生命值!"); + if (ReduceHP > 0) builder.Append($"受到了 {ReduceHP} 点伤害!"); + if (AddHR > 0) builder.Append($"每回合将额外恢复 {AddHR} 点生命值!"); + if (ReduceHR > 0) builder.Append($"每回合将少恢复 {ReduceHR} 点生命值!"); + if (ChangePosition != 0) builder.Append($"{(ChangePosition > 0 ? "前进" : "后退")}了 {Math.Abs(ChangePosition)} 步!"); + + return builder.ToString().Trim(); + } + } + + /// + /// 用于追踪马匹身上正在生效的技能效果 + /// + public class ActiveSkillEffect(HorseSkill skill) + { + public HorseSkill Skill { get; } = skill; + public int RemainDuration { get; set; } = skill.Duration; + } +} diff --git a/OshimaServers/Model/HorseRacing.cs b/OshimaServers/Model/HorseRacing.cs new file mode 100644 index 0000000..02b18ca --- /dev/null +++ b/OshimaServers/Model/HorseRacing.cs @@ -0,0 +1,306 @@ +using System.Text; +using Milimoe.FunGame.Core.Entity; +using Oshima.FunGame.OshimaModules.Models; + +namespace Oshima.FunGame.OshimaServers.Model +{ + public static class HorseRacing + { + private const int MaxTurns = 100; + private static readonly Random _random = new(); + + public static Dictionary RunHorseRacing(List msgs, Room room) + { + Dictionary userPoints = []; + + StringBuilder builder = new(); + builder.AppendLine("--- 参赛选手 ---"); + + List horses = []; + foreach (User user in room.UserAndIsReady.Keys) + { + if (FunGameConstant.UserIdAndUsername.TryGetValue(user.Id, out User? temp) && temp != null) + { + user.Username = temp.Username; + } + Horse horse = new(user); + AssignRandomSkills(horse); + horses.Add(horse); + builder.AppendLine($"[ {horse}({horse.HP}) ] 已准备就绪!初始步数: {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("\r\n--- 比赛开始! ---"); + + int maxLength = _random.Next(8, 16); + builder.AppendLine($"本次抽取赛道长度:{maxLength} 步!"); + + for (int turn = 1; turn <= MaxTurns; turn++) + { + builder.AppendLine($"\r\n--- 第 {turn} 回合 ---"); + bool raceFinished = false; + Dictionary turnSteps = []; + Dictionary> turnSkills = []; + + // 先触发技能,后面一起结算,不然技能下个回合才能触发 + foreach (Horse horse in horses) + { + turnSkills.TryAdd(horse, []); + // 触发永久技能 + foreach (HorseSkill skill in horse.Skills) + { + if (_random.NextDouble() < skill.CastProbability) + { + Horse target = horse; + if (skill.ToEnemy) + { + target = horses.OrderBy(o => _random.Next(horses.Count)).First(h => h != horse); + } + turnSkills[horse].Add(skill, target); + target.ActiveEffects.Add(new ActiveSkillEffect(skill)); + } + } + } + + foreach (Horse horse in horses) + { + turnSteps[horse] = 0; + int effectiveStep = horse.Step; // 从基础步数开始计算 + int effectiveHPRecovery = horse.HPRecovery; // 从基础HP恢复开始计算 + List turnEvents = []; // 记录本回合发生的事件 + + // 触发永久技能 + Dictionary skills = turnSkills[horse]; + foreach (HorseSkill skill in skills.Keys) + { + Horse target = skills[skill]; + // 如果是敌人 + if (skill.Duration > 0) + { + turnEvents.Add($"✨ 发动了 [ {skill.Name} ],持续 {skill.Duration} 回合!{(skill.ToEnemy ? $"[ {target} ] " : "")}{skill}"); + } + else + { + turnEvents.Add($"✨ 发动了 [ {skill.Name} ]!{(skill.ToEnemy ? $"[ {target} ] " : "")}{skill}"); + } + } + + // 处理正在生效的持续技能效果 + List expiredEffects = []; + foreach (ActiveSkillEffect activeEffect in horse.ActiveEffects) + { + HorseSkill skill = activeEffect.Skill; + // 应用持续效果 + effectiveStep += skill.AddStep - skill.ReduceStep; + effectiveHPRecovery += skill.AddHR - skill.ReduceHR; + horse.HP += skill.AddHP - skill.ReduceHP; + horse.CurrentPosition += skill.ChangePosition; + turnSteps[horse] += skill.ChangePosition; + + Horse? source = skill.Horse; + if (source != null && source != horse) turnEvents.Add($"💥 受到了 [ {skill.Name}(来自:{source})] 的影响,{skill}"); + + activeEffect.RemainDuration--; + if (activeEffect.RemainDuration <= 0) + { + expiredEffects.Add(activeEffect); + } + } + // 移除已结束的持续效果 + foreach (ActiveSkillEffect expiredEffect in expiredEffects) + { + horse.ActiveEffects.Remove(expiredEffect); + } + + // 随机事件 + if (_random.NextDouble() < 0.5) + { + HorseSkill eventSkill = GenerateRandomEventSkill(); + // 随机事件技能也可能持续多回合 + if (eventSkill.Duration > 0) + { + horse.ActiveEffects.Add(new ActiveSkillEffect(eventSkill)); + turnEvents.Add($"💥 遭遇随机事件 [ {eventSkill.Name} ],持续 {eventSkill.Duration} 回合!{eventSkill}"); + } + else + { + // 如果没有持续时间(Duration=0),则立即应用效果 + effectiveStep += eventSkill.AddStep - eventSkill.ReduceStep; + effectiveHPRecovery += eventSkill.AddHR - eventSkill.ReduceHR; + horse.HP += eventSkill.AddHP - eventSkill.ReduceHP; + turnEvents.Add($"💥 遭遇随机事件 [ {eventSkill.Name} ]!{eventSkill}"); + } + } + + // 恢复HP,基于计算后的有效HP恢复值 + int hp = horse.HP; + horse.HP += effectiveHPRecovery; + if (hp != horse.HP) + { + builder.AppendLine($"[ {horse}({horse.HP}) ] ❤️ 生命值恢复至 {horse.HP} 点(+{effectiveHPRecovery})。"); + } + + if (horse.HP <= 0) + { + continue; + } + + // 确保步数不为负 + effectiveStep = Math.Max(0, effectiveStep); + horse.CurrentPosition += effectiveStep; // 移动 + + turnSteps[horse] += effectiveStep; + //if (effectiveStep > 1) builder.AppendLine($"[ {horse}({horse.HP}) ] 移动了 {effectiveStep} 步!"); + if (turnEvents.Count != 0) + { + builder.AppendLine($"[ {horse}({horse.HP}) ] {string.Join(";", turnEvents)}"); + } + + if (horse.CurrentPosition >= maxLength) + { + builder.AppendLine($"\r\n🎯 恭喜 [ {horse}({horse.HP}) ] 冲过终点线!它赢得了比赛!"); + raceFinished = true; + break; + } + } + + builder.AppendLine("\r\n--- 赛道状况 ---"); + for (int i = 0; i < horses.Count; i++) + { + builder.AppendLine(GenerateTrackString(horses[i], i + 1, maxLength, turnSteps)); + } + + msgs.Add(builder.ToString().Trim()); + builder.Clear(); + + if (raceFinished) + { + break; + } + } + + builder.Clear(); + builder.AppendLine("\r\n--- 比赛结果 ---"); + List finalRanking = [.. horses.OrderByDescending(h => h.CurrentPosition)]; + int points = 10; + 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; + } + + builder.AppendLine("\r\n--- 比赛结束,奖励将在稍后发放! ---"); + msgs.Add(builder.ToString().Trim()); + + return userPoints; + } + + private static void AssignRandomSkills(Horse horse) + { + // 技能池 + List skillPool = [ + new() { Name = "冲刺", AddStep = 2, CastProbability = 0.3, Duration = 1 }, // 普通冲刺,1回合 + new() { Name = "耐力爆发", AddHP = 2, CastProbability = 0.3, Duration = 1 }, // 瞬间回血 + new() { Name = "神行百变", AddStep = 1, AddHR = 1, CastProbability = 0.3, Duration = 3 }, // 持续3回合的加速和恢复 + new() { Name = "稳健", AddStep = 0, AddHR = 1, CastProbability = 0.3, Duration = 2 }, // 持续2回合的额外恢复 + new() { Name = "疾风步", AddStep = 1, CastProbability = 0.15, Duration = 2 }, // 强大的2回合加速 + new() { Name = "疲惫", ToEnemy = true, ReduceStep = 1, CastProbability = 0.4, Duration = 2 }, // 持续2回合的减速 + new() { Name = "摔跤", ToEnemy = true, ReduceHP = 2, ReduceStep = 1, CastProbability = 0.4, Duration = 1 }, // 瞬间掉血减速 + new() { Name = "干扰", ToEnemy = true, ReduceStep = 2, CastProbability = 0.3, Duration = 1 }, // 瞬间减速 + new() { Name = "石头攻击", ToEnemy = true, ReduceHP = 3, CastProbability = 0.4, Duration = 1 }, // 瞬间掉血 + new() { Name = "台风", ToEnemy = true, ReduceHP = 2, CastProbability = 0.4, Duration = 2 }, // 强大的2回合掉血 + new() { Name = "死刑宣告", ToEnemy = true, ReduceHP = 2, ReduceHR = 1, CastProbability = 0.3, Duration = 2 }, // 瞬间掉血+重伤 + new() { Name = "后退吧!", ToEnemy = true, ChangePosition = -2, CastProbability = 0.3, Duration = 1 }, // 直接改变位置 + ]; + + int skillsToAssign = _random.Next(1, 4); + + for (int i = 0; i < skillsToAssign; i++) + { + HorseSkill chosenSkill = skillPool[_random.Next(skillPool.Count)]; + while (true) + { + if (!horse.Skills.Any(s => s.Name == chosenSkill.Name)) + { + break; + } + // 如果技能已存在,重新选择 + chosenSkill = skillPool[_random.Next(skillPool.Count)]; + } + horse.Skills.Add(new HorseSkill + { + Horse = horse, + Name = chosenSkill.Name, + ToEnemy = chosenSkill.ToEnemy, + AddStep = chosenSkill.AddStep, + ReduceStep = chosenSkill.ReduceStep, + AddHP = chosenSkill.AddHP, + ReduceHP = chosenSkill.ReduceHP, + AddHR = chosenSkill.AddHR, + ReduceHR = chosenSkill.ReduceHR, + ChangePosition = chosenSkill.ChangePosition, + CastProbability = chosenSkill.CastProbability, + Duration = chosenSkill.Duration + }); + } + } + + private static HorseSkill GenerateRandomEventSkill() + { + // 随机事件 + List eventPool = [ + new() { Name = "加速带", AddStep = 3, Duration = 1 }, // 瞬间加速 + new() { Name = "泥泞区", ReduceStep = 2, Duration = 2 }, // 持续2回合减速 + new() { Name = "观众欢呼", AddHP = 2, Duration = 1 }, // 瞬间回血 + new() { Name = "小石子", ReduceHP = 1, Duration = 1 }, // 瞬间掉血 + new() { Name = "顺风", AddStep = 1, Duration = 3 }, // 持续3回合微加速 + new() { Name = "逆风", ReduceStep = 1, Duration = 2 }, // 持续2回合微减速 + new() { Name = "兴奋剂", AddStep = 2, Duration = 4, ReduceHP = 2 } // 兴奋剂,有副作用 + ]; + + return eventPool[_random.Next(eventPool.Count)]; + } + + private static string GenerateTrackString(Horse horse, int trackNumber, int maxLength, Dictionary turnSteps) + { + StringBuilder builder = new(); + + builder.Append($"[{trackNumber}]|"); + + if (horse.CurrentPosition < 0) horse.CurrentPosition = 0; + int dashesBeforeHorse = Math.Min(horse.CurrentPosition, maxLength); + int dashesAfterHorse = Math.Max(0, maxLength - horse.CurrentPosition); + builder.Append(new string('=', dashesAfterHorse)); + + string horseMarker = $"<{horse}>"; + if (horse.ActiveEffects.Count > 0 || horse.HP == 0) + { + if (horse.HP == 0) + { + horseMarker = $"[💀死亡]<{horse}>"; + } + if (horse.ActiveEffects.Count > 0) + { + horseMarker += $"[{string.Join("][", horse.ActiveEffects.Select(e => e.Skill.Name))}]"; + } + } + + builder.Append(horseMarker); + builder.Append(new string('=', dashesBeforeHorse)); + + int turnStep = 1; + if (turnSteps.TryGetValue(horse, out int step)) + { + turnStep = step; + } + builder.Append($"|({horse.CurrentPosition})({(turnStep >= 0 ? "+" : "")}{turnStep})"); + + return builder.ToString(); + } + } +} diff --git a/OshimaServers/Service/FunGameOrderList.cs b/OshimaServers/Service/FunGameOrderList.cs index 03beb95..1de5fe7 100644 --- a/OshimaServers/Service/FunGameOrderList.cs +++ b/OshimaServers/Service/FunGameOrderList.cs @@ -98,6 +98,15 @@ {"取消锻造", "取消已经创建的锻造配方" }, {"模拟锻造", "模拟锻造结果" }, {"确认开始锻造", "确认已经创建的锻造配方并开始锻造" }, + {"创建房间 <类型>", "类型:mix/team/cooperative/horseracing对应混战/团队/共斗/赛马" }, + {"加入房间 <房间号>", "加入多人游戏房间" }, + {"退出房间", "退出多人游戏房间" }, + {"开始游戏", "开始多人游戏" }, + {"创建混战", "快速创建混战房间" }, + {"创建团战", "快速创建团队死斗房间" }, + {"创建共斗", "快速创建共斗房间" }, + {"创建赛马", "快速创建赛马房间" }, + {"加入赛马", "快速加入赛马房间" }, }; public static Dictionary ClubHelp { get; } = new() { @@ -146,7 +155,8 @@ {"商店2", "查看武器商会商品"}, {"商店3", "查看杂货铺商品"}, {"商店4", "查看慈善基金会商品"}, - {"锻造商店", "查看锻造积分商店商品"}, + {"锻造商店/商店5", "查看锻造积分商店商品"}, + {"赛马商店/商店6", "查看锻造积分商店商品"}, {"商店查看 <商品序号>", "查看指定商品详情,访问任意商店后2分钟内可用"}, {"商店购买 <商品序号>", "购买指定商品,访问任意商店后2分钟内可用"}, {"商店出售 <物品序号>", "向商店出售具有回收价的指定物品"}, diff --git a/OshimaServers/Service/FunGameService.cs b/OshimaServers/Service/FunGameService.cs index fc47db5..ef61775 100644 --- a/OshimaServers/Service/FunGameService.cs +++ b/OshimaServers/Service/FunGameService.cs @@ -624,6 +624,12 @@ namespace Oshima.FunGame.OshimaServers.Service } } + // 检查在线状态 + if (FunGameConstant.UsersInRoom.ContainsKey(user.Id)) + { + user.OnlineState = OnlineState.InRoom; + } + return user; } @@ -2161,6 +2167,19 @@ namespace Oshima.FunGame.OshimaServers.Service return $"你的{needy}不足 {reduce} 呢,无法购买【{goods.Name}】!"; } } + else if (needy == "赛马积分") + { + double reduce = Calculation.Round2Digits(goods.Prices[needy] * count); + if (pc.TryGetValue("horseRacingPoints", out object? value) && double.TryParse(value.ToString(), out double points) && points >= reduce) + { + points -= reduce; + pc.Add("horseRacingPoints", points); + } + else + { + return $"你的{needy}不足 {reduce} 呢,无法购买【{goods.Name}】!"; + } + } else { return $"不支持的货币类型:{needy},无法购买【{goods.Name}】!"; @@ -2169,7 +2188,11 @@ namespace Oshima.FunGame.OshimaServers.Service foreach (Item item in goods.Items) { - if (item.Id == (long)SpecialItemID.探索许可) + if (item.Id == (long)SpecialItemID.钻石) + { + user.Inventory.Materials += count; + } + else if (item.Id == (long)SpecialItemID.探索许可) { int exploreTimes = FunGameConstant.MaxExploreTimes + count; if (pc.TryGetValue("exploreTimes", out object? value) && int.TryParse(value.ToString(), out exploreTimes)) @@ -4456,7 +4479,45 @@ namespace Oshima.FunGame.OshimaServers.Service public static void RefreshSavedCache() { - string directoryPath = $@"{AppDomain.CurrentDomain.BaseDirectory}configs/saved"; + string directoryPath; + Dictionary hrPoints = []; + try + { + OnlineService.GetHorseRacingSettleSemaphoreSlim(); + directoryPath = $@"{AppDomain.CurrentDomain.BaseDirectory}configs/horseracing"; + if (Directory.Exists(directoryPath)) + { + string[] filePaths = Directory.GetFiles(directoryPath); + foreach (string filePath in filePaths) + { + string fileName = Path.GetFileNameWithoutExtension(filePath); + PluginConfig pc = new("horseracing", fileName); + pc.LoadConfig(); + if (pc.Get>("points") is Dictionary points) + { + foreach (long userId in points.Keys) + { + if (hrPoints.ContainsKey(userId)) + { + hrPoints[userId] += points[userId]; + } + else + { + hrPoints[userId] = points[userId]; + } + } + } + pc.Remove("points"); + pc.SaveConfig(); + } + } + } + catch { } + finally + { + OnlineService.ReleaseHorseRacingSettleSemaphoreSlim(); + } + directoryPath = $@"{AppDomain.CurrentDomain.BaseDirectory}configs/saved"; if (Directory.Exists(directoryPath)) { string[] filePaths = Directory.GetFiles(directoryPath); @@ -4471,6 +4532,7 @@ namespace Oshima.FunGame.OshimaServers.Service FunGameConstant.UserIdAndUsername[user.Id] = user; bool updateQuest = false; bool updateExplore = false; + bool updateHorseRacing = false; // 任务结算 EntityModuleConfig quests = new("quests", user.Id.ToString()); quests.LoadConfig(); @@ -4487,7 +4549,20 @@ namespace Oshima.FunGame.OshimaServers.Service pc2.SaveConfig(); updateExplore = true; } - if (updateQuest || updateExplore) + // 赛马结算 + if (hrPoints.TryGetValue(user.Id, out int points)) + { + if (pc.TryGetValue("horseRacingPoints", out object? value2) && int.TryParse(value2.ToString(), out int userPoints)) + { + pc.Add("horseRacingPoints", userPoints + points); + } + else + { + pc.Add("horseRacingPoints", points); + } + updateHorseRacing = true; + } + if (updateQuest || updateExplore || updateHorseRacing) { SetUserConfigButNotRelease(user.Id, pc, user); } diff --git a/OshimaServers/Service/OnlineService.cs b/OshimaServers/Service/OnlineService.cs new file mode 100644 index 0000000..a25af7b --- /dev/null +++ b/OshimaServers/Service/OnlineService.cs @@ -0,0 +1,381 @@ +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.OshimaServers.Model; + +namespace Oshima.FunGame.OshimaServers.Service +{ + public class OnlineService + { + public static SemaphoreSlim RoomSemaphoreSlim { get; } = new(1, 1); + public static SemaphoreSlim HorseRacingSettleSemaphoreSlim { get; } = new(1, 1); + public static Dictionary GroupsHasHorseRacing { get; } = []; + + public static void GetRoomSemaphoreSlim() + { + RoomSemaphoreSlim.Wait(FunGameConstant.SemaphoreSlimTimeout); + } + + public static void ReleaseRoomSemaphoreSlim() + { + if (RoomSemaphoreSlim.CurrentCount == 0) + { + RoomSemaphoreSlim.Release(); + } + } + + public static void GetHorseRacingSettleSemaphoreSlim() + { + HorseRacingSettleSemaphoreSlim.Wait(FunGameConstant.SemaphoreSlimTimeout); + } + + public static void ReleaseHorseRacingSettleSemaphoreSlim() + { + if (HorseRacingSettleSemaphoreSlim.CurrentCount == 0) + { + HorseRacingSettleSemaphoreSlim.Release(); + } + } + + public static Room CreateRoom(User user, string roomType, string password, string groupId, out string msg) + { + try + { + GetRoomSemaphoreSlim(); + long id = FunGameConstant.Rooms.Count > 0 ? FunGameConstant.Rooms.Values.Max(r => r.Id) + 1 : 1; + Room room = Factory.GetRoom(id, password: password); + msg = ""; + if (FunGameConstant.UsersInRoom.TryGetValue(user.Id, out Room? room2) && room2 != null) + { + msg = $"你已经在{room2.Name} [ {room2.Roomid} ] 中了,请先离开房间后再创建房间。"; + } + else + { + switch (roomType) + { + case "horseracing": + if (GroupsHasHorseRacing.TryGetValue(groupId, out bool has) && has) + { + msg = "本群已经存在一个赛马房间!空闲房间会在 6 分钟后自动解散,请先等待该房间完成比赛或自动解散。"; + } + else + { + room.RoomType = RoomType.Custom; + room.Name = "赛马房间"; + room.GameMap = groupId; + room.MaxUsers = 8; + } + break; + case "mix": + room.RoomType = RoomType.Mix; + room.Name = "混战房间"; + room.MaxUsers = 10; + break; + case "team": + room.RoomType = RoomType.Team; + room.Name = "团队死斗房间"; + room.MaxUsers = 8; + break; + case "cooperative": + room.RoomType = RoomType.Custom; + room.Name = "共斗房间"; + room.MaxUsers = 4; + break; + default: + msg = "不支持的房间类型。"; + break; + } + } + if (msg == "") + { + room.Roomid = Verification.CreateVerifyCode(VerifyCodeType.MixVerifyCode, 7); + while (true) + { + if (!FunGameConstant.Rooms.ContainsKey(room.Roomid)) + { + break; + } + room.Roomid = Verification.CreateVerifyCode(VerifyCodeType.MixVerifyCode, 7); + } + msg = $"房间创建成功,房间号为:{room.Roomid}\r\n注意:房间若在 6 分钟后仍处于空闲状态,将自动解散。"; + room.RoomMaster = user; + room.CreateTime = DateTime.Now; + } + if (room.Roomid != "-1") + { + FunGameConstant.Rooms[room.Roomid] = room; + if (room.Name == "赛马房间") + { + GroupsHasHorseRacing[room.GameMap] = true; + } + } + return room; + } + catch (Exception e) + { + msg = $"创建房间失败,错误信息:{e.Message}"; + return Factory.GetRoom(); + } + finally + { + ReleaseRoomSemaphoreSlim(); + } + } + + public static bool IntoRoom(User user, string roomid, string password, out string msg) + { + try + { + GetRoomSemaphoreSlim(); + msg = ""; + if (FunGameConstant.Rooms.TryGetValue(roomid, out Room? room) && room != null) + { + if (password == room.Password) + { + if (FunGameConstant.UsersInRoom.TryGetValue(user.Id, out Room? room2) && room2 != null) + { + msg = $"你已经在{room2.Name} [ {room2.Roomid} ] 中了,请先退出房间后再加入房间。"; + return false; + } + if (room.UserAndIsReady.Count >= room.MaxUsers) + { + msg = "房间人数已满,无法加入。"; + return false; + } + if (room.RoomState != RoomState.Created) + { + msg = "房间状态异常,无法加入。"; + return false; + } + FunGameConstant.UsersInRoom[user.Id] = room; + room.UserAndIsReady[user] = true; + user.OnlineState = OnlineState.InRoom; + msg = $"成功加入{room.Name}:{room.Roomid}\r\n房间人数:{room.UserAndIsReady.Count} / {room.MaxUsers}"; + return true; + } + else + { + msg = "密码错误,无法加入房间。"; + return false; + } + } + else + { + msg = "房间不存在,无法加入。"; + return false; + } + } + catch (Exception e) + { + msg = $"加入房间失败,错误信息:{e.Message}"; + return false; + } + finally + { + ReleaseRoomSemaphoreSlim(); + } + } + + public static bool QuitRoom(User user, out string msg) + { + try + { + GetRoomSemaphoreSlim(); + msg = ""; + if (FunGameConstant.UsersInRoom.TryGetValue(user.Id, out Room? room) && room != null) + { + if (room.RoomState != RoomState.Created) + { + msg = "房间状态异常,无法退出。"; + return false; + } + else + { + FunGameConstant.UsersInRoom.Remove(user.Id); + msg = $"成功退出{room.Name}:{room.Roomid}"; + User[] users = [.. room.UserAndIsReady.Keys.Where(u => u.Id == user.Id)]; + foreach (User userTemp in users) + { + room.UserAndIsReady.Remove(userTemp); + } + if (room.UserAndIsReady.Count == 0) + { + FunGameConstant.Rooms.Remove(room.Roomid); + msg += ",该房间人数为零,已解散该房间。"; + if (room.Name == "赛马房间") + { + GroupsHasHorseRacing[room.GameMap] = false; + } + } + else if (room.RoomMaster.Id == user.Id) + { + User newRoomMaster = room.UserAndIsReady.Keys.First(); + room.RoomMaster = newRoomMaster; + string newRoomMasterName = newRoomMaster.Username; + if (FunGameConstant.UserIdAndUsername.TryGetValue(newRoomMaster.Id, out User? temp) && temp != null) + { + newRoomMasterName = temp.Username; + } + msg += $",新房主为:{newRoomMasterName}。"; + } + return true; + } + } + else + { + msg = "你当前不在任何房间中。"; + return false; + } + } + catch (Exception e) + { + msg = $"退出房间失败,错误信息:{e.Message}"; + return false; + } + finally + { + ReleaseRoomSemaphoreSlim(); + } + } + + public static void RoomsAutoDisband() + { + try + { + GetRoomSemaphoreSlim(); + Room[] rooms = [.. FunGameConstant.Rooms.Values]; + foreach (Room room in rooms) + { + if (room.RoomState == RoomState.Created && room.CreateTime.AddMinutes(6) < DateTime.Now) + { + foreach (User user in room.UserAndIsReady.Keys) + { + FunGameConstant.UsersInRoom.Remove(user.Id); + } + FunGameConstant.Rooms.Remove(room.Roomid); + if (room.Name == "赛马房间") + { + GroupsHasHorseRacing[room.GameMap] = false; + } + } + } + } + catch { } + finally + { + ReleaseRoomSemaphoreSlim(); + } + } + + public static void ReSetRoomState(string roomid) + { + try + { + GetRoomSemaphoreSlim(); + if (FunGameConstant.Rooms.TryGetValue(roomid, out Room? room) && room != null) + { + room.CreateTime = DateTime.Now; + room.RoomState = RoomState.Created; + } + } + catch { } + finally + { + ReleaseRoomSemaphoreSlim(); + } + } + + public static async Task<(Room, List)> RunGameAsync(User user) + { + Room room = General.HallInstance; + try + { + GetRoomSemaphoreSlim(); + List msgs = []; + if (FunGameConstant.UsersInRoom.TryGetValue(user.Id, out Room? value) && value != null) + { + room = value; + if (room.RoomMaster.Id != user.Id) + { + msgs.Add("你不是房主,无法开始游戏。"); + } + else if (room.RoomState != RoomState.Created) + { + msgs.Add("房间状态异常,无法开始游戏,已自动解散该房间。"); + foreach (User userTemp in room.UserAndIsReady.Keys) + { + FunGameConstant.UsersInRoom.Remove(userTemp.Id); + } + FunGameConstant.Rooms.Remove(room.Roomid); + if (room.Name == "赛马房间") + { + GroupsHasHorseRacing[room.GameMap] = false; + } + } + else if (room.UserAndIsReady.Count < 2) + { + msgs.Add("房间人数不足,无法开始游戏。"); + } + else + { + room.RoomState = RoomState.Gaming; + switch (room.Name) + { + case "赛马房间": + try + { + GetHorseRacingSettleSemaphoreSlim(); + Dictionary userPoints = HorseRacing.RunHorseRacing(msgs, room); + PluginConfig pc = new("horseracing", room.GameMap); + pc.LoadConfig(); + Dictionary waitforsettle = pc.Get>("points") ?? []; + foreach (long uid in waitforsettle.Keys) + { + int points = waitforsettle[uid]; + if (userPoints.ContainsKey(uid)) + { + userPoints[uid] += points; + } + else + { + userPoints[uid] = points; + } + } + pc.Add("points", userPoints); + pc.SaveConfig(); + } + catch (Exception e2) + { + msgs.Add("Error: " + e2.Message); + } + finally + { + ReleaseHorseRacingSettleSemaphoreSlim(); + } + break; + default: + msgs.Add("游戏已开始!"); + await Task.Delay(5000); + msgs.Add("游戏已结束!"); + break; + } + } + } + else + { + msgs.Add("你当前不在任何房间中。"); + } + return (room, msgs); + } + catch (Exception e) + { + return (room, [$"游戏遇到错误,错误信息:{e.Message}"]); + } + finally + { + ReleaseRoomSemaphoreSlim(); + } + } + } +} diff --git a/OshimaWebAPI/Controllers/FunGameController.cs b/OshimaWebAPI/Controllers/FunGameController.cs index 62d4941..1aa0be7 100644 --- a/OshimaWebAPI/Controllers/FunGameController.cs +++ b/OshimaWebAPI/Controllers/FunGameController.cs @@ -2813,6 +2813,32 @@ namespace Oshima.FunGame.WebAPI.Controllers pc2.Add("exploreTimes", exploreTimes); msg = $"已为 [ {user2} ] 生成 {itemCount} 个探索许可"; } + else if (itemName == "锻造积分") + { + if (pc.TryGetValue("forgepoints", out object? value) && int.TryParse(value.ToString(), out int forgepoints)) + { + forgepoints += itemCount; + } + else + { + forgepoints = itemCount; + } + pc2.Add("forgepoints", forgepoints); + msg = $"已为 [ {user2} ] 生成 {itemCount} 个锻造积分"; + } + else if (itemName == "赛马积分") + { + if (pc.TryGetValue("horseRacingPoints", out object? value) && int.TryParse(value.ToString(), out int horseRacingPoints)) + { + horseRacingPoints += itemCount; + } + else + { + horseRacingPoints = itemCount; + } + pc2.Add("horseRacingPoints", horseRacingPoints); + msg = $"已为 [ {user2} ] 生成 {itemCount} 个锻造积分"; + } else if (itemName.Contains("魔法卡礼包")) { foreach (string type in ItemSet.QualityTypeNameArray) @@ -2926,7 +2952,7 @@ namespace Oshima.FunGame.WebAPI.Controllers FunGameService.SetUserConfigAndReleaseSemaphoreSlim(userid, pc, user); string msg = ""; - if (user.IsAdmin || userid > 0) + if (user.IsAdmin) { PluginConfig pc2 = FunGameService.GetUserConfig(targetid, out _); if (pc2.Count > 0) @@ -6709,7 +6735,7 @@ namespace Oshima.FunGame.WebAPI.Controllers { User user = FunGameService.GetUser(pc); - if (user.IsAdmin || user.IsOperator) + if (user.IsAdmin) { PluginConfig renameExamine = new("examines", "rename"); renameExamine.LoadConfig(); @@ -6793,7 +6819,7 @@ namespace Oshima.FunGame.WebAPI.Controllers FunGameService.ReleaseUserSemaphoreSlim(userid); } - if (user.IsAdmin || user.IsOperator) + if (user.IsAdmin) { PluginConfig renameExamine = new("examines", "rename"); renameExamine.LoadConfig(); @@ -7959,6 +7985,162 @@ namespace Oshima.FunGame.WebAPI.Controllers } } + [HttpPost("createroom")] + public string CreateRoom([FromQuery] long uid = -1, [FromQuery] string roomType = "", [FromQuery] string password = "", [FromQuery] string groupId = "") + { + try + { + PluginConfig pc = FunGameService.GetUserConfig(uid, out bool isTimeout); + if (isTimeout) + { + return busy; + } + + string msg = ""; + if (pc.Count > 0) + { + User user = FunGameService.GetUser(pc); + + Room room = OnlineService.CreateRoom(user, roomType, password, groupId, out msg); + if (room.Roomid != "-1") + { + if (OnlineService.IntoRoom(user, room.Roomid, password, out string msg2)) + { + msg += $"\r\n{msg2}"; + } + } + + 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("intoroom")] + public string IntoRoom([FromQuery] long uid = -1, [FromQuery] string roomid = "", [FromQuery] string password = "") + { + try + { + PluginConfig pc = FunGameService.GetUserConfig(uid, out bool isTimeout); + if (isTimeout) + { + return busy; + } + + string msg = ""; + if (pc.Count > 0) + { + User user = FunGameService.GetUser(pc); + + OnlineService.IntoRoom(user, roomid, password, out msg); + + 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("quitroom")] + public string QuitRoom([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); + + OnlineService.QuitRoom(user, out msg); + + 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) + { + Room room = General.HallInstance; + try + { + PluginConfig pc = FunGameService.GetUserConfig(uid, out bool isTimeout); + if (isTimeout) + { + return (room, [busy]); + } + + List msgs = []; + if (pc.Count > 0) + { + User user = FunGameService.GetUser(pc); + + (room, msgs) = await OnlineService.RunGameAsync(user); + + FunGameService.SetUserConfigButNotRelease(uid, pc, user); + return (room, msgs); + } + else + { + return (room, [noSaved]); + } + } + catch (Exception e) + { + Logger.LogError(e, "Error: {e}", e); + return (room, [busy]); + } + finally + { + FunGameService.ReleaseUserSemaphoreSlim(uid); + } + } + [HttpPost("template")] public string Template([FromQuery] long uid = -1) { diff --git a/OshimaWebAPI/Models/QQBot.cs b/OshimaWebAPI/Models/QQBot.cs index c00abec..916a125 100644 --- a/OshimaWebAPI/Models/QQBot.cs +++ b/OshimaWebAPI/Models/QQBot.cs @@ -94,6 +94,9 @@ namespace Oshima.FunGame.WebAPI.Models [JsonPropertyName("isgroup")] public bool IsGroup { get; set; } = false; + [JsonPropertyName("group_openid")] + public string GroupOpenId { get; set; } = ""; + [JsonPropertyName("detail")] public string Detail { get; set; } = ""; diff --git a/OshimaWebAPI/OshimaWebAPI.cs b/OshimaWebAPI/OshimaWebAPI.cs index ccf1239..afc6ce3 100644 --- a/OshimaWebAPI/OshimaWebAPI.cs +++ b/OshimaWebAPI/OshimaWebAPI.cs @@ -47,10 +47,10 @@ namespace Oshima.FunGame.WebAPI { // 获取 RainBOTService 实例 RainBOTService bot = serviceProvider.GetRequiredService(); - Controller.WriteLine("成功获取 RainBOTService 实例!"); ThirdPartyMessage message = new() { - IsGroup = false, + IsGroup = true, + GroupOpenId = "1", AuthorOpenId = "1", OpenId = "1", Detail = input, diff --git a/OshimaWebAPI/Services/RainBOTService.cs b/OshimaWebAPI/Services/RainBOTService.cs index 580e575..869a6ed 100644 --- a/OshimaWebAPI/Services/RainBOTService.cs +++ b/OshimaWebAPI/Services/RainBOTService.cs @@ -320,7 +320,7 @@ namespace Oshima.FunGame.WebAPI.Services FunGameSimulation = true; List msgs = await Controller.GetTest(false, maxRespawnTimesMix: 0); List real = []; - int remain = 7; + int remain = msgs.Count > 7 ? 7 : msgs.Count - 1; string merge = ""; for (int i = 0; i < msgs.Count - 2; i++) { @@ -350,7 +350,7 @@ namespace Oshima.FunGame.WebAPI.Services foreach (string msg in real) { await SendAsync(e, "筽祀牻", msg.Trim(), msgSeq: count++); - await Task.Delay(5500); + if (count != real.Count) await Task.Delay(5500); } FunGameSimulation = false; } @@ -375,7 +375,7 @@ namespace Oshima.FunGame.WebAPI.Services FunGameSimulation = true; List msgs = await Controller.GetTest(false, maxRespawnTimesMix: maxRespawnTimesMix); List real = []; - int remain = 7; + int remain = msgs.Count > 7 ? 7 : msgs.Count - 1; string merge = ""; for (int i = 0; i < msgs.Count - 2; i++) { @@ -405,7 +405,7 @@ namespace Oshima.FunGame.WebAPI.Services foreach (string msg in real) { await SendAsync(e, "筽祀牻", msg.Trim(), msgSeq: count++); - await Task.Delay(5500); + if (count != real.Count) await Task.Delay(5500); } FunGameSimulation = false; } @@ -435,7 +435,7 @@ namespace Oshima.FunGame.WebAPI.Services { real.Add(msgs[0]); } - int remain = 7; + int remain = msgs.Count > 7 ? 7 : msgs.Count - 1; string merge = ""; for (int i = 1; i < msgs.Count - 2; i++) { @@ -465,7 +465,7 @@ namespace Oshima.FunGame.WebAPI.Services foreach (string msg in real) { await SendAsync(e, "筽祀牻", msg.Trim(), msgSeq: count++); - await Task.Delay(5500); + if (count != real.Count) await Task.Delay(5500); } FunGameSimulation = false; } @@ -1693,7 +1693,7 @@ namespace Oshima.FunGame.WebAPI.Services { if (msgs.Count < 20) { - int remain = 7; + int remain = msgs.Count > 7 ? 7 : msgs.Count - 1; string merge = ""; for (int i = 0; i < msgs.Count - 1; i++) { @@ -1730,7 +1730,7 @@ namespace Oshima.FunGame.WebAPI.Services foreach (string msg in real) { await SendAsync(e, "完整决斗", msg.Trim(), msgSeq: count++); - await Task.Delay(1500); + if (count != real.Count) await Task.Delay(1500); } return result; } @@ -1750,7 +1750,7 @@ namespace Oshima.FunGame.WebAPI.Services List real = []; if (msgs.Count > 2) { - int remain = 7; + int remain = msgs.Count > 7 ? 7 : msgs.Count - 1; string merge = ""; for (int i = 0; i < msgs.Count - 1; i++) { @@ -1781,7 +1781,7 @@ namespace Oshima.FunGame.WebAPI.Services foreach (string msg in real) { await SendAsync(e, "决斗", msg.Trim(), msgSeq: count++); - await Task.Delay(1500); + if (count != real.Count) await Task.Delay(1500); } return result; } @@ -1803,7 +1803,7 @@ namespace Oshima.FunGame.WebAPI.Services { if (msgs.Count < 20) { - int remain = 7; + int remain = msgs.Count > 7 ? 7 : msgs.Count - 1; string merge = ""; for (int i = 0; i < msgs.Count - 1; i++) { @@ -1840,7 +1840,7 @@ namespace Oshima.FunGame.WebAPI.Services foreach (string msg in real) { await SendAsync(e, "完整决斗", msg.Trim(), msgSeq: count++); - await Task.Delay(1500); + if (count != real.Count) await Task.Delay(1500); } return result; } @@ -1876,7 +1876,7 @@ namespace Oshima.FunGame.WebAPI.Services { if (msgs.Count < 20) { - int remain = 7; + int remain = msgs.Count > 7 ? 7 : msgs.Count - 1; string merge = ""; for (int i = 0; i < msgs.Count - 1; i++) { @@ -1913,7 +1913,7 @@ namespace Oshima.FunGame.WebAPI.Services foreach (string msg in real) { await SendAsync(e, "BOSS", msg.Trim(), msgSeq: count++); - await Task.Delay(1500); + if (count != real.Count) await Task.Delay(1500); } } else @@ -1935,7 +1935,7 @@ namespace Oshima.FunGame.WebAPI.Services { if (msgs.Count < 20) { - int remain = 7; + int remain = msgs.Count > 7 ? 7 : msgs.Count - 1; string merge = ""; for (int i = 0; i < msgs.Count - 1; i++) { @@ -1972,7 +1972,7 @@ namespace Oshima.FunGame.WebAPI.Services foreach (string msg in real) { await SendAsync(e, "BOSS", msg.Trim(), msgSeq: count++); - await Task.Delay(1500); + if (count != real.Count) await Task.Delay(1500); } } else @@ -2903,8 +2903,67 @@ namespace Oshima.FunGame.WebAPI.Services } return result; } + + if (e.Detail == "后勤部") + { + string msg = Controller.ShowSystemStore(uid, "铎京城", "dokyo_logistics"); + if (msg.Trim() != "") + { + await SendAsync(e, "商店", msg); + } + return result; + } + if (e.Detail == "武器商会") + { + string msg = Controller.ShowSystemStore(uid, "铎京城", "dokyo_weapons"); + if (msg.Trim() != "") + { + await SendAsync(e, "商店", msg); + } + return result; + } + if (e.Detail == "杂货铺") + { + string msg = Controller.ShowSystemStore(uid, "铎京城", "dokyo_yuki"); + if (msg.Trim() != "") + { + await SendAsync(e, "商店", msg); + } + return result; + } + + if (e.Detail == "基金会") + { + string msg = Controller.ShowSystemStore(uid, "铎京城", "dokyo_welfare"); + if (msg.Trim() != "") + { + await SendAsync(e, "商店", msg); + } + return result; + } + + if (e.Detail == "锻造商店") + { + string msg = Controller.ShowSystemStore(uid, "铎京城", "dokyo_forge"); + if (msg.Trim() != "") + { + await SendAsync(e, "商店", msg); + } + return result; + } + + if (e.Detail == "赛马商店") + { + string msg = Controller.ShowSystemStore(uid, "铎京城", "dokyo_horseracing"); + if (msg.Trim() != "") + { + await SendAsync(e, "商店", msg); + } + return result; + } + if (e.Detail.StartsWith("商店")) { string detail = e.Detail.Replace("商店", "").Trim(); @@ -2928,6 +2987,9 @@ namespace Oshima.FunGame.WebAPI.Services case 5: msg = Controller.ShowSystemStore(uid, "铎京城", "dokyo_forge"); break; + case 6: + msg = Controller.ShowSystemStore(uid, "铎京城", "dokyo_horseracing"); + break; default: break; } @@ -3218,6 +3280,230 @@ 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, "horseracing", "", 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 != "") + { + if (FunGameConstant.Rooms.Values.FirstOrDefault(r => r.GameMap == groupId) is Room room) + { + string msg = Controller.IntoRoom(uid, room.Roomid, ""); + if (msg.Trim() != "") + { + await SendAsync(e, "赛马", msg); + } + } + else + { + await SendAsync(e, "赛马", "本群还没有创建赛马房间,请使用【创建赛马】指令来创建一个房间。"); + } + } + else + { + await SendAsync(e, "赛马", "请在群聊中进行多人游戏。"); + } + return result; + } + + if (e.Detail.StartsWith("创建房间")) + { + 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 detail = e.Detail.Replace("创建房间", "").Trim(); + string[] strings = detail.Split(" ", StringSplitOptions.RemoveEmptyEntries); + string roomType = "", password = ""; + if (strings.Length > 0) roomType = strings[0]; + if (strings.Length > 1) + { + int firstSpaceIndex = detail.IndexOf(' '); + if (firstSpaceIndex != -1 && firstSpaceIndex + 1 < detail.Length) + { + password = detail[(firstSpaceIndex + 1)..].Trim(); + } + } + string msg = Controller.CreateRoom(uid, roomType, password, groupId); + if (msg.Trim() != "") + { + await SendAsync(e, "房间", msg); + } + } + else + { + await SendAsync(e, "房间", "请在群聊中进行多人游戏。"); + } + return result; + } + + if (e.Detail.StartsWith("加入房间")) + { + 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 detail = e.Detail.Replace("加入房间", "").Trim(); + string[] strings = detail.Split(" ", StringSplitOptions.RemoveEmptyEntries); + string roomid = "", password = ""; + if (strings.Length > 0) roomid = strings[0]; + if (strings.Length > 1) + { + int firstSpaceIndex = detail.IndexOf(' '); + if (firstSpaceIndex != -1 && firstSpaceIndex + 1 < detail.Length) + { + password = detail[(firstSpaceIndex + 1)..].Trim(); + } + } + string msg = Controller.IntoRoom(uid, roomid, password); + 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 != "") + { + (Room room, List msgs) = await Controller.RunGame(uid); + List real = []; + if (msgs.Count > 1) + { + if (msgs.Count > 20) + { + msgs = [msgs[0], .. msgs[^20..]]; + } + int remain = msgs.Count > 7 ? 7 : msgs.Count - 1; + string merge = ""; + for (int i = 0; i < msgs.Count - 1; i++) + { + remain--; + merge += msgs[i] + "\r\n"; + if (remain == 0) + { + real.Add(merge); + merge = ""; + if ((msgs.Count - i - 1) < 7) + { + remain = msgs.Count - i - 1; + } + else remain = 7; + } + } + real.Add(msgs[^1]); + } + else + { + real = msgs; + } + if (real.Count >= 3) + { + real = [real[0], .. real[^2..]]; + } + int count = 1; + foreach (string msg in real) + { + await SendAsync(e, "房间", msg.Trim(), msgSeq: count++); + if (count <= real.Count) await Task.Delay(1500); + } + OnlineService.ReSetRoomState(room.Roomid); + } + 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.QuitRoom(uid); + if (msg.Trim() != "") + { + await SendAsync(e, "房间", msg); + } + } + else + { + await SendAsync(e, "房间", "请在群聊中进行多人游戏。"); + } + return result; + } + if (uid == GeneralSettings.Master && e.Detail.StartsWith("重载FunGame", StringComparison.CurrentCultureIgnoreCase)) { string msg = Controller.Relaod(uid);