添加了市场和私信功能

This commit is contained in:
milimoe 2025-07-28 20:02:44 +08:00
parent 4b0c7bb942
commit e743997f65
Signed by: milimoe
GPG Key ID: 9554D37E4B8991D0
15 changed files with 953 additions and 98 deletions

View File

@ -33,9 +33,9 @@ namespace Oshima.FunGame.OshimaModules.Models
public static List<Item> AllItems { get; } = []; public static List<Item> AllItems { get; } = [];
public static List<Skill> AllSkills { get; } = []; public static List<Skill> AllSkills { get; } = [];
public static Dictionary<long, User> UserIdAndUsername { get; } = []; public static Dictionary<long, User> UserIdAndUsername { get; } = [];
public static Dictionary<long, Item> MarketItemIdAndItem { get; } = [];
public static Dictionary<long, LastStoreModel> UserLastVisitStore { get; } = []; public static Dictionary<long, LastStoreModel> UserLastVisitStore { get; } = [];
public static ConcurrentDictionary<string, SemaphoreSlim> UserSemaphoreSlims { get; } = []; public static ConcurrentDictionary<string, SemaphoreSlim> UserSemaphoreSlims { get; } = [];
public static SemaphoreSlim MarketSemaphoreSlim { get; } = new(1, 1);
public static ItemType[] ItemCanUsed => [ItemType.Consumable, ItemType.MagicCard, ItemType.SpecialItem, ItemType.GiftBox, ItemType.Others]; public static ItemType[] ItemCanUsed => [ItemType.Consumable, ItemType.MagicCard, ItemType.SpecialItem, ItemType.GiftBox, ItemType.Others];
public static ItemType[] ItemCanNotDrawCard => [ItemType.Collectible, ItemType.QuestItem, ItemType.GiftBox, ItemType.Others]; public static ItemType[] ItemCanNotDrawCard => [ItemType.Collectible, ItemType.QuestItem, ItemType.GiftBox, ItemType.Others];

View File

@ -10,7 +10,7 @@ namespace Oshima.FunGame.OshimaModules.Skills
public override string Description => Effects.Count > 0 ? Effects.First().Description : ""; public override string Description => Effects.Count > 0 ? Effects.First().Description : "";
public override string DispelDescription => Effects.Count > 0 ? Effects.First().DispelDescription : ""; public override string DispelDescription => Effects.Count > 0 ? Effects.First().DispelDescription : "";
public override double EPCost => 100; public override double EPCost => 100;
public override double CD => 40 - 1 * (Level - 1); public override double CD => 60 - 1.5 * (Level - 1);
public override double HardnessTime { get; set; } = 8; public override double HardnessTime { get; set; } = 8;
public override bool CanSelectSelf => true; public override bool CanSelectSelf => true;
public override bool CanSelectEnemy => false; public override bool CanSelectEnemy => false;

View File

@ -10,7 +10,7 @@ namespace Oshima.FunGame.OshimaModules.Skills
public override string Description => Effects.Count > 0 ? Effects.First().Description : ""; public override string Description => Effects.Count > 0 ? Effects.First().Description : "";
public override string DispelDescription => Effects.Count > 0 ? Effects.First().DispelDescription : ""; public override string DispelDescription => Effects.Count > 0 ? Effects.First().DispelDescription : "";
public override double EPCost => 100; public override double EPCost => 100;
public override double CD => 35 - 2 * (Level - 1); public override double CD => 80 - 4 * (Level - 1);
public override double HardnessTime { get; set; } = 0; public override double HardnessTime { get; set; } = 0;
public override bool CanSelectSelf => true; public override bool CanSelectSelf => true;
public override bool CanSelectEnemy => false; public override bool CanSelectEnemy => false;
@ -25,13 +25,26 @@ namespace Oshima.FunGame.OshimaModules.Skills
{ {
public override long Id => Skill.Id; public override long Id => Skill.Id;
public override string Name => "三重叠加"; public override string Name => "三重叠加";
public override string Description => $"使 [ 灵能反射 ] 支持普通攻击,且当前释放魔法次数归零,最大硬直消除次数提高到 {灵能反射次数} 次;在魔法命中和普通攻击命中时能够回复所回复能量值的 3 倍魔法值,持续 {技能持续次数} 次(灵能反射每消除次数达到最大时算一次)。" + public override string Description => $"使 [ 灵能反射 ] 支持普通攻击,且当前释放魔法次数归零,最大硬直消除次数提高到 {灵能反射次数} 次;在魔法命中和普通攻击命中时能够回复所回复能量值的 {魔法值倍数:0.#} 倍魔法值,持续 {技能持续次数} 次(灵能反射每消除次数达到最大时算一次)。" +
$"(剩余:{剩余持续次数} 次)"; $"(剩余:{剩余持续次数} 次)";
public override DispelledType DispelledType => DispelledType.CannotBeDispelled; public override DispelledType DispelledType => DispelledType.CannotBeDispelled;
public int { get; set; } = 0; public int { get; set; } = 0;
private readonly int = 3; private readonly int = 3;
private readonly int = 2; private double
{
get
{
return Skill.Level * 0.5;
}
}
private int
{
get
{
return Skill.Level > 3 ? 2 : 1;
}
}
public override void OnEffectGained(Character character) public override void OnEffectGained(Character character)
{ {

View File

@ -59,13 +59,13 @@ namespace Oshima.FunGame.OshimaModules.Skills
{ {
return Level switch return Level switch
{ {
1 => 3, 1 => 2,
2 => 3, 2 => 2,
3 => 4, 3 => 3,
4 => 4, 4 => 3,
5 => 5, 5 => 4,
6 => 5, 6 => 3,
_ => 3 _ => 2
}; };
} }
} }
@ -76,11 +76,11 @@ namespace Oshima.FunGame.OshimaModules.Skills
return Level switch return Level switch
{ {
1 => 1, 1 => 1,
2 => 2, 2 => 1,
3 => 2, 3 => 2,
4 => 3, 4 => 2,
5 => 3, 5 => 3,
6 => 4, 6 => 3,
_ => 1 _ => 1
}; };
} }

View File

@ -10,7 +10,7 @@ namespace Oshima.FunGame.OshimaModules.Skills
public override string Description => Effects.Count > 0 ? Effects.First().Description : ""; public override string Description => Effects.Count > 0 ? Effects.First().Description : "";
public override string DispelDescription => Effects.Count > 0 ? Effects.First().DispelDescription : ""; public override string DispelDescription => Effects.Count > 0 ? Effects.First().DispelDescription : "";
public override double EPCost => 100; public override double EPCost => 100;
public override double CD => 60 - 2 * (Level - 1); public override double CD => 80 - 4 * (Level - 1);
public override double HardnessTime { get; set; } = 15; public override double HardnessTime { get; set; } = 15;
public override bool CanSelectSelf => true; public override bool CanSelectSelf => true;
public override bool CanSelectEnemy => false; public override bool CanSelectEnemy => false;

View File

@ -9,8 +9,8 @@ namespace Oshima.FunGame.OshimaModules.Skills
public override string Name => "能量毁灭"; public override string Name => "能量毁灭";
public override string Description => Effects.Count > 0 ? Effects.First().Description : ""; public override string Description => Effects.Count > 0 ? Effects.First().Description : "";
public override double EPCost => 100; public override double EPCost => 100;
public override double CD => 60 - 1 * (Level - 1); public override double CD => 100 - 4 * (Level - 1);
public override double HardnessTime { get; set; } = 25; public override double HardnessTime { get; set; } = 18;
public override string Slogan => "从深渊引爆力量,世界将为之颤抖!!!!"; public override string Slogan => "从深渊引爆力量,世界将为之颤抖!!!!";
public override bool CanSelectSelf => false; public override bool CanSelectSelf => false;
public override bool CanSelectEnemy => true; public override bool CanSelectEnemy => true;
@ -39,7 +39,7 @@ namespace Oshima.FunGame.OshimaModules.Skills
private double => 0.2 * Level; private double => 0.2 * Level;
private double => * Skill.Character?.INT ?? 0; private double => * Skill.Character?.INT ?? 0;
private double => 1.05 * Level; private double => 1 * Level;
public override void OnSkillCasted(Character caster, List<Character> targets, Dictionary<string, object> others) public override void OnSkillCasted(Character caster, List<Character> targets, Dictionary<string, object> others)
{ {

View File

@ -10,7 +10,7 @@ namespace Oshima.FunGame.OshimaModules.Skills
public override string Description => Effects.Count > 0 ? Effects.First().Description : ""; public override string Description => Effects.Count > 0 ? Effects.First().Description : "";
public override string DispelDescription => Effects.Count > 0 ? Effects.First().DispelDescription : ""; public override string DispelDescription => Effects.Count > 0 ? Effects.First().DispelDescription : "";
public override double EPCost => 100; public override double EPCost => 100;
public override double CD => 45; public override double CD => 45 + 1 * (Level - 1);
public override double HardnessTime { get; set; } = 7; public override double HardnessTime { get; set; } = 7;
public override bool CanSelectSelf => true; public override bool CanSelectSelf => true;
public override bool CanSelectEnemy => false; public override bool CanSelectEnemy => false;
@ -25,16 +25,18 @@ namespace Oshima.FunGame.OshimaModules.Skills
{ {
public override long Id => Skill.Id; public override long Id => Skill.Id;
public override string Name => Skill.Name; public override string Name => Skill.Name;
public override string Description => $"获得 40% 吸血,持续 {Duration:0.##} {GameplayEquilibriumConstant.InGameTime}。"; public override string Description => $"获得 {吸血系数 * 100:0.##}% 吸血,持续 {Duration:0.##} {GameplayEquilibriumConstant.InGameTime}。";
public override bool Durative => true; public override bool Durative => true;
public override double Duration => 30; public override double Duration => 30;
public override DispelledType DispelledType => DispelledType.CannotBeDispelled; public override DispelledType DispelledType => DispelledType.CannotBeDispelled;
private double => 0.2 + 0.05 * (Level - 1);
public override void AfterDamageCalculation(Character character, Character enemy, double damage, double actualDamage, bool isNormalAttack, DamageType damageType, MagicType magicType, DamageResult damageResult) public override void AfterDamageCalculation(Character character, Character enemy, double damage, double actualDamage, bool isNormalAttack, DamageType damageType, MagicType magicType, DamageResult damageResult)
{ {
if (character == Skill.Character && (damageResult == DamageResult.Normal || damageResult == DamageResult.Critical) && character.HP < character.MaxHP) if (character == Skill.Character && (damageResult == DamageResult.Normal || damageResult == DamageResult.Critical) && character.HP < character.MaxHP)
{ {
double = 0.4 * damage; double = * damage;
HealToTarget(character, character, ); HealToTarget(character, character, );
} }
} }

View File

@ -25,22 +25,25 @@ namespace Oshima.FunGame.OshimaModules.Skills
{ {
public override long Id => Skill.Id; public override long Id => Skill.Id;
public override string Name => Skill.Name; public override string Name => Skill.Name;
public override string Description => $"{Duration:0.##} {GameplayEquilibriumConstant.InGameTime}内,增加 40% 攻击力 [ {攻击力提升:0.##} ]、30% 物理穿透和 25% 闪避率(不可叠加),普通攻击硬直时间额外减少 20%,基于 {系数 * 100:0.##}% 敏捷 [ {伤害加成:0.##} ] 强化普通攻击的伤害。在持续时间内,[ 心灵之火 ] 的冷却时间降低至 3 {GameplayEquilibriumConstant.InGameTime}。"; public override string Description => $"{Duration:0.##} {GameplayEquilibriumConstant.InGameTime}内,增加 {攻击力提升系数 * 100:0.##}% 攻击力 [ {攻击力提升:0.##} ]、{物理穿透提升 * 100:0.##}% 物理穿透和 {闪避率提升 * 100:0.##}% 闪避率(不可叠加),普通攻击硬直时间额外减少 20%,基于 {系数 * 100:0.##}% 敏捷 [ {伤害加成:0.##} ] 强化普通攻击的伤害。在持续时间内,[ 心灵之火 ] 的冷却时间降低至 3 {GameplayEquilibriumConstant.InGameTime}。";
public override bool Durative => true; public override bool Durative => true;
public override double Duration => 40; public override double Duration => 30;
public override DispelledType DispelledType => DispelledType.CannotBeDispelled; public override DispelledType DispelledType => DispelledType.CannotBeDispelled;
private double => 1.2 * (1 + 0.6 * (Skill.Level - 1)); private double => 1.2 * (1 + 0.5 * (Skill.Level - 1));
private double => * Skill.Character?.AGI ?? 0; private double => * Skill.Character?.AGI ?? 0;
private double => 0.4 * Skill.Character?.BaseATK ?? 0; private double => Skill.Level > 0 ? 0.15 + 0.03 * (Skill.Level - 1) : 0.15;
private double => * Skill.Character?.BaseATK ?? 0;
private double 穿 => Skill.Level > 0 ? 0.1 + 0.03 * (Skill.Level - 1) : 0.1;
private double => Skill.Level > 0 ? 0.1 + 0.02 * (Skill.Level - 1) : 0.1;
private double = 0; private double = 0;
public override void OnEffectGained(Character character) public override void OnEffectGained(Character character)
{ {
= ; = ;
character.ExATK2 += ; character.ExATK2 += ;
character.PhysicalPenetration += 0.3; character.PhysicalPenetration += 穿;
character.ExEvadeRate += 0.25; character.ExEvadeRate += ;
if (character.Effects.Where(e => e is ).FirstOrDefault() is e) if (character.Effects.Where(e => e is ).FirstOrDefault() is e)
{ {
e. = 3; e. = 3;
@ -51,11 +54,11 @@ namespace Oshima.FunGame.OshimaModules.Skills
public override void OnEffectLost(Character character) public override void OnEffectLost(Character character)
{ {
character.ExATK2 -= ; character.ExATK2 -= ;
character.PhysicalPenetration -= 0.3; character.PhysicalPenetration -= 穿;
character.ExEvadeRate -= 0.25; character.ExEvadeRate -= ;
if (character.Effects.Where(e => e is ).FirstOrDefault() is e) if (character.Effects.Where(e => e is ).FirstOrDefault() is e)
{ {
e. = 8; e. = 10;
} }
} }

View File

@ -28,7 +28,7 @@ namespace Oshima.FunGame.OshimaModules.Skills
( > 0 ? $"(正在冷却:剩余 {冷却时间:0.##} {GameplayEquilibriumConstant.InGameTime}" : ""); ( > 0 ? $"(正在冷却:剩余 {冷却时间:0.##} {GameplayEquilibriumConstant.InGameTime}" : "");
public double { get; set; } = 0; public double { get; set; } = 0;
public double { get; set; } = 12; public double { get; set; } = 10;
private bool = false; private bool = false;
public override double AlterActualDamageAfterCalculation(Character character, Character enemy, double damage, bool isNormalAttack, DamageType damageType, MagicType magicType, DamageResult damageResult, ref bool isEvaded, Dictionary<Effect, double> totalDamageBonus) public override double AlterActualDamageAfterCalculation(Character character, Character enemy, double damage, bool isNormalAttack, DamageType damageType, MagicType magicType, DamageResult damageResult, ref bool isEvaded, Dictionary<Effect, double> totalDamageBonus)

View File

@ -10,7 +10,7 @@ namespace Oshima.FunGame.OshimaModules.Skills
public override string Description => Effects.Count > 0 ? Effects.First().Description : ""; public override string Description => Effects.Count > 0 ? Effects.First().Description : "";
public override string DispelDescription => Effects.Count > 0 ? Effects.First().DispelDescription : ""; public override string DispelDescription => Effects.Count > 0 ? Effects.First().DispelDescription : "";
public override double EPCost => 100; public override double EPCost => 100;
public override double CD => 35; public override double CD => 50;
public override double HardnessTime { get; set; } = 3; public override double HardnessTime { get; set; } = 3;
public override bool CanSelectSelf => true; public override bool CanSelectSelf => true;
public override bool CanSelectEnemy => false; public override bool CanSelectEnemy => false;

View File

@ -155,6 +155,11 @@ namespace Oshima.FunGame.OshimaServers
FunGameService.RefreshStoreData(); FunGameService.RefreshStoreData();
Controller.WriteLine("刷新商店"); Controller.WriteLine("刷新商店");
}); });
Task.Run(() =>
{
FunGameService.RefreshMarketData();
Controller.WriteLine("刷新市场");
});
// 刷新活动缓存 // 刷新活动缓存
FunGameService.GetEventCenter(); FunGameService.GetEventCenter();
FunGameService.RefreshNotice(); FunGameService.RefreshNotice();

View File

@ -102,6 +102,7 @@
public static Dictionary<string, string> ClubHelp { get; } = new() { public static Dictionary<string, string> ClubHelp { get; } = new() {
{"我的社团", "查看社团信息"}, {"我的社团", "查看社团信息"},
{"私信 <对方UID/昵称> <内容>", "发送私信给对方"},
{"加入社团 <社团序号>", "申请加入社团"}, {"加入社团 <社团序号>", "申请加入社团"},
{"退出社团", "退出当前社团"}, {"退出社团", "退出当前社团"},
{"创建社团 <前缀>", "创建一个公开社团,若指令中包含私密一词,将创建私密社团\r\n社团前缀3-4个字符允许英文字母和数字、部分特殊字符"}, {"创建社团 <前缀>", "创建一个公开社团,若指令中包含私密一词,将创建私密社团\r\n社团前缀3-4个字符允许英文字母和数字、部分特殊字符"},
@ -122,14 +123,17 @@
{"社团商店查看 <商品序号>", "查看指定商品详情"}, {"社团商店查看 <商品序号>", "查看指定商品详情"},
{"社团商店购买 <商品序号>", "购买指定商品"}, {"社团商店购买 <商品序号>", "购买指定商品"},
{"社团商店出售 <物品序号>", "向商店出售具有回收价的指定物品"}, {"社团商店出售 <物品序号>", "向商店出售具有回收价的指定物品"},
{"社团市场上架 <物品序号> <定价>", "将物品寄售到社团市场上手续费10%并且8%进入社团基金"}, {"社团市场 [页码]", "查看社团市场商品"},
{"社团市场出售 <物品序号> <定价>", "将物品寄售到社团市场上系统手续费4%社团收取8%作为社团基金"},
{"社团市场下架 <市场物品序号>", "下架指定物品"}, {"社团市场下架 <市场物品序号>", "下架指定物品"},
{"社团市场查看 <市场物品序号>", "查看指定物品"},
{"社团市场购买 <市场物品序号>", "购买指定物品"}, {"社团市场购买 <市场物品序号>", "购买指定物品"},
{"社团市场清空", "管理员可下架所有物品"}, {"社团市场清空", "管理员可下架所有物品"},
}; };
public static Dictionary<string, string> ActivityHelp { get; } = new() { public static Dictionary<string, string> ActivityHelp { get; } = new() {
{"签到", "每日签到奖励"}, {"签到", "每日签到奖励"},
{"公告", "查看系统公告"},
{"活动", "查看活动中心"}, {"活动", "查看活动中心"},
{"查活动 <活动序号>", "查看指定活动详情"}, {"查活动 <活动序号>", "查看指定活动详情"},
{"做活动任务 <活动序号> <任务序号>", "开始指定活动任务"}, {"做活动任务 <活动序号> <任务序号>", "开始指定活动任务"},
@ -156,11 +160,15 @@
{"报价添加对方物品 <报价序号> <{物品序号}...>", "仅发起方可操作"}, {"报价添加对方物品 <报价序号> <{物品序号}...>", "仅发起方可操作"},
{"报价移除物品 <报价序号> <{报价物品序号}...>", "仅发起方可操作"}, {"报价移除物品 <报价序号> <{报价物品序号}...>", "仅发起方可操作"},
{"报价移除对方物品 <报价序号> <{报价物品序号}...>", "仅发起方可操作"}, {"报价移除对方物品 <报价序号> <{报价物品序号}...>", "仅发起方可操作"},
{"市场上架 <物品序号> <定价>", "将物品寄售到市场上手续费15%"}, {"市场 [页码]", "查看市场商品"},
{"市场出售 <物品序号> <定价>", "将物品寄售到市场上手续费15%"},
{"市场下架 <市场物品序号>", "下架指定物品"}, {"市场下架 <市场物品序号>", "下架指定物品"},
{"市场查看 <市场物品序号>", "查看指定物品"},
{"市场购买 <市场物品序号>", "购买指定物品"}, {"市场购买 <市场物品序号>", "购买指定物品"},
{"社团市场上架 <物品序号> <定价>", "将物品寄售到社团市场上手续费10%并且8%进入社团基金"}, {"社团市场 [页码]", "查看社团市场商品"},
{"社团市场出售 <物品序号> <定价>", "将物品寄售到社团市场上系统手续费4%社团收取8%作为社团基金"},
{"社团市场下架 <市场物品序号>", "下架指定物品"}, {"社团市场下架 <市场物品序号>", "下架指定物品"},
{"社团市场查看 <市场物品序号>", "查看指定物品"},
{"社团市场购买 <市场物品序号>", "购买指定物品"}, {"社团市场购买 <市场物品序号>", "购买指定物品"},
{"社团市场清空", "管理员可下架所有物品"}, {"社团市场清空", "管理员可下架所有物品"},
}; };

View File

@ -2113,13 +2113,13 @@ namespace Oshima.FunGame.OshimaServers.Service
if (goods.Stock != -1 && goods.Stock - count < 0) if (goods.Stock != -1 && goods.Stock - count < 0)
{ {
return msg = $"此商品【{goods.Name}】库存不足,无法购买!\r\n你想要购买 {count} 件,但库存只有 {goods.Stock} 件。"; return $"此商品【{goods.Name}】库存不足,无法购买!\r\n你想要购买 {count} 件,但库存只有 {goods.Stock} 件。";
} }
goods.UsersBuyCount.TryGetValue(user.Id, out int buyCount); goods.UsersBuyCount.TryGetValue(user.Id, out int buyCount);
if (goods.Quota > 0 && (buyCount + count > goods.Quota)) if (goods.Quota > 0 && (buyCount + count > goods.Quota))
{ {
return msg = $"此商品【{goods.Name}】限量购买 {goods.Quota} 件!\r\n你已经购买了 {buyCount} 件,想要购买 {count} 件,超过了购买限制。"; return $"此商品【{goods.Name}】限量购买 {goods.Quota} 件!\r\n你已经购买了 {buyCount} 件,想要购买 {count} 件,超过了购买限制。";
} }
foreach (string needy in goods.Prices.Keys) foreach (string needy in goods.Prices.Keys)
@ -2169,7 +2169,7 @@ namespace Oshima.FunGame.OshimaServers.Service
foreach (Item item in goods.Items) foreach (Item item in goods.Items)
{ {
if (item.Name == nameof()) if (item.Id == (long)SpecialItemID.)
{ {
int exploreTimes = FunGameConstant.MaxExploreTimes + count; int exploreTimes = FunGameConstant.MaxExploreTimes + count;
if (pc.TryGetValue("exploreTimes", out object? value) && int.TryParse(value.ToString(), out exploreTimes)) if (pc.TryGetValue("exploreTimes", out object? value) && int.TryParse(value.ToString(), out exploreTimes))
@ -2209,6 +2209,80 @@ namespace Oshima.FunGame.OshimaServers.Service
return msg; return msg;
} }
public static string MarketBuyItem(Market market, MarketItem item, PluginConfig pc, User user, int count, out bool result)
{
result = false;
string msg = "";
DateTime now = DateTime.Now;
if (market.StartTime > now || market.EndTime < now)
{
return "铎京集市未处于营业时间内。";
}
if (item.Status != MarketItemState.Listed)
{
return $"无法购买此商品,原因:该商品的状态是:{CommonSet.GetMarketItemStatus(item.Status)}。";
}
if (item.User == user.Id)
{
return $"不能购买自己上架的商品!如需下架,请使用【市场下架+序号】指令。";
}
if (item.Stock != -1 && item.Stock - count < 0)
{
return $"此商品【{item.Name}】库存不足,无法购买!\r\n你想要购买 {count} 件,但库存只有 {item.Stock} 件。";
}
double reduce = Calculation.Round2Digits(item.Price * count);
if (user.Inventory.Credits >= reduce)
{
user.Inventory.Credits -= reduce;
}
else
{
return $"你的{General.GameplayEquilibriumConstant.InGameCurrency}不足 {reduce} 呢,无法购买【{item.Name}】!";
}
if (item.Item.Id == (long)SpecialItemID.)
{
int exploreTimes = FunGameConstant.MaxExploreTimes + count;
if (pc.TryGetValue("exploreTimes", out object? value) && int.TryParse(value.ToString(), out exploreTimes))
{
exploreTimes += count;
}
pc.Add("exploreTimes", exploreTimes);
}
else
{
for (int i = 0; i < count; i++)
{
AddItemToUserInventory(user, item.Item, copyLevel: true, useOriginalPrice: true, toExploreCache: false, toActivitiesCache: false);
}
}
if (item.Stock != -1)
{
item.Stock -= count;
if (item.Stock < 0) item.Stock = 0;
if (item.Stock == 0)
{
item.Status = MarketItemState.Purchased;
item.FinishTime = DateTime.Now;
}
}
item.Buyers.Add(user.Id);
result = true;
msg += $"恭喜你成功购买 {count} 件【{item.Name}】!\r\n" +
$"总计消费:{(item.Price > 0 ? item.Price : "")}\r\n" +
$"包含物品:[{ItemSet.GetQualityTypeName(item.Item.QualityType)}|{ItemSet.GetItemTypeName(item.Item.ItemType)}] {item.Item.Name} * {count}\r\n" +
$"铎京集市期待你的下次光临。";
return msg;
}
public static string Select_CheckAutoKey(SQLHelper SQLHelper, string AutoKey) public static string Select_CheckAutoKey(SQLHelper SQLHelper, string AutoKey)
{ {
@ -3289,7 +3363,7 @@ namespace Oshima.FunGame.OshimaServers.Service
{ {
user.Inventory.Items.Remove(item); user.Inventory.Items.Remove(item);
Item newItem = item.Copy(copyGuid: true); Item newItem = item.Copy(true, true);
newItem.User = user2; newItem.User = user2;
newItem.IsSellable = false; newItem.IsSellable = false;
newItem.IsTradable = false; newItem.IsTradable = false;
@ -3306,7 +3380,7 @@ namespace Oshima.FunGame.OshimaServers.Service
{ {
user2.Inventory.Items.Remove(item); user2.Inventory.Items.Remove(item);
Item newItem = item.Copy(copyGuid: true); Item newItem = item.Copy(true, true);
newItem.User = user; newItem.User = user;
newItem.IsSellable = false; newItem.IsSellable = false;
newItem.IsTradable = false; newItem.IsTradable = false;
@ -4204,6 +4278,156 @@ namespace Oshima.FunGame.OshimaServers.Service
} }
} }
public static string GetMarketInfo(Market market, User? user = null, int page = 1, bool simply = false, bool showListed = true)
{
if (page <= 0) page = 1;
int maxPage = market.MarketItems.Values.MaxPage(10);
if (page > maxPage) page = maxPage;
IEnumerable<MarketItem> marketItems = market.MarketItems.Values.GetPage(page, 10);
StringBuilder builder = new();
builder.AppendLine($"☆★☆ {market.Name} ☆★☆");
if (market.Description != "") builder.AppendLine($"{market.Description}");
if (market.StartTime.HasValue && market.EndTime.HasValue)
{
builder.AppendLine($"开放时间:{market.StartTime.Value.ToString(General.GeneralDateTimeFormatChinese)} 至 {market.EndTime.Value.ToString(General.GeneralDateTimeFormatChinese)}");
}
else if (market.StartTime.HasValue && !market.EndTime.HasValue)
{
builder.AppendLine($"开始开放时间:{market.StartTime.Value.ToString(General.GeneralDateTimeFormatChinese)}");
}
else if (!market.StartTime.HasValue && market.EndTime.HasValue)
{
builder.AppendLine($"停止开放时间:{market.EndTime.Value.ToString(General.GeneralDateTimeFormatChinese)}");
}
else
{
builder.AppendLine($"开放时间:全年无休,永久开放");
}
if (market.StartTimeOfDay.HasValue && market.EndTimeOfDay.HasValue)
{
builder.AppendLine($"每日交易时间:{market.StartTimeOfDay.Value.ToString(General.GeneralDateTimeFormatTimeOnly)} 至 {market.EndTimeOfDay.Value.ToString(General.GeneralDateTimeFormatTimeOnly)}");
}
else
{
builder.AppendLine($"[ 24H ] 全天开放交易");
}
DateTime now = DateTime.Now;
TimeSpan nowTimeOfDay = now.TimeOfDay;
bool isStoreOpen = true;
if (market.StartTime.HasValue && market.StartTime.Value > now || market.EndTime.HasValue && market.EndTime.Value < now)
{
isStoreOpen = false;
}
if (isStoreOpen && market.StartTimeOfDay.HasValue && market.EndTimeOfDay.HasValue)
{
TimeSpan startTimeSpan = market.StartTimeOfDay.Value.TimeOfDay;
TimeSpan endTimeSpan = market.EndTimeOfDay.Value.TimeOfDay;
if (startTimeSpan <= endTimeSpan)
{
isStoreOpen = nowTimeOfDay >= startTimeSpan && nowTimeOfDay <= endTimeSpan;
}
else
{
isStoreOpen = nowTimeOfDay >= startTimeSpan || nowTimeOfDay <= endTimeSpan;
}
}
if (!isStoreOpen)
{
builder.AppendLine($"市场现在不在交易时间内。");
}
builder.AppendLine($"☆--- 市场商品列表 ---☆");
MarketItem[] MarketItemsValid = [.. market.MarketItems.Values];
if (showListed) MarketItemsValid = [.. MarketItemsValid.Where(g => g.Status == MarketItemState.Listed)];
if (MarketItemsValid.Length == 0)
{
builder.AppendLine("当前没有商品可供购买,过一段时间再来吧。");
}
else
{
foreach (MarketItem marketItem in MarketItemsValid)
{
builder.AppendLine(FunGameService.GetMarketItemInfo(marketItem, true, user?.Id ?? 0));
}
builder.AppendLine("提示:使用【市场查看+序号】查看商品详细信息,使用【市场购买+序号】购买商品。");
}
if (user != null)
{
builder.AppendLine($"你的现有{General.GameplayEquilibriumConstant.InGameCurrency}{user.Inventory.Credits:0.##}");
}
builder.AppendLine($"页数:{page} / {maxPage},使用【市场+页码】快速跳转指定页面。");
return builder.ToString().Trim();
}
public static string GetMarketItemInfo(MarketItem item, bool simply, long visitUser)
{
StringBuilder builder = new();
if (simply)
{
builder.AppendLine($"{item.Id}. {item.Name}");
builder.AppendLine($"{ItemSet.GetQualityTypeName(item.Item.QualityType)} {ItemSet.GetItemTypeName(item.Item.ItemType)}");
string username = item.Username;
if (FunGameConstant.UserIdAndUsername.TryGetValue(item.User, out User? user) && user != null)
{
username = user.Username;
}
builder.AppendLine($"卖家:{username}");
builder.AppendLine($"商品描述:{item.Item.Description}");
builder.AppendLine($"商品售价:{(item.Price > 0 ? item.Price : "")} {General.GameplayEquilibriumConstant.InGameCurrency}");
if (item.Status == MarketItemState.Purchased)
{
builder.AppendLine($"商品已售罄");
}
else if (item.Status == MarketItemState.Delisted)
{
builder.AppendLine($"商品已下架");
}
else builder.AppendLine($"剩余库存:{(item.Stock == -1 ? "" : item.Stock)}");
}
else
{
builder.AppendLine($"{item.Id}. {item.Name}");
string username = item.Username;
if (FunGameConstant.UserIdAndUsername.TryGetValue(item.User, out User? user) && user != null)
{
username = user.Username;
}
builder.AppendLine($"卖家:{username}");
builder.AppendLine($"上架时间:{item.CreateTime.ToString(General.GeneralDateTimeFormatChinese)}");
builder.AppendLine($"商品售价:{(item.Price > 0 ? item.Price : "")} {General.GameplayEquilibriumConstant.InGameCurrency}");
if (item.Status == MarketItemState.Purchased)
{
builder.AppendLine($"商品已售罄");
if (item.FinishTime.HasValue) builder.AppendLine($"售罄时间:{item.FinishTime.Value.ToString(General.GeneralDateTimeFormatChinese)}");
if (visitUser == item.User && item.Buyers.Count > 0)
{
HashSet<string> buyers = [];
foreach (long buyerid in item.Buyers)
{
username = "Unknown";
if (FunGameConstant.UserIdAndUsername.TryGetValue(buyerid, out User? buyer) && buyer != null)
{
username = buyer.Username;
}
buyers.Add(username);
}
builder.AppendLine($"买家:{string.Join("", buyers)}");
}
}
else if (item.Status == MarketItemState.Delisted)
{
builder.AppendLine($"商品已下架");
if (item.FinishTime.HasValue) builder.AppendLine($"下架时间:{item.FinishTime.Value.ToString(General.GeneralDateTimeFormatChinese)}");
}
else builder.AppendLine($"剩余库存:{(item.Stock == -1 ? "" : item.Stock)}");
builder.AppendLine($"☆--- 物品信息 ---☆\r\n{item.Item}");
}
return builder.ToString().Trim();
}
public static void RefreshNotice() public static void RefreshNotice()
{ {
Notices.LoadConfig(); Notices.LoadConfig();
@ -4335,7 +4559,6 @@ namespace Oshima.FunGame.OshimaServers.Service
{ {
if (store.ExpireTime != null && store.ExpireTime.Value.Date <= DateTime.Today) if (store.ExpireTime != null && store.ExpireTime.Value.Date <= DateTime.Today)
{ {
stores.Remove(key);
continue; continue;
} }
if (store.AutoRefresh && store.NextRefreshDate.Date <= DateTime.Today) if (store.AutoRefresh && store.NextRefreshDate.Date <= DateTime.Today)
@ -4347,6 +4570,7 @@ namespace Oshima.FunGame.OshimaServers.Service
} }
store.NextRefreshDate = DateTime.Today.AddHours(4).AddDays(store.RefreshInterval); store.NextRefreshDate = DateTime.Today.AddHours(4).AddDays(store.RefreshInterval);
} }
stores.Add(key, store);
} }
} }
} }
@ -4354,6 +4578,44 @@ namespace Oshima.FunGame.OshimaServers.Service
} }
} }
} }
public static void RefreshMarketData()
{
// 刷新市场
GetMarketSemaphoreSlim();
string directoryPath = $@"{AppDomain.CurrentDomain.BaseDirectory}configs/markets";
if (Directory.Exists(directoryPath))
{
DateTime now = DateTime.Now;
string[] filePaths = Directory.GetFiles(directoryPath);
foreach (string filePath in filePaths)
{
string fileName = Path.GetFileNameWithoutExtension(filePath);
EntityModuleConfig<Market> markets = new("markets", fileName);
markets.LoadConfig();
string[] marketNames = [.. markets.Keys];
foreach (string key in marketNames)
{
Market? market = markets.Get(key);
if (market != null)
{
long[] items = [.. market.MarketItems.Keys];
foreach (long id in items)
{
MarketItem item = market.MarketItems[id];
if ((item.Status == MarketItemState.Delisted || item.Status == MarketItemState.Purchased) && item.FinishTime.HasValue && (now - item.FinishTime.Value).TotalDays >= 3)
{
market.MarketItems.Remove(id);
}
}
markets.Add(key, market);
}
}
markets.SaveConfig();
}
}
ReleaseMarketSemaphoreSlim();
}
public static void PreRefreshStore() public static void PreRefreshStore()
{ {
@ -4465,5 +4727,18 @@ namespace Oshima.FunGame.OshimaServers.Service
} }
public static bool CheckSemaphoreSlim(long uid) => CheckSemaphoreSlim(uid.ToString()); public static bool CheckSemaphoreSlim(long uid) => CheckSemaphoreSlim(uid.ToString());
public static void GetMarketSemaphoreSlim()
{
FunGameConstant.MarketSemaphoreSlim.Wait(FunGameConstant.SemaphoreSlimTimeout);
}
public static void ReleaseMarketSemaphoreSlim()
{
if (FunGameConstant.MarketSemaphoreSlim.CurrentCount == 0)
{
FunGameConstant.MarketSemaphoreSlim.Release();
}
}
} }
} }

View File

@ -16,7 +16,6 @@ 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;
using ProjectRedbud.FunGame.SQLQueryExtension;
namespace Oshima.FunGame.WebAPI.Controllers namespace Oshima.FunGame.WebAPI.Controllers
{ {
@ -6877,12 +6876,24 @@ namespace Oshima.FunGame.WebAPI.Controllers
} }
[HttpPost("marketsellitem")] [HttpPost("marketsellitem")]
public string MarketSellItem([FromQuery] long? uid = null, [FromQuery] int itemIndex = -1, [FromQuery] double price = 0) public string MarketSellItem([FromQuery] long uid = -1, [FromQuery] double price = 0, [FromBody] int[]? itemIndexs = null)
{ {
long userid = uid ?? Convert.ToInt64("10" + Verification.CreateVerifyCode(VerifyCodeType.NumberVerifyCode, 11)); long userid = uid;
itemIndexs ??= [];
if (price <= 0)
{
return "请输入一个大于 0 的售价。";
}
try try
{ {
using SQLHelper? sql = Factory.OpenFactory.GetSQLHelper();
if (sql is null)
{
return busy;
}
PluginConfig pc = FunGameService.GetUserConfig(userid, out _); PluginConfig pc = FunGameService.GetUserConfig(userid, out _);
if (pc.Count > 0) if (pc.Count > 0)
@ -6890,73 +6901,75 @@ namespace Oshima.FunGame.WebAPI.Controllers
User user = FunGameService.GetUser(pc); User user = FunGameService.GetUser(pc);
List<string> msgs = []; List<string> msgs = [];
using SQLHelper? sql = Factory.OpenFactory.GetSQLHelper();
List<Guid> itemTrading = []; List<Guid> itemTrading = [];
if (sql != null) itemTrading = SQLService.GetUserItemGuids(sql, userid);
{
itemTrading = SQLService.GetUserItemGuids(sql, userid);
}
Dictionary<int, Item> dict = user.Inventory.Items.Select((item, index) => new { item, index }).ToDictionary(x => x.index + 1, x => x.item); Dictionary<int, Item> dict = user.Inventory.Items.Select((item, index) => new { item, index }).ToDictionary(x => x.index + 1, x => x.item);
Item? item = null; FunGameService.GetMarketSemaphoreSlim();
if (itemIndex > 0 && itemIndex <= user.Inventory.Items.Count)
List<Item> successItems = [];
foreach (int itemIndex in itemIndexs)
{ {
item = user.Inventory.Items.ToList()[itemIndex - 1]; if (itemIndex > 0 && itemIndex <= user.Inventory.Items.Count)
if (price <= 0)
{ {
msgs.Add($"请输入一个大于 0 的售价。"); Item item = user.Inventory.Items.ToList()[itemIndex - 1];
if (item.IsLock)
{
msgs.Add($"物品 {itemIndex}. {item.Name}:此物品已上锁,无法出售。");
}
if (item.Character != null)
{
msgs.Add($"物品 {itemIndex}. {item.Name}:此物品已被 {item.Character} 装备中,无法出售。");
}
if (itemTrading.Contains(item.Guid))
{
msgs.Add($"物品 {itemIndex}. {item.Name}:此物品正在进行交易,无法出售,请检查交易报价。");
}
if (!item.IsSellable)
{
msgs.Add($"物品 {itemIndex}. {item.Name}:此物品无法出售{(item.NextSellableTime != DateTime.MinValue ? $" {item.NextSellableTime.ToString(General.GeneralDateTimeFormatChinese)} " : "")}。");
}
if (msgs.Count == 0)
{
user.Inventory.Items.Remove(item);
successItems.Add(item);
msgs.Add($"物品 {itemIndex}. {item.Name}:上架市场成功!定价:{price:0.##} {General.GameplayEquilibriumConstant.InGameCurrency}。");
}
}
else
{
msgs.Add($"{itemIndex}. 没有找到与这个序号相对应的物品!");
}
}
if (successItems.Count > 0)
{
EntityModuleConfig<Market> emc = new("markets", "general");
emc.LoadConfig();
Market market = emc.Get("dokyo") ?? new("铎京集市");
foreach (Item successItem in successItems)
{
Item newItem = successItem.Copy(true);
market.AddItem(user, newItem, price, 1);
} }
if (item.IsLock) emc.Add("dokyo", market);
{ emc.SaveConfig();
msgs.Add($"物品 {itemIndex}. {item.Name}:此物品已上锁,无法出售。");
}
if (item.Character != null)
{
msgs.Add($"物品 {itemIndex}. {item.Name}:此物品已被 {item.Character} 装备中,无法出售。");
}
if (itemTrading.Contains(item.Guid))
{
msgs.Add($"物品 {itemIndex}. {item.Name}:此物品正在进行交易,无法出售,请检查交易报价。");
}
if (!item.IsSellable)
{
msgs.Add($"物品 {itemIndex}. {item.Name}:此物品无法出售{(item.NextSellableTime != DateTime.MinValue ? $" {item.NextSellableTime.ToString(General.GeneralDateTimeFormatChinese)} " : "")}。");
}
if (msgs.Count > 0) item = null;
} }
else else
{
msgs.Add($"没有找到与这个序号相对应的物品!");
}
if (item != null && sql != null && user.Inventory.Items.Remove(item))
{
EntityModuleConfig<Item> emc = new("markets", user.Id.ToString());
emc.LoadConfig();
Item newItem = item.Copy(true);
emc.Add(item.Guid.ToString(), newItem);
sql.AddMarketItem(newItem.Guid, userid, price);
long marketId = sql.LastInsertId;
FunGameConstant.MarketItemIdAndItem[marketId] = newItem;
if (sql.Success)
{
emc.SaveConfig();
msgs.Add($"物品 {itemIndex}. {item.Name} 上架市场成功,售价:{price} {General.GameplayEquilibriumConstant.InGameCurrency},市场编号:{marketId}。");
}
}
if (msgs.Count == 0)
{ {
msgs.Add($"没有成功上架任何物品。请检查物品是否存在或是否满足上架条件。"); msgs.Add($"没有成功上架任何物品。请检查物品是否存在或是否满足上架条件。");
} }
FunGameService.ReleaseMarketSemaphoreSlim();
FunGameService.SetUserConfigAndReleaseSemaphoreSlim(userid, pc, user); FunGameService.SetUserConfigAndReleaseSemaphoreSlim(userid, pc, user);
return string.Join("\r\n", msgs); return string.Join("\r\n", msgs);
} }
else else
@ -6967,6 +6980,262 @@ namespace Oshima.FunGame.WebAPI.Controllers
} }
catch (Exception e) catch (Exception e)
{ {
FunGameService.ReleaseMarketSemaphoreSlim();
FunGameService.ReleaseUserSemaphoreSlim(userid);
Logger.LogError(e, "Error: {e}", e);
return busy;
}
}
[HttpPost("marketshowlist")]
public string MarketShowList([FromQuery] long userid = -1, [FromQuery] int page = 0)
{
try
{
PluginConfig pc = FunGameService.GetUserConfig(userid, out _);
if (pc.Count > 0)
{
User user = FunGameService.GetUser(pc);
FunGameService.GetMarketSemaphoreSlim();
EntityModuleConfig<Market> emc = new("markets", "general");
emc.LoadConfig();
Market market = emc.Get("dokyo") ?? new("铎京集市");
string msg = FunGameService.GetMarketInfo(market, user, page, true);
FunGameService.ReleaseMarketSemaphoreSlim();
FunGameService.SetUserConfigAndReleaseSemaphoreSlim(userid, pc, user);
return msg;
}
else
{
FunGameService.ReleaseUserSemaphoreSlim(userid);
return noSaved;
}
}
catch (Exception e)
{
FunGameService.ReleaseMarketSemaphoreSlim();
FunGameService.ReleaseUserSemaphoreSlim(userid);
Logger.LogError(e, "Error: {e}", e);
return busy;
}
}
[HttpPost("marketshowlistmysells")]
public string MarketShowListMySells([FromQuery] long userid = -1, [FromQuery] int page = 0)
{
try
{
PluginConfig pc = FunGameService.GetUserConfig(userid, out _);
if (pc.Count > 0)
{
User user = FunGameService.GetUser(pc);
FunGameService.GetMarketSemaphoreSlim();
EntityModuleConfig<Market> emc = new("markets", "general");
emc.LoadConfig();
Market market = emc.Get("dokyo") ?? new("铎京集市");
string msg = "☆--- 我的市场商品 ---☆\r\n";
MarketItem[] marketItems = [.. market.MarketItems.Values.Where(m => m.User == userid)];
if (marketItems.Length > 0)
{
foreach (MarketItem marketItem in marketItems)
{
msg += FunGameService.GetMarketItemInfo(marketItem, true, userid) + "\r\n";
}
}
else msg += "你还没有上架过任何物品。";
FunGameService.ReleaseMarketSemaphoreSlim();
FunGameService.SetUserConfigAndReleaseSemaphoreSlim(userid, pc, user);
return msg.Trim();
}
else
{
FunGameService.ReleaseUserSemaphoreSlim(userid);
return noSaved;
}
}
catch (Exception e)
{
FunGameService.ReleaseMarketSemaphoreSlim();
FunGameService.ReleaseUserSemaphoreSlim(userid);
Logger.LogError(e, "Error: {e}", e);
return busy;
}
}
[HttpPost("marketiteminfo")]
public string MarketItemInfo([FromQuery] long userid = -1, [FromQuery] long itemid = 0)
{
try
{
PluginConfig pc = FunGameService.GetUserConfig(userid, out _);
if (pc.Count > 0)
{
User user = FunGameService.GetUser(pc);
FunGameService.GetMarketSemaphoreSlim();
EntityModuleConfig<Market> emc = new("markets", "general");
emc.LoadConfig();
Market market = emc.Get("dokyo") ?? new("铎京集市");
string msg = "";
if (market.MarketItems.TryGetValue(itemid, out MarketItem? item) && item != null)
{
msg = FunGameService.GetMarketItemInfo(item, false, userid);
}
if (msg != "")
{
msg += $"\r\n提示使用【市场查看+序号】查看商品详细信息,使用【市场购买+序号】购买商品。\r\n你的现有{General.GameplayEquilibriumConstant.InGameCurrency}{user.Inventory.Credits:0.##}";
}
FunGameService.ReleaseMarketSemaphoreSlim();
FunGameService.SetUserConfigAndReleaseSemaphoreSlim(userid, pc, user);
return msg;
}
else
{
FunGameService.ReleaseUserSemaphoreSlim(userid);
return noSaved;
}
}
catch (Exception e)
{
FunGameService.ReleaseMarketSemaphoreSlim();
FunGameService.ReleaseUserSemaphoreSlim(userid);
Logger.LogError(e, "Error: {e}", e);
return busy;
}
}
[HttpPost("marketbuyitem")]
public string MarketBuyItem([FromQuery] long userid = -1, [FromQuery] long itemid = 0, [FromQuery] int count = 1)
{
if (count <= 0) count = 1;
try
{
PluginConfig pc = FunGameService.GetUserConfig(userid, out _);
if (pc.Count > 0)
{
User user = FunGameService.GetUser(pc);
FunGameService.GetMarketSemaphoreSlim();
EntityModuleConfig<Market> emc = new("markets", "general");
emc.LoadConfig();
Market market = emc.Get("dokyo") ?? new("铎京集市");
string msg = "";
if (market.MarketItems.TryGetValue(itemid, out MarketItem? item) && item != null)
{
msg = FunGameService.MarketBuyItem(market, item, pc, user, count, out bool result);
if (result)
{
long userid2 = item.User;
try
{
PluginConfig pc2 = FunGameService.GetUserConfig(userid2, out _);
if (pc2.Count > 0)
{
User user2 = FunGameService.GetUser(pc2);
double amount = item.Price * count;
double fee = amount * 0.15;
double net = amount - fee;
user2.Inventory.Credits += net;
FunGameService.AddNotice(userid2, $"【市场通知】你售出了 {count} 件{item.Name}!净收入 {net:0.##} {General.GameplayEquilibriumConstant.InGameCurrency};市场收取手续费 {fee:0.##} {General.GameplayEquilibriumConstant.InGameCurrency}。");
FunGameService.SetUserConfigButNotRelease(userid2, pc2, user2, false);
}
FunGameService.ReleaseUserSemaphoreSlim(userid2);
}
catch (Exception e)
{
FunGameService.ReleaseUserSemaphoreSlim(userid2);
Logger.LogError(e, "Error: {e}", e);
throw;
}
}
}
emc.Add("dokyo", market);
emc.SaveConfig();
FunGameService.ReleaseMarketSemaphoreSlim();
FunGameService.SetUserConfigAndReleaseSemaphoreSlim(userid, pc, user);
return msg;
}
else
{
FunGameService.ReleaseUserSemaphoreSlim(userid);
return noSaved;
}
}
catch (Exception e)
{
FunGameService.ReleaseMarketSemaphoreSlim();
FunGameService.ReleaseUserSemaphoreSlim(userid);
Logger.LogError(e, "Error: {e}", e);
return busy;
}
}
[HttpPost("marketdelistitem")]
public string MarketDelistItem([FromQuery] long userid = -1, [FromQuery] long itemid = 0)
{
try
{
PluginConfig pc = FunGameService.GetUserConfig(userid, out _);
if (pc.Count > 0)
{
User user = FunGameService.GetUser(pc);
FunGameService.GetMarketSemaphoreSlim();
EntityModuleConfig<Market> emc = new("markets", "general");
emc.LoadConfig();
Market market = emc.Get("dokyo") ?? new("铎京集市");
string msg = "";
if (market.MarketItems.TryGetValue(itemid, out MarketItem? item) && item != null && item.User == user.Id)
{
item.Status = MarketItemState.Delisted;
item.FinishTime = DateTime.Now;
for (int i = 0; i < item.Stock; i++)
{
FunGameService.AddItemToUserInventory(user, item.Item, copyLevel: true, useOriginalPrice: true, toExploreCache: false, toActivitiesCache: false);
}
msg = $"下架商品 {item.Name} 成功!{item.Stock} 个 {item.Name} 已回到你的库存。";
}
else
{
msg = $"没有找到指定的商品,或者你不是该商品的卖家,下架失败。";
}
emc.Add("dokyo", market);
emc.SaveConfig();
FunGameService.ReleaseMarketSemaphoreSlim();
FunGameService.SetUserConfigAndReleaseSemaphoreSlim(userid, pc, user);
return msg;
}
else
{
FunGameService.ReleaseUserSemaphoreSlim(userid);
return noSaved;
}
}
catch (Exception e)
{
FunGameService.ReleaseMarketSemaphoreSlim();
FunGameService.ReleaseUserSemaphoreSlim(userid); FunGameService.ReleaseUserSemaphoreSlim(userid);
Logger.LogError(e, "Error: {e}", e); Logger.LogError(e, "Error: {e}", e);
return busy; return busy;
@ -7563,6 +7832,130 @@ namespace Oshima.FunGame.WebAPI.Controllers
} }
} }
[HttpPost("chat")]
public string Chat([FromQuery] long uid = -1, [FromQuery] long uid2 = -1, [FromQuery] string msgTo = "")
{
try
{
PluginConfig pc = FunGameService.GetUserConfig(uid, out bool isTimeout);
if (isTimeout)
{
return busy;
}
if (msgTo == "")
{
return "发送了空信息。";
}
if (msgTo.Length > 30)
{
return "超过 30 字符。";
}
string msg = "";
if (pc.Count > 0)
{
User user = FunGameService.GetUser(pc);
if (uid == uid2)
{
msg = "不能对自己发私信。";
}
else if (FunGameConstant.UserIdAndUsername.TryGetValue(uid2, out User? user2) && user2 != null)
{
FunGameService.AddNotice(user2.Id, $"【私信】{DateTime.Now.ToString(General.GeneralDateTimeFormatChinese)} [ {user.Username} ] 说:{msgTo}");
msg = $"私信已经成功发送至 [ {user2.Username} ] 的离线信箱。";
}
else
{
msg = $"没有找到 UID 为 {uid2} 的玩家,无法发送私信。";
}
FunGameService.SetUserConfigButNotRelease(uid, pc, user);
return msg;
}
else
{
return noSaved;
}
}
catch (Exception e)
{
Logger.LogError(e, "Error: {e}", e);
return busy;
}
finally
{
FunGameService.ReleaseUserSemaphoreSlim(uid);
}
}
[HttpPost("chatname")]
public string Chat_Name([FromQuery] long uid = -1, [FromQuery] string name = "", [FromQuery] string msgTo = "")
{
try
{
PluginConfig pc = FunGameService.GetUserConfig(uid, out bool isTimeout);
if (isTimeout)
{
return busy;
}
name = name.Trim();
if (name == "")
{
return "未输入目标玩家的昵称。";
}
if (msgTo == "")
{
return "发送了空信息。";
}
if (msgTo.Length > 30)
{
return "超过 30 字符。";
}
string msg = "";
if (pc.Count > 0)
{
User user = FunGameService.GetUser(pc);
if (user.Username == name)
{
msg = "不能对自己发私信。";
}
else if (FunGameConstant.UserIdAndUsername.Values.FirstOrDefault(u => u.Username == name) is User user2 && user2 != null)
{
FunGameService.AddNotice(user2.Id, $"【私信】{DateTime.Now.ToString(General.GeneralDateTimeFormatChinese)} [ {user.Username} ] 说:{msgTo}");
msg = $"私信已经成功发送至 [ {user2.Username} ] 的离线信箱。";
}
else
{
msg = $"没有找到昵称为 {name} 的玩家,无法发送私信。";
}
FunGameService.SetUserConfigButNotRelease(uid, pc, user);
return msg;
}
else
{
return noSaved;
}
}
catch (Exception e)
{
Logger.LogError(e, "Error: {e}", e);
return busy;
}
finally
{
FunGameService.ReleaseUserSemaphoreSlim(uid);
}
}
[HttpPost("template")] [HttpPost("template")]
public string Template([FromQuery] long uid = -1) public string Template([FromQuery] long uid = -1)
{ {

View File

@ -3064,6 +3064,162 @@ namespace Oshima.FunGame.WebAPI.Services
return result; return result;
} }
if (e.Detail.StartsWith("市场出售"))
{
string detail = e.Detail.Replace("市场出售", "").Trim();
string[] strings = detail.Split(' ', StringSplitOptions.TrimEntries | StringSplitOptions.RemoveEmptyEntries);
if (strings.Length < 2 || !double.TryParse(strings[^1], out double price))
{
await SendAsync(e, "市场出售", "格式不正确,请使用:市场出售 <{物品序号...}> <价格>。多个物品序号使用空格隔开。");
return result;
}
List<int> ids = [];
for (int i = 0; i < strings.Length; i++)
{
if (i != strings.Length - 1 && int.TryParse(strings[i], out int id))
{
ids.Add(id);
}
}
string msg = Controller.MarketSellItem(uid, price, [.. ids]);
if (msg != "")
{
await SendAsync(e, "市场出售", msg);
}
return result;
}
if (e.Detail.StartsWith("市场购买"))
{
string detail = e.Detail.Replace("市场购买", "").Trim();
string[] strings = detail.Split(" ", StringSplitOptions.TrimEntries | StringSplitOptions.RemoveEmptyEntries);
int id = -1;
if (strings.Length > 0 && int.TryParse(strings[0].Trim(), out id))
{
int count = 1;
if (strings.Length > 1) _ = int.TryParse(strings[1].Trim(), out count);
if (id != -1)
{
string msg = Controller.MarketBuyItem(uid, id, count);
if (msg != "")
{
await SendAsync(e, "市场购买", msg);
}
}
}
return result;
}
if (e.Detail.StartsWith("市场查看"))
{
string detail = e.Detail.Replace("市场查看", "").Trim();
string msg = "";
if (int.TryParse(detail, out int id))
{
msg = Controller.MarketItemInfo(uid, id);
if (msg.Trim() != "")
{
await SendAsync(e, "市场查看", msg);
}
}
return result;
}
if (e.Detail.StartsWith("市场下架"))
{
string detail = e.Detail.Replace("市场下架", "").Trim();
string msg = "";
if (int.TryParse(detail, out int id))
{
msg = Controller.MarketDelistItem(uid, id);
if (msg.Trim() != "")
{
await SendAsync(e, "市场下架", msg);
}
}
return result;
}
if (e.Detail.StartsWith("我的市场"))
{
string detail = e.Detail.Replace("我的市场", "").Trim();
string msg = "";
if (int.TryParse(detail, out int page))
{
msg = Controller.MarketShowListMySells(uid, page);
}
else
{
msg = Controller.MarketShowListMySells(uid, 1);
}
if (msg.Trim() != "")
{
await SendAsync(e, "我的市场", msg);
}
return result;
}
if (e.Detail.StartsWith("市场"))
{
string detail = e.Detail.Replace("市场", "").Trim();
string msg = "";
if (int.TryParse(detail, out int page))
{
msg = Controller.MarketShowList(uid, page);
}
else
{
msg = Controller.MarketShowList(uid, 1);
}
if (msg.Trim() != "")
{
await SendAsync(e, "市场", msg);
}
return result;
}
if (e.Detail.StartsWith("私信"))
{
string detail = e.Detail.Replace("私信", "").Trim();
string[] strings = detail.Split(" ", StringSplitOptions.TrimEntries | StringSplitOptions.RemoveEmptyEntries);
long id = -1;
string name = "";
bool useId = false;
if (strings.Length > 0)
{
if (long.TryParse(strings[0].Trim(), out id))
{
useId = true;
}
else
{
name = strings[0].Trim();
}
}
else
{
await SendAsync(e, "私信", "你输入的格式不正确,正确格式:私信 <对方UID/昵称> <内容>");
return result;
}
string content = "";
if (strings.Length > 1) content = string.Join(" ", strings[1..]);
string msg = "";
if (useId)
{
msg = Controller.Chat(uid, id, content);
}
else
{
msg = Controller.Chat_Name(uid, name, content);
}
if (msg != "")
{
await SendAsync(e, "私信", msg);
}
return result;
}
if (uid == GeneralSettings.Master && e.Detail.StartsWith("重载FunGame", StringComparison.CurrentCultureIgnoreCase)) if (uid == GeneralSettings.Master && e.Detail.StartsWith("重载FunGame", StringComparison.CurrentCultureIgnoreCase))
{ {
string msg = Controller.Relaod(uid); string msg = Controller.Relaod(uid);