diff --git a/OshimaModules/Items/ItemID.cs b/OshimaModules/Items/ItemID.cs index b0ab311..93aeeda 100644 --- a/OshimaModules/Items/ItemID.cs +++ b/OshimaModules/Items/ItemID.cs @@ -46,6 +46,7 @@ 原初之印 = 18011, 创生之印 = 18012, 法则精粹 = 18013, + 大师锻造券 = 18014, 探索许可 = 18999 } diff --git a/OshimaModules/Items/SpecialItem/大师锻造券.cs b/OshimaModules/Items/SpecialItem/大师锻造券.cs new file mode 100644 index 0000000..e80921f --- /dev/null +++ b/OshimaModules/Items/SpecialItem/大师锻造券.cs @@ -0,0 +1,14 @@ +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 => "锻造大师为你的锻造护航。当库存存在任意一张大师锻造券时,你可以在锻造时指定锻造的出货地区和品质,当锻造失败或锻造结果的出货地区和品质与你指定的不符时,将消耗一张大师锻造券并返还所有材料。\r\n" + + "使用指令:大师锻造 <目标地区编号> <目标品质序号>\r\n地区编号请通过【世界地图】查询,品质序号为 0~5(对应:普通/优秀/稀有/史诗/传说/神话)。"; + public override QualityType QualityType => QualityType.Orange; + } +} diff --git a/OshimaModules/Models/ForgeModel.cs b/OshimaModules/Models/ForgeModel.cs new file mode 100644 index 0000000..930acf9 --- /dev/null +++ b/OshimaModules/Models/ForgeModel.cs @@ -0,0 +1,52 @@ +using System.Text; +using Milimoe.FunGame.Core.Library.Constant; + +namespace Oshima.FunGame.OshimaModules.Models +{ + public class ForgeModel + { + public Guid Guid { get; set; } = Guid.NewGuid(); + public DateTime CreateTime { get; set; } = DateTime.Now; + public bool MasterForge { get; set; } = false; + public bool MasterForgingSuccess { get; set; } = false; + public Dictionary ForgeMaterials { get; set; } = []; + public long TargetRegionId { get; set; } = 0; + public QualityType TargetQuality { get; set; } = QualityType.White; + public Dictionary RegionProbabilities { get; set; } = []; + public bool Result { get; set; } = false; + public QualityType ResultQuality { get; set; } = QualityType.White; + public string ResultItem { get; set; } = ""; + public long ResultRegion { get; set; } = 0; + public string ResultString { get; set; } = ""; + public double ResultPoints => ResultPointsGeneral + ResultPointsSuccess + ResultPointsFail; + public double ResultPointsGeneral { get; set; } = 0; + public double ResultPointsSuccess { get; set; } = 0; + public double ResultPointsFail { get; set; } = 0; + + public string GetMaterials() + { + return $"☆--- 配方 ---☆\r\n{string.Join("\r\n", ForgeMaterials.Select(kv => $"{kv.Key}:{kv.Value} 个"))}"; + } + + public string GetForgingInfo() + { + StringBuilder builder = new(); + + builder.AppendLine($"☆★☆ 锻造信息 ☆★☆"); + builder.AppendLine($"创建时间:{CreateTime.ToString(General.GeneralDateTimeFormatChinese)}"); + if (MasterForge) + { + builder.AppendLine($"大师锻造:是"); + builder.AppendLine($"目标地区:{FunGameConstant.RegionsName[TargetRegionId]}"); + builder.AppendLine($"目标品质:{ItemSet.GetQualityTypeName(TargetQuality)}"); + } + else + { + builder.AppendLine($"大师锻造:否"); + } + builder.AppendLine(GetMaterials()); + + return builder.ToString().Trim(); + } + } +} diff --git a/OshimaServers/Service/FunGameConstant.cs b/OshimaModules/Models/FunGameConstant.cs similarity index 99% rename from OshimaServers/Service/FunGameConstant.cs rename to OshimaModules/Models/FunGameConstant.cs index 2ceddaf..8ef256a 100644 --- a/OshimaServers/Service/FunGameConstant.cs +++ b/OshimaModules/Models/FunGameConstant.cs @@ -5,9 +5,8 @@ using Oshima.Core.Constant; using Oshima.FunGame.OshimaModules.Effects.OpenEffects; using Oshima.FunGame.OshimaModules.Items; using Oshima.FunGame.OshimaModules.Regions; -using Oshima.FunGame.OshimaServers.Model; -namespace Oshima.FunGame.OshimaServers.Service +namespace Oshima.FunGame.OshimaModules.Models { public class FunGameConstant { @@ -334,6 +333,10 @@ namespace Oshima.FunGame.OshimaServers.Service new 铎京城() ]; + public static Dictionary RegionsName { get; } = []; + + public static Dictionary RegionsDifficulty { get; } = []; + private readonly static Dictionary _precomputeTotalExperience = []; public static Dictionary PrecomputeTotalExperience { diff --git a/OshimaServers/Model/LastStoreModel.cs b/OshimaModules/Models/LastStoreModel.cs similarity index 84% rename from OshimaServers/Model/LastStoreModel.cs rename to OshimaModules/Models/LastStoreModel.cs index e5db5ec..cb18a70 100644 --- a/OshimaServers/Model/LastStoreModel.cs +++ b/OshimaModules/Models/LastStoreModel.cs @@ -1,4 +1,4 @@ -namespace Oshima.FunGame.OshimaServers.Model +namespace Oshima.FunGame.OshimaModules.Models { public class LastStoreModel { diff --git a/OshimaServers/Model/NoticeModel.cs b/OshimaModules/Models/NoticeModel.cs similarity index 94% rename from OshimaServers/Model/NoticeModel.cs rename to OshimaModules/Models/NoticeModel.cs index 946ba58..21c2f23 100644 --- a/OshimaServers/Model/NoticeModel.cs +++ b/OshimaModules/Models/NoticeModel.cs @@ -2,7 +2,7 @@ using Milimoe.FunGame.Core.Interface.Entity; using Milimoe.FunGame.Core.Library.Constant; -namespace Oshima.FunGame.OshimaServers.Model +namespace Oshima.FunGame.OshimaModules.Models { public class NoticeModel : BaseEntity { diff --git a/OshimaModules/Modules/ItemModule.cs b/OshimaModules/Modules/ItemModule.cs index 9feff30..01020eb 100644 --- a/OshimaModules/Modules/ItemModule.cs +++ b/OshimaModules/Modules/ItemModule.cs @@ -55,6 +55,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/OshimaRegion.cs b/OshimaModules/Regions/OshimaRegion.cs index dfdd6a4..2060941 100644 --- a/OshimaModules/Regions/OshimaRegion.cs +++ b/OshimaModules/Regions/OshimaRegion.cs @@ -30,6 +30,11 @@ namespace Oshima.FunGame.OshimaModules.Regions public virtual void SaveGlobalStore(Store store, string storeName) { + } + + public virtual void UpdateNextRefreshGoods() + { + } public override string ToString() diff --git a/OshimaModules/Regions/Players.cs b/OshimaModules/Regions/Players.cs index 8ba7313..b221867 100644 --- a/OshimaModules/Regions/Players.cs +++ b/OshimaModules/Regions/Players.cs @@ -1,5 +1,7 @@ using Milimoe.FunGame.Core.Api.Utility; using Milimoe.FunGame.Core.Entity; +using Oshima.FunGame.OshimaModules.Items; +using Oshima.FunGame.OshimaModules.Models; namespace Oshima.FunGame.OshimaModules.Regions { @@ -30,7 +32,11 @@ namespace Oshima.FunGame.OshimaModules.Regions if (template is null) { - return null; + if (storeName == "dokyo_forge") + { + template = CreateNewForgeStore(); + } + else return null; } if (template.NextRefreshDate < DateTime.Now) @@ -99,5 +105,47 @@ namespace Oshima.FunGame.OshimaModules.Regions storeTemplate.Add(storeName, store); storeTemplate.SaveConfig(); } + + public override void UpdateNextRefreshGoods() + { + EntityModuleConfig storeTemplate = new("stores", "dokyo"); + storeTemplate.LoadConfig(); + Store? store = storeTemplate.Get("dokyo_forge"); + if (store is null) + { + store = CreateNewForgeStore(); + } + else + { + Store newStore = CreateNewForgeStore(); + store.NextRefreshGoods.Clear(); + store.CopyGoodsToNextRefreshGoods(newStore.Goods); + } + storeTemplate.Add("dokyo_forge", store); + storeTemplate.SaveConfig(); + } + + private static Store CreateNewForgeStore() + { + Store store = new("锻造积分商店") + { + AutoRefresh = true, + RefreshInterval = 3, + NextRefreshDate = DateTime.Today.AddHours(4), + GlobalStock = true, + }; + Item item = new 大师锻造券(); + store.AddItem(item, -1); + store.SetPrice(1, "锻造积分", 400); + Dictionary items = FunGameConstant.ExploreItems.OrderBy(o => Random.Shared.Next()).Take(5).ToDictionary(kv => kv.Key, kv => kv.Value.OrderBy(o => Random.Shared.Next()).First()); + int i = 2; + foreach (OshimaRegion region in items.Keys) + { + store.AddItem(items[region], -1); + store.SetPrice(i, "锻造积分", 3 * ((int)region.Difficulty + 1)); + i++; + } + return store; + } } } diff --git a/OshimaServers/AnonymousServer.cs b/OshimaServers/AnonymousServer.cs index b9b6535..3a0660c 100644 --- a/OshimaServers/AnonymousServer.cs +++ b/OshimaServers/AnonymousServer.cs @@ -118,6 +118,7 @@ namespace Oshima.FunGame.OshimaServers // 刷新活动缓存 FunGameService.GetEventCenter(); FunGameService.RefreshNotice(); + FunGameService.PreRefreshStore(); }); TaskScheduler.Shared.AddTask("上九", new TimeSpan(9, 0, 0), () => { diff --git a/OshimaServers/Model/ForgeModel.cs b/OshimaServers/Model/ForgeModel.cs deleted file mode 100644 index 9cef327..0000000 --- a/OshimaServers/Model/ForgeModel.cs +++ /dev/null @@ -1,23 +0,0 @@ -using Milimoe.FunGame.Core.Library.Constant; - -namespace Oshima.FunGame.OshimaServers.Model -{ - public class ForgeModel - { - public Guid Guid { get; set; } = Guid.NewGuid(); - public bool MasterForge { get; set; } = false; - public Dictionary ForgeMaterials { get; set; } = []; - public long TargetRegionId { get; set; } = 0; - public QualityType TargetQuality { get; set; } = QualityType.White; - public Dictionary RegionProbabilities { get; set; } = []; - public bool Result { get; set; } = false; - public QualityType ResultQuality { get; set; } = QualityType.White; - public string ResultItem { get; set; } = ""; - public long ResultRegion { get; set; } = 0; - public string ResultString { get; set; } = ""; - public double ResultPoints => ResultPointsGeneral + ResultPointsSuccess + ResultPointsFail; - public double ResultPointsGeneral { get; set; } = 0; - public double ResultPointsSuccess { get; set; } = 0; - public double ResultPointsFail { get; set; } = 0; - } -} diff --git a/OshimaServers/Service/FunGameActionQueue.cs b/OshimaServers/Service/FunGameActionQueue.cs index 1ec1202..560a8cc 100644 --- a/OshimaServers/Service/FunGameActionQueue.cs +++ b/OshimaServers/Service/FunGameActionQueue.cs @@ -2,6 +2,7 @@ using Milimoe.FunGame.Core.Entity; using Milimoe.FunGame.Core.Model; using Oshima.FunGame.OshimaModules.Effects.OpenEffects; +using Oshima.FunGame.OshimaModules.Models; namespace Oshima.FunGame.OshimaServers.Service { diff --git a/OshimaServers/Service/FunGameService.cs b/OshimaServers/Service/FunGameService.cs index bdf5610..99b955c 100644 --- a/OshimaServers/Service/FunGameService.cs +++ b/OshimaServers/Service/FunGameService.cs @@ -1,5 +1,4 @@ using System.Text; -using Milimoe.FunGame; using Milimoe.FunGame.Core.Api.Transmittal; using Milimoe.FunGame.Core.Api.Utility; using Milimoe.FunGame.Core.Entity; @@ -8,6 +7,7 @@ using Oshima.Core.Constant; using Oshima.FunGame.OshimaModules.Characters; using Oshima.FunGame.OshimaModules.Effects.OpenEffects; using Oshima.FunGame.OshimaModules.Items; +using Oshima.FunGame.OshimaModules.Models; using Oshima.FunGame.OshimaModules.Regions; using Oshima.FunGame.OshimaModules.Skills; using Oshima.FunGame.OshimaModules.Units; @@ -65,7 +65,7 @@ namespace Oshima.FunGame.OshimaServers.Service FunGameConstant.Items.AddRange(exItems.Values.Where(i => (int)i.ItemType > 4)); FunGameConstant.Items.AddRange([new 小经验书(), new 中经验书(), new 大经验书(), new 升华之印(), new 流光之印(), new 永恒之印(), new 技能卷轴(), new 智慧之果(), new 奥术符文(), new 混沌之核(), new 小回复药(), new 中回复药(), new 大回复药(), new 魔力填充剂1(), new 魔力填充剂2(), new 魔力填充剂3(), new 能量饮料1(), new 能量饮料2(), new 能量饮料3(), new 年夜饭(), new 蛇年大吉(), new 新春快乐(), new 毕业礼包(), - new 复苏药1(), new 复苏药2(), new 复苏药3(), new 全回复药(), new 魔法卡礼包(), new 奖券(), new 十连奖券(), new 改名卡(), new 原初之印(), new 创生之印(), new 法则精粹() + new 复苏药1(), new 复苏药2(), new 复苏药3(), new 全回复药(), new 魔法卡礼包(), new 奖券(), new 十连奖券(), new 改名卡(), new 原初之印(), new 创生之印(), new 法则精粹(), new 大师锻造券() ]); FunGameConstant.AllItems.AddRange(FunGameConstant.Equipment); @@ -85,6 +85,8 @@ namespace Oshima.FunGame.OshimaServers.Service foreach (OshimaRegion region in FunGameConstant.Regions) { + FunGameConstant.RegionsName[region.Id] = region.Name; + FunGameConstant.RegionsDifficulty[region.Id] = region.Difficulty; IEnumerable items = FunGameConstant.AllItems.Where(i => i.Others.TryGetValue("region", out object? value) && int.TryParse(value.ToString(), out int rid) && rid == region.Id).Select(i => i.Copy()).OrderByDescending(i => i.QualityType); foreach (Item item in items) { @@ -94,6 +96,8 @@ namespace Oshima.FunGame.OshimaServers.Service foreach (OshimaRegion region in FunGameConstant.PlayerRegions) { + FunGameConstant.RegionsName[region.Id] = region.Name; + FunGameConstant.RegionsDifficulty[region.Id] = region.Difficulty; IEnumerable items; if (region.Id == 0) { @@ -2144,6 +2148,23 @@ namespace Oshima.FunGame.OshimaServers.Service return $"你的{General.GameplayEquilibriumConstant.InGameMaterial}不足 {reduce} 呢,无法购买【{goods.Name}】!"; } } + else if (needy == "锻造积分") + { + double reduce = Calculation.Round2Digits(goods.Prices[needy] * count); + if (pc.TryGetValue("forgepoints", out object? value) && double.TryParse(value.ToString(), out double points) && points >= reduce) + { + points -= reduce; + pc.Add("forgepoints", points); + } + else + { + return $"你的{needy}不足 {reduce} 呢,无法购买【{goods.Name}】!"; + } + } + else + { + return $"不支持的货币类型:{needy},无法购买【{goods.Name}】!"; + } } foreach (Item item in goods.Items) @@ -3597,7 +3618,7 @@ namespace Oshima.FunGame.OshimaServers.Service case InstanceType.Material: for (int i = 0; i < characterCount; i++) { - award += Random.Shared.Next(4, 9) * difficulty; + award += Random.Shared.Next(3, 7) * difficulty; } user.Inventory.Materials += award; builder.AppendLine($"{award} {General.GameplayEquilibriumConstant.InGameMaterial}!"); @@ -4001,7 +4022,7 @@ namespace Oshima.FunGame.OshimaServers.Service return msg; } - public static void GenerateForgeResult(User user, ForgeModel model) + public static void GenerateForgeResult(User user, ForgeModel model, bool simulate = false) { if (model.ForgeMaterials.Count == 0) { @@ -4030,8 +4051,8 @@ namespace Oshima.FunGame.OshimaServers.Service } OshimaRegion resultRegion; - bool isSimplyForge = regionMaterialCount.Count == 1; int count = 0; + bool isSimplyForge = regionMaterialCount.Count == 1; if (isSimplyForge) { OshimaRegion region = regionMaterialCount.Keys.First(); @@ -4041,7 +4062,8 @@ namespace Oshima.FunGame.OshimaServers.Service model.ResultString = $"提交的锻造材料不足 {FunGameConstant.ForgeNeedy[QualityType.White]} 个,请重新提交。"; return; } - resultRegion = FunGameConstant.ExploreItems.Keys.First(r => r.Id == region.Id); + model.RegionProbabilities = regionMaterialCount.ToDictionary(kv => kv.Key.Id, kv => (double)kv.Value / count); + resultRegion = regionMaterialCount.Keys.First(); } else { @@ -4064,8 +4086,8 @@ namespace Oshima.FunGame.OshimaServers.Service break; } } - model.ResultRegion = resultRegion.Id; } + model.ResultRegion = resultRegion.Id; if (count >= FunGameConstant.ForgeNeedy[QualityType.Red]) { @@ -4090,13 +4112,13 @@ namespace Oshima.FunGame.OshimaServers.Service model.ResultPointsGeneral = count * 0.3; string resultItemString = ""; - Item? resultItem = resultRegion.Items.OrderBy(o => Random.Shared.Next()).First(i => i.QualityType == model.ResultQuality); + Item? resultItem = resultRegion.Items.OrderBy(o => Random.Shared.Next()).FirstOrDefault(i => i.QualityType == model.ResultQuality); if (resultItem != null) { string itemquality = ItemSet.GetQualityTypeName(resultItem.QualityType); string itemtype = ItemSet.GetItemTypeName(resultItem.ItemType) + (resultItem.ItemType == ItemType.Weapon && resultItem.WeaponType != WeaponType.None ? "-" + ItemSet.GetWeaponTypeName(resultItem.WeaponType) : ""); if (itemtype != "") itemtype = $"|{itemtype}"; - resultItemString = $"{itemtype}{resultItem.Name}"; + resultItemString = $"[{itemquality}{itemtype}]{resultItem.Name}"; model.ResultItem = resultItem.Name; } @@ -4114,7 +4136,10 @@ namespace Oshima.FunGame.OshimaServers.Service model.ResultPointsSuccess = count - FunGameConstant.ForgeNeedy[model.ResultQuality]; model.ResultString = $"锻造成功!本次锻造物品的品质为:{ItemSet.GetQualityTypeName(model.ResultQuality)},地区为:{resultRegion.Name},获得了:{resultItemString}!\r\n" + $"本次提交 {count} 个地区 [ {resultRegion.Name} ] 的锻造材料,获得 {model.ResultPoints:0.##} 点锻造积分。"; - AddItemToUserInventory(user, resultItem); + if (!simulate) + { + AddItemToUserInventory(user, resultItem); + } } } else @@ -4123,17 +4148,49 @@ namespace Oshima.FunGame.OshimaServers.Service { model.ResultPointsFail = count * 0.2; model.ResultString = $"锻造失败!本次锻造物品的品质为:{ItemSet.GetQualityTypeName(model.ResultQuality)},地区为:{resultRegion.Name},该地区不存在该品质的物品!\r\n" + - $"本次提交 {regionContributions.Count} 个地区的锻造材料({string.Join("、", regionMaterialCount.Select(kv => $"{kv.Value} 个来自{kv.Key}"))}),总共 {count} 有效材料用量,获得 {model.ResultPoints:0.##} 点锻造积分。"; + $"本次提交 {regionContributions.Count} 个地区的锻造材料({string.Join("、", regionMaterialCount.Select(kv => $"{kv.Value} 个来自{kv.Key.Name}"))}),总共 {count} 有效材料用量,获得 {model.ResultPoints:0.##} 点锻造积分。"; } else { model.Result = true; model.ResultPointsSuccess = count - FunGameConstant.ForgeNeedy[model.ResultQuality]; model.ResultString = $"锻造成功!本次锻造物品的品质为:{ItemSet.GetQualityTypeName(model.ResultQuality)},地区为:{resultRegion.Name},获得了:{resultItemString}!\r\n" + - $"本次提交 {regionContributions.Count} 个地区的锻造材料({string.Join("、", regionMaterialCount.Select(kv => $"{kv.Value} 个来自{kv.Key}"))}),总共 {count} 有效材料用量,获得 {model.ResultPoints:0.##} 点锻造积分。"; - AddItemToUserInventory(user, resultItem); + $"本次提交 {regionContributions.Count} 个地区的锻造材料({string.Join("、", regionMaterialCount.Select(kv => $"{kv.Value} 个来自{kv.Key.Name}"))}),总共 {count} 有效材料用量,获得 {model.ResultPoints:0.##} 点锻造积分。"; + if (!simulate) + { + AddItemToUserInventory(user, resultItem); + } } } + + if (model.MasterForge) + { + if (model.Result && model.TargetRegionId == resultRegion.Id && model.TargetQuality == model.ResultQuality) + { + model.ResultString += "\r\n大师锻造券正在时刻为你护航。"; + } + else if (user.Inventory.Items.FirstOrDefault(i => i.Name == "大师锻造券") is Item item) + { + model.MasterForgingSuccess = true; + model.ResultString += "\r\n发动了大师锻造券的效果!为你返还了所有的锻造材料!"; + user.Inventory.Items.Remove(item); + } + else + { + model.ResultString += "\r\n你似乎没有大师锻造券,因此本次锻造一切如常。"; + } + } + + if (simulate) + { + model.ResultString += $"\r\n\r\n☆--- 模拟调试信息 ---☆\r\n本次锻造的出货地区概率:\r\n" + + $"{string.Join("\r\n", model.RegionProbabilities.Select(kv => $"{kv.Key}. {FunGameConstant.RegionsName[kv.Key]}:{kv.Value * 100:0.##}%({CharacterSet.GetRarityTypeName(FunGameConstant.RegionsDifficulty[kv.Key])})"))}\r\n" + + $"有效材料用量贡献:" + + (isSimplyForge ? $"单一锻造模式\r\n{string.Join("\r\n", model.ForgeMaterials.Select(kv => $"{kv.Key}:{kv.Value} 个"))}" : + $"混合锻造模式\r\n{string.Join("\r\n", regionMaterialEquivalent.Select(kv => $"{kv.Key}:{kv.Value:0.##} 有效材料用量"))}") + "\r\n" + + $"出货品质:{ItemSet.GetQualityTypeName(model.ResultQuality)}\r\n所需有效材料用量:{FunGameConstant.ForgeNeedy[model.ResultQuality]}\r\n总计有效材料用量:{count}\r\n" + + $"基础锻造积分:{count * 0.3:0.##} 点\r\n失败积分补偿:{count * 0.2:0.##} 点\r\n超量积分补偿:{count - FunGameConstant.ForgeNeedy[model.ResultQuality]:0.##} 点"; + } } public static void RefreshNotice() @@ -4287,6 +4344,14 @@ namespace Oshima.FunGame.OshimaServers.Service } } + public static void PreRefreshStore() + { + foreach (OshimaRegion region in FunGameConstant.PlayerRegions) + { + region.UpdateNextRefreshGoods(); + } + } + public static void OnUserConfigSaving(PluginConfig pc, User user) { DateTime now = DateTime.Now; diff --git a/OshimaServers/Service/FunGameSimulation.cs b/OshimaServers/Service/FunGameSimulation.cs index af77099..618ee8b 100644 --- a/OshimaServers/Service/FunGameSimulation.cs +++ b/OshimaServers/Service/FunGameSimulation.cs @@ -6,6 +6,7 @@ using Milimoe.FunGame.Core.Entity; using Milimoe.FunGame.Core.Library.Constant; using Milimoe.FunGame.Core.Model; using Oshima.FunGame.OshimaModules.Effects.OpenEffects; +using Oshima.FunGame.OshimaModules.Models; using Oshima.FunGame.OshimaModules.Skills; namespace Oshima.FunGame.OshimaServers.Service diff --git a/OshimaWebAPI/Controllers/FunGameController.cs b/OshimaWebAPI/Controllers/FunGameController.cs index 89d54bf..0b27e23 100644 --- a/OshimaWebAPI/Controllers/FunGameController.cs +++ b/OshimaWebAPI/Controllers/FunGameController.cs @@ -6,14 +6,13 @@ using Microsoft.Extensions.Logging; using Milimoe.FunGame.Core.Api.Transmittal; using Milimoe.FunGame.Core.Api.Utility; using Milimoe.FunGame.Core.Entity; -using Milimoe.FunGame.Core.Interface.Entity; -using Milimoe.FunGame.Core.Library.Common.Event; using Milimoe.FunGame.Core.Library.Constant; using Milimoe.FunGame.Core.Library.SQLScript.Entity; using Oshima.Core.Configs; using Oshima.Core.Constant; using Oshima.FunGame.OshimaModules.Characters; using Oshima.FunGame.OshimaModules.Items; +using Oshima.FunGame.OshimaModules.Models; using Oshima.FunGame.OshimaModules.Regions; using Oshima.FunGame.OshimaServers.Model; using Oshima.FunGame.OshimaServers.Service; @@ -3059,7 +3058,7 @@ namespace Oshima.FunGame.WebAPI.Controllers { itemTrading = SQLService.GetUserItemGuids(sql, userid); } - IEnumerable items = user.Inventory.Items.Where(i => i.Name == name && i.Character is null && (!allowLock || !i.IsLock) && !itemTrading.Contains(i.Guid)); + IEnumerable items = user.Inventory.Items.Where(i => i.Name == name && i.Character is null && (!i.IsLock || i.IsLock == allowLock) && !itemTrading.Contains(i.Guid)); if (!items.Any()) { FunGameService.ReleaseUserSemaphoreSlim(userid); @@ -3367,7 +3366,7 @@ namespace Oshima.FunGame.WebAPI.Controllers if (user.Inventory.Training.Count == 0) { FunGameService.ReleaseUserSemaphoreSlim(userid); - return $"你目前没有角色在练级中,请使用【开启练级+角色序号】指令进行练级。"; + return $"你目前没有角色在练级中,请使用【开始练级+角色序号】指令进行练级。"; } long cid = user.Inventory.Training.Keys.First(); @@ -3445,7 +3444,7 @@ namespace Oshima.FunGame.WebAPI.Controllers if (user.Inventory.Training.Count == 0) { - return $"你目前没有角色在练级中,请使用【开启练级+角色序号】指令进行练级。"; + return $"你目前没有角色在练级中,请使用【开始练级+角色序号】指令进行练级。"; } long cid = user.Inventory.Training.Keys.First(); @@ -5412,8 +5411,17 @@ namespace Oshima.FunGame.WebAPI.Controllers itemMsg += $"[ {count} ] {newItem.ToString(false, true)}".Trim(); } msg = good.ToString(user).Split("包含物品:")[0].Trim(); + int buyCount = 0; + if (user != null) + { + good.UsersBuyCount.TryGetValue(user.Id, out buyCount); + } msg += $"\r\n包含物品:\r\n" + itemMsg + - $"\r\n剩余库存:{(good.Stock == -1 ? "不限量提供" : good.Stock)}"; + $"\r\n剩余库存:{(good.Stock == -1 ? "不限" : good.Stock)}(已购:{buyCount})"; + if (good.Quota > 0) + { + msg += $"\r\n限购数量:{good.Quota}"; + } } else { @@ -7026,10 +7034,11 @@ namespace Oshima.FunGame.WebAPI.Controllers if (msg != "") msg += "\r\n"; msg += $"本次秘境挑战消耗探索许可 {reduce} 个,你的剩余探索许可:{exploreTimes} 个。"; } + + pc.Add("exploreTimes", exploreTimes); } } - pc.Add("exploreTimes", exploreTimes); FunGameService.SetUserConfigAndReleaseSemaphoreSlim(userid, pc, user); return msg; @@ -7157,8 +7166,17 @@ namespace Oshima.FunGame.WebAPI.Controllers itemMsg += $"[ {count} ] {newItem.ToString(false, true)}".Trim(); } msg = good.ToString(user).Split("包含物品:")[0].Trim(); + int buyCount = 0; + if (user != null) + { + good.UsersBuyCount.TryGetValue(user.Id, out buyCount); + } msg += $"\r\n包含物品:\r\n" + itemMsg + - $"\r\n剩余库存:{(good.Stock == -1 ? "不限量提供" : good.Stock)}"; + $"\r\n剩余库存:{(good.Stock == -1 ? "不限" : good.Stock)}(已购:{buyCount})"; + if (good.Quota > 0) + { + msg += $"\r\n限购数量:{good.Quota}"; + } } else { @@ -7179,6 +7197,372 @@ namespace Oshima.FunGame.WebAPI.Controllers } } + [HttpPost("forgeitemcreate")] + public string ForgeItem_Create([FromQuery] long uid = -1, [FromBody] Dictionary? materials = null) + { + materials ??= []; + + try + { + PluginConfig pc = FunGameService.GetUserConfig(uid, out bool isTimeout); + if (isTimeout) + { + return busy; + } + + string msg = ""; + if (pc.Count > 0) + { + User user = FunGameService.GetUser(pc); + + PluginConfig pc2 = new("forging", uid.ToString()); + pc2.LoadConfig(); + ForgeModel? model = pc2.Get("now"); + if (model != null) + { + msg = $"你已经有一个尚未完成的锻造配方:\r\n{model.GetForgingInfo()}\r\n请先【确认开始锻造】或者【取消锻造】后再创建新的配方!"; + } + else + { + model = new() + { + ForgeMaterials = materials + }; + pc2.Add("now", model); + pc2.SaveConfig(); + msg = $"创建配方成功!\r\n{model.GetForgingInfo()}\r\n接下来,你可以【确认开始锻造】或者【取消锻造】、【模拟锻造】。"; + } + + 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("forgeitemmaster")] + public string ForgeItem_Master([FromQuery] long uid = -1, [FromQuery] long rid = 0, [FromQuery] int q = 0) + { + try + { + PluginConfig pc = FunGameService.GetUserConfig(uid, out bool isTimeout); + if (isTimeout) + { + return busy; + } + + if (!FunGameConstant.Regions.Any(r => r.Id == rid)) + { + return $"指定了一个虚无地区,请检查【世界地图】,然后重新输入!"; + } + + QualityType type = QualityType.White; + if (q >= 0 && q <= 5) + { + type = (QualityType)q; + } + else + { + return $"指定了一个无效的品质序号,请重新输入!"; + } + + string msg = ""; + if (pc.Count > 0) + { + User user = FunGameService.GetUser(pc); + + PluginConfig pc2 = new("forging", uid.ToString()); + pc2.LoadConfig(); + ForgeModel? model = pc2.Get("now"); + if (model is null) + { + msg = $"你还没有创建锻造配方!大师对此无能为力。"; + } + else + { + model.MasterForge = true; + model.TargetRegionId = rid; + model.TargetQuality = type; + pc2.Add("now", model); + pc2.SaveConfig(); + msg = $"指定大师锻造的目标成功!\r\n{model.GetForgingInfo()}"; + } + + 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); + } + } + + [HttpGet("forgeiteminfo")] + public string ForgeItem_Info([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); + + PluginConfig pc2 = new("forging", uid.ToString()); + pc2.LoadConfig(); + ForgeModel? model = pc2.Get("now"); + if (model is null) + { + msg = $"你还没有创建锻造配方。"; + } + else + { + msg = model.GetForgingInfo(); + } + + double points = 0; + if (pc.TryGetValue("forgepoints", out object? value) && double.TryParse(value.ToString(), out double temp)) + { + points = temp; + } + + if (msg != "") msg += "\r\n"; + msg += $"你现在拥有的锻造积分:{points:0.##} 点。"; + + 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("forgeitemcancel")] + public string ForgeItem_Cancel([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); + + PluginConfig pc2 = new("forging", uid.ToString()); + pc2.LoadConfig(); + ForgeModel? model = pc2.Get("now"); + if (model is null) + { + msg = $"你还没有创建锻造配方。"; + } + else + { + pc2.Remove("now"); + pc2.SaveConfig(); + msg = $"取消现有的锻造配方成功!\r\n{model.GetForgingInfo()}"; + } + + 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("forgeitemsimulate")] + public string ForgeItem_Simulate([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); + FunGameService.SetUserConfigButNotRelease(uid, pc, user); + + PluginConfig pc2 = new("forging", uid.ToString()); + pc2.LoadConfig(); + ForgeModel? model = pc2.Get("now"); + if (model is null) + { + msg = $"你还没有创建锻造配方。"; + } + else + { + msg = $"{model.GetForgingInfo()}\r\n\r\n正在启动模拟……\r\n\r\n☆★☆模拟结果☆★☆\r\n"; + FunGameService.GenerateForgeResult(user, model, true); + msg += model.ResultString; + } + return msg.Trim(); + } + else + { + return noSaved; + } + } + catch (Exception e) + { + Logger.LogError(e, "Error: {e}", e); + return busy; + } + finally + { + FunGameService.ReleaseUserSemaphoreSlim(uid); + } + } + + [HttpPost("forgeitemcomplete")] + public string ForgeItem_Complete([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); + + PluginConfig pc2 = new("forging", uid.ToString()); + pc2.LoadConfig(); + ForgeModel? model = pc2.Get("now"); + if (model is null) + { + msg = $"你还没有创建锻造配方。"; + } + else + { + List willDelete = []; + // 检查材料 + foreach (string material in model.ForgeMaterials.Keys) + { + IEnumerable items = user.Inventory.Items.Where(i => i.Name == material); + if (items.Count() < model.ForgeMaterials[material]) + { + msg += $"{material}不足 {model.ForgeMaterials[material]} 个!库存中只有 {items.Count()} 个。\r\n"; + } + else + { + willDelete.AddRange(items.TakeLast(model.ForgeMaterials[material])); + } + } + + if (msg != "") + { + msg = $"锻造失败,原因:\r\n{msg}\r\n你可以在完成材料收集后,重新【确认开始锻造】,或者【取消锻造】来创建一个新的配方。"; + } + else + { + msg = $"{model.GetForgingInfo()}\r\n\r\n☆★☆锻造结果☆★☆\r\n"; + FunGameService.GenerateForgeResult(user, model); + msg += model.ResultString; + + if (!model.MasterForgingSuccess) + { + // 删除材料 + foreach (Item item in willDelete) + { + user.Inventory.Items.Remove(item); + } + } + + double points = 0; + if (pc.TryGetValue("forgepoints", out object? value) && double.TryParse(value.ToString(), out points)) + { + points += model.ResultPoints; + } + else points = model.ResultPoints; + pc.Add("forgepoints", points); + + pc2.Remove("now"); + pc2.SaveConfig(); + } + } + + FunGameService.SetUserConfigButNotRelease(uid, pc, user); + return msg.Trim(); + } + 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 512d9b7..6770d2f 100644 --- a/OshimaWebAPI/Services/RainBOTService.cs +++ b/OshimaWebAPI/Services/RainBOTService.cs @@ -8,7 +8,7 @@ using Milimoe.FunGame.Core.Entity; using Milimoe.FunGame.Core.Library.Constant; using Oshima.Core.Configs; using Oshima.Core.Constant; -using Oshima.FunGame.OshimaServers.Model; +using Oshima.FunGame.OshimaModules.Models; using Oshima.FunGame.OshimaServers.Service; using Oshima.FunGame.WebAPI.Constant; using Oshima.FunGame.WebAPI.Controllers; @@ -230,6 +230,7 @@ namespace Oshima.FunGame.WebAPI.Services EndTime = DateTime.Today.AddHours(3).AddMinutes(59).AddSeconds(59).AddDays(days) }); FunGameService.Notices.SaveConfig(); + FunGameService.RefreshNotice(); await SendAsync(e, "添加公告", $"添加完毕,请查看【公告】列表!"); return true; } @@ -2940,12 +2941,46 @@ namespace Oshima.FunGame.WebAPI.Services return result; } - if (e.Detail.StartsWith("锻造配方")) + if (e.Detail.StartsWith("模拟锻造配方")) { - string pattern = @"锻造配方\s*(?:(?[^\d\s][^\d]*?)\s+(?\d+)\s*)+"; + string detail = e.Detail.Replace("模拟", "").Trim(); + string pattern = @"锻造配方\s*(?:(?[^\d]+?)\s*(?\d+)\s*)+"; + Dictionary recipeItems = []; + + MatchCollection matches = Regex.Matches(detail, pattern, RegexOptions.ExplicitCapture); + foreach (Match match in matches) + { + CaptureCollection itemNames = match.Groups["itemName"].Captures; + CaptureCollection counts = match.Groups["count"].Captures; + + for (int i = 0; i < itemNames.Count; i++) + { + string itemName = itemNames[i].Value.Trim(); + if (int.TryParse(counts[i].Value, out int count)) + { + recipeItems[itemName] = count; + } + } + } + + User user = Factory.GetUser(); + ForgeModel model = new() + { + ForgeMaterials = recipeItems + }; + FunGameService.GenerateForgeResult(user, model, true); + if (model.ResultString != "") + { + await SendAsync(e, "模拟锻造配方", model.ResultString); + } + return result; + } + + if (e.Detail.StartsWith("锻造配方")) + { + string pattern = @"锻造配方\s*(?:(?[^\d]+?)\s*(?\d+)\s*)+"; Dictionary recipeItems = []; - StringBuilder builder = new(); MatchCollection matches = Regex.Matches(e.Detail, pattern, RegexOptions.ExplicitCapture); foreach (Match match in matches) { @@ -2958,28 +2993,77 @@ namespace Oshima.FunGame.WebAPI.Services if (int.TryParse(counts[i].Value, out int count)) { recipeItems[itemName] = count; - builder.AppendLine($"{itemName} x{count}"); } } } - User user = Factory.GetUser(); - ForgeModel model = new() + string msg = Controller.ForgeItem_Create(uid, recipeItems); + if (msg != "") { - ForgeMaterials = recipeItems - }; - FunGameService.GenerateForgeResult(user, model); - if (model.ResultString != "") - { - await SendAsync(e, "锻造配方", model.ResultString); - } - else - { - await SendAsync(e, "锻造配方", $"失败了……\r\n{builder}"); + await SendAsync(e, "锻造配方", msg); } return result; } + if (e.Detail.StartsWith("模拟锻造")) + { + string msg = Controller.ForgeItem_Simulate(uid); + if (msg != "") + { + await SendAsync(e, "模拟锻造", msg); + } + return result; + } + + if (e.Detail.StartsWith("取消锻造")) + { + string msg = Controller.ForgeItem_Cancel(uid); + if (msg != "") + { + await SendAsync(e, "取消锻造", msg); + } + return result; + } + + if (e.Detail.StartsWith("确认开始锻造")) + { + string msg = Controller.ForgeItem_Complete(uid); + if (msg != "") + { + await SendAsync(e, "确认开始锻造", msg); + } + return result; + } + + if (e.Detail.StartsWith("锻造信息")) + { + string msg = Controller.ForgeItem_Info(uid); + 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 r = -1, q = -1; + if (strings.Length > 0 && int.TryParse(strings[0].Trim(), out r) && strings.Length > 1 && int.TryParse(strings[1].Trim(), out q)) + { + if (r != -1 && q != -1) + { + string msg = Controller.ForgeItem_Master(uid, r, q); + if (msg != "") + { + await SendAsync(e, "大师锻造", msg); + } + } + } + return result; + } + if (uid == GeneralSettings.Master && e.Detail.StartsWith("重载FunGame", StringComparison.CurrentCultureIgnoreCase)) { string msg = Controller.Relaod(uid);