diff --git a/OshimaModules/Models/FunGameConstant.cs b/OshimaModules/Models/FunGameConstant.cs index 8ef256a..eca9a50 100644 --- a/OshimaModules/Models/FunGameConstant.cs +++ b/OshimaModules/Models/FunGameConstant.cs @@ -33,9 +33,9 @@ namespace Oshima.FunGame.OshimaModules.Models public static List AllItems { get; } = []; public static List AllSkills { get; } = []; public static Dictionary UserIdAndUsername { get; } = []; - public static Dictionary MarketItemIdAndItem { get; } = []; public static Dictionary UserLastVisitStore { get; } = []; public static ConcurrentDictionary UserSemaphoreSlims { get; } = []; + public static SemaphoreSlim MarketSemaphoreSlim { get; } = new(1, 1); 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]; diff --git a/OshimaModules/Skills/Mayor/精准打击.cs b/OshimaModules/Skills/Mayor/精准打击.cs index 858412f..3ea41a0 100644 --- a/OshimaModules/Skills/Mayor/精准打击.cs +++ b/OshimaModules/Skills/Mayor/精准打击.cs @@ -10,7 +10,7 @@ namespace Oshima.FunGame.OshimaModules.Skills public override string Description => Effects.Count > 0 ? Effects.First().Description : ""; public override string DispelDescription => Effects.Count > 0 ? Effects.First().DispelDescription : ""; public override double EPCost => 100; - public override double CD => 40 - 1 * (Level - 1); + public override double CD => 60 - 1.5 * (Level - 1); public override double HardnessTime { get; set; } = 8; public override bool CanSelectSelf => true; public override bool CanSelectEnemy => false; diff --git a/OshimaModules/Skills/NanGanyu/三重叠加.cs b/OshimaModules/Skills/NanGanyu/三重叠加.cs index adc4c3c..2a501f9 100644 --- a/OshimaModules/Skills/NanGanyu/三重叠加.cs +++ b/OshimaModules/Skills/NanGanyu/三重叠加.cs @@ -10,7 +10,7 @@ namespace Oshima.FunGame.OshimaModules.Skills public override string Description => Effects.Count > 0 ? Effects.First().Description : ""; public override string DispelDescription => Effects.Count > 0 ? Effects.First().DispelDescription : ""; public override double EPCost => 100; - public override double CD => 35 - 2 * (Level - 1); + public override double CD => 80 - 4 * (Level - 1); public override double HardnessTime { get; set; } = 0; public override bool CanSelectSelf => true; public override bool CanSelectEnemy => false; @@ -25,13 +25,26 @@ namespace Oshima.FunGame.OshimaModules.Skills { public override long Id => Skill.Id; public override string Name => "三重叠加"; - public override string Description => $"使 [ 灵能反射 ] 支持普通攻击,且当前释放魔法次数归零,最大硬直消除次数提高到 {灵能反射次数} 次;在魔法命中和普通攻击命中时能够回复所回复能量值的 3 倍魔法值,持续 {技能持续次数} 次(灵能反射每消除次数达到最大时算一次)。" + + public override string Description => $"使 [ 灵能反射 ] 支持普通攻击,且当前释放魔法次数归零,最大硬直消除次数提高到 {灵能反射次数} 次;在魔法命中和普通攻击命中时能够回复所回复能量值的 {魔法值倍数:0.#} 倍魔法值,持续 {技能持续次数} 次(灵能反射每消除次数达到最大时算一次)。" + $"(剩余:{剩余持续次数} 次)"; public override DispelledType DispelledType => DispelledType.CannotBeDispelled; public int 剩余持续次数 { get; set; } = 0; private readonly int 灵能反射次数 = 3; - private readonly int 技能持续次数 = 2; + private double 魔法值倍数 + { + get + { + return Skill.Level * 0.5; + } + } + private int 技能持续次数 + { + get + { + return Skill.Level > 3 ? 2 : 1; + } + } public override void OnEffectGained(Character character) { diff --git a/OshimaModules/Skills/NiuNan/变幻之心.cs b/OshimaModules/Skills/NiuNan/变幻之心.cs index 7a892b9..866a507 100644 --- a/OshimaModules/Skills/NiuNan/变幻之心.cs +++ b/OshimaModules/Skills/NiuNan/变幻之心.cs @@ -59,13 +59,13 @@ namespace Oshima.FunGame.OshimaModules.Skills { return Level switch { - 1 => 3, - 2 => 3, - 3 => 4, - 4 => 4, - 5 => 5, - 6 => 5, - _ => 3 + 1 => 2, + 2 => 2, + 3 => 3, + 4 => 3, + 5 => 4, + 6 => 3, + _ => 2 }; } } @@ -76,11 +76,11 @@ namespace Oshima.FunGame.OshimaModules.Skills return Level switch { 1 => 1, - 2 => 2, + 2 => 1, 3 => 2, - 4 => 3, + 4 => 2, 5 => 3, - 6 => 4, + 6 => 3, _ => 1 }; } diff --git a/OshimaModules/Skills/QWQAQW/迅捷之势.cs b/OshimaModules/Skills/QWQAQW/迅捷之势.cs index 88cd4b0..27b2539 100644 --- a/OshimaModules/Skills/QWQAQW/迅捷之势.cs +++ b/OshimaModules/Skills/QWQAQW/迅捷之势.cs @@ -10,7 +10,7 @@ namespace Oshima.FunGame.OshimaModules.Skills public override string Description => Effects.Count > 0 ? Effects.First().Description : ""; public override string DispelDescription => Effects.Count > 0 ? Effects.First().DispelDescription : ""; public override double EPCost => 100; - public override double CD => 60 - 2 * (Level - 1); + public override double CD => 80 - 4 * (Level - 1); public override double HardnessTime { get; set; } = 15; public override bool CanSelectSelf => true; public override bool CanSelectEnemy => false; diff --git a/OshimaModules/Skills/QingXiang/能量毁灭.cs b/OshimaModules/Skills/QingXiang/能量毁灭.cs index 6ad285f..b55edad 100644 --- a/OshimaModules/Skills/QingXiang/能量毁灭.cs +++ b/OshimaModules/Skills/QingXiang/能量毁灭.cs @@ -9,8 +9,8 @@ namespace Oshima.FunGame.OshimaModules.Skills public override string Name => "能量毁灭"; public override string Description => Effects.Count > 0 ? Effects.First().Description : ""; public override double EPCost => 100; - public override double CD => 60 - 1 * (Level - 1); - public override double HardnessTime { get; set; } = 25; + public override double CD => 100 - 4 * (Level - 1); + public override double HardnessTime { get; set; } = 18; public override string Slogan => "从深渊引爆力量,世界将为之颤抖!!!!"; public override bool CanSelectSelf => false; public override bool CanSelectEnemy => true; @@ -39,7 +39,7 @@ namespace Oshima.FunGame.OshimaModules.Skills private double 智力系数 => 0.2 * Level; private double 智力伤害 => 智力系数 * Skill.Character?.INT ?? 0; - private double 能量系数 => 1.05 * Level; + private double 能量系数 => 1 * Level; public override void OnSkillCasted(Character caster, List targets, Dictionary others) { diff --git a/OshimaModules/Skills/QuDuoduo/血之狂欢.cs b/OshimaModules/Skills/QuDuoduo/血之狂欢.cs index 22671fb..188342a 100644 --- a/OshimaModules/Skills/QuDuoduo/血之狂欢.cs +++ b/OshimaModules/Skills/QuDuoduo/血之狂欢.cs @@ -10,7 +10,7 @@ namespace Oshima.FunGame.OshimaModules.Skills public override string Description => Effects.Count > 0 ? Effects.First().Description : ""; public override string DispelDescription => Effects.Count > 0 ? Effects.First().DispelDescription : ""; public override double EPCost => 100; - public override double CD => 45; + public override double CD => 45 + 1 * (Level - 1); public override double HardnessTime { get; set; } = 7; public override bool CanSelectSelf => true; public override bool CanSelectEnemy => false; @@ -25,16 +25,18 @@ namespace Oshima.FunGame.OshimaModules.Skills { public override long Id => Skill.Id; public override string Name => Skill.Name; - public override string Description => $"获得 40% 吸血,持续 {Duration:0.##} {GameplayEquilibriumConstant.InGameTime}。"; + public override string Description => $"获得 {吸血系数 * 100:0.##}% 吸血,持续 {Duration:0.##} {GameplayEquilibriumConstant.InGameTime}。"; public override bool Durative => true; public override double Duration => 30; public override DispelledType DispelledType => DispelledType.CannotBeDispelled; + private double 吸血系数 => 0.2 + 0.05 * (Level - 1); + public override void AfterDamageCalculation(Character character, Character enemy, double damage, double actualDamage, bool isNormalAttack, DamageType damageType, MagicType magicType, DamageResult damageResult) { if (character == Skill.Character && (damageResult == DamageResult.Normal || damageResult == DamageResult.Critical) && character.HP < character.MaxHP) { - double 实际吸血 = 0.4 * damage; + double 实际吸血 = 吸血系数 * damage; HealToTarget(character, character, 实际吸血); } } diff --git a/OshimaModules/Skills/XinYin/天赐之力.cs b/OshimaModules/Skills/XinYin/天赐之力.cs index 35df53f..2274df3 100644 --- a/OshimaModules/Skills/XinYin/天赐之力.cs +++ b/OshimaModules/Skills/XinYin/天赐之力.cs @@ -25,22 +25,25 @@ namespace Oshima.FunGame.OshimaModules.Skills { public override long Id => Skill.Id; public override string Name => Skill.Name; - public override string Description => $"{Duration:0.##} {GameplayEquilibriumConstant.InGameTime}内,增加 40% 攻击力 [ {攻击力提升:0.##} ]、30% 物理穿透和 25% 闪避率(不可叠加),普通攻击硬直时间额外减少 20%,基于 {系数 * 100:0.##}% 敏捷 [ {伤害加成:0.##} ] 强化普通攻击的伤害。在持续时间内,[ 心灵之火 ] 的冷却时间降低至 3 {GameplayEquilibriumConstant.InGameTime}。"; + public override string Description => $"{Duration:0.##} {GameplayEquilibriumConstant.InGameTime}内,增加 {攻击力提升系数 * 100:0.##}% 攻击力 [ {攻击力提升:0.##} ]、{物理穿透提升 * 100:0.##}% 物理穿透和 {闪避率提升 * 100:0.##}% 闪避率(不可叠加),普通攻击硬直时间额外减少 20%,基于 {系数 * 100:0.##}% 敏捷 [ {伤害加成:0.##} ] 强化普通攻击的伤害。在持续时间内,[ 心灵之火 ] 的冷却时间降低至 3 {GameplayEquilibriumConstant.InGameTime}。"; public override bool Durative => true; - public override double Duration => 40; + public override double Duration => 30; public override DispelledType DispelledType => DispelledType.CannotBeDispelled; - private double 系数 => 1.2 * (1 + 0.6 * (Skill.Level - 1)); + private double 系数 => 1.2 * (1 + 0.5 * (Skill.Level - 1)); private double 伤害加成 => 系数 * Skill.Character?.AGI ?? 0; - private double 攻击力提升 => 0.4 * Skill.Character?.BaseATK ?? 0; + private double 攻击力提升系数 => Skill.Level > 0 ? 0.15 + 0.03 * (Skill.Level - 1) : 0.15; + private double 攻击力提升 => 攻击力提升系数 * Skill.Character?.BaseATK ?? 0; + private double 物理穿透提升 => Skill.Level > 0 ? 0.1 + 0.03 * (Skill.Level - 1) : 0.1; + private double 闪避率提升 => Skill.Level > 0 ? 0.1 + 0.02 * (Skill.Level - 1) : 0.1; private double 实际的攻击力提升 = 0; public override void OnEffectGained(Character character) { 实际的攻击力提升 = 攻击力提升; character.ExATK2 += 实际的攻击力提升; - character.PhysicalPenetration += 0.3; - character.ExEvadeRate += 0.25; + character.PhysicalPenetration += 物理穿透提升; + character.ExEvadeRate += 闪避率提升; if (character.Effects.Where(e => e is 心灵之火特效).FirstOrDefault() is 心灵之火特效 e) { e.基础冷却时间 = 3; @@ -51,11 +54,11 @@ namespace Oshima.FunGame.OshimaModules.Skills public override void OnEffectLost(Character character) { character.ExATK2 -= 实际的攻击力提升; - character.PhysicalPenetration -= 0.3; - character.ExEvadeRate -= 0.25; + character.PhysicalPenetration -= 物理穿透提升; + character.ExEvadeRate -= 闪避率提升; if (character.Effects.Where(e => e is 心灵之火特效).FirstOrDefault() is 心灵之火特效 e) { - e.基础冷却时间 = 8; + e.基础冷却时间 = 10; } } diff --git a/OshimaModules/Skills/XinYin/心灵之火.cs b/OshimaModules/Skills/XinYin/心灵之火.cs index 42dedd7..b43c123 100644 --- a/OshimaModules/Skills/XinYin/心灵之火.cs +++ b/OshimaModules/Skills/XinYin/心灵之火.cs @@ -28,7 +28,7 @@ namespace Oshima.FunGame.OshimaModules.Skills (冷却时间 > 0 ? $"(正在冷却:剩余 {冷却时间:0.##} {GameplayEquilibriumConstant.InGameTime})" : ""); public double 冷却时间 { get; set; } = 0; - public double 基础冷却时间 { get; set; } = 12; + public double 基础冷却时间 { get; set; } = 10; private bool 是否是嵌套普通攻击 = false; public override double AlterActualDamageAfterCalculation(Character character, Character enemy, double damage, bool isNormalAttack, DamageType damageType, MagicType magicType, DamageResult damageResult, ref bool isEvaded, Dictionary totalDamageBonus) diff --git a/OshimaModules/Skills/Yang/魔法涌流.cs b/OshimaModules/Skills/Yang/魔法涌流.cs index 78415f5..ab8b33b 100644 --- a/OshimaModules/Skills/Yang/魔法涌流.cs +++ b/OshimaModules/Skills/Yang/魔法涌流.cs @@ -10,7 +10,7 @@ namespace Oshima.FunGame.OshimaModules.Skills public override string Description => Effects.Count > 0 ? Effects.First().Description : ""; public override string DispelDescription => Effects.Count > 0 ? Effects.First().DispelDescription : ""; public override double EPCost => 100; - public override double CD => 35; + public override double CD => 50; public override double HardnessTime { get; set; } = 3; public override bool CanSelectSelf => true; public override bool CanSelectEnemy => false; diff --git a/OshimaServers/AnonymousServer.cs b/OshimaServers/AnonymousServer.cs index 3a0660c..a54cf54 100644 --- a/OshimaServers/AnonymousServer.cs +++ b/OshimaServers/AnonymousServer.cs @@ -155,6 +155,11 @@ namespace Oshima.FunGame.OshimaServers FunGameService.RefreshStoreData(); Controller.WriteLine("刷新商店"); }); + Task.Run(() => + { + FunGameService.RefreshMarketData(); + Controller.WriteLine("刷新市场"); + }); // 刷新活动缓存 FunGameService.GetEventCenter(); FunGameService.RefreshNotice(); diff --git a/OshimaServers/Service/FunGameOrderList.cs b/OshimaServers/Service/FunGameOrderList.cs index 3ceebda..03beb95 100644 --- a/OshimaServers/Service/FunGameOrderList.cs +++ b/OshimaServers/Service/FunGameOrderList.cs @@ -102,6 +102,7 @@ public static Dictionary ClubHelp { get; } = new() { {"我的社团", "查看社团信息"}, + {"私信 <对方UID/昵称> <内容>", "发送私信给对方"}, {"加入社团 <社团序号>", "申请加入社团"}, {"退出社团", "退出当前社团"}, {"创建社团 <前缀>", "创建一个公开社团,若指令中包含私密一词,将创建私密社团\r\n社团前缀:3-4个字符,允许:英文字母和数字、部分特殊字符"}, @@ -122,14 +123,17 @@ {"社团商店查看 <商品序号>", "查看指定商品详情"}, {"社团商店购买 <商品序号>", "购买指定商品"}, {"社团商店出售 <物品序号>", "向商店出售具有回收价的指定物品"}, - {"社团市场上架 <物品序号> <定价>", "将物品寄售到社团市场上,手续费10%,并且8%进入社团基金"}, + {"社团市场 [页码]", "查看社团市场商品"}, + {"社团市场出售 <物品序号> <定价>", "将物品寄售到社团市场上,系统手续费4%,社团收取8%作为社团基金"}, {"社团市场下架 <市场物品序号>", "下架指定物品"}, + {"社团市场查看 <市场物品序号>", "查看指定物品"}, {"社团市场购买 <市场物品序号>", "购买指定物品"}, {"社团市场清空", "管理员可下架所有物品"}, }; public static Dictionary ActivityHelp { get; } = new() { {"签到", "每日签到奖励"}, + {"公告", "查看系统公告"}, {"活动", "查看活动中心"}, {"查活动 <活动序号>", "查看指定活动详情"}, {"做活动任务 <活动序号> <任务序号>", "开始指定活动任务"}, @@ -156,11 +160,15 @@ {"报价添加对方物品 <报价序号> <{物品序号}...>", "仅发起方可操作"}, {"报价移除物品 <报价序号> <{报价物品序号}...>", "仅发起方可操作"}, {"报价移除对方物品 <报价序号> <{报价物品序号}...>", "仅发起方可操作"}, - {"市场上架 <物品序号> <定价>", "将物品寄售到市场上,手续费15%"}, + {"市场 [页码]", "查看市场商品"}, + {"市场出售 <物品序号> <定价>", "将物品寄售到市场上,手续费15%"}, {"市场下架 <市场物品序号>", "下架指定物品"}, + {"市场查看 <市场物品序号>", "查看指定物品"}, {"市场购买 <市场物品序号>", "购买指定物品"}, - {"社团市场上架 <物品序号> <定价>", "将物品寄售到社团市场上,手续费10%,并且8%进入社团基金"}, + {"社团市场 [页码]", "查看社团市场商品"}, + {"社团市场出售 <物品序号> <定价>", "将物品寄售到社团市场上,系统手续费4%,社团收取8%作为社团基金"}, {"社团市场下架 <市场物品序号>", "下架指定物品"}, + {"社团市场查看 <市场物品序号>", "查看指定物品"}, {"社团市场购买 <市场物品序号>", "购买指定物品"}, {"社团市场清空", "管理员可下架所有物品"}, }; diff --git a/OshimaServers/Service/FunGameService.cs b/OshimaServers/Service/FunGameService.cs index fc99549..61eca65 100644 --- a/OshimaServers/Service/FunGameService.cs +++ b/OshimaServers/Service/FunGameService.cs @@ -2113,13 +2113,13 @@ namespace Oshima.FunGame.OshimaServers.Service if (goods.Stock != -1 && goods.Stock - count < 0) { - return msg = $"此商品【{goods.Name}】库存不足,无法购买!\r\n你想要购买 {count} 件,但库存只有 {goods.Stock} 件。"; + return $"此商品【{goods.Name}】库存不足,无法购买!\r\n你想要购买 {count} 件,但库存只有 {goods.Stock} 件。"; } goods.UsersBuyCount.TryGetValue(user.Id, out int buyCount); if (goods.Quota > 0 && (buyCount + count > goods.Quota)) { - return msg = $"此商品【{goods.Name}】限量购买 {goods.Quota} 件!\r\n你已经购买了 {buyCount} 件,想要购买 {count} 件,超过了购买限制。"; + return $"此商品【{goods.Name}】限量购买 {goods.Quota} 件!\r\n你已经购买了 {buyCount} 件,想要购买 {count} 件,超过了购买限制。"; } foreach (string needy in goods.Prices.Keys) @@ -2169,7 +2169,7 @@ namespace Oshima.FunGame.OshimaServers.Service foreach (Item item in goods.Items) { - if (item.Name == nameof(探索许可)) + if (item.Id == (long)SpecialItemID.探索许可) { int exploreTimes = FunGameConstant.MaxExploreTimes + count; if (pc.TryGetValue("exploreTimes", out object? value) && int.TryParse(value.ToString(), out exploreTimes)) @@ -2209,6 +2209,80 @@ namespace Oshima.FunGame.OshimaServers.Service return msg; } + + public static string MarketBuyItem(Market market, MarketItem item, PluginConfig pc, User user, int count, out bool result) + { + result = false; + string msg = ""; + + DateTime now = DateTime.Now; + if (market.StartTime > now || market.EndTime < now) + { + return "铎京集市未处于营业时间内。"; + } + + if (item.Status != MarketItemState.Listed) + { + return $"无法购买此商品,原因:该商品的状态是:{CommonSet.GetMarketItemStatus(item.Status)}。"; + } + + if (item.User == user.Id) + { + return $"不能购买自己上架的商品!如需下架,请使用【市场下架+序号】指令。"; + } + + if (item.Stock != -1 && item.Stock - count < 0) + { + return $"此商品【{item.Name}】库存不足,无法购买!\r\n你想要购买 {count} 件,但库存只有 {item.Stock} 件。"; + } + + double reduce = Calculation.Round2Digits(item.Price * count); + if (user.Inventory.Credits >= reduce) + { + user.Inventory.Credits -= reduce; + } + else + { + return $"你的{General.GameplayEquilibriumConstant.InGameCurrency}不足 {reduce} 呢,无法购买【{item.Name}】!"; + } + + if (item.Item.Id == (long)SpecialItemID.探索许可) + { + int exploreTimes = FunGameConstant.MaxExploreTimes + count; + if (pc.TryGetValue("exploreTimes", out object? value) && int.TryParse(value.ToString(), out exploreTimes)) + { + exploreTimes += count; + } + pc.Add("exploreTimes", exploreTimes); + } + else + { + for (int i = 0; i < count; i++) + { + AddItemToUserInventory(user, item.Item, copyLevel: true, useOriginalPrice: true, toExploreCache: false, toActivitiesCache: false); + } + } + + if (item.Stock != -1) + { + item.Stock -= count; + if (item.Stock < 0) item.Stock = 0; + if (item.Stock == 0) + { + item.Status = MarketItemState.Purchased; + item.FinishTime = DateTime.Now; + } + } + + item.Buyers.Add(user.Id); + result = true; + msg += $"恭喜你成功购买 {count} 件【{item.Name}】!\r\n" + + $"总计消费:{(item.Price > 0 ? item.Price : "免单")}\r\n" + + $"包含物品:[{ItemSet.GetQualityTypeName(item.Item.QualityType)}|{ItemSet.GetItemTypeName(item.Item.ItemType)}] {item.Item.Name} * {count}\r\n" + + $"铎京集市期待你的下次光临。"; + + return msg; + } public static string Select_CheckAutoKey(SQLHelper SQLHelper, string AutoKey) { @@ -3289,7 +3363,7 @@ namespace Oshima.FunGame.OshimaServers.Service { user.Inventory.Items.Remove(item); - Item newItem = item.Copy(copyGuid: true); + Item newItem = item.Copy(true, true); newItem.User = user2; newItem.IsSellable = false; newItem.IsTradable = false; @@ -3306,7 +3380,7 @@ namespace Oshima.FunGame.OshimaServers.Service { user2.Inventory.Items.Remove(item); - Item newItem = item.Copy(copyGuid: true); + Item newItem = item.Copy(true, true); newItem.User = user; newItem.IsSellable = false; newItem.IsTradable = false; @@ -4204,6 +4278,156 @@ namespace Oshima.FunGame.OshimaServers.Service } } + public static string GetMarketInfo(Market market, User? user = null, int page = 1, bool simply = false, bool showListed = true) + { + if (page <= 0) page = 1; + int maxPage = market.MarketItems.Values.MaxPage(10); + if (page > maxPage) page = maxPage; + IEnumerable marketItems = market.MarketItems.Values.GetPage(page, 10); + + StringBuilder builder = new(); + + builder.AppendLine($"☆★☆ {market.Name} ☆★☆"); + if (market.Description != "") builder.AppendLine($"{market.Description}"); + if (market.StartTime.HasValue && market.EndTime.HasValue) + { + builder.AppendLine($"开放时间:{market.StartTime.Value.ToString(General.GeneralDateTimeFormatChinese)} 至 {market.EndTime.Value.ToString(General.GeneralDateTimeFormatChinese)}"); + } + else if (market.StartTime.HasValue && !market.EndTime.HasValue) + { + builder.AppendLine($"开始开放时间:{market.StartTime.Value.ToString(General.GeneralDateTimeFormatChinese)}"); + } + else if (!market.StartTime.HasValue && market.EndTime.HasValue) + { + builder.AppendLine($"停止开放时间:{market.EndTime.Value.ToString(General.GeneralDateTimeFormatChinese)}"); + } + else + { + builder.AppendLine($"开放时间:全年无休,永久开放"); + } + if (market.StartTimeOfDay.HasValue && market.EndTimeOfDay.HasValue) + { + builder.AppendLine($"每日交易时间:{market.StartTimeOfDay.Value.ToString(General.GeneralDateTimeFormatTimeOnly)} 至 {market.EndTimeOfDay.Value.ToString(General.GeneralDateTimeFormatTimeOnly)}"); + } + else + { + builder.AppendLine($"[ 24H ] 全天开放交易"); + } + DateTime now = DateTime.Now; + TimeSpan nowTimeOfDay = now.TimeOfDay; + bool isStoreOpen = true; + if (market.StartTime.HasValue && market.StartTime.Value > now || market.EndTime.HasValue && market.EndTime.Value < now) + { + isStoreOpen = false; + } + if (isStoreOpen && market.StartTimeOfDay.HasValue && market.EndTimeOfDay.HasValue) + { + TimeSpan startTimeSpan = market.StartTimeOfDay.Value.TimeOfDay; + TimeSpan endTimeSpan = market.EndTimeOfDay.Value.TimeOfDay; + if (startTimeSpan <= endTimeSpan) + { + isStoreOpen = nowTimeOfDay >= startTimeSpan && nowTimeOfDay <= endTimeSpan; + } + else + { + isStoreOpen = nowTimeOfDay >= startTimeSpan || nowTimeOfDay <= endTimeSpan; + } + } + if (!isStoreOpen) + { + builder.AppendLine($"市场现在不在交易时间内。"); + } + builder.AppendLine($"☆--- 市场商品列表 ---☆"); + MarketItem[] MarketItemsValid = [.. market.MarketItems.Values]; + if (showListed) MarketItemsValid = [.. MarketItemsValid.Where(g => g.Status == MarketItemState.Listed)]; + if (MarketItemsValid.Length == 0) + { + builder.AppendLine("当前没有商品可供购买,过一段时间再来吧。"); + } + else + { + foreach (MarketItem marketItem in MarketItemsValid) + { + builder.AppendLine(FunGameService.GetMarketItemInfo(marketItem, true, user?.Id ?? 0)); + } + builder.AppendLine("提示:使用【市场查看+序号】查看商品详细信息,使用【市场购买+序号】购买商品。"); + } + if (user != null) + { + builder.AppendLine($"你的现有{General.GameplayEquilibriumConstant.InGameCurrency}:{user.Inventory.Credits:0.##}"); + } + + builder.AppendLine($"页数:{page} / {maxPage},使用【市场+页码】快速跳转指定页面。"); + + return builder.ToString().Trim(); + } + + public static string GetMarketItemInfo(MarketItem item, bool simply, long visitUser) + { + StringBuilder builder = new(); + if (simply) + { + builder.AppendLine($"{item.Id}. {item.Name}"); + builder.AppendLine($"{ItemSet.GetQualityTypeName(item.Item.QualityType)} {ItemSet.GetItemTypeName(item.Item.ItemType)}"); + string username = item.Username; + if (FunGameConstant.UserIdAndUsername.TryGetValue(item.User, out User? user) && user != null) + { + username = user.Username; + } + builder.AppendLine($"卖家:{username}"); + builder.AppendLine($"商品描述:{item.Item.Description}"); + builder.AppendLine($"商品售价:{(item.Price > 0 ? item.Price : "免费")} {General.GameplayEquilibriumConstant.InGameCurrency}"); + if (item.Status == MarketItemState.Purchased) + { + builder.AppendLine($"商品已售罄"); + } + else if (item.Status == MarketItemState.Delisted) + { + builder.AppendLine($"商品已下架"); + } + else builder.AppendLine($"剩余库存:{(item.Stock == -1 ? "不限" : item.Stock)}"); + } + else + { + builder.AppendLine($"{item.Id}. {item.Name}"); + string username = item.Username; + if (FunGameConstant.UserIdAndUsername.TryGetValue(item.User, out User? user) && user != null) + { + username = user.Username; + } + builder.AppendLine($"卖家:{username}"); + builder.AppendLine($"上架时间:{item.CreateTime.ToString(General.GeneralDateTimeFormatChinese)}"); + builder.AppendLine($"商品售价:{(item.Price > 0 ? item.Price : "免费")} {General.GameplayEquilibriumConstant.InGameCurrency}"); + if (item.Status == MarketItemState.Purchased) + { + builder.AppendLine($"商品已售罄"); + if (item.FinishTime.HasValue) builder.AppendLine($"售罄时间:{item.FinishTime.Value.ToString(General.GeneralDateTimeFormatChinese)}"); + if (visitUser == item.User && item.Buyers.Count > 0) + { + HashSet buyers = []; + foreach (long buyerid in item.Buyers) + { + username = "Unknown"; + if (FunGameConstant.UserIdAndUsername.TryGetValue(buyerid, out User? buyer) && buyer != null) + { + username = buyer.Username; + } + buyers.Add(username); + } + builder.AppendLine($"买家:{string.Join(",", buyers)}"); + } + } + else if (item.Status == MarketItemState.Delisted) + { + builder.AppendLine($"商品已下架"); + if (item.FinishTime.HasValue) builder.AppendLine($"下架时间:{item.FinishTime.Value.ToString(General.GeneralDateTimeFormatChinese)}"); + } + else builder.AppendLine($"剩余库存:{(item.Stock == -1 ? "不限" : item.Stock)}"); + builder.AppendLine($"☆--- 物品信息 ---☆\r\n{item.Item}"); + } + return builder.ToString().Trim(); + } + public static void RefreshNotice() { Notices.LoadConfig(); @@ -4335,7 +4559,6 @@ namespace Oshima.FunGame.OshimaServers.Service { if (store.ExpireTime != null && store.ExpireTime.Value.Date <= DateTime.Today) { - stores.Remove(key); continue; } if (store.AutoRefresh && store.NextRefreshDate.Date <= DateTime.Today) @@ -4347,6 +4570,7 @@ namespace Oshima.FunGame.OshimaServers.Service } store.NextRefreshDate = DateTime.Today.AddHours(4).AddDays(store.RefreshInterval); } + stores.Add(key, store); } } } @@ -4354,6 +4578,44 @@ namespace Oshima.FunGame.OshimaServers.Service } } } + + public static void RefreshMarketData() + { + // 刷新市场 + GetMarketSemaphoreSlim(); + string directoryPath = $@"{AppDomain.CurrentDomain.BaseDirectory}configs/markets"; + if (Directory.Exists(directoryPath)) + { + DateTime now = DateTime.Now; + string[] filePaths = Directory.GetFiles(directoryPath); + foreach (string filePath in filePaths) + { + string fileName = Path.GetFileNameWithoutExtension(filePath); + EntityModuleConfig markets = new("markets", fileName); + markets.LoadConfig(); + string[] marketNames = [.. markets.Keys]; + foreach (string key in marketNames) + { + Market? market = markets.Get(key); + if (market != null) + { + long[] items = [.. market.MarketItems.Keys]; + foreach (long id in items) + { + MarketItem item = market.MarketItems[id]; + if ((item.Status == MarketItemState.Delisted || item.Status == MarketItemState.Purchased) && item.FinishTime.HasValue && (now - item.FinishTime.Value).TotalDays >= 3) + { + market.MarketItems.Remove(id); + } + } + markets.Add(key, market); + } + } + markets.SaveConfig(); + } + } + ReleaseMarketSemaphoreSlim(); + } public static void PreRefreshStore() { @@ -4465,5 +4727,18 @@ namespace Oshima.FunGame.OshimaServers.Service } public static bool CheckSemaphoreSlim(long uid) => CheckSemaphoreSlim(uid.ToString()); + + public static void GetMarketSemaphoreSlim() + { + FunGameConstant.MarketSemaphoreSlim.Wait(FunGameConstant.SemaphoreSlimTimeout); + } + + public static void ReleaseMarketSemaphoreSlim() + { + if (FunGameConstant.MarketSemaphoreSlim.CurrentCount == 0) + { + FunGameConstant.MarketSemaphoreSlim.Release(); + } + } } } diff --git a/OshimaWebAPI/Controllers/FunGameController.cs b/OshimaWebAPI/Controllers/FunGameController.cs index 54ec6b0..76fa7b0 100644 --- a/OshimaWebAPI/Controllers/FunGameController.cs +++ b/OshimaWebAPI/Controllers/FunGameController.cs @@ -16,7 +16,6 @@ using Oshima.FunGame.OshimaModules.Models; using Oshima.FunGame.OshimaModules.Regions; using Oshima.FunGame.OshimaServers.Model; using Oshima.FunGame.OshimaServers.Service; -using ProjectRedbud.FunGame.SQLQueryExtension; namespace Oshima.FunGame.WebAPI.Controllers { @@ -6877,12 +6876,24 @@ namespace Oshima.FunGame.WebAPI.Controllers } [HttpPost("marketsellitem")] - public string MarketSellItem([FromQuery] long? uid = null, [FromQuery] int itemIndex = -1, [FromQuery] double price = 0) + public string MarketSellItem([FromQuery] long uid = -1, [FromQuery] double price = 0, [FromBody] int[]? itemIndexs = null) { - long userid = uid ?? Convert.ToInt64("10" + Verification.CreateVerifyCode(VerifyCodeType.NumberVerifyCode, 11)); + long userid = uid; + itemIndexs ??= []; + + if (price <= 0) + { + return "请输入一个大于 0 的售价。"; + } try { + using SQLHelper? sql = Factory.OpenFactory.GetSQLHelper(); + if (sql is null) + { + return busy; + } + PluginConfig pc = FunGameService.GetUserConfig(userid, out _); if (pc.Count > 0) @@ -6890,73 +6901,75 @@ namespace Oshima.FunGame.WebAPI.Controllers User user = FunGameService.GetUser(pc); List msgs = []; - using SQLHelper? sql = Factory.OpenFactory.GetSQLHelper(); List itemTrading = []; - if (sql != null) - { - itemTrading = SQLService.GetUserItemGuids(sql, userid); - } + itemTrading = SQLService.GetUserItemGuids(sql, userid); Dictionary dict = user.Inventory.Items.Select((item, index) => new { item, index }).ToDictionary(x => x.index + 1, x => x.item); - Item? item = null; - if (itemIndex > 0 && itemIndex <= user.Inventory.Items.Count) + FunGameService.GetMarketSemaphoreSlim(); + + List successItems = []; + foreach (int itemIndex in itemIndexs) { - item = user.Inventory.Items.ToList()[itemIndex - 1]; - - if (price <= 0) + if (itemIndex > 0 && itemIndex <= user.Inventory.Items.Count) { - msgs.Add($"请输入一个大于 0 的售价。"); + Item item = user.Inventory.Items.ToList()[itemIndex - 1]; + + if (item.IsLock) + { + msgs.Add($"物品 {itemIndex}. {item.Name}:此物品已上锁,无法出售。"); + } + + if (item.Character != null) + { + msgs.Add($"物品 {itemIndex}. {item.Name}:此物品已被 {item.Character} 装备中,无法出售。"); + } + + if (itemTrading.Contains(item.Guid)) + { + msgs.Add($"物品 {itemIndex}. {item.Name}:此物品正在进行交易,无法出售,请检查交易报价。"); + } + + if (!item.IsSellable) + { + msgs.Add($"物品 {itemIndex}. {item.Name}:此物品无法出售{(item.NextSellableTime != DateTime.MinValue ? $",此物品将在 {item.NextSellableTime.ToString(General.GeneralDateTimeFormatChinese)} 后可出售" : "")}。"); + } + + if (msgs.Count == 0) + { + user.Inventory.Items.Remove(item); + successItems.Add(item); + msgs.Add($"物品 {itemIndex}. {item.Name}:上架市场成功!定价:{price:0.##} {General.GameplayEquilibriumConstant.InGameCurrency}。"); + } + } + else + { + msgs.Add($"{itemIndex}. 没有找到与这个序号相对应的物品!"); + } + } + + if (successItems.Count > 0) + { + EntityModuleConfig emc = new("markets", "general"); + emc.LoadConfig(); + Market market = emc.Get("dokyo") ?? new("铎京集市"); + + foreach (Item successItem in successItems) + { + Item newItem = successItem.Copy(true); + market.AddItem(user, newItem, price, 1); } - if (item.IsLock) - { - msgs.Add($"物品 {itemIndex}. {item.Name}:此物品已上锁,无法出售。"); - } - - if (item.Character != null) - { - msgs.Add($"物品 {itemIndex}. {item.Name}:此物品已被 {item.Character} 装备中,无法出售。"); - } - - if (itemTrading.Contains(item.Guid)) - { - msgs.Add($"物品 {itemIndex}. {item.Name}:此物品正在进行交易,无法出售,请检查交易报价。"); - } - - if (!item.IsSellable) - { - msgs.Add($"物品 {itemIndex}. {item.Name}:此物品无法出售{(item.NextSellableTime != DateTime.MinValue ? $",此物品将在 {item.NextSellableTime.ToString(General.GeneralDateTimeFormatChinese)} 后可出售" : "")}。"); - } - - if (msgs.Count > 0) item = null; + emc.Add("dokyo", market); + emc.SaveConfig(); } else - { - msgs.Add($"没有找到与这个序号相对应的物品!"); - } - - if (item != null && sql != null && user.Inventory.Items.Remove(item)) - { - EntityModuleConfig emc = new("markets", user.Id.ToString()); - emc.LoadConfig(); - Item newItem = item.Copy(true); - emc.Add(item.Guid.ToString(), newItem); - sql.AddMarketItem(newItem.Guid, userid, price); - long marketId = sql.LastInsertId; - FunGameConstant.MarketItemIdAndItem[marketId] = newItem; - if (sql.Success) - { - emc.SaveConfig(); - msgs.Add($"物品 {itemIndex}. {item.Name} 上架市场成功,售价:{price} {General.GameplayEquilibriumConstant.InGameCurrency},市场编号:{marketId}。"); - } - } - - if (msgs.Count == 0) { msgs.Add($"没有成功上架任何物品。请检查物品是否存在或是否满足上架条件。"); } + FunGameService.ReleaseMarketSemaphoreSlim(); FunGameService.SetUserConfigAndReleaseSemaphoreSlim(userid, pc, user); + return string.Join("\r\n", msgs); } else @@ -6967,6 +6980,262 @@ namespace Oshima.FunGame.WebAPI.Controllers } catch (Exception e) { + FunGameService.ReleaseMarketSemaphoreSlim(); + FunGameService.ReleaseUserSemaphoreSlim(userid); + Logger.LogError(e, "Error: {e}", e); + return busy; + } + } + + [HttpPost("marketshowlist")] + public string MarketShowList([FromQuery] long userid = -1, [FromQuery] int page = 0) + { + try + { + PluginConfig pc = FunGameService.GetUserConfig(userid, out _); + + if (pc.Count > 0) + { + User user = FunGameService.GetUser(pc); + + FunGameService.GetMarketSemaphoreSlim(); + + EntityModuleConfig emc = new("markets", "general"); + emc.LoadConfig(); + Market market = emc.Get("dokyo") ?? new("铎京集市"); + string msg = FunGameService.GetMarketInfo(market, user, page, true); + + FunGameService.ReleaseMarketSemaphoreSlim(); + FunGameService.SetUserConfigAndReleaseSemaphoreSlim(userid, pc, user); + + return msg; + } + else + { + FunGameService.ReleaseUserSemaphoreSlim(userid); + return noSaved; + } + } + catch (Exception e) + { + FunGameService.ReleaseMarketSemaphoreSlim(); + FunGameService.ReleaseUserSemaphoreSlim(userid); + Logger.LogError(e, "Error: {e}", e); + return busy; + } + } + + [HttpPost("marketshowlistmysells")] + public string MarketShowListMySells([FromQuery] long userid = -1, [FromQuery] int page = 0) + { + try + { + PluginConfig pc = FunGameService.GetUserConfig(userid, out _); + + if (pc.Count > 0) + { + User user = FunGameService.GetUser(pc); + + FunGameService.GetMarketSemaphoreSlim(); + + EntityModuleConfig emc = new("markets", "general"); + emc.LoadConfig(); + Market market = emc.Get("dokyo") ?? new("铎京集市"); + string msg = "☆--- 我的市场商品 ---☆\r\n"; + MarketItem[] marketItems = [.. market.MarketItems.Values.Where(m => m.User == userid)]; + if (marketItems.Length > 0) + { + foreach (MarketItem marketItem in marketItems) + { + msg += FunGameService.GetMarketItemInfo(marketItem, true, userid) + "\r\n"; + } + } + else msg += "你还没有上架过任何物品。"; + + FunGameService.ReleaseMarketSemaphoreSlim(); + FunGameService.SetUserConfigAndReleaseSemaphoreSlim(userid, pc, user); + + return msg.Trim(); + } + else + { + FunGameService.ReleaseUserSemaphoreSlim(userid); + return noSaved; + } + } + catch (Exception e) + { + FunGameService.ReleaseMarketSemaphoreSlim(); + FunGameService.ReleaseUserSemaphoreSlim(userid); + Logger.LogError(e, "Error: {e}", e); + return busy; + } + } + + [HttpPost("marketiteminfo")] + public string MarketItemInfo([FromQuery] long userid = -1, [FromQuery] long itemid = 0) + { + try + { + PluginConfig pc = FunGameService.GetUserConfig(userid, out _); + + if (pc.Count > 0) + { + User user = FunGameService.GetUser(pc); + + FunGameService.GetMarketSemaphoreSlim(); + + EntityModuleConfig emc = new("markets", "general"); + emc.LoadConfig(); + Market market = emc.Get("dokyo") ?? new("铎京集市"); + string msg = ""; + if (market.MarketItems.TryGetValue(itemid, out MarketItem? item) && item != null) + { + msg = FunGameService.GetMarketItemInfo(item, false, userid); + } + if (msg != "") + { + msg += $"\r\n提示:使用【市场查看+序号】查看商品详细信息,使用【市场购买+序号】购买商品。\r\n你的现有{General.GameplayEquilibriumConstant.InGameCurrency}:{user.Inventory.Credits:0.##}"; + } + + FunGameService.ReleaseMarketSemaphoreSlim(); + FunGameService.SetUserConfigAndReleaseSemaphoreSlim(userid, pc, user); + + return msg; + } + else + { + FunGameService.ReleaseUserSemaphoreSlim(userid); + return noSaved; + } + } + catch (Exception e) + { + FunGameService.ReleaseMarketSemaphoreSlim(); + FunGameService.ReleaseUserSemaphoreSlim(userid); + Logger.LogError(e, "Error: {e}", e); + return busy; + } + } + + [HttpPost("marketbuyitem")] + public string MarketBuyItem([FromQuery] long userid = -1, [FromQuery] long itemid = 0, [FromQuery] int count = 1) + { + if (count <= 0) count = 1; + try + { + PluginConfig pc = FunGameService.GetUserConfig(userid, out _); + + if (pc.Count > 0) + { + User user = FunGameService.GetUser(pc); + + FunGameService.GetMarketSemaphoreSlim(); + + EntityModuleConfig emc = new("markets", "general"); + emc.LoadConfig(); + Market market = emc.Get("dokyo") ?? new("铎京集市"); + string msg = ""; + if (market.MarketItems.TryGetValue(itemid, out MarketItem? item) && item != null) + { + msg = FunGameService.MarketBuyItem(market, item, pc, user, count, out bool result); + if (result) + { + long userid2 = item.User; + try + { + PluginConfig pc2 = FunGameService.GetUserConfig(userid2, out _); + if (pc2.Count > 0) + { + User user2 = FunGameService.GetUser(pc2); + double amount = item.Price * count; + double fee = amount * 0.15; + double net = amount - fee; + user2.Inventory.Credits += net; + FunGameService.AddNotice(userid2, $"【市场通知】你售出了 {count} 件{item.Name}!净收入 {net:0.##} {General.GameplayEquilibriumConstant.InGameCurrency};市场收取手续费 {fee:0.##} {General.GameplayEquilibriumConstant.InGameCurrency}。"); + FunGameService.SetUserConfigButNotRelease(userid2, pc2, user2, false); + } + FunGameService.ReleaseUserSemaphoreSlim(userid2); + } + catch (Exception e) + { + FunGameService.ReleaseUserSemaphoreSlim(userid2); + Logger.LogError(e, "Error: {e}", e); + throw; + } + } + } + + emc.Add("dokyo", market); + emc.SaveConfig(); + FunGameService.ReleaseMarketSemaphoreSlim(); + FunGameService.SetUserConfigAndReleaseSemaphoreSlim(userid, pc, user); + + return msg; + } + else + { + FunGameService.ReleaseUserSemaphoreSlim(userid); + return noSaved; + } + } + catch (Exception e) + { + FunGameService.ReleaseMarketSemaphoreSlim(); + FunGameService.ReleaseUserSemaphoreSlim(userid); + Logger.LogError(e, "Error: {e}", e); + return busy; + } + } + + [HttpPost("marketdelistitem")] + public string MarketDelistItem([FromQuery] long userid = -1, [FromQuery] long itemid = 0) + { + try + { + PluginConfig pc = FunGameService.GetUserConfig(userid, out _); + + if (pc.Count > 0) + { + User user = FunGameService.GetUser(pc); + + FunGameService.GetMarketSemaphoreSlim(); + + EntityModuleConfig emc = new("markets", "general"); + emc.LoadConfig(); + Market market = emc.Get("dokyo") ?? new("铎京集市"); + string msg = ""; + if (market.MarketItems.TryGetValue(itemid, out MarketItem? item) && item != null && item.User == user.Id) + { + item.Status = MarketItemState.Delisted; + item.FinishTime = DateTime.Now; + for (int i = 0; i < item.Stock; i++) + { + FunGameService.AddItemToUserInventory(user, item.Item, copyLevel: true, useOriginalPrice: true, toExploreCache: false, toActivitiesCache: false); + } + msg = $"下架商品 {item.Name} 成功!{item.Stock} 个 {item.Name} 已回到你的库存。"; + } + else + { + msg = $"没有找到指定的商品,或者你不是该商品的卖家,下架失败。"; + } + + emc.Add("dokyo", market); + emc.SaveConfig(); + FunGameService.ReleaseMarketSemaphoreSlim(); + FunGameService.SetUserConfigAndReleaseSemaphoreSlim(userid, pc, user); + + return msg; + } + else + { + FunGameService.ReleaseUserSemaphoreSlim(userid); + return noSaved; + } + } + catch (Exception e) + { + FunGameService.ReleaseMarketSemaphoreSlim(); FunGameService.ReleaseUserSemaphoreSlim(userid); Logger.LogError(e, "Error: {e}", e); return busy; @@ -7563,6 +7832,130 @@ namespace Oshima.FunGame.WebAPI.Controllers } } + [HttpPost("chat")] + public string Chat([FromQuery] long uid = -1, [FromQuery] long uid2 = -1, [FromQuery] string msgTo = "") + { + try + { + PluginConfig pc = FunGameService.GetUserConfig(uid, out bool isTimeout); + if (isTimeout) + { + return busy; + } + + if (msgTo == "") + { + return "发送了空信息。"; + } + + if (msgTo.Length > 30) + { + return "超过 30 字符。"; + } + + string msg = ""; + if (pc.Count > 0) + { + User user = FunGameService.GetUser(pc); + + if (uid == uid2) + { + msg = "不能对自己发私信。"; + } + else if (FunGameConstant.UserIdAndUsername.TryGetValue(uid2, out User? user2) && user2 != null) + { + FunGameService.AddNotice(user2.Id, $"【私信】{DateTime.Now.ToString(General.GeneralDateTimeFormatChinese)} [ {user.Username} ] 说:{msgTo}"); + msg = $"私信已经成功发送至 [ {user2.Username} ] 的离线信箱。"; + } + else + { + msg = $"没有找到 UID 为 {uid2} 的玩家,无法发送私信。"; + } + + 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("chatname")] + public string Chat_Name([FromQuery] long uid = -1, [FromQuery] string name = "", [FromQuery] string msgTo = "") + { + try + { + PluginConfig pc = FunGameService.GetUserConfig(uid, out bool isTimeout); + if (isTimeout) + { + return busy; + } + + name = name.Trim(); + if (name == "") + { + return "未输入目标玩家的昵称。"; + } + + if (msgTo == "") + { + return "发送了空信息。"; + } + + if (msgTo.Length > 30) + { + return "超过 30 字符。"; + } + + string msg = ""; + if (pc.Count > 0) + { + User user = FunGameService.GetUser(pc); + + if (user.Username == name) + { + msg = "不能对自己发私信。"; + } + else if (FunGameConstant.UserIdAndUsername.Values.FirstOrDefault(u => u.Username == name) is User user2 && user2 != null) + { + FunGameService.AddNotice(user2.Id, $"【私信】{DateTime.Now.ToString(General.GeneralDateTimeFormatChinese)} [ {user.Username} ] 说:{msgTo}"); + msg = $"私信已经成功发送至 [ {user2.Username} ] 的离线信箱。"; + } + else + { + msg = $"没有找到昵称为 {name} 的玩家,无法发送私信。"; + } + + 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 6770d2f..6f1e9c3 100644 --- a/OshimaWebAPI/Services/RainBOTService.cs +++ b/OshimaWebAPI/Services/RainBOTService.cs @@ -3064,6 +3064,162 @@ namespace Oshima.FunGame.WebAPI.Services return result; } + if (e.Detail.StartsWith("市场出售")) + { + string detail = e.Detail.Replace("市场出售", "").Trim(); + string[] strings = detail.Split(' ', StringSplitOptions.TrimEntries | StringSplitOptions.RemoveEmptyEntries); + if (strings.Length < 2 || !double.TryParse(strings[^1], out double price)) + { + await SendAsync(e, "市场出售", "格式不正确,请使用:市场出售 <{物品序号...}> <价格>。多个物品序号使用空格隔开。"); + return result; + } + List ids = []; + for (int i = 0; i < strings.Length; i++) + { + if (i != strings.Length - 1 && int.TryParse(strings[i], out int id)) + { + ids.Add(id); + } + } + string msg = Controller.MarketSellItem(uid, price, [.. ids]); + if (msg != "") + { + await SendAsync(e, "市场出售", msg); + } + + return result; + } + + if (e.Detail.StartsWith("市场购买")) + { + string detail = e.Detail.Replace("市场购买", "").Trim(); + string[] strings = detail.Split(" ", StringSplitOptions.TrimEntries | StringSplitOptions.RemoveEmptyEntries); + int id = -1; + if (strings.Length > 0 && int.TryParse(strings[0].Trim(), out id)) + { + int count = 1; + if (strings.Length > 1) _ = int.TryParse(strings[1].Trim(), out count); + if (id != -1) + { + string msg = Controller.MarketBuyItem(uid, id, count); + if (msg != "") + { + await SendAsync(e, "市场购买", msg); + } + } + } + return result; + } + + if (e.Detail.StartsWith("市场查看")) + { + string detail = e.Detail.Replace("市场查看", "").Trim(); + string msg = ""; + if (int.TryParse(detail, out int id)) + { + msg = Controller.MarketItemInfo(uid, id); + if (msg.Trim() != "") + { + await SendAsync(e, "市场查看", msg); + } + } + return result; + } + + if (e.Detail.StartsWith("市场下架")) + { + string detail = e.Detail.Replace("市场下架", "").Trim(); + string msg = ""; + if (int.TryParse(detail, out int id)) + { + msg = Controller.MarketDelistItem(uid, id); + if (msg.Trim() != "") + { + await SendAsync(e, "市场下架", msg); + } + } + return result; + } + + if (e.Detail.StartsWith("我的市场")) + { + string detail = e.Detail.Replace("我的市场", "").Trim(); + string msg = ""; + if (int.TryParse(detail, out int page)) + { + msg = Controller.MarketShowListMySells(uid, page); + } + else + { + msg = Controller.MarketShowListMySells(uid, 1); + } + if (msg.Trim() != "") + { + await SendAsync(e, "我的市场", msg); + } + return result; + } + + if (e.Detail.StartsWith("市场")) + { + string detail = e.Detail.Replace("市场", "").Trim(); + string msg = ""; + if (int.TryParse(detail, out int page)) + { + msg = Controller.MarketShowList(uid, page); + } + else + { + msg = Controller.MarketShowList(uid, 1); + } + if (msg.Trim() != "") + { + await SendAsync(e, "市场", msg); + } + return result; + } + + if (e.Detail.StartsWith("私信")) + { + string detail = e.Detail.Replace("私信", "").Trim(); + string[] strings = detail.Split(" ", StringSplitOptions.TrimEntries | StringSplitOptions.RemoveEmptyEntries); + long id = -1; + string name = ""; + bool useId = false; + if (strings.Length > 0) + { + if (long.TryParse(strings[0].Trim(), out id)) + { + useId = true; + } + else + { + name = strings[0].Trim(); + } + } + else + { + await SendAsync(e, "私信", "你输入的格式不正确,正确格式:私信 <对方UID/昵称> <内容>"); + return result; + } + string content = ""; + if (strings.Length > 1) content = string.Join(" ", strings[1..]); + string msg = ""; + if (useId) + { + msg = Controller.Chat(uid, id, content); + } + else + { + msg = Controller.Chat_Name(uid, name, content); + } + if (msg != "") + { + await SendAsync(e, "私信", msg); + } + return result; + } + if (uid == GeneralSettings.Master && e.Detail.StartsWith("重载FunGame", StringComparison.CurrentCultureIgnoreCase)) { string msg = Controller.Relaod(uid);