添加多人游戏房间

This commit is contained in:
milimoe 2025-07-30 23:24:37 +08:00
parent c994bd2054
commit a23802bdb0
Signed by: milimoe
GPG Key ID: 9554D37E4B8991D0
15 changed files with 1465 additions and 25 deletions

View File

@ -47,6 +47,7 @@
= 18012, = 18012,
= 18013, = 18013,
= 18014, = 18014,
= 18998,
= 18999 = 18999
} }

View File

@ -0,0 +1,13 @@
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 => "钻石是筽祀牻大陆通用的稀有矿物,是第二流通货币。钻石可以以稳定的 1:200 兑换率兑换大陆第一流通货币金币。";
public override QualityType QualityType => QualityType.White;
}
}

View File

@ -36,6 +36,8 @@ namespace Oshima.FunGame.OshimaModules.Models
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 SemaphoreSlim MarketSemaphoreSlim { get; } = new(1, 1);
public static Dictionary<string, Room> Rooms { get; set; } = [];
public static Dictionary<long, Room> UsersInRoom { get; set; } = [];
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];
public static char[] SplitChars => [',', ' ', '', '', ';']; public static char[] SplitChars => [',', ' ', '', '', ';'];

View File

@ -56,6 +56,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

@ -36,6 +36,10 @@ namespace Oshima.FunGame.OshimaModules.Regions
{ {
template = CreateNewForgeStore(); template = CreateNewForgeStore();
} }
else if (storeName == "dokyo_horseracing")
{
template = CreateNewHorseRacingStore();
}
else return null; else return null;
} }
@ -109,6 +113,15 @@ namespace Oshima.FunGame.OshimaModules.Regions
} }
return $"现有锻造积分:{forgePoints:0.##}"; return $"现有锻造积分:{forgePoints:0.##}";
} }
else if (storeName == "dokyo_horseracing")
{
double horseRacingPoints = 0;
if (pc.TryGetValue("horseRacingPoints", out object? value) && double.TryParse(value.ToString(), out double points))
{
horseRacingPoints = points;
}
return $"现有赛马积分:{horseRacingPoints:0.##}";
}
return ""; return "";
} }
@ -124,6 +137,7 @@ namespace Oshima.FunGame.OshimaModules.Regions
{ {
EntityModuleConfig<Store> storeTemplate = new("stores", "dokyo"); EntityModuleConfig<Store> storeTemplate = new("stores", "dokyo");
storeTemplate.LoadConfig(); storeTemplate.LoadConfig();
Store? store = storeTemplate.Get("dokyo_forge"); Store? store = storeTemplate.Get("dokyo_forge");
if (store is null) if (store is null)
{ {
@ -136,6 +150,20 @@ namespace Oshima.FunGame.OshimaModules.Regions
store.CopyGoodsToNextRefreshGoods(newStore.Goods); store.CopyGoodsToNextRefreshGoods(newStore.Goods);
} }
storeTemplate.Add("dokyo_forge", store); storeTemplate.Add("dokyo_forge", store);
store = storeTemplate.Get("dokyo_horseracing");
if (store is null)
{
store = CreateNewHorseRacingStore();
}
else
{
Store newStore = CreateNewHorseRacingStore();
store.NextRefreshGoods.Clear();
store.CopyGoodsToNextRefreshGoods(newStore.Goods);
}
storeTemplate.Add("dokyo_horseracing", store);
storeTemplate.SaveConfig(); storeTemplate.SaveConfig();
} }
@ -143,6 +171,7 @@ namespace Oshima.FunGame.OshimaModules.Regions
{ {
Store store = new("锻造积分商店") Store store = new("锻造积分商店")
{ {
GetNewerGoodsOnVisiting = true,
AutoRefresh = true, AutoRefresh = true,
RefreshInterval = 3, RefreshInterval = 3,
NextRefreshDate = DateTime.Today.AddHours(4), NextRefreshDate = DateTime.Today.AddHours(4),
@ -161,5 +190,22 @@ namespace Oshima.FunGame.OshimaModules.Regions
} }
return store; return store;
} }
private static Store CreateNewHorseRacingStore()
{
Store store = new("赛马积分商店")
{
GetNewerGoodsOnVisiting = true,
AutoRefresh = true,
RefreshInterval = 1,
NextRefreshDate = DateTime.Today.AddHours(4),
GlobalStock = true,
};
Item item = new ();
store.AddItem(item, -1);
store.SetPrice(1, "赛马积分", 5);
store.Goods[1].Quota = 300;
return store;
}
} }
} }

View File

@ -136,6 +136,8 @@ namespace Oshima.FunGame.OshimaServers
{ {
FunGameService.RefreshSavedCache(); FunGameService.RefreshSavedCache();
Controller.WriteLine("读取 FunGame 存档缓存", LogLevel.Debug); Controller.WriteLine("读取 FunGame 存档缓存", LogLevel.Debug);
OnlineService.RoomsAutoDisband();
Controller.WriteLine("清除空闲房间", LogLevel.Debug);
}, true); }, true);
TaskScheduler.Shared.AddTask("刷新每日任务", new TimeSpan(4, 0, 0), () => TaskScheduler.Shared.AddTask("刷新每日任务", new TimeSpan(4, 0, 0), () =>
{ {

View File

@ -0,0 +1,132 @@
using System.Text;
using Milimoe.FunGame.Core.Entity;
namespace Oshima.FunGame.OshimaServers.Model
{
public class Horse(User user)
{
public long Id => user.Id;
public string Name => user.Username;
// 基础属性,技能会在此基础上进行增减
private int _step = 1;
private int _hr = 1;
private int _hp = 3;
public int MaxHP { get; set; } = 3;
/// <summary>
/// 每回合行动的步数
/// </summary>
public int Step
{
get
{
return _step;
}
set
{
_step = Math.Max(0, value);
}
}
/// <summary>
/// 当前生命值
/// </summary>
public int HP
{
get
{
return _hp;
}
set
{
_hp = Math.Min(MaxHP, Math.Max(0, value));
}
}
/// <summary>
/// 每回合恢复的HP值
/// </summary>
public int HPRecovery
{
get
{
return _hr;
}
set
{
_hr = Math.Max(0, value);
}
}
/// <summary>
/// 马匹当前在赛道上的位置
/// </summary>
public int CurrentPosition { get; set; } = 0;
/// <summary>
/// 马匹拥有的永久技能
/// </summary>
public HashSet<HorseSkill> Skills { get; set; } = [];
/// <summary>
/// 马匹当前正在生效的技能效果列表
/// </summary>
public List<ActiveSkillEffect> ActiveEffects { get; set; } = [];
public override string ToString()
{
return Name;
}
}
/// <summary>
/// 技能定义
/// </summary>
public class HorseSkill(Horse? horse = null)
{
public Horse? Horse { get; set; } = horse;
public string Name { get; set; } = "";
public bool ToEnemy { get; set; } = false;
public int AddStep { get; set; } = 0;
public int ReduceStep { get; set; } = 0;
public int AddHP { get; set; } = 0;
public int ReduceHP { get; set; } = 0;
public int AddHR { get; set; } = 0;
public int ReduceHR { get; set; } = 0;
public int ChangePosition { get; set; } = 0;
/// <summary>
/// 技能发动概率1表示每回合都发动
/// </summary>
public double CastProbability { get; set; } = 1;
/// <summary>
/// 技能持续回合数默认1回合即立即生效并结束
/// </summary>
public int Duration { get; set; } = 1;
public override string ToString()
{
StringBuilder builder = new();
if (AddStep > 0) builder.Append($"每回合将额外移动 {AddStep} 步!");
if (ReduceStep > 0) builder.Append($"每回合将少移动 {ReduceStep} 步!");
if (AddHP > 0) builder.AppendLine($"恢复了 {AddHP} 点生命值!");
if (ReduceHP > 0) builder.Append($"受到了 {ReduceHP} 点伤害!");
if (AddHR > 0) builder.Append($"每回合将额外恢复 {AddHR} 点生命值!");
if (ReduceHR > 0) builder.Append($"每回合将少恢复 {ReduceHR} 点生命值!");
if (ChangePosition != 0) builder.Append($"{(ChangePosition > 0 ? "" : "退")}了 {Math.Abs(ChangePosition)} 步!");
return builder.ToString().Trim();
}
}
/// <summary>
/// 用于追踪马匹身上正在生效的技能效果
/// </summary>
public class ActiveSkillEffect(HorseSkill skill)
{
public HorseSkill Skill { get; } = skill;
public int RemainDuration { get; set; } = skill.Duration;
}
}

View File

@ -0,0 +1,306 @@
using System.Text;
using Milimoe.FunGame.Core.Entity;
using Oshima.FunGame.OshimaModules.Models;
namespace Oshima.FunGame.OshimaServers.Model
{
public static class HorseRacing
{
private const int MaxTurns = 100;
private static readonly Random _random = new();
public static Dictionary<long, int> RunHorseRacing(List<string> msgs, Room room)
{
Dictionary<long, int> userPoints = [];
StringBuilder builder = new();
builder.AppendLine("--- 参赛选手 ---");
List<Horse> horses = [];
foreach (User user in room.UserAndIsReady.Keys)
{
if (FunGameConstant.UserIdAndUsername.TryGetValue(user.Id, out User? temp) && temp != null)
{
user.Username = temp.Username;
}
Horse horse = new(user);
AssignRandomSkills(horse);
horses.Add(horse);
builder.AppendLine($"[ {horse}({horse.HP}) ] 已准备就绪!初始步数: {horse.Step}, 生命值: {horse.HP}, 每回合恢复生命值: {horse.HPRecovery}");
if (horse.Skills.Count != 0)
{
builder.AppendLine($"[ {horse}({horse.HP}) ] 拥有技能: {string.Join("", horse.Skills.Select(s => $"{s.Name} {s.Duration} "))}");
}
}
builder.AppendLine("\r\n--- 比赛开始! ---");
int maxLength = _random.Next(8, 16);
builder.AppendLine($"本次抽取赛道长度:{maxLength} 步!");
for (int turn = 1; turn <= MaxTurns; turn++)
{
builder.AppendLine($"\r\n--- 第 {turn} 回合 ---");
bool raceFinished = false;
Dictionary<Horse, int> turnSteps = [];
Dictionary<Horse, Dictionary<HorseSkill, Horse>> turnSkills = [];
// 先触发技能,后面一起结算,不然技能下个回合才能触发
foreach (Horse horse in horses)
{
turnSkills.TryAdd(horse, []);
// 触发永久技能
foreach (HorseSkill skill in horse.Skills)
{
if (_random.NextDouble() < skill.CastProbability)
{
Horse target = horse;
if (skill.ToEnemy)
{
target = horses.OrderBy(o => _random.Next(horses.Count)).First(h => h != horse);
}
turnSkills[horse].Add(skill, target);
target.ActiveEffects.Add(new ActiveSkillEffect(skill));
}
}
}
foreach (Horse horse in horses)
{
turnSteps[horse] = 0;
int effectiveStep = horse.Step; // 从基础步数开始计算
int effectiveHPRecovery = horse.HPRecovery; // 从基础HP恢复开始计算
List<string> turnEvents = []; // 记录本回合发生的事件
// 触发永久技能
Dictionary<HorseSkill, Horse> skills = turnSkills[horse];
foreach (HorseSkill skill in skills.Keys)
{
Horse target = skills[skill];
// 如果是敌人
if (skill.Duration > 0)
{
turnEvents.Add($"✨ 发动了 [ {skill.Name} ],持续 {skill.Duration} 回合!{(skill.ToEnemy ? $"[ {target} ] " : "")}{skill}");
}
else
{
turnEvents.Add($"✨ 发动了 [ {skill.Name} ]{(skill.ToEnemy ? $"[ {target} ] " : "")}{skill}");
}
}
// 处理正在生效的持续技能效果
List<ActiveSkillEffect> expiredEffects = [];
foreach (ActiveSkillEffect activeEffect in horse.ActiveEffects)
{
HorseSkill skill = activeEffect.Skill;
// 应用持续效果
effectiveStep += skill.AddStep - skill.ReduceStep;
effectiveHPRecovery += skill.AddHR - skill.ReduceHR;
horse.HP += skill.AddHP - skill.ReduceHP;
horse.CurrentPosition += skill.ChangePosition;
turnSteps[horse] += skill.ChangePosition;
Horse? source = skill.Horse;
if (source != null && source != horse) turnEvents.Add($"💥 受到了 [ {skill.Name}(来自:{source}] 的影响,{skill}");
activeEffect.RemainDuration--;
if (activeEffect.RemainDuration <= 0)
{
expiredEffects.Add(activeEffect);
}
}
// 移除已结束的持续效果
foreach (ActiveSkillEffect expiredEffect in expiredEffects)
{
horse.ActiveEffects.Remove(expiredEffect);
}
// 随机事件
if (_random.NextDouble() < 0.5)
{
HorseSkill eventSkill = GenerateRandomEventSkill();
// 随机事件技能也可能持续多回合
if (eventSkill.Duration > 0)
{
horse.ActiveEffects.Add(new ActiveSkillEffect(eventSkill));
turnEvents.Add($"💥 遭遇随机事件 [ {eventSkill.Name} ],持续 {eventSkill.Duration} 回合!{eventSkill}");
}
else
{
// 如果没有持续时间Duration=0则立即应用效果
effectiveStep += eventSkill.AddStep - eventSkill.ReduceStep;
effectiveHPRecovery += eventSkill.AddHR - eventSkill.ReduceHR;
horse.HP += eventSkill.AddHP - eventSkill.ReduceHP;
turnEvents.Add($"💥 遭遇随机事件 [ {eventSkill.Name} ]{eventSkill}");
}
}
// 恢复HP基于计算后的有效HP恢复值
int hp = horse.HP;
horse.HP += effectiveHPRecovery;
if (hp != horse.HP)
{
builder.AppendLine($"[ {horse}({horse.HP}) ] ❤️ 生命值恢复至 {horse.HP} 点(+{effectiveHPRecovery})。");
}
if (horse.HP <= 0)
{
continue;
}
// 确保步数不为负
effectiveStep = Math.Max(0, effectiveStep);
horse.CurrentPosition += effectiveStep; // 移动
turnSteps[horse] += effectiveStep;
//if (effectiveStep > 1) builder.AppendLine($"[ {horse}({horse.HP}) ] 移动了 {effectiveStep} 步!");
if (turnEvents.Count != 0)
{
builder.AppendLine($"[ {horse}({horse.HP}) ] {string.Join("", turnEvents)}");
}
if (horse.CurrentPosition >= maxLength)
{
builder.AppendLine($"\r\n🎯 恭喜 [ {horse}({horse.HP}) ] 冲过终点线!它赢得了比赛!");
raceFinished = true;
break;
}
}
builder.AppendLine("\r\n--- 赛道状况 ---");
for (int i = 0; i < horses.Count; i++)
{
builder.AppendLine(GenerateTrackString(horses[i], i + 1, maxLength, turnSteps));
}
msgs.Add(builder.ToString().Trim());
builder.Clear();
if (raceFinished)
{
break;
}
}
builder.Clear();
builder.AppendLine("\r\n--- 比赛结果 ---");
List<Horse> finalRanking = [.. horses.OrderByDescending(h => h.CurrentPosition)];
int points = 10;
for (int i = 0; i < finalRanking.Count; i++)
{
userPoints[finalRanking[i].Id] = points;
builder.AppendLine($"第 {i + 1} 名:{finalRanking[i].Name}(获得 {points} 点赛马积分)");
points = (int)(points * 0.8);
if (points == 0) points = 1;
}
builder.AppendLine("\r\n--- 比赛结束,奖励将在稍后发放! ---");
msgs.Add(builder.ToString().Trim());
return userPoints;
}
private static void AssignRandomSkills(Horse horse)
{
// 技能池
List<HorseSkill> skillPool = [
new() { Name = "冲刺", AddStep = 2, CastProbability = 0.3, Duration = 1 }, // 普通冲刺1回合
new() { Name = "耐力爆发", AddHP = 2, CastProbability = 0.3, Duration = 1 }, // 瞬间回血
new() { Name = "神行百变", AddStep = 1, AddHR = 1, CastProbability = 0.3, Duration = 3 }, // 持续3回合的加速和恢复
new() { Name = "稳健", AddStep = 0, AddHR = 1, CastProbability = 0.3, Duration = 2 }, // 持续2回合的额外恢复
new() { Name = "疾风步", AddStep = 1, CastProbability = 0.15, Duration = 2 }, // 强大的2回合加速
new() { Name = "疲惫", ToEnemy = true, ReduceStep = 1, CastProbability = 0.4, Duration = 2 }, // 持续2回合的减速
new() { Name = "摔跤", ToEnemy = true, ReduceHP = 2, ReduceStep = 1, CastProbability = 0.4, Duration = 1 }, // 瞬间掉血减速
new() { Name = "干扰", ToEnemy = true, ReduceStep = 2, CastProbability = 0.3, Duration = 1 }, // 瞬间减速
new() { Name = "石头攻击", ToEnemy = true, ReduceHP = 3, CastProbability = 0.4, Duration = 1 }, // 瞬间掉血
new() { Name = "台风", ToEnemy = true, ReduceHP = 2, CastProbability = 0.4, Duration = 2 }, // 强大的2回合掉血
new() { Name = "死刑宣告", ToEnemy = true, ReduceHP = 2, ReduceHR = 1, CastProbability = 0.3, Duration = 2 }, // 瞬间掉血+重伤
new() { Name = "后退吧!", ToEnemy = true, ChangePosition = -2, CastProbability = 0.3, Duration = 1 }, // 直接改变位置
];
int skillsToAssign = _random.Next(1, 4);
for (int i = 0; i < skillsToAssign; i++)
{
HorseSkill chosenSkill = skillPool[_random.Next(skillPool.Count)];
while (true)
{
if (!horse.Skills.Any(s => s.Name == chosenSkill.Name))
{
break;
}
// 如果技能已存在,重新选择
chosenSkill = skillPool[_random.Next(skillPool.Count)];
}
horse.Skills.Add(new HorseSkill
{
Horse = horse,
Name = chosenSkill.Name,
ToEnemy = chosenSkill.ToEnemy,
AddStep = chosenSkill.AddStep,
ReduceStep = chosenSkill.ReduceStep,
AddHP = chosenSkill.AddHP,
ReduceHP = chosenSkill.ReduceHP,
AddHR = chosenSkill.AddHR,
ReduceHR = chosenSkill.ReduceHR,
ChangePosition = chosenSkill.ChangePosition,
CastProbability = chosenSkill.CastProbability,
Duration = chosenSkill.Duration
});
}
}
private static HorseSkill GenerateRandomEventSkill()
{
// 随机事件
List<HorseSkill> eventPool = [
new() { Name = "加速带", AddStep = 3, Duration = 1 }, // 瞬间加速
new() { Name = "泥泞区", ReduceStep = 2, Duration = 2 }, // 持续2回合减速
new() { Name = "观众欢呼", AddHP = 2, Duration = 1 }, // 瞬间回血
new() { Name = "小石子", ReduceHP = 1, Duration = 1 }, // 瞬间掉血
new() { Name = "顺风", AddStep = 1, Duration = 3 }, // 持续3回合微加速
new() { Name = "逆风", ReduceStep = 1, Duration = 2 }, // 持续2回合微减速
new() { Name = "兴奋剂", AddStep = 2, Duration = 4, ReduceHP = 2 } // 兴奋剂,有副作用
];
return eventPool[_random.Next(eventPool.Count)];
}
private static string GenerateTrackString(Horse horse, int trackNumber, int maxLength, Dictionary<Horse, int> turnSteps)
{
StringBuilder builder = new();
builder.Append($"[{trackNumber}]|");
if (horse.CurrentPosition < 0) horse.CurrentPosition = 0;
int dashesBeforeHorse = Math.Min(horse.CurrentPosition, maxLength);
int dashesAfterHorse = Math.Max(0, maxLength - horse.CurrentPosition);
builder.Append(new string('=', dashesAfterHorse));
string horseMarker = $"<{horse}>";
if (horse.ActiveEffects.Count > 0 || horse.HP == 0)
{
if (horse.HP == 0)
{
horseMarker = $"[💀死亡]<{horse}>";
}
if (horse.ActiveEffects.Count > 0)
{
horseMarker += $"[{string.Join("][", horse.ActiveEffects.Select(e => e.Skill.Name))}]";
}
}
builder.Append(horseMarker);
builder.Append(new string('=', dashesBeforeHorse));
int turnStep = 1;
if (turnSteps.TryGetValue(horse, out int step))
{
turnStep = step;
}
builder.Append($"|({horse.CurrentPosition})({(turnStep >= 0 ? "+" : "")}{turnStep})");
return builder.ToString();
}
}
}

View File

@ -98,6 +98,15 @@
{"取消锻造", "取消已经创建的锻造配方" }, {"取消锻造", "取消已经创建的锻造配方" },
{"模拟锻造", "模拟锻造结果" }, {"模拟锻造", "模拟锻造结果" },
{"确认开始锻造", "确认已经创建的锻造配方并开始锻造" }, {"确认开始锻造", "确认已经创建的锻造配方并开始锻造" },
{"创建房间 <类型>", "类型mix/team/cooperative/horseracing对应混战/团队/共斗/赛马" },
{"加入房间 <房间号>", "加入多人游戏房间" },
{"退出房间", "退出多人游戏房间" },
{"开始游戏", "开始多人游戏" },
{"创建混战", "快速创建混战房间" },
{"创建团战", "快速创建团队死斗房间" },
{"创建共斗", "快速创建共斗房间" },
{"创建赛马", "快速创建赛马房间" },
{"加入赛马", "快速加入赛马房间" },
}; };
public static Dictionary<string, string> ClubHelp { get; } = new() { public static Dictionary<string, string> ClubHelp { get; } = new() {
@ -146,7 +155,8 @@
{"商店2", "查看武器商会商品"}, {"商店2", "查看武器商会商品"},
{"商店3", "查看杂货铺商品"}, {"商店3", "查看杂货铺商品"},
{"商店4", "查看慈善基金会商品"}, {"商店4", "查看慈善基金会商品"},
{"锻造商店", "查看锻造积分商店商品"}, {"锻造商店/商店5", "查看锻造积分商店商品"},
{"赛马商店/商店6", "查看锻造积分商店商品"},
{"商店查看 <商品序号>", "查看指定商品详情访问任意商店后2分钟内可用"}, {"商店查看 <商品序号>", "查看指定商品详情访问任意商店后2分钟内可用"},
{"商店购买 <商品序号>", "购买指定商品访问任意商店后2分钟内可用"}, {"商店购买 <商品序号>", "购买指定商品访问任意商店后2分钟内可用"},
{"商店出售 <物品序号>", "向商店出售具有回收价的指定物品"}, {"商店出售 <物品序号>", "向商店出售具有回收价的指定物品"},

View File

@ -624,6 +624,12 @@ namespace Oshima.FunGame.OshimaServers.Service
} }
} }
// 检查在线状态
if (FunGameConstant.UsersInRoom.ContainsKey(user.Id))
{
user.OnlineState = OnlineState.InRoom;
}
return user; return user;
} }
@ -2161,6 +2167,19 @@ namespace Oshima.FunGame.OshimaServers.Service
return $"你的{needy}不足 {reduce} 呢,无法购买【{goods.Name}】!"; return $"你的{needy}不足 {reduce} 呢,无法购买【{goods.Name}】!";
} }
} }
else if (needy == "赛马积分")
{
double reduce = Calculation.Round2Digits(goods.Prices[needy] * count);
if (pc.TryGetValue("horseRacingPoints", out object? value) && double.TryParse(value.ToString(), out double points) && points >= reduce)
{
points -= reduce;
pc.Add("horseRacingPoints", points);
}
else
{
return $"你的{needy}不足 {reduce} 呢,无法购买【{goods.Name}】!";
}
}
else else
{ {
return $"不支持的货币类型:{needy},无法购买【{goods.Name}】!"; return $"不支持的货币类型:{needy},无法购买【{goods.Name}】!";
@ -2169,7 +2188,11 @@ namespace Oshima.FunGame.OshimaServers.Service
foreach (Item item in goods.Items) foreach (Item item in goods.Items)
{ {
if (item.Id == (long)SpecialItemID.) if (item.Id == (long)SpecialItemID.)
{
user.Inventory.Materials += count;
}
else 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))
@ -4456,7 +4479,45 @@ namespace Oshima.FunGame.OshimaServers.Service
public static void RefreshSavedCache() public static void RefreshSavedCache()
{ {
string directoryPath = $@"{AppDomain.CurrentDomain.BaseDirectory}configs/saved"; string directoryPath;
Dictionary<long, int> hrPoints = [];
try
{
OnlineService.GetHorseRacingSettleSemaphoreSlim();
directoryPath = $@"{AppDomain.CurrentDomain.BaseDirectory}configs/horseracing";
if (Directory.Exists(directoryPath))
{
string[] filePaths = Directory.GetFiles(directoryPath);
foreach (string filePath in filePaths)
{
string fileName = Path.GetFileNameWithoutExtension(filePath);
PluginConfig pc = new("horseracing", fileName);
pc.LoadConfig();
if (pc.Get<Dictionary<long, int>>("points") is Dictionary<long, int> points)
{
foreach (long userId in points.Keys)
{
if (hrPoints.ContainsKey(userId))
{
hrPoints[userId] += points[userId];
}
else
{
hrPoints[userId] = points[userId];
}
}
}
pc.Remove("points");
pc.SaveConfig();
}
}
}
catch { }
finally
{
OnlineService.ReleaseHorseRacingSettleSemaphoreSlim();
}
directoryPath = $@"{AppDomain.CurrentDomain.BaseDirectory}configs/saved";
if (Directory.Exists(directoryPath)) if (Directory.Exists(directoryPath))
{ {
string[] filePaths = Directory.GetFiles(directoryPath); string[] filePaths = Directory.GetFiles(directoryPath);
@ -4471,6 +4532,7 @@ namespace Oshima.FunGame.OshimaServers.Service
FunGameConstant.UserIdAndUsername[user.Id] = user; FunGameConstant.UserIdAndUsername[user.Id] = user;
bool updateQuest = false; bool updateQuest = false;
bool updateExplore = false; bool updateExplore = false;
bool updateHorseRacing = false;
// 任务结算 // 任务结算
EntityModuleConfig<Quest> quests = new("quests", user.Id.ToString()); EntityModuleConfig<Quest> quests = new("quests", user.Id.ToString());
quests.LoadConfig(); quests.LoadConfig();
@ -4487,7 +4549,20 @@ namespace Oshima.FunGame.OshimaServers.Service
pc2.SaveConfig(); pc2.SaveConfig();
updateExplore = true; updateExplore = true;
} }
if (updateQuest || updateExplore) // 赛马结算
if (hrPoints.TryGetValue(user.Id, out int points))
{
if (pc.TryGetValue("horseRacingPoints", out object? value2) && int.TryParse(value2.ToString(), out int userPoints))
{
pc.Add("horseRacingPoints", userPoints + points);
}
else
{
pc.Add("horseRacingPoints", points);
}
updateHorseRacing = true;
}
if (updateQuest || updateExplore || updateHorseRacing)
{ {
SetUserConfigButNotRelease(user.Id, pc, user); SetUserConfigButNotRelease(user.Id, pc, user);
} }

View File

@ -0,0 +1,381 @@
using Milimoe.FunGame.Core.Api.Utility;
using Milimoe.FunGame.Core.Entity;
using Milimoe.FunGame.Core.Library.Constant;
using Oshima.FunGame.OshimaModules.Models;
using Oshima.FunGame.OshimaServers.Model;
namespace Oshima.FunGame.OshimaServers.Service
{
public class OnlineService
{
public static SemaphoreSlim RoomSemaphoreSlim { get; } = new(1, 1);
public static SemaphoreSlim HorseRacingSettleSemaphoreSlim { get; } = new(1, 1);
public static Dictionary<string, bool> GroupsHasHorseRacing { get; } = [];
public static void GetRoomSemaphoreSlim()
{
RoomSemaphoreSlim.Wait(FunGameConstant.SemaphoreSlimTimeout);
}
public static void ReleaseRoomSemaphoreSlim()
{
if (RoomSemaphoreSlim.CurrentCount == 0)
{
RoomSemaphoreSlim.Release();
}
}
public static void GetHorseRacingSettleSemaphoreSlim()
{
HorseRacingSettleSemaphoreSlim.Wait(FunGameConstant.SemaphoreSlimTimeout);
}
public static void ReleaseHorseRacingSettleSemaphoreSlim()
{
if (HorseRacingSettleSemaphoreSlim.CurrentCount == 0)
{
HorseRacingSettleSemaphoreSlim.Release();
}
}
public static Room CreateRoom(User user, string roomType, string password, string groupId, out string msg)
{
try
{
GetRoomSemaphoreSlim();
long id = FunGameConstant.Rooms.Count > 0 ? FunGameConstant.Rooms.Values.Max(r => r.Id) + 1 : 1;
Room room = Factory.GetRoom(id, password: password);
msg = "";
if (FunGameConstant.UsersInRoom.TryGetValue(user.Id, out Room? room2) && room2 != null)
{
msg = $"你已经在{room2.Name} [ {room2.Roomid} ] 中了,请先离开房间后再创建房间。";
}
else
{
switch (roomType)
{
case "horseracing":
if (GroupsHasHorseRacing.TryGetValue(groupId, out bool has) && has)
{
msg = "本群已经存在一个赛马房间!空闲房间会在 6 分钟后自动解散,请先等待该房间完成比赛或自动解散。";
}
else
{
room.RoomType = RoomType.Custom;
room.Name = "赛马房间";
room.GameMap = groupId;
room.MaxUsers = 8;
}
break;
case "mix":
room.RoomType = RoomType.Mix;
room.Name = "混战房间";
room.MaxUsers = 10;
break;
case "team":
room.RoomType = RoomType.Team;
room.Name = "团队死斗房间";
room.MaxUsers = 8;
break;
case "cooperative":
room.RoomType = RoomType.Custom;
room.Name = "共斗房间";
room.MaxUsers = 4;
break;
default:
msg = "不支持的房间类型。";
break;
}
}
if (msg == "")
{
room.Roomid = Verification.CreateVerifyCode(VerifyCodeType.MixVerifyCode, 7);
while (true)
{
if (!FunGameConstant.Rooms.ContainsKey(room.Roomid))
{
break;
}
room.Roomid = Verification.CreateVerifyCode(VerifyCodeType.MixVerifyCode, 7);
}
msg = $"房间创建成功,房间号为:{room.Roomid}\r\n注意房间若在 6 分钟后仍处于空闲状态,将自动解散。";
room.RoomMaster = user;
room.CreateTime = DateTime.Now;
}
if (room.Roomid != "-1")
{
FunGameConstant.Rooms[room.Roomid] = room;
if (room.Name == "赛马房间")
{
GroupsHasHorseRacing[room.GameMap] = true;
}
}
return room;
}
catch (Exception e)
{
msg = $"创建房间失败,错误信息:{e.Message}";
return Factory.GetRoom();
}
finally
{
ReleaseRoomSemaphoreSlim();
}
}
public static bool IntoRoom(User user, string roomid, string password, out string msg)
{
try
{
GetRoomSemaphoreSlim();
msg = "";
if (FunGameConstant.Rooms.TryGetValue(roomid, out Room? room) && room != null)
{
if (password == room.Password)
{
if (FunGameConstant.UsersInRoom.TryGetValue(user.Id, out Room? room2) && room2 != null)
{
msg = $"你已经在{room2.Name} [ {room2.Roomid} ] 中了,请先退出房间后再加入房间。";
return false;
}
if (room.UserAndIsReady.Count >= room.MaxUsers)
{
msg = "房间人数已满,无法加入。";
return false;
}
if (room.RoomState != RoomState.Created)
{
msg = "房间状态异常,无法加入。";
return false;
}
FunGameConstant.UsersInRoom[user.Id] = room;
room.UserAndIsReady[user] = true;
user.OnlineState = OnlineState.InRoom;
msg = $"成功加入{room.Name}{room.Roomid}\r\n房间人数{room.UserAndIsReady.Count} / {room.MaxUsers}";
return true;
}
else
{
msg = "密码错误,无法加入房间。";
return false;
}
}
else
{
msg = "房间不存在,无法加入。";
return false;
}
}
catch (Exception e)
{
msg = $"加入房间失败,错误信息:{e.Message}";
return false;
}
finally
{
ReleaseRoomSemaphoreSlim();
}
}
public static bool QuitRoom(User user, out string msg)
{
try
{
GetRoomSemaphoreSlim();
msg = "";
if (FunGameConstant.UsersInRoom.TryGetValue(user.Id, out Room? room) && room != null)
{
if (room.RoomState != RoomState.Created)
{
msg = "房间状态异常,无法退出。";
return false;
}
else
{
FunGameConstant.UsersInRoom.Remove(user.Id);
msg = $"成功退出{room.Name}{room.Roomid}";
User[] users = [.. room.UserAndIsReady.Keys.Where(u => u.Id == user.Id)];
foreach (User userTemp in users)
{
room.UserAndIsReady.Remove(userTemp);
}
if (room.UserAndIsReady.Count == 0)
{
FunGameConstant.Rooms.Remove(room.Roomid);
msg += ",该房间人数为零,已解散该房间。";
if (room.Name == "赛马房间")
{
GroupsHasHorseRacing[room.GameMap] = false;
}
}
else if (room.RoomMaster.Id == user.Id)
{
User newRoomMaster = room.UserAndIsReady.Keys.First();
room.RoomMaster = newRoomMaster;
string newRoomMasterName = newRoomMaster.Username;
if (FunGameConstant.UserIdAndUsername.TryGetValue(newRoomMaster.Id, out User? temp) && temp != null)
{
newRoomMasterName = temp.Username;
}
msg += $",新房主为:{newRoomMasterName}。";
}
return true;
}
}
else
{
msg = "你当前不在任何房间中。";
return false;
}
}
catch (Exception e)
{
msg = $"退出房间失败,错误信息:{e.Message}";
return false;
}
finally
{
ReleaseRoomSemaphoreSlim();
}
}
public static void RoomsAutoDisband()
{
try
{
GetRoomSemaphoreSlim();
Room[] rooms = [.. FunGameConstant.Rooms.Values];
foreach (Room room in rooms)
{
if (room.RoomState == RoomState.Created && room.CreateTime.AddMinutes(6) < DateTime.Now)
{
foreach (User user in room.UserAndIsReady.Keys)
{
FunGameConstant.UsersInRoom.Remove(user.Id);
}
FunGameConstant.Rooms.Remove(room.Roomid);
if (room.Name == "赛马房间")
{
GroupsHasHorseRacing[room.GameMap] = false;
}
}
}
}
catch { }
finally
{
ReleaseRoomSemaphoreSlim();
}
}
public static void ReSetRoomState(string roomid)
{
try
{
GetRoomSemaphoreSlim();
if (FunGameConstant.Rooms.TryGetValue(roomid, out Room? room) && room != null)
{
room.CreateTime = DateTime.Now;
room.RoomState = RoomState.Created;
}
}
catch { }
finally
{
ReleaseRoomSemaphoreSlim();
}
}
public static async Task<(Room, List<string>)> RunGameAsync(User user)
{
Room room = General.HallInstance;
try
{
GetRoomSemaphoreSlim();
List<string> msgs = [];
if (FunGameConstant.UsersInRoom.TryGetValue(user.Id, out Room? value) && value != null)
{
room = value;
if (room.RoomMaster.Id != user.Id)
{
msgs.Add("你不是房主,无法开始游戏。");
}
else if (room.RoomState != RoomState.Created)
{
msgs.Add("房间状态异常,无法开始游戏,已自动解散该房间。");
foreach (User userTemp in room.UserAndIsReady.Keys)
{
FunGameConstant.UsersInRoom.Remove(userTemp.Id);
}
FunGameConstant.Rooms.Remove(room.Roomid);
if (room.Name == "赛马房间")
{
GroupsHasHorseRacing[room.GameMap] = false;
}
}
else if (room.UserAndIsReady.Count < 2)
{
msgs.Add("房间人数不足,无法开始游戏。");
}
else
{
room.RoomState = RoomState.Gaming;
switch (room.Name)
{
case "赛马房间":
try
{
GetHorseRacingSettleSemaphoreSlim();
Dictionary<long, int> userPoints = HorseRacing.RunHorseRacing(msgs, room);
PluginConfig pc = new("horseracing", room.GameMap);
pc.LoadConfig();
Dictionary<long, int> waitforsettle = pc.Get<Dictionary<long, int>>("points") ?? [];
foreach (long uid in waitforsettle.Keys)
{
int points = waitforsettle[uid];
if (userPoints.ContainsKey(uid))
{
userPoints[uid] += points;
}
else
{
userPoints[uid] = points;
}
}
pc.Add("points", userPoints);
pc.SaveConfig();
}
catch (Exception e2)
{
msgs.Add("Error: " + e2.Message);
}
finally
{
ReleaseHorseRacingSettleSemaphoreSlim();
}
break;
default:
msgs.Add("游戏已开始!");
await Task.Delay(5000);
msgs.Add("游戏已结束!");
break;
}
}
}
else
{
msgs.Add("你当前不在任何房间中。");
}
return (room, msgs);
}
catch (Exception e)
{
return (room, [$"游戏遇到错误,错误信息:{e.Message}"]);
}
finally
{
ReleaseRoomSemaphoreSlim();
}
}
}
}

View File

@ -2813,6 +2813,32 @@ namespace Oshima.FunGame.WebAPI.Controllers
pc2.Add("exploreTimes", exploreTimes); pc2.Add("exploreTimes", exploreTimes);
msg = $"已为 [ {user2} ] 生成 {itemCount} 个探索许可"; msg = $"已为 [ {user2} ] 生成 {itemCount} 个探索许可";
} }
else if (itemName == "锻造积分")
{
if (pc.TryGetValue("forgepoints", out object? value) && int.TryParse(value.ToString(), out int forgepoints))
{
forgepoints += itemCount;
}
else
{
forgepoints = itemCount;
}
pc2.Add("forgepoints", forgepoints);
msg = $"已为 [ {user2} ] 生成 {itemCount} 个锻造积分";
}
else if (itemName == "赛马积分")
{
if (pc.TryGetValue("horseRacingPoints", out object? value) && int.TryParse(value.ToString(), out int horseRacingPoints))
{
horseRacingPoints += itemCount;
}
else
{
horseRacingPoints = itemCount;
}
pc2.Add("horseRacingPoints", horseRacingPoints);
msg = $"已为 [ {user2} ] 生成 {itemCount} 个锻造积分";
}
else if (itemName.Contains("魔法卡礼包")) else if (itemName.Contains("魔法卡礼包"))
{ {
foreach (string type in ItemSet.QualityTypeNameArray) foreach (string type in ItemSet.QualityTypeNameArray)
@ -2926,7 +2952,7 @@ namespace Oshima.FunGame.WebAPI.Controllers
FunGameService.SetUserConfigAndReleaseSemaphoreSlim(userid, pc, user); FunGameService.SetUserConfigAndReleaseSemaphoreSlim(userid, pc, user);
string msg = ""; string msg = "";
if (user.IsAdmin || userid > 0) if (user.IsAdmin)
{ {
PluginConfig pc2 = FunGameService.GetUserConfig(targetid, out _); PluginConfig pc2 = FunGameService.GetUserConfig(targetid, out _);
if (pc2.Count > 0) if (pc2.Count > 0)
@ -6709,7 +6735,7 @@ namespace Oshima.FunGame.WebAPI.Controllers
{ {
User user = FunGameService.GetUser(pc); User user = FunGameService.GetUser(pc);
if (user.IsAdmin || user.IsOperator) if (user.IsAdmin)
{ {
PluginConfig renameExamine = new("examines", "rename"); PluginConfig renameExamine = new("examines", "rename");
renameExamine.LoadConfig(); renameExamine.LoadConfig();
@ -6793,7 +6819,7 @@ namespace Oshima.FunGame.WebAPI.Controllers
FunGameService.ReleaseUserSemaphoreSlim(userid); FunGameService.ReleaseUserSemaphoreSlim(userid);
} }
if (user.IsAdmin || user.IsOperator) if (user.IsAdmin)
{ {
PluginConfig renameExamine = new("examines", "rename"); PluginConfig renameExamine = new("examines", "rename");
renameExamine.LoadConfig(); renameExamine.LoadConfig();
@ -7959,6 +7985,162 @@ namespace Oshima.FunGame.WebAPI.Controllers
} }
} }
[HttpPost("createroom")]
public string CreateRoom([FromQuery] long uid = -1, [FromQuery] string roomType = "", [FromQuery] string password = "", [FromQuery] string groupId = "")
{
try
{
PluginConfig pc = FunGameService.GetUserConfig(uid, out bool isTimeout);
if (isTimeout)
{
return busy;
}
string msg = "";
if (pc.Count > 0)
{
User user = FunGameService.GetUser(pc);
Room room = OnlineService.CreateRoom(user, roomType, password, groupId, out msg);
if (room.Roomid != "-1")
{
if (OnlineService.IntoRoom(user, room.Roomid, password, out string msg2))
{
msg += $"\r\n{msg2}";
}
}
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("intoroom")]
public string IntoRoom([FromQuery] long uid = -1, [FromQuery] string roomid = "", [FromQuery] string password = "")
{
try
{
PluginConfig pc = FunGameService.GetUserConfig(uid, out bool isTimeout);
if (isTimeout)
{
return busy;
}
string msg = "";
if (pc.Count > 0)
{
User user = FunGameService.GetUser(pc);
OnlineService.IntoRoom(user, roomid, password, out msg);
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("quitroom")]
public string QuitRoom([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);
OnlineService.QuitRoom(user, out msg);
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("rungame")]
public async Task<(Room, List<string>)> RunGame([FromQuery] long uid = -1)
{
Room room = General.HallInstance;
try
{
PluginConfig pc = FunGameService.GetUserConfig(uid, out bool isTimeout);
if (isTimeout)
{
return (room, [busy]);
}
List<string> msgs = [];
if (pc.Count > 0)
{
User user = FunGameService.GetUser(pc);
(room, msgs) = await OnlineService.RunGameAsync(user);
FunGameService.SetUserConfigButNotRelease(uid, pc, user);
return (room, msgs);
}
else
{
return (room, [noSaved]);
}
}
catch (Exception e)
{
Logger.LogError(e, "Error: {e}", e);
return (room, [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

@ -94,6 +94,9 @@ namespace Oshima.FunGame.WebAPI.Models
[JsonPropertyName("isgroup")] [JsonPropertyName("isgroup")]
public bool IsGroup { get; set; } = false; public bool IsGroup { get; set; } = false;
[JsonPropertyName("group_openid")]
public string GroupOpenId { get; set; } = "";
[JsonPropertyName("detail")] [JsonPropertyName("detail")]
public string Detail { get; set; } = ""; public string Detail { get; set; } = "";

View File

@ -47,10 +47,10 @@ namespace Oshima.FunGame.WebAPI
{ {
// 获取 RainBOTService 实例 // 获取 RainBOTService 实例
RainBOTService bot = serviceProvider.GetRequiredService<RainBOTService>(); RainBOTService bot = serviceProvider.GetRequiredService<RainBOTService>();
Controller.WriteLine("成功获取 RainBOTService 实例!");
ThirdPartyMessage message = new() ThirdPartyMessage message = new()
{ {
IsGroup = false, IsGroup = true,
GroupOpenId = "1",
AuthorOpenId = "1", AuthorOpenId = "1",
OpenId = "1", OpenId = "1",
Detail = input, Detail = input,

View File

@ -320,7 +320,7 @@ namespace Oshima.FunGame.WebAPI.Services
FunGameSimulation = true; FunGameSimulation = true;
List<string> msgs = await Controller.GetTest(false, maxRespawnTimesMix: 0); List<string> msgs = await Controller.GetTest(false, maxRespawnTimesMix: 0);
List<string> real = []; List<string> real = [];
int remain = 7; int remain = msgs.Count > 7 ? 7 : msgs.Count - 1;
string merge = ""; string merge = "";
for (int i = 0; i < msgs.Count - 2; i++) for (int i = 0; i < msgs.Count - 2; i++)
{ {
@ -350,7 +350,7 @@ namespace Oshima.FunGame.WebAPI.Services
foreach (string msg in real) foreach (string msg in real)
{ {
await SendAsync(e, "筽祀牻", msg.Trim(), msgSeq: count++); await SendAsync(e, "筽祀牻", msg.Trim(), msgSeq: count++);
await Task.Delay(5500); if (count != real.Count) await Task.Delay(5500);
} }
FunGameSimulation = false; FunGameSimulation = false;
} }
@ -375,7 +375,7 @@ namespace Oshima.FunGame.WebAPI.Services
FunGameSimulation = true; FunGameSimulation = true;
List<string> msgs = await Controller.GetTest(false, maxRespawnTimesMix: maxRespawnTimesMix); List<string> msgs = await Controller.GetTest(false, maxRespawnTimesMix: maxRespawnTimesMix);
List<string> real = []; List<string> real = [];
int remain = 7; int remain = msgs.Count > 7 ? 7 : msgs.Count - 1;
string merge = ""; string merge = "";
for (int i = 0; i < msgs.Count - 2; i++) for (int i = 0; i < msgs.Count - 2; i++)
{ {
@ -405,7 +405,7 @@ namespace Oshima.FunGame.WebAPI.Services
foreach (string msg in real) foreach (string msg in real)
{ {
await SendAsync(e, "筽祀牻", msg.Trim(), msgSeq: count++); await SendAsync(e, "筽祀牻", msg.Trim(), msgSeq: count++);
await Task.Delay(5500); if (count != real.Count) await Task.Delay(5500);
} }
FunGameSimulation = false; FunGameSimulation = false;
} }
@ -435,7 +435,7 @@ namespace Oshima.FunGame.WebAPI.Services
{ {
real.Add(msgs[0]); real.Add(msgs[0]);
} }
int remain = 7; int remain = msgs.Count > 7 ? 7 : msgs.Count - 1;
string merge = ""; string merge = "";
for (int i = 1; i < msgs.Count - 2; i++) for (int i = 1; i < msgs.Count - 2; i++)
{ {
@ -465,7 +465,7 @@ namespace Oshima.FunGame.WebAPI.Services
foreach (string msg in real) foreach (string msg in real)
{ {
await SendAsync(e, "筽祀牻", msg.Trim(), msgSeq: count++); await SendAsync(e, "筽祀牻", msg.Trim(), msgSeq: count++);
await Task.Delay(5500); if (count != real.Count) await Task.Delay(5500);
} }
FunGameSimulation = false; FunGameSimulation = false;
} }
@ -1693,7 +1693,7 @@ namespace Oshima.FunGame.WebAPI.Services
{ {
if (msgs.Count < 20) if (msgs.Count < 20)
{ {
int remain = 7; int remain = msgs.Count > 7 ? 7 : msgs.Count - 1;
string merge = ""; string merge = "";
for (int i = 0; i < msgs.Count - 1; i++) for (int i = 0; i < msgs.Count - 1; i++)
{ {
@ -1730,7 +1730,7 @@ namespace Oshima.FunGame.WebAPI.Services
foreach (string msg in real) foreach (string msg in real)
{ {
await SendAsync(e, "完整决斗", msg.Trim(), msgSeq: count++); await SendAsync(e, "完整决斗", msg.Trim(), msgSeq: count++);
await Task.Delay(1500); if (count != real.Count) await Task.Delay(1500);
} }
return result; return result;
} }
@ -1750,7 +1750,7 @@ namespace Oshima.FunGame.WebAPI.Services
List<string> real = []; List<string> real = [];
if (msgs.Count > 2) if (msgs.Count > 2)
{ {
int remain = 7; int remain = msgs.Count > 7 ? 7 : msgs.Count - 1;
string merge = ""; string merge = "";
for (int i = 0; i < msgs.Count - 1; i++) for (int i = 0; i < msgs.Count - 1; i++)
{ {
@ -1781,7 +1781,7 @@ namespace Oshima.FunGame.WebAPI.Services
foreach (string msg in real) foreach (string msg in real)
{ {
await SendAsync(e, "决斗", msg.Trim(), msgSeq: count++); await SendAsync(e, "决斗", msg.Trim(), msgSeq: count++);
await Task.Delay(1500); if (count != real.Count) await Task.Delay(1500);
} }
return result; return result;
} }
@ -1803,7 +1803,7 @@ namespace Oshima.FunGame.WebAPI.Services
{ {
if (msgs.Count < 20) if (msgs.Count < 20)
{ {
int remain = 7; int remain = msgs.Count > 7 ? 7 : msgs.Count - 1;
string merge = ""; string merge = "";
for (int i = 0; i < msgs.Count - 1; i++) for (int i = 0; i < msgs.Count - 1; i++)
{ {
@ -1840,7 +1840,7 @@ namespace Oshima.FunGame.WebAPI.Services
foreach (string msg in real) foreach (string msg in real)
{ {
await SendAsync(e, "完整决斗", msg.Trim(), msgSeq: count++); await SendAsync(e, "完整决斗", msg.Trim(), msgSeq: count++);
await Task.Delay(1500); if (count != real.Count) await Task.Delay(1500);
} }
return result; return result;
} }
@ -1876,7 +1876,7 @@ namespace Oshima.FunGame.WebAPI.Services
{ {
if (msgs.Count < 20) if (msgs.Count < 20)
{ {
int remain = 7; int remain = msgs.Count > 7 ? 7 : msgs.Count - 1;
string merge = ""; string merge = "";
for (int i = 0; i < msgs.Count - 1; i++) for (int i = 0; i < msgs.Count - 1; i++)
{ {
@ -1913,7 +1913,7 @@ namespace Oshima.FunGame.WebAPI.Services
foreach (string msg in real) foreach (string msg in real)
{ {
await SendAsync(e, "BOSS", msg.Trim(), msgSeq: count++); await SendAsync(e, "BOSS", msg.Trim(), msgSeq: count++);
await Task.Delay(1500); if (count != real.Count) await Task.Delay(1500);
} }
} }
else else
@ -1935,7 +1935,7 @@ namespace Oshima.FunGame.WebAPI.Services
{ {
if (msgs.Count < 20) if (msgs.Count < 20)
{ {
int remain = 7; int remain = msgs.Count > 7 ? 7 : msgs.Count - 1;
string merge = ""; string merge = "";
for (int i = 0; i < msgs.Count - 1; i++) for (int i = 0; i < msgs.Count - 1; i++)
{ {
@ -1972,7 +1972,7 @@ namespace Oshima.FunGame.WebAPI.Services
foreach (string msg in real) foreach (string msg in real)
{ {
await SendAsync(e, "BOSS", msg.Trim(), msgSeq: count++); await SendAsync(e, "BOSS", msg.Trim(), msgSeq: count++);
await Task.Delay(1500); if (count != real.Count) await Task.Delay(1500);
} }
} }
else else
@ -2904,6 +2904,65 @@ namespace Oshima.FunGame.WebAPI.Services
return result; return result;
} }
if (e.Detail == "后勤部")
{
string msg = Controller.ShowSystemStore(uid, "铎京城", "dokyo_logistics");
if (msg.Trim() != "")
{
await SendAsync(e, "商店", msg);
}
return result;
}
if (e.Detail == "武器商会")
{
string msg = Controller.ShowSystemStore(uid, "铎京城", "dokyo_weapons");
if (msg.Trim() != "")
{
await SendAsync(e, "商店", msg);
}
return result;
}
if (e.Detail == "杂货铺")
{
string msg = Controller.ShowSystemStore(uid, "铎京城", "dokyo_yuki");
if (msg.Trim() != "")
{
await SendAsync(e, "商店", msg);
}
return result;
}
if (e.Detail == "基金会")
{
string msg = Controller.ShowSystemStore(uid, "铎京城", "dokyo_welfare");
if (msg.Trim() != "")
{
await SendAsync(e, "商店", msg);
}
return result;
}
if (e.Detail == "锻造商店")
{
string msg = Controller.ShowSystemStore(uid, "铎京城", "dokyo_forge");
if (msg.Trim() != "")
{
await SendAsync(e, "商店", msg);
}
return result;
}
if (e.Detail == "赛马商店")
{
string msg = Controller.ShowSystemStore(uid, "铎京城", "dokyo_horseracing");
if (msg.Trim() != "")
{
await SendAsync(e, "商店", msg);
}
return result;
}
if (e.Detail.StartsWith("商店")) if (e.Detail.StartsWith("商店"))
{ {
@ -2928,6 +2987,9 @@ namespace Oshima.FunGame.WebAPI.Services
case 5: case 5:
msg = Controller.ShowSystemStore(uid, "铎京城", "dokyo_forge"); msg = Controller.ShowSystemStore(uid, "铎京城", "dokyo_forge");
break; break;
case 6:
msg = Controller.ShowSystemStore(uid, "铎京城", "dokyo_horseracing");
break;
default: default:
break; break;
} }
@ -3218,6 +3280,230 @@ namespace Oshima.FunGame.WebAPI.Services
return result; return result;
} }
if (e.Detail == "创建赛马")
{
string groupId = "";
if (e.IsGroup && e is GroupAtMessage groupAtMessage && groupAtMessage.GroupOpenId != "")
{
groupId = groupAtMessage.GroupOpenId;
}
else if (e.IsGroup && e is ThirdPartyMessage thirdPartyMessage && thirdPartyMessage.GroupOpenId != "")
{
groupId = thirdPartyMessage.GroupOpenId;
}
if (groupId != "")
{
string msg = Controller.CreateRoom(uid, "horseracing", "", groupId);
if (msg.Trim() != "")
{
await SendAsync(e, "赛马", msg);
}
}
else
{
await SendAsync(e, "赛马", "请在群聊中进行多人游戏。");
}
return result;
}
if (e.Detail == "加入赛马")
{
string groupId = "";
if (e.IsGroup && e is GroupAtMessage groupAtMessage && groupAtMessage.GroupOpenId != "")
{
groupId = groupAtMessage.GroupOpenId;
}
else if (e.IsGroup && e is ThirdPartyMessage thirdPartyMessage && thirdPartyMessage.GroupOpenId != "")
{
groupId = thirdPartyMessage.GroupOpenId;
}
if (groupId != "")
{
if (FunGameConstant.Rooms.Values.FirstOrDefault(r => r.GameMap == groupId) is Room room)
{
string msg = Controller.IntoRoom(uid, room.Roomid, "");
if (msg.Trim() != "")
{
await SendAsync(e, "赛马", msg);
}
}
else
{
await SendAsync(e, "赛马", "本群还没有创建赛马房间,请使用【创建赛马】指令来创建一个房间。");
}
}
else
{
await SendAsync(e, "赛马", "请在群聊中进行多人游戏。");
}
return result;
}
if (e.Detail.StartsWith("创建房间"))
{
string groupId = "";
if (e.IsGroup && e is GroupAtMessage groupAtMessage && groupAtMessage.GroupOpenId != "")
{
groupId = groupAtMessage.GroupOpenId;
}
else if (e.IsGroup && e is ThirdPartyMessage thirdPartyMessage && thirdPartyMessage.GroupOpenId != "")
{
groupId = thirdPartyMessage.GroupOpenId;
}
if (groupId != "")
{
string detail = e.Detail.Replace("创建房间", "").Trim();
string[] strings = detail.Split(" ", StringSplitOptions.RemoveEmptyEntries);
string roomType = "", password = "";
if (strings.Length > 0) roomType = strings[0];
if (strings.Length > 1)
{
int firstSpaceIndex = detail.IndexOf(' ');
if (firstSpaceIndex != -1 && firstSpaceIndex + 1 < detail.Length)
{
password = detail[(firstSpaceIndex + 1)..].Trim();
}
}
string msg = Controller.CreateRoom(uid, roomType, password, groupId);
if (msg.Trim() != "")
{
await SendAsync(e, "房间", msg);
}
}
else
{
await SendAsync(e, "房间", "请在群聊中进行多人游戏。");
}
return result;
}
if (e.Detail.StartsWith("加入房间"))
{
string groupId = "";
if (e.IsGroup && e is GroupAtMessage groupAtMessage && groupAtMessage.GroupOpenId != "")
{
groupId = groupAtMessage.GroupOpenId;
}
else if (e.IsGroup && e is ThirdPartyMessage thirdPartyMessage && thirdPartyMessage.GroupOpenId != "")
{
groupId = thirdPartyMessage.GroupOpenId;
}
if (groupId != "")
{
string detail = e.Detail.Replace("加入房间", "").Trim();
string[] strings = detail.Split(" ", StringSplitOptions.RemoveEmptyEntries);
string roomid = "", password = "";
if (strings.Length > 0) roomid = strings[0];
if (strings.Length > 1)
{
int firstSpaceIndex = detail.IndexOf(' ');
if (firstSpaceIndex != -1 && firstSpaceIndex + 1 < detail.Length)
{
password = detail[(firstSpaceIndex + 1)..].Trim();
}
}
string msg = Controller.IntoRoom(uid, roomid, password);
if (msg.Trim() != "")
{
await SendAsync(e, "房间", msg);
}
}
else
{
await SendAsync(e, "房间", "请在群聊中进行多人游戏。");
}
return result;
}
if (e.Detail == "开始游戏")
{
string groupId = "";
if (e.IsGroup && e is GroupAtMessage groupAtMessage && groupAtMessage.GroupOpenId != "")
{
groupId = groupAtMessage.GroupOpenId;
}
else if (e.IsGroup && e is ThirdPartyMessage thirdPartyMessage && thirdPartyMessage.GroupOpenId != "")
{
groupId = thirdPartyMessage.GroupOpenId;
}
if (groupId != "")
{
(Room room, List<string> msgs) = await Controller.RunGame(uid);
List<string> real = [];
if (msgs.Count > 1)
{
if (msgs.Count > 20)
{
msgs = [msgs[0], .. msgs[^20..]];
}
int remain = msgs.Count > 7 ? 7 : msgs.Count - 1;
string merge = "";
for (int i = 0; i < msgs.Count - 1; i++)
{
remain--;
merge += msgs[i] + "\r\n";
if (remain == 0)
{
real.Add(merge);
merge = "";
if ((msgs.Count - i - 1) < 7)
{
remain = msgs.Count - i - 1;
}
else remain = 7;
}
}
real.Add(msgs[^1]);
}
else
{
real = msgs;
}
if (real.Count >= 3)
{
real = [real[0], .. real[^2..]];
}
int count = 1;
foreach (string msg in real)
{
await SendAsync(e, "房间", msg.Trim(), msgSeq: count++);
if (count <= real.Count) await Task.Delay(1500);
}
OnlineService.ReSetRoomState(room.Roomid);
}
else
{
await SendAsync(e, "房间", "请在群聊中进行多人游戏。");
}
return result;
}
if (e.Detail == "退出房间")
{
string groupId = "";
if (e.IsGroup && e is GroupAtMessage groupAtMessage && groupAtMessage.GroupOpenId != "")
{
groupId = groupAtMessage.GroupOpenId;
}
else if (e.IsGroup && e is ThirdPartyMessage thirdPartyMessage && thirdPartyMessage.GroupOpenId != "")
{
groupId = thirdPartyMessage.GroupOpenId;
}
if (groupId != "")
{
string msg = Controller.QuitRoom(uid);
if (msg.Trim() != "")
{
await SendAsync(e, "房间", msg);
}
}
else
{
await SendAsync(e, "房间", "请在群聊中进行多人游戏。");
}
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);