允许运行时修改商品价格和减少库存量;添加限购和全局库存商店;可以自适应刷新商品的属性;添加了过期检查

This commit is contained in:
milimoe 2025-07-23 01:10:49 +08:00
parent 08f4664604
commit 596611edd8
Signed by: milimoe
GPG Key ID: 9554D37E4B8991D0
11 changed files with 372 additions and 86 deletions

View File

@ -196,6 +196,27 @@ namespace Oshima.FunGame.OshimaModules.Items
} }
} }
public class : Item, .GiftBox
{
public override long Id => (long)GiftBoxID.;
public override string Name => "探索助力礼包";
public override string Description => Skills.Active?.Description ?? "";
public override QualityType QualityType => QualityType.Red;
public Dictionary<string, int> Gifts { get; set; } = [];
public (User? user = null, int remainUseTimes = 1) : base(ItemType.GiftBox)
{
User = user;
.Init(this, new()
{
{ General.GameplayEquilibriumConstant.InGameCurrency, 20000 },
{ General.GameplayEquilibriumConstant.InGameMaterial, 50 },
{ new ().Name, 20 },
{ new ().Name, 20 },
}, remainUseTimes);
}
}
public class : Skill public class : Skill
{ {
public override long Id => (long)ItemActiveID.; public override long Id => (long)ItemActiveID.;

View File

@ -55,6 +55,7 @@
= 20002, = 20002,
= 20003, = 20003,
= 20004, = 20004,
= 20005 = 20005,
= 20006
} }
} }

View File

@ -74,6 +74,7 @@ namespace Oshima.FunGame.OshimaModules
(long)GiftBoxID. => new (), (long)GiftBoxID. => new (),
(long)GiftBoxID. => new (), (long)GiftBoxID. => new (),
(long)GiftBoxID. => new (), (long)GiftBoxID. => new (),
(long)GiftBoxID. => new (),
_ => null, _ => null,
}; };
}; };

View File

@ -22,9 +22,14 @@ namespace Oshima.FunGame.OshimaModules.Regions
return other is OshimaRegion && other.GetIdName() == GetIdName(); return other is OshimaRegion && other.GetIdName() == GetIdName();
} }
public virtual string VisitStore(EntityModuleConfig<Store> stores, User user, string storeName) public virtual Store? VisitStore(EntityModuleConfig<Store> stores, User user, string storeName)
{ {
return ""; return null;
}
public virtual void SaveGlobalStore(Store store, string storeName)
{
} }
public override string ToString() public override string ToString()

View File

@ -1,7 +1,5 @@
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.Library.Constant;
using Oshima.FunGame.OshimaModules.Items;
namespace Oshima.FunGame.OshimaModules.Regions namespace Oshima.FunGame.OshimaModules.Regions
{ {
@ -24,37 +22,82 @@ namespace Oshima.FunGame.OshimaModules.Regions
ChangeRandomWeather(); ChangeRandomWeather();
} }
public override string VisitStore(EntityModuleConfig<Store> stores, User user, string storeName) public override Store? VisitStore(EntityModuleConfig<Store> stores, User user, string storeName)
{ {
EntityModuleConfig<Store> storeTemplate = new("stores", "dokyo");
storeTemplate.LoadConfig();
Store? template = storeTemplate.Get(storeName);
if (template is null)
{
return null;
}
if (template.NextRefreshDate < DateTime.Now)
{
template.NextRefreshDate = DateTime.Today.AddHours(4);
template.UpdateRefreshTime(template.NextRefreshDate);
storeTemplate.Add(storeName, template);
storeTemplate.SaveConfig();
}
if (template.GlobalStock)
{
return template;
}
Store? store = stores.Get(storeName); Store? store = stores.Get(storeName);
if (store is null) if (store is null)
{ {
EntityModuleConfig<Store> storeTemplate = new("stores", "dokyo"); template.NextRefreshGoods.Clear();
storeTemplate.LoadConfig(); stores.Add(storeName, template);
Store? template = storeTemplate.Get(storeName); stores.SaveConfig();
if (template != null) stores.LoadConfig();
store = stores.Get(storeName);
}
else
{
if (template.GetNewerGoodsOnVisiting)
{ {
if (template.NextRefreshDate < DateTime.Now) Dictionary<string, int> goodsNameAndStock = store.Goods.Values.ToDictionary(g => g.Name, g => g.Stock);
{ Dictionary<string, Dictionary<long, int>> usersBuyCount = store.Goods.Values.ToDictionary(g => g.Name, g => g.UsersBuyCount);
template.NextRefreshDate = DateTime.Today.AddHours(4); template.NextRefreshGoods.Clear();
template.UpdateRefreshTime(template.NextRefreshDate);
storeTemplate.Add(storeName, template);
storeTemplate.SaveConfig();
}
stores.Add(storeName, template); stores.Add(storeName, template);
stores.SaveConfig(); stores.SaveConfig();
stores.LoadConfig(); stores.LoadConfig();
store = stores.Get(storeName); store = stores.Get(storeName);
if (store != null)
{
foreach (Goods goods in store.Goods.Values)
{
if (goodsNameAndStock.TryGetValue(goods.Name, out int stock) && stock < goods.Stock)
{
goods.Stock = stock;
}
if (usersBuyCount.TryGetValue(goods.Name, out Dictionary<long, int>? userBuyCount) && userBuyCount != null)
{
foreach (long uid in userBuyCount.Keys)
{
goods.UsersBuyCount[uid] = userBuyCount[uid];
}
}
}
stores.Add(storeName, store);
stores.SaveConfig();
}
} }
} }
if (store != null) return store;
{ }
return store.ToString();
}
return ""; public override void SaveGlobalStore(Store store, string storeName)
{
EntityModuleConfig<Store> storeTemplate = new("stores", "dokyo");
storeTemplate.LoadConfig();
storeTemplate.Add(storeName, store);
storeTemplate.SaveConfig();
} }
} }
} }

View File

@ -218,30 +218,57 @@ namespace Oshima.FunGame.OshimaServers
Task.Run(() => Task.Run(() =>
{ {
// 刷新商店 // 刷新商店
string directoryPath = $@"{AppDomain.CurrentDomain.BaseDirectory}configs/stores"; string directoryPath = $@"{AppDomain.CurrentDomain.BaseDirectory}configs/storeNames";
if (Directory.Exists(directoryPath)) if (Directory.Exists(directoryPath))
{ {
string[] filePaths = Directory.GetFiles(directoryPath); string[] filePaths = Directory.GetFiles(directoryPath);
foreach (string filePath in filePaths) foreach (string filePath in filePaths)
{ {
string fileName = Path.GetFileNameWithoutExtension(filePath); string fileName = Path.GetFileNameWithoutExtension(filePath);
EntityModuleConfig<Store> stores = new("storeNames", fileName);
stores.LoadConfig();
string[] storeNames = [.. stores.Keys];
if (long.TryParse(fileName, out long userId) && FunGameConstant.UserIdAndUsername.TryGetValue(userId, out User? user) && user != null) if (long.TryParse(fileName, out long userId) && FunGameConstant.UserIdAndUsername.TryGetValue(userId, out User? user) && user != null)
{ {
EntityModuleConfig<Store> store = new("stores", fileName); // 更新玩家商店数据,移除所有当天刷新的商店
store.LoadConfig(); FunGameConstant.UserLastVisitStore.Remove(userId);
store.Remove("daily"); stores.Remove("daily");
string[] stores = [.. store.Keys]; foreach (string key in storeNames)
foreach (string key in stores)
{ {
Store? s = store.Get(key); Store? store = stores.Get(key);
if (s != null && s.AutoRefresh && s.NextRefreshDate.Date == DateTime.Today) if (store != null && (store.GlobalStock || (store.AutoRefresh && store.NextRefreshDate.Date <= DateTime.Today)))
{ {
store.Remove(key); stores.Remove(key);
} }
} }
FunGameService.CheckDailyStore(store, user); FunGameService.CheckDailyStore(stores, user);
store.SaveConfig();
} }
else
{
// 非玩家商店数据,需要更新模板的商品
foreach (string key in storeNames)
{
Store? store = stores.Get(key);
if (store != null)
{
if (store.ExpireTime != null && store.ExpireTime.Value.Date <= DateTime.Today)
{
stores.Remove(key);
continue;
}
if (store.AutoRefresh && store.NextRefreshDate.Date <= DateTime.Today)
{
store.Goods.Clear();
foreach (long goodsId in store.NextRefreshGoods.Keys)
{
store.Goods[goodsId] = store.NextRefreshGoods[goodsId];
}
store.NextRefreshDate = DateTime.Today.AddHours(4).AddDays(store.RefreshInterval);
}
}
}
}
stores.SaveConfig();
} }
Controller.WriteLine("刷新商店"); Controller.WriteLine("刷新商店");
} }

View File

@ -427,6 +427,17 @@ namespace Oshima.FunGame.OshimaServers.Service
{ QualityType.Gold, (130000, 240000) } { QualityType.Gold, (130000, 240000) }
}; };
public static Dictionary<QualityType, double> DecomposedMaterials { get; } = new()
{
{ QualityType.Gold, 128 },
{ QualityType.Red, 6 },
{ QualityType.Orange, 32 },
{ QualityType.Purple, 16 },
{ QualityType.Blue, 8 },
{ QualityType.Green, 3 },
{ QualityType.White, 1 }
};
public static string[] GreekAlphabet { get; } = ["α", "β", "γ", "δ", "ε", "ζ", "η", "θ", "ι", "κ", "λ", "μ", "ν", "ξ", "ο", "π", "ρ", "σ", "τ", "υ", "φ", "χ", "ψ", "ω"]; public static string[] GreekAlphabet { get; } = ["α", "β", "γ", "δ", "ε", "ζ", "η", "θ", "ι", "κ", "λ", "μ", "ν", "ξ", "ο", "π", "ρ", "σ", "τ", "υ", "φ", "χ", "ψ", "ω"];
public static string[] CommonSurnames { get; } = [ public static string[] CommonSurnames { get; } = [

View File

@ -54,6 +54,7 @@
{"合成魔法卡 <{物品序号...}>", "3张魔法卡合成空格隔开"}, {"合成魔法卡 <{物品序号...}>", "3张魔法卡合成空格隔开"},
{"分解物品 <{物品序号...}>", "分解指定物品"}, {"分解物品 <{物品序号...}>", "分解指定物品"},
{"分解 <物品名称> <数量>", "分解指定数量物品"}, {"分解 <物品名称> <数量>", "分解指定数量物品"},
{"强制分解 <物品名称> <数量>", "分解指定数量物品"},
{"品质分解 <品质索引>", "按品质分解0-6普通/优秀/稀有/史诗/传说/神话/不朽)"}, {"品质分解 <品质索引>", "按品质分解0-6普通/优秀/稀有/史诗/传说/神话/不朽)"},
}; };
@ -130,6 +131,7 @@
{"商店1", "查看后勤部商品"}, {"商店1", "查看后勤部商品"},
{"商店2", "查看武器商会商品"}, {"商店2", "查看武器商会商品"},
{"商店3", "查看杂货铺商品"}, {"商店3", "查看杂货铺商品"},
{"商店4", "查看慈善基金会商品"},
{"商店查看 <商品序号>", "查看指定商品详情访问任意商店后2分钟内可用"}, {"商店查看 <商品序号>", "查看指定商品详情访问任意商店后2分钟内可用"},
{"商店购买 <商品序号>", "购买指定商品访问任意商店后2分钟内可用"}, {"商店购买 <商品序号>", "购买指定商品访问任意商店后2分钟内可用"},
{"商店出售 <物品序号>", "向商店出售具有回收价的指定物品"}, {"商店出售 <物品序号>", "向商店出售具有回收价的指定物品"},

View File

@ -2118,6 +2118,12 @@ namespace Oshima.FunGame.OshimaServers.Service
return msg = $"此商品【{goods.Name}】库存不足,无法购买!\r\n你想要购买 {count} 件,但库存只有 {goods.Stock} 件。"; return msg = $"此商品【{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} 件,超过了购买限制。";
}
foreach (string needy in goods.Prices.Keys) foreach (string needy in goods.Prices.Keys)
{ {
if (needy == General.GameplayEquilibriumConstant.InGameCurrency) if (needy == General.GameplayEquilibriumConstant.InGameCurrency)
@ -2176,7 +2182,12 @@ namespace Oshima.FunGame.OshimaServers.Service
if (goods.Stock < 0) goods.Stock = 0; if (goods.Stock < 0) goods.Stock = 0;
} }
msg += $"恭喜你成功购买 {count} 件【{goods.Name}】!\r\n" + if (!goods.UsersBuyCount.TryAdd(user.Id, count))
{
goods.UsersBuyCount[user.Id] += count;
}
msg += $"恭喜你成功购买 {count} 件【{goods.Name}】!\r\n" + (goods.Quota > 0 ? $"此商品限购 {goods.Quota} 件,你还可以再购买 {goods.Quota - count - buyCount} 件。\r\n" : "") +
$"总计消费:{(goods.Prices.Count > 0 ? string.Join("", goods.Prices.Select(kv => $"{kv.Value * count:0.##} {kv.Key}")) : "")}\r\n" + $"总计消费:{(goods.Prices.Count > 0 ? string.Join("", goods.Prices.Select(kv => $"{kv.Value * count:0.##} {kv.Key}")) : "")}\r\n" +
$"包含物品:{string.Join("", goods.Items.Select(i => $"[{ItemSet.GetQualityTypeName(i.QualityType)}|{ItemSet.GetItemTypeName(i.ItemType)}] {i.Name} * {count}"))}\r\n" + $"包含物品:{string.Join("", goods.Items.Select(i => $"[{ItemSet.GetQualityTypeName(i.QualityType)}|{ItemSet.GetItemTypeName(i.ItemType)}] {i.Name} * {count}"))}\r\n" +
$"{store.Name}期待你的下次光临。"; $"{store.Name}期待你的下次光临。";
@ -3397,7 +3408,7 @@ namespace Oshima.FunGame.OshimaServers.Service
Item newItem = item; Item newItem = item;
if (copyNew) newItem = item.Copy(copyLevel); if (copyNew) newItem = item.Copy(copyLevel);
newItem.User = user; newItem.User = user;
if (hasLock && newItem.QualityType >= QualityType.Orange) newItem.IsLock = true; if (hasLock && (newItem.QualityType >= QualityType.Orange || FunGameConstant.CharacterLevelBreakItems.Any(c => c.Id == item.Id)) || FunGameConstant.SkillLevelUpItems.Any(c => c.Id == item.Id)) newItem.IsLock = true;
if (hasSellAndTradeTime) SetSellAndTradeTime(newItem); if (hasSellAndTradeTime) SetSellAndTradeTime(newItem);
if (hasPrice) if (hasPrice)
{ {
@ -3947,6 +3958,27 @@ namespace Oshima.FunGame.OshimaServers.Service
return builder.ToString().Trim(); return builder.ToString().Trim();
} }
public static Store? GetRegionStore(EntityModuleConfig<Store> stores, User user, string storeRegion, string storeName)
{
Store? store = null;
Dictionary<string, OshimaRegion> regionStores = FunGameConstant.PlayerRegions.ToDictionary(r => r.Name, r => r);
if (regionStores.TryGetValue(storeRegion, out OshimaRegion? value) && value != null)
{
store = value.VisitStore(stores, user, storeName);
}
return store;
}
public static void SaveRegionStore(Store store, string storeRegion, string storeName)
{
if (FunGameConstant.PlayerRegions.FirstOrDefault(r => r.Name == storeRegion) is OshimaRegion value)
{
value.SaveGlobalStore(store, storeName);
}
}
public static string CheckRegionStore(EntityModuleConfig<Store> stores, User user, string storeRegion, string storeName, out bool exist) public static string CheckRegionStore(EntityModuleConfig<Store> stores, User user, string storeRegion, string storeName, out bool exist)
{ {
string msg = ""; string msg = "";
@ -3955,8 +3987,9 @@ namespace Oshima.FunGame.OshimaServers.Service
Dictionary<string, OshimaRegion> regionStores = FunGameConstant.PlayerRegions.ToDictionary(r => r.Name, r => r); Dictionary<string, OshimaRegion> regionStores = FunGameConstant.PlayerRegions.ToDictionary(r => r.Name, r => r);
if (regionStores.TryGetValue(storeRegion, out OshimaRegion? value) && value != null) if (regionStores.TryGetValue(storeRegion, out OshimaRegion? value) && value != null)
{ {
msg = value.VisitStore(stores, user, storeName); Store? store = value.VisitStore(stores, user, storeName);
exist = msg != ""; exist = store != null;
msg = store?.ToString() ?? "";
} }
if (!exist) if (!exist)

View File

@ -6,6 +6,7 @@ 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.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;
@ -2238,7 +2239,7 @@ namespace Oshima.FunGame.WebAPI.Controllers
else else
{ {
FunGameService.ReleaseUserSemaphoreSlim(userid); FunGameService.ReleaseUserSemaphoreSlim(userid);
return "此物品的可使用数量小于你想要使用的数量!"; return $"此物品的可使用数量{items.Count()}小于你想要使用的数量{useCount}";
} }
} }
else else
@ -3006,16 +3007,7 @@ namespace Oshima.FunGame.WebAPI.Controllers
if (user.Inventory.Items.Remove(item)) if (user.Inventory.Items.Remove(item))
{ {
double gained = item.QualityType switch double gained = FunGameConstant.DecomposedMaterials[item.QualityType];
{
QualityType.Gold => 128,
QualityType.Red => 64,
QualityType.Orange => 32,
QualityType.Purple => 16,
QualityType.Blue => 8,
QualityType.Green => 3,
_ => 1
};
totalGained += gained; totalGained += gained;
successCount++; successCount++;
} }
@ -3043,7 +3035,7 @@ namespace Oshima.FunGame.WebAPI.Controllers
} }
[HttpPost("decomposeitem2")] [HttpPost("decomposeitem2")]
public string DecomposeItem2([FromQuery] long? uid = null, [FromQuery] string? name = null, [FromQuery] int? count = null) public string DecomposeItem2([FromQuery] long? uid = null, [FromQuery] string? name = null, [FromQuery] int? count = null, [FromQuery] bool allowLock = false)
{ {
try try
{ {
@ -3067,11 +3059,11 @@ 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 && !i.IsLock && !itemTrading.Contains(i.Guid)); IEnumerable<Item> items = user.Inventory.Items.Where(i => i.Name == name && i.Character is null && (!allowLock || !i.IsLock) && !itemTrading.Contains(i.Guid));
if (!items.Any()) if (!items.Any())
{ {
FunGameService.ReleaseUserSemaphoreSlim(userid); FunGameService.ReleaseUserSemaphoreSlim(userid);
return $"库存中不存在名称为【{name}】,且未上锁、未在进行交易的物品!"; return $"库存中不存在名称为【{name}】,且{(!allowLock ? "" : "")}未在进行交易的物品!";
} }
if (items.Count() >= useCount) if (items.Count() >= useCount)
@ -3085,16 +3077,7 @@ namespace Oshima.FunGame.WebAPI.Controllers
{ {
if (user.Inventory.Items.Remove(item)) if (user.Inventory.Items.Remove(item))
{ {
double gained = item.QualityType switch double gained = FunGameConstant.DecomposedMaterials[item.QualityType];
{
QualityType.Gold => 128,
QualityType.Red => 64,
QualityType.Orange => 32,
QualityType.Purple => 16,
QualityType.Blue => 8,
QualityType.Green => 3,
_ => 1
};
totalGained += gained; totalGained += gained;
successCount++; successCount++;
} }
@ -3109,7 +3092,7 @@ namespace Oshima.FunGame.WebAPI.Controllers
else else
{ {
FunGameService.ReleaseUserSemaphoreSlim(userid); FunGameService.ReleaseUserSemaphoreSlim(userid);
return $"此物品的可分解数量({items.Count()} 件)小于你想要分解的数量"; return $"此物品的可分解数量({items.Count()} 件)小于你想要分解的数量{useCount}";
} }
} }
else else
@ -3161,16 +3144,7 @@ namespace Oshima.FunGame.WebAPI.Controllers
List<string> msgs = []; List<string> msgs = [];
int successCount = 0; int successCount = 0;
double gained = items.First().QualityType switch double gained = FunGameConstant.DecomposedMaterials[items.First().QualityType];
{
QualityType.Gold => 128,
QualityType.Red => 64,
QualityType.Orange => 32,
QualityType.Purple => 16,
QualityType.Blue => 8,
QualityType.Green => 3,
_ => 1
};
foreach (Item item in items) foreach (Item item in items)
{ {
@ -3529,7 +3503,7 @@ namespace Oshima.FunGame.WebAPI.Controllers
{ {
if (skill.SkillType == SkillType.Skill || skill.SkillType == SkillType.SuperSkill) if (skill.SkillType == SkillType.Skill || skill.SkillType == SkillType.SuperSkill)
{ {
if (skill.Level + 1 == General.GameplayEquilibriumConstant.MaxSkillLevel) if (skill.Level == General.GameplayEquilibriumConstant.MaxSkillLevel)
{ {
return $"此技能【{skill.Name}】已经升至满级!"; return $"此技能【{skill.Name}】已经升至满级!";
} }
@ -3725,7 +3699,7 @@ namespace Oshima.FunGame.WebAPI.Controllers
NormalAttack na = character.NormalAttack; NormalAttack na = character.NormalAttack;
if (na.Level + 1 == General.GameplayEquilibriumConstant.MaxNormalAttackLevel) if (na.Level == General.GameplayEquilibriumConstant.MaxNormalAttackLevel)
{ {
return $"角色 [ {character} ] 的【{na.Name}】已经升至满级!"; return $"角色 [ {character} ] 的【{na.Name}】已经升至满级!";
} }
@ -5377,7 +5351,7 @@ namespace Oshima.FunGame.WebAPI.Controllers
Store? daily = stores.Get("daily"); Store? daily = stores.Get("daily");
if (daily != null) if (daily != null)
{ {
if (daily.Goods.Values.FirstOrDefault(g => g.Id == goodid) is Goods good) if (daily.Goods.Values.FirstOrDefault(g => g.Id == goodid && (!g.ExpireTime.HasValue || g.ExpireTime.Value > DateTime.Now)) is Goods good)
{ {
msg = FunGameService.StoreBuyItem(daily, good, pc, user, buycount); msg = FunGameService.StoreBuyItem(daily, good, pc, user, buycount);
} }
@ -5425,7 +5399,7 @@ namespace Oshima.FunGame.WebAPI.Controllers
Store? daily = stores.Get("daily"); Store? daily = stores.Get("daily");
if (daily != null) if (daily != null)
{ {
if (daily.Goods.Values.FirstOrDefault(g => g.Id == goodid) is Goods good) if (daily.Goods.Values.FirstOrDefault(g => g.Id == goodid && (!g.ExpireTime.HasValue || g.ExpireTime.Value > DateTime.Now)) is Goods good)
{ {
int count = 0; int count = 0;
string itemMsg = ""; string itemMsg = "";
@ -5439,7 +5413,7 @@ namespace Oshima.FunGame.WebAPI.Controllers
} }
msg = good.ToString().Split("包含物品:")[0].Trim(); msg = good.ToString().Split("包含物品:")[0].Trim();
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)}";
} }
else else
{ {
@ -6050,12 +6024,13 @@ namespace Oshima.FunGame.WebAPI.Controllers
Item item = user.Inventory.Items.ToList()[itemIndex - 1]; Item item = user.Inventory.Items.ToList()[itemIndex - 1];
if (msg != "") msg += "\r\n"; if (msg != "") msg += "\r\n";
string isUnLock = unlock ? "解锁" : "锁定";
using SQLHelper? sql = Factory.OpenFactory.GetSQLHelper(); using SQLHelper? sql = Factory.OpenFactory.GetSQLHelper();
try try
{ {
if (sql != null && SQLService.IsItemInOffers(sql, item.Guid)) if (sql != null && SQLService.IsItemInOffers(sql, item.Guid))
{ {
msg += $"这个物品 {itemIndex}. {item.Name} 无法上锁/解锁。因为它正在进行交易,请检查交易报价!"; msg += $"这个物品 {itemIndex}. {item.Name} 无法{isUnLock}。因为它正在进行交易,请检查交易报价!";
continue; continue;
} }
} }
@ -6088,7 +6063,7 @@ namespace Oshima.FunGame.WebAPI.Controllers
else else
{ {
item.IsLock = true; item.IsLock = true;
msg += $"物品锁成功:{itemIndex}. {item.Name}"; msg += $"物品成功:{itemIndex}. {item.Name}";
} }
} }
else else
@ -6120,6 +6095,100 @@ namespace Oshima.FunGame.WebAPI.Controllers
} }
} }
[HttpPost("lockitems")]
public string LockItems([FromQuery] long uid = -1, [FromQuery] string name = "", [FromQuery] int count = 0, [FromQuery] bool unlock = false)
{
try
{
if (count <= 0)
{
return "数量必须大于0";
}
PluginConfig pc = FunGameService.GetUserConfig(uid, out _);
if (pc.Count > 0)
{
User user = FunGameService.GetUser(pc);
string msg = "";
using SQLHelper? sql = Factory.OpenFactory.GetSQLHelper();
List<Guid> itemTrading = [];
if (sql != null)
{
itemTrading = SQLService.GetUserItemGuids(sql, uid);
}
Dictionary<Item, int> items = user.Inventory.Items.Select((i, index) => new { Item = i, Index = index + 1 }).
Where(x => x.Item.Name == name && x.Item.Character is null && x.Item.IsLock == unlock && !itemTrading.Contains(x.Item.Guid)).
ToDictionary(x => x.Item, x => x.Index);
string isUnLock = unlock ? "解锁" : "锁定";
if (items.Count == 0)
{
msg = $"库存中不存在名称为【{name}】,且未在进行交易的物品,或者这些物品都已经被{isUnLock}了!";
}
else if (items.Count >= count)
{
items = items.TakeLast(count).ToDictionary();
List<string> msgs = [];
int successCount = 0;
foreach (Item item in items.Keys)
{
if (unlock)
{
PluginConfig renameExamine = new("examines", "rename");
renameExamine.LoadConfig();
List<string> strings = renameExamine.Get<List<string>>(user.Id.ToString()) ?? [];
if (strings.Count > 0)
{
string guid = strings[1];
Item? gmk = user.Inventory.Items.FirstOrDefault(i => i.Guid.ToString() == guid);
if (gmk != null)
{
msg = $"物品 {items[item]}. {item.Name} 已被自定义改名系统锁定,无法自行解锁。";
continue;
}
}
item.IsLock = false;
msg += $"物品解锁成功:{items[item]}. {item.Name}";
successCount++;
}
else
{
item.IsLock = true;
msg += $"物品锁定成功:{items[item]}. {item.Name}";
successCount++;
}
}
FunGameService.SetUserConfigAndReleaseSemaphoreSlim(uid, pc, user);
return $"{isUnLock}完毕!{isUnLock} {count} 件,库存允许{isUnLock} {items.Count} 件,成功 {successCount} 件!";
}
else
{
msg = $"此物品的可{isUnLock}数量({items.Count})小于你想要{isUnLock}的数量({count}";
}
FunGameService.ReleaseUserSemaphoreSlim(uid);
return msg;
}
else
{
FunGameService.ReleaseUserSemaphoreSlim(uid);
return noSaved;
}
}
catch (Exception e)
{
FunGameService.ReleaseUserSemaphoreSlim(uid.ToString() ?? "");
Logger.LogError(e, "Error: {e}", e);
return busy;
}
}
[HttpPost("makeoffer")] [HttpPost("makeoffer")]
public string MakeOffer([FromQuery] long? uid = null, [FromQuery] long? offeree = null) public string MakeOffer([FromQuery] long? uid = null, [FromQuery] long? offeree = null)
{ {
@ -6992,7 +7061,7 @@ namespace Oshima.FunGame.WebAPI.Controllers
EntityModuleConfig<Store> stores = new("stores", userid.ToString()); EntityModuleConfig<Store> stores = new("stores", userid.ToString());
stores.LoadConfig(); stores.LoadConfig();
string msg = FunGameService.CheckRegionStore(stores, user, storeRegion, storeName, out bool exist); string msg = FunGameService.CheckRegionStore(stores, user, storeRegion, storeName, out _);
FunGameService.SetUserConfigAndReleaseSemaphoreSlim(userid, pc, user); FunGameService.SetUserConfigAndReleaseSemaphoreSlim(userid, pc, user);
return msg; return msg;
@ -7003,7 +7072,7 @@ namespace Oshima.FunGame.WebAPI.Controllers
return noSaved; return noSaved;
} }
} }
[HttpPost("systemstorebuy")] [HttpPost("systemstorebuy")]
public string SystemStoreBuy([FromQuery] long? uid = null, [FromQuery] string storeRegion = "", [FromQuery] string storeName = "", [FromQuery] long id = 0, [FromQuery] int count = 0) public string SystemStoreBuy([FromQuery] long? uid = null, [FromQuery] string storeRegion = "", [FromQuery] string storeName = "", [FromQuery] long id = 0, [FromQuery] int count = 0)
{ {
@ -7018,14 +7087,21 @@ namespace Oshima.FunGame.WebAPI.Controllers
EntityModuleConfig<Store> stores = new("stores", userid.ToString()); EntityModuleConfig<Store> stores = new("stores", userid.ToString());
stores.LoadConfig(); stores.LoadConfig();
string msg = ""; string msg = "";
Store? store = stores.Get(storeName); Store? store = FunGameService.GetRegionStore(stores, user, storeRegion, storeName);
if (store != null) if (store != null)
{ {
if (store.Goods.Values.FirstOrDefault(g => g.Id == id) is Goods good) if (store.Goods.Values.FirstOrDefault(g => g.Id == id && (!g.ExpireTime.HasValue || g.ExpireTime.Value > DateTime.Now)) is Goods good)
{ {
msg = FunGameService.StoreBuyItem(store, good, pc, user, count); msg = FunGameService.StoreBuyItem(store, good, pc, user, count);
stores.Add(storeName, store); if (store.GlobalStock)
stores.SaveConfig(); {
FunGameService.SaveRegionStore(store, storeRegion, storeName);
}
else
{
stores.Add(storeName, store);
stores.SaveConfig();
}
} }
else else
{ {
@ -7068,7 +7144,7 @@ namespace Oshima.FunGame.WebAPI.Controllers
Store? store = stores.Get(storeName); Store? store = stores.Get(storeName);
if (store != null) if (store != null)
{ {
if (store.Goods.Values.FirstOrDefault(g => g.Id == goodid) is Goods good) if (store.Goods.Values.FirstOrDefault(g => g.Id == goodid && (!g.ExpireTime.HasValue || g.ExpireTime.Value > DateTime.Now)) is Goods good)
{ {
int count = 0; int count = 0;
string itemMsg = ""; string itemMsg = "";

View File

@ -1511,6 +1511,27 @@ namespace Oshima.FunGame.WebAPI.Services
return result; return result;
} }
if (e.Detail.StartsWith("强制分解", StringComparison.CurrentCultureIgnoreCase))
{
string detail = e.Detail.Replace("强制分解", "").Trim();
string pattern = @"\s*(?<itemName>[^\d]+)\s*(?<count>\d+)\s*";
Match match = Regex.Match(detail, pattern);
if (match.Success)
{
string itemName = match.Groups["itemName"].Value.Trim();
if (int.TryParse(match.Groups["count"].Value, out int count))
{
string msg = Controller.DecomposeItem2(uid, itemName, count, true);
if (msg != "")
{
await SendAsync(e, "分解", msg);
}
}
}
return result;
}
if (e.Detail.StartsWith("分解", StringComparison.CurrentCultureIgnoreCase)) if (e.Detail.StartsWith("分解", StringComparison.CurrentCultureIgnoreCase))
{ {
string detail = e.Detail.Replace("分解", "").Trim(); string detail = e.Detail.Replace("分解", "").Trim();
@ -2383,6 +2404,48 @@ namespace Oshima.FunGame.WebAPI.Services
return result; return result;
} }
if (e.Detail.StartsWith("批量锁定", StringComparison.CurrentCultureIgnoreCase))
{
string detail = e.Detail.Replace("批量锁定", "").Trim();
string pattern = @"\s*(?<itemName>[^\d]+)\s*(?<count>\d+)\s*";
Match match = Regex.Match(detail, pattern);
if (match.Success)
{
string itemName = match.Groups["itemName"].Value.Trim();
if (int.TryParse(match.Groups["count"].Value, out int count))
{
string msg = Controller.LockItems(uid, itemName, count, false);
if (msg != "")
{
await SendAsync(e, "批量锁定", msg);
}
}
}
return result;
}
if (e.Detail.StartsWith("批量解锁", StringComparison.CurrentCultureIgnoreCase))
{
string detail = e.Detail.Replace("批量解锁", "").Trim();
string pattern = @"\s*(?<itemName>[^\d]+)\s*(?<count>\d+)\s*";
Match match = Regex.Match(detail, pattern);
if (match.Success)
{
string itemName = match.Groups["itemName"].Value.Trim();
if (int.TryParse(match.Groups["count"].Value, out int count))
{
string msg = Controller.LockItems(uid, itemName, count, true);
if (msg != "")
{
await SendAsync(e, "批量解锁", msg);
}
}
}
return result;
}
if (e.Detail.StartsWith("上锁") || e.Detail.StartsWith("锁定")) if (e.Detail.StartsWith("上锁") || e.Detail.StartsWith("锁定"))
{ {
string detail = e.Detail.Replace("上锁", "").Replace("锁定", "").Trim(); string detail = e.Detail.Replace("上锁", "").Replace("锁定", "").Trim();
@ -2779,6 +2842,9 @@ namespace Oshima.FunGame.WebAPI.Services
case 3: case 3:
msg = Controller.ShowSystemStore(uid, "铎京城", "dokyo_yuki"); msg = Controller.ShowSystemStore(uid, "铎京城", "dokyo_yuki");
break; break;
case 4:
msg = Controller.ShowSystemStore(uid, "铎京城", "dokyo_welfare");
break;
default: default:
break; break;
} }