项目结构调整,完善锻造系统

This commit is contained in:
milimoe 2025-07-26 02:36:20 +08:00
parent 59836e5e69
commit c94c9b1c2f
Signed by: milimoe
GPG Key ID: 9554D37E4B8991D0
16 changed files with 703 additions and 66 deletions

View File

@ -46,6 +46,7 @@
= 18011, = 18011,
= 18012, = 18012,
= 18013, = 18013,
= 18014,
= 18999 = 18999
} }

View File

@ -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地区编号请通过【世界地图】查询品质序号为 05对应普通/优秀/稀有/史诗/传说/神话)。";
public override QualityType QualityType => QualityType.Orange;
}
}

View File

@ -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<string, int> ForgeMaterials { get; set; } = [];
public long TargetRegionId { get; set; } = 0;
public QualityType TargetQuality { get; set; } = QualityType.White;
public Dictionary<long, double> 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();
}
}
}

View File

@ -5,9 +5,8 @@ using Oshima.Core.Constant;
using Oshima.FunGame.OshimaModules.Effects.OpenEffects; using Oshima.FunGame.OshimaModules.Effects.OpenEffects;
using Oshima.FunGame.OshimaModules.Items; using Oshima.FunGame.OshimaModules.Items;
using Oshima.FunGame.OshimaModules.Regions; using Oshima.FunGame.OshimaModules.Regions;
using Oshima.FunGame.OshimaServers.Model;
namespace Oshima.FunGame.OshimaServers.Service namespace Oshima.FunGame.OshimaModules.Models
{ {
public class FunGameConstant public class FunGameConstant
{ {
@ -334,6 +333,10 @@ namespace Oshima.FunGame.OshimaServers.Service
new () new ()
]; ];
public static Dictionary<long, string> RegionsName { get; } = [];
public static Dictionary<long, RarityType> RegionsDifficulty { get; } = [];
private readonly static Dictionary<int, double> _precomputeTotalExperience = []; private readonly static Dictionary<int, double> _precomputeTotalExperience = [];
public static Dictionary<int, double> PrecomputeTotalExperience public static Dictionary<int, double> PrecomputeTotalExperience
{ {

View File

@ -1,4 +1,4 @@
namespace Oshima.FunGame.OshimaServers.Model namespace Oshima.FunGame.OshimaModules.Models
{ {
public class LastStoreModel public class LastStoreModel
{ {

View File

@ -2,7 +2,7 @@
using Milimoe.FunGame.Core.Interface.Entity; using Milimoe.FunGame.Core.Interface.Entity;
using Milimoe.FunGame.Core.Library.Constant; using Milimoe.FunGame.Core.Library.Constant;
namespace Oshima.FunGame.OshimaServers.Model namespace Oshima.FunGame.OshimaModules.Models
{ {
public class NoticeModel : BaseEntity public class NoticeModel : BaseEntity
{ {

View File

@ -55,6 +55,7 @@ namespace Oshima.FunGame.OshimaModules
(long)SpecialItemID. => new (), (long)SpecialItemID. => new (),
(long)SpecialItemID. => new (), (long)SpecialItemID. => new (),
(long)SpecialItemID. => new (), (long)SpecialItemID. => new (),
(long)SpecialItemID. => new (),
(long)SpecialItemID. => new (), (long)SpecialItemID. => new (),
(long)ConsumableID. => new (), (long)ConsumableID. => new (),
(long)ConsumableID. => new (), (long)ConsumableID. => new (),

View File

@ -30,6 +30,11 @@ namespace Oshima.FunGame.OshimaModules.Regions
public virtual void SaveGlobalStore(Store store, string storeName) public virtual void SaveGlobalStore(Store store, string storeName)
{ {
}
public virtual void UpdateNextRefreshGoods()
{
} }
public override string ToString() public override string ToString()

View File

@ -1,5 +1,7 @@
using Milimoe.FunGame.Core.Api.Utility; using Milimoe.FunGame.Core.Api.Utility;
using Milimoe.FunGame.Core.Entity; using Milimoe.FunGame.Core.Entity;
using Oshima.FunGame.OshimaModules.Items;
using Oshima.FunGame.OshimaModules.Models;
namespace Oshima.FunGame.OshimaModules.Regions namespace Oshima.FunGame.OshimaModules.Regions
{ {
@ -30,7 +32,11 @@ namespace Oshima.FunGame.OshimaModules.Regions
if (template is null) if (template is null)
{ {
return null; if (storeName == "dokyo_forge")
{
template = CreateNewForgeStore();
}
else return null;
} }
if (template.NextRefreshDate < DateTime.Now) if (template.NextRefreshDate < DateTime.Now)
@ -99,5 +105,47 @@ namespace Oshima.FunGame.OshimaModules.Regions
storeTemplate.Add(storeName, store); storeTemplate.Add(storeName, store);
storeTemplate.SaveConfig(); storeTemplate.SaveConfig();
} }
public override void UpdateNextRefreshGoods()
{
EntityModuleConfig<Store> 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<OshimaRegion, Item> 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;
}
} }
} }

View File

@ -118,6 +118,7 @@ namespace Oshima.FunGame.OshimaServers
// 刷新活动缓存 // 刷新活动缓存
FunGameService.GetEventCenter(); FunGameService.GetEventCenter();
FunGameService.RefreshNotice(); FunGameService.RefreshNotice();
FunGameService.PreRefreshStore();
}); });
TaskScheduler.Shared.AddTask("上九", new TimeSpan(9, 0, 0), () => TaskScheduler.Shared.AddTask("上九", new TimeSpan(9, 0, 0), () =>
{ {

View File

@ -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<string, int> ForgeMaterials { get; set; } = [];
public long TargetRegionId { get; set; } = 0;
public QualityType TargetQuality { get; set; } = QualityType.White;
public Dictionary<long, double> 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;
}
}

View File

@ -2,6 +2,7 @@
using Milimoe.FunGame.Core.Entity; using Milimoe.FunGame.Core.Entity;
using Milimoe.FunGame.Core.Model; using Milimoe.FunGame.Core.Model;
using Oshima.FunGame.OshimaModules.Effects.OpenEffects; using Oshima.FunGame.OshimaModules.Effects.OpenEffects;
using Oshima.FunGame.OshimaModules.Models;
namespace Oshima.FunGame.OshimaServers.Service namespace Oshima.FunGame.OshimaServers.Service
{ {

View File

@ -1,5 +1,4 @@
using System.Text; using System.Text;
using Milimoe.FunGame;
using Milimoe.FunGame.Core.Api.Transmittal; using Milimoe.FunGame.Core.Api.Transmittal;
using Milimoe.FunGame.Core.Api.Utility; using Milimoe.FunGame.Core.Api.Utility;
using Milimoe.FunGame.Core.Entity; using Milimoe.FunGame.Core.Entity;
@ -8,6 +7,7 @@ using Oshima.Core.Constant;
using Oshima.FunGame.OshimaModules.Characters; using Oshima.FunGame.OshimaModules.Characters;
using Oshima.FunGame.OshimaModules.Effects.OpenEffects; using Oshima.FunGame.OshimaModules.Effects.OpenEffects;
using Oshima.FunGame.OshimaModules.Items; using Oshima.FunGame.OshimaModules.Items;
using Oshima.FunGame.OshimaModules.Models;
using Oshima.FunGame.OshimaModules.Regions; using Oshima.FunGame.OshimaModules.Regions;
using Oshima.FunGame.OshimaModules.Skills; using Oshima.FunGame.OshimaModules.Skills;
using Oshima.FunGame.OshimaModules.Units; 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(exItems.Values.Where(i => (int)i.ItemType > 4));
FunGameConstant.Items.AddRange([new (), new (), new (), new (), new (), new (), new (), new (), new (), new (), 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 (), 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); FunGameConstant.AllItems.AddRange(FunGameConstant.Equipment);
@ -85,6 +85,8 @@ namespace Oshima.FunGame.OshimaServers.Service
foreach (OshimaRegion region in FunGameConstant.Regions) foreach (OshimaRegion region in FunGameConstant.Regions)
{ {
FunGameConstant.RegionsName[region.Id] = region.Name;
FunGameConstant.RegionsDifficulty[region.Id] = region.Difficulty;
IEnumerable<Item> 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); IEnumerable<Item> 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) foreach (Item item in items)
{ {
@ -94,6 +96,8 @@ namespace Oshima.FunGame.OshimaServers.Service
foreach (OshimaRegion region in FunGameConstant.PlayerRegions) foreach (OshimaRegion region in FunGameConstant.PlayerRegions)
{ {
FunGameConstant.RegionsName[region.Id] = region.Name;
FunGameConstant.RegionsDifficulty[region.Id] = region.Difficulty;
IEnumerable<Item> items; IEnumerable<Item> items;
if (region.Id == 0) if (region.Id == 0)
{ {
@ -2144,6 +2148,23 @@ namespace Oshima.FunGame.OshimaServers.Service
return $"你的{General.GameplayEquilibriumConstant.InGameMaterial}不足 {reduce} 呢,无法购买【{goods.Name}】!"; 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) foreach (Item item in goods.Items)
@ -3597,7 +3618,7 @@ namespace Oshima.FunGame.OshimaServers.Service
case InstanceType.Material: case InstanceType.Material:
for (int i = 0; i < characterCount; i++) 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; user.Inventory.Materials += award;
builder.AppendLine($"{award} {General.GameplayEquilibriumConstant.InGameMaterial}"); builder.AppendLine($"{award} {General.GameplayEquilibriumConstant.InGameMaterial}");
@ -4001,7 +4022,7 @@ namespace Oshima.FunGame.OshimaServers.Service
return msg; 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) if (model.ForgeMaterials.Count == 0)
{ {
@ -4030,8 +4051,8 @@ namespace Oshima.FunGame.OshimaServers.Service
} }
OshimaRegion resultRegion; OshimaRegion resultRegion;
bool isSimplyForge = regionMaterialCount.Count == 1;
int count = 0; int count = 0;
bool isSimplyForge = regionMaterialCount.Count == 1;
if (isSimplyForge) if (isSimplyForge)
{ {
OshimaRegion region = regionMaterialCount.Keys.First(); OshimaRegion region = regionMaterialCount.Keys.First();
@ -4041,7 +4062,8 @@ namespace Oshima.FunGame.OshimaServers.Service
model.ResultString = $"提交的锻造材料不足 {FunGameConstant.ForgeNeedy[QualityType.White]} 个,请重新提交。"; model.ResultString = $"提交的锻造材料不足 {FunGameConstant.ForgeNeedy[QualityType.White]} 个,请重新提交。";
return; 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 else
{ {
@ -4064,8 +4086,8 @@ namespace Oshima.FunGame.OshimaServers.Service
break; break;
} }
} }
model.ResultRegion = resultRegion.Id;
} }
model.ResultRegion = resultRegion.Id;
if (count >= FunGameConstant.ForgeNeedy[QualityType.Red]) if (count >= FunGameConstant.ForgeNeedy[QualityType.Red])
{ {
@ -4090,13 +4112,13 @@ namespace Oshima.FunGame.OshimaServers.Service
model.ResultPointsGeneral = count * 0.3; model.ResultPointsGeneral = count * 0.3;
string resultItemString = ""; 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) if (resultItem != null)
{ {
string itemquality = ItemSet.GetQualityTypeName(resultItem.QualityType); string itemquality = ItemSet.GetQualityTypeName(resultItem.QualityType);
string itemtype = ItemSet.GetItemTypeName(resultItem.ItemType) + (resultItem.ItemType == ItemType.Weapon && resultItem.WeaponType != WeaponType.None ? "-" + ItemSet.GetWeaponTypeName(resultItem.WeaponType) : ""); string itemtype = ItemSet.GetItemTypeName(resultItem.ItemType) + (resultItem.ItemType == ItemType.Weapon && resultItem.WeaponType != WeaponType.None ? "-" + ItemSet.GetWeaponTypeName(resultItem.WeaponType) : "");
if (itemtype != "") itemtype = $"|{itemtype}"; if (itemtype != "") itemtype = $"|{itemtype}";
resultItemString = $"{itemtype}{resultItem.Name}"; resultItemString = $"[{itemquality}{itemtype}]{resultItem.Name}";
model.ResultItem = resultItem.Name; model.ResultItem = resultItem.Name;
} }
@ -4114,7 +4136,10 @@ namespace Oshima.FunGame.OshimaServers.Service
model.ResultPointsSuccess = count - FunGameConstant.ForgeNeedy[model.ResultQuality]; model.ResultPointsSuccess = count - FunGameConstant.ForgeNeedy[model.ResultQuality];
model.ResultString = $"锻造成功!本次锻造物品的品质为:{ItemSet.GetQualityTypeName(model.ResultQuality)},地区为:{resultRegion.Name},获得了:{resultItemString}\r\n" + model.ResultString = $"锻造成功!本次锻造物品的品质为:{ItemSet.GetQualityTypeName(model.ResultQuality)},地区为:{resultRegion.Name},获得了:{resultItemString}\r\n" +
$"本次提交 {count} 个地区 [ {resultRegion.Name} ] 的锻造材料,获得 {model.ResultPoints:0.##} 点锻造积分。"; $"本次提交 {count} 个地区 [ {resultRegion.Name} ] 的锻造材料,获得 {model.ResultPoints:0.##} 点锻造积分。";
AddItemToUserInventory(user, resultItem); if (!simulate)
{
AddItemToUserInventory(user, resultItem);
}
} }
} }
else else
@ -4123,17 +4148,49 @@ namespace Oshima.FunGame.OshimaServers.Service
{ {
model.ResultPointsFail = count * 0.2; model.ResultPointsFail = count * 0.2;
model.ResultString = $"锻造失败!本次锻造物品的品质为:{ItemSet.GetQualityTypeName(model.ResultQuality)},地区为:{resultRegion.Name},该地区不存在该品质的物品!\r\n" + 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 else
{ {
model.Result = true; model.Result = true;
model.ResultPointsSuccess = count - FunGameConstant.ForgeNeedy[model.ResultQuality]; model.ResultPointsSuccess = count - FunGameConstant.ForgeNeedy[model.ResultQuality];
model.ResultString = $"锻造成功!本次锻造物品的品质为:{ItemSet.GetQualityTypeName(model.ResultQuality)},地区为:{resultRegion.Name},获得了:{resultItemString}\r\n" + 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.##} 点锻造积分。"; $"本次提交 {regionContributions.Count} 个地区的锻造材料({string.Join("", regionMaterialCount.Select(kv => $"{kv.Value} {kv.Key.Name}"))}),总共 {count} 有效材料用量,获得 {model.ResultPoints:0.##} 点锻造积分。";
AddItemToUserInventory(user, resultItem); 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() 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) public static void OnUserConfigSaving(PluginConfig pc, User user)
{ {
DateTime now = DateTime.Now; DateTime now = DateTime.Now;

View File

@ -6,6 +6,7 @@ using Milimoe.FunGame.Core.Entity;
using Milimoe.FunGame.Core.Library.Constant; using Milimoe.FunGame.Core.Library.Constant;
using Milimoe.FunGame.Core.Model; using Milimoe.FunGame.Core.Model;
using Oshima.FunGame.OshimaModules.Effects.OpenEffects; using Oshima.FunGame.OshimaModules.Effects.OpenEffects;
using Oshima.FunGame.OshimaModules.Models;
using Oshima.FunGame.OshimaModules.Skills; using Oshima.FunGame.OshimaModules.Skills;
namespace Oshima.FunGame.OshimaServers.Service namespace Oshima.FunGame.OshimaServers.Service

View File

@ -6,14 +6,13 @@ using Microsoft.Extensions.Logging;
using Milimoe.FunGame.Core.Api.Transmittal; using Milimoe.FunGame.Core.Api.Transmittal;
using Milimoe.FunGame.Core.Api.Utility; using Milimoe.FunGame.Core.Api.Utility;
using Milimoe.FunGame.Core.Entity; 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.Constant;
using Milimoe.FunGame.Core.Library.SQLScript.Entity; using Milimoe.FunGame.Core.Library.SQLScript.Entity;
using Oshima.Core.Configs; using Oshima.Core.Configs;
using Oshima.Core.Constant; using Oshima.Core.Constant;
using Oshima.FunGame.OshimaModules.Characters; using Oshima.FunGame.OshimaModules.Characters;
using Oshima.FunGame.OshimaModules.Items; using Oshima.FunGame.OshimaModules.Items;
using Oshima.FunGame.OshimaModules.Models;
using Oshima.FunGame.OshimaModules.Regions; using Oshima.FunGame.OshimaModules.Regions;
using Oshima.FunGame.OshimaServers.Model; using Oshima.FunGame.OshimaServers.Model;
using Oshima.FunGame.OshimaServers.Service; using Oshima.FunGame.OshimaServers.Service;
@ -3059,7 +3058,7 @@ namespace Oshima.FunGame.WebAPI.Controllers
{ {
itemTrading = SQLService.GetUserItemGuids(sql, userid); itemTrading = SQLService.GetUserItemGuids(sql, userid);
} }
IEnumerable<Item> items = user.Inventory.Items.Where(i => i.Name == name && i.Character is null && (!allowLock || !i.IsLock) && !itemTrading.Contains(i.Guid)); IEnumerable<Item> 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()) if (!items.Any())
{ {
FunGameService.ReleaseUserSemaphoreSlim(userid); FunGameService.ReleaseUserSemaphoreSlim(userid);
@ -3367,7 +3366,7 @@ namespace Oshima.FunGame.WebAPI.Controllers
if (user.Inventory.Training.Count == 0) if (user.Inventory.Training.Count == 0)
{ {
FunGameService.ReleaseUserSemaphoreSlim(userid); FunGameService.ReleaseUserSemaphoreSlim(userid);
return $"你目前没有角色在练级中,请使用【开练级+角色序号】指令进行练级。"; return $"你目前没有角色在练级中,请使用【开练级+角色序号】指令进行练级。";
} }
long cid = user.Inventory.Training.Keys.First(); long cid = user.Inventory.Training.Keys.First();
@ -3445,7 +3444,7 @@ namespace Oshima.FunGame.WebAPI.Controllers
if (user.Inventory.Training.Count == 0) if (user.Inventory.Training.Count == 0)
{ {
return $"你目前没有角色在练级中,请使用【开练级+角色序号】指令进行练级。"; return $"你目前没有角色在练级中,请使用【开练级+角色序号】指令进行练级。";
} }
long cid = user.Inventory.Training.Keys.First(); long cid = user.Inventory.Training.Keys.First();
@ -5412,8 +5411,17 @@ namespace Oshima.FunGame.WebAPI.Controllers
itemMsg += $"[ {count} ] {newItem.ToString(false, true)}".Trim(); itemMsg += $"[ {count} ] {newItem.ToString(false, true)}".Trim();
} }
msg = good.ToString(user).Split("包含物品:")[0].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 + 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 else
{ {
@ -7026,10 +7034,11 @@ namespace Oshima.FunGame.WebAPI.Controllers
if (msg != "") msg += "\r\n"; if (msg != "") msg += "\r\n";
msg += $"本次秘境挑战消耗探索许可 {reduce} 个,你的剩余探索许可:{exploreTimes} 个。"; msg += $"本次秘境挑战消耗探索许可 {reduce} 个,你的剩余探索许可:{exploreTimes} 个。";
} }
pc.Add("exploreTimes", exploreTimes);
} }
} }
pc.Add("exploreTimes", exploreTimes);
FunGameService.SetUserConfigAndReleaseSemaphoreSlim(userid, pc, user); FunGameService.SetUserConfigAndReleaseSemaphoreSlim(userid, pc, user);
return msg; return msg;
@ -7157,8 +7166,17 @@ namespace Oshima.FunGame.WebAPI.Controllers
itemMsg += $"[ {count} ] {newItem.ToString(false, true)}".Trim(); itemMsg += $"[ {count} ] {newItem.ToString(false, true)}".Trim();
} }
msg = good.ToString(user).Split("包含物品:")[0].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 + 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 else
{ {
@ -7179,6 +7197,372 @@ namespace Oshima.FunGame.WebAPI.Controllers
} }
} }
[HttpPost("forgeitemcreate")]
public string ForgeItem_Create([FromQuery] long uid = -1, [FromBody] Dictionary<string, int>? 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<ForgeModel>("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<ForgeModel>("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<ForgeModel>("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<ForgeModel>("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<ForgeModel>("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<ForgeModel>("now");
if (model is null)
{
msg = $"你还没有创建锻造配方。";
}
else
{
List<Item> willDelete = [];
// 检查材料
foreach (string material in model.ForgeMaterials.Keys)
{
IEnumerable<Item> 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")] [HttpPost("template")]
public string Template([FromQuery] long uid = -1) public string Template([FromQuery] long uid = -1)
{ {

View File

@ -8,7 +8,7 @@ using Milimoe.FunGame.Core.Entity;
using Milimoe.FunGame.Core.Library.Constant; using Milimoe.FunGame.Core.Library.Constant;
using Oshima.Core.Configs; using Oshima.Core.Configs;
using Oshima.Core.Constant; using Oshima.Core.Constant;
using Oshima.FunGame.OshimaServers.Model; using Oshima.FunGame.OshimaModules.Models;
using Oshima.FunGame.OshimaServers.Service; using Oshima.FunGame.OshimaServers.Service;
using Oshima.FunGame.WebAPI.Constant; using Oshima.FunGame.WebAPI.Constant;
using Oshima.FunGame.WebAPI.Controllers; 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) EndTime = DateTime.Today.AddHours(3).AddMinutes(59).AddSeconds(59).AddDays(days)
}); });
FunGameService.Notices.SaveConfig(); FunGameService.Notices.SaveConfig();
FunGameService.RefreshNotice();
await SendAsync(e, "添加公告", $"添加完毕,请查看【公告】列表!"); await SendAsync(e, "添加公告", $"添加完毕,请查看【公告】列表!");
return true; return true;
} }
@ -2940,12 +2941,46 @@ namespace Oshima.FunGame.WebAPI.Services
return result; return result;
} }
if (e.Detail.StartsWith("锻造配方")) if (e.Detail.StartsWith("模拟锻造配方"))
{ {
string pattern = @"锻造配方\s*(?:(?<itemName>[^\d\s][^\d]*?)\s+(?<count>\d+)\s*)+"; string detail = e.Detail.Replace("模拟", "").Trim();
string pattern = @"锻造配方\s*(?:(?<itemName>[^\d]+?)\s*(?<count>\d+)\s*)+";
Dictionary<string, int> 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*(?:(?<itemName>[^\d]+?)\s*(?<count>\d+)\s*)+";
Dictionary<string, int> recipeItems = []; Dictionary<string, int> recipeItems = [];
StringBuilder builder = new();
MatchCollection matches = Regex.Matches(e.Detail, pattern, RegexOptions.ExplicitCapture); MatchCollection matches = Regex.Matches(e.Detail, pattern, RegexOptions.ExplicitCapture);
foreach (Match match in matches) foreach (Match match in matches)
{ {
@ -2958,28 +2993,77 @@ namespace Oshima.FunGame.WebAPI.Services
if (int.TryParse(counts[i].Value, out int count)) if (int.TryParse(counts[i].Value, out int count))
{ {
recipeItems[itemName] = count; recipeItems[itemName] = count;
builder.AppendLine($"{itemName} x{count}");
} }
} }
} }
User user = Factory.GetUser(); string msg = Controller.ForgeItem_Create(uid, recipeItems);
ForgeModel model = new() if (msg != "")
{ {
ForgeMaterials = recipeItems await SendAsync(e, "锻造配方", msg);
};
FunGameService.GenerateForgeResult(user, model);
if (model.ResultString != "")
{
await SendAsync(e, "锻造配方", model.ResultString);
}
else
{
await SendAsync(e, "锻造配方", $"失败了……\r\n{builder}");
} }
return result; 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)) if (uid == GeneralSettings.Master && e.Detail.StartsWith("重载FunGame", StringComparison.CurrentCultureIgnoreCase))
{ {
string msg = Controller.Relaod(uid); string msg = Controller.Relaod(uid);