添加 rogue

This commit is contained in:
milimoe 2026-04-04 04:13:33 +08:00
parent 512d7d7d6a
commit 727f13aba6
Signed by: milimoe
GPG Key ID: 9554D37E4B8991D0
5 changed files with 934 additions and 6 deletions

View File

@ -9,11 +9,10 @@ using Oshima.FunGame.OshimaServers.Service;
using Oshima.FunGame.WebAPI.Controllers;
//_ = new Milimoe.FunGame.Testing.Solutions.Novels();
//Console.ReadKey();
//_ = new Milimoe.FunGame.Testing.Tests.CheckDLL();
Console.WriteLine();
//_ = new Milimoe.FunGame.Testing.Tests.WebSocketTest();
CharacterModule cm = new();
@ -23,10 +22,22 @@ sm.Load();
ItemModule im = new();
im.Load();
Milimoe.FunGame.Testing.Tests.RogueLikeTest rogue = new();
await rogue.RogueLike.StartGame();
FunGameConstant.InitFunGame();
FunGameSimulation.InitFunGameSimulation();
FunGameController controller = new(new Logger<FunGameController>(new LoggerFactory()));
//foreach (OshimaRegion or in FunGameConstant.Regions)
//{
// Console.WriteLine(or.ToString());
//}
//Console.ReadKey();
//await Milimoe.FunGame.Testing.Tests.FunGameBO5.StartBO5();
//Console.ReadKey();
//foreach (Character c in FunGameSimulation.CharacterStatistics.Keys)
//{
// Console.WriteLine(controller.GetStats((int)c.Id));

View File

@ -0,0 +1,248 @@
using Milimoe.FunGame.Core.Entity;
using Milimoe.FunGame.Core.Library.Constant;
using Milimoe.FunGame.Core.Model;
using Milimoe.FunGame.Testing.Tests;
using Oshima.FunGame.OshimaModules.Characters;
using Oshima.FunGame.OshimaServers.Service;
namespace Milimoe.FunGame.Testing.Solutions
{
/// <summary>
/// 客户端逻辑层
/// </summary>
/// <param name="dispatcher"></param>
public class RogueLike(RogueLikeDispatcher dispatcher)
{
public RogueLikeDispatcher Dispatcher { get; set; } = dispatcher;
public Dictionary<string, Func<string[], Task>> Commands { get; set; } = [];
public Dictionary<string, string> CommandAlias { get; set; } = [];
public User User { get; set; } = General.UnknownUserInstance;
public bool Running { get; set; } = false;
public bool InGame { get; set; } = false;
public Lock Lock { get; } = new();
/// <summary>
/// 为空表示没有正在进行的询问
/// </summary>
private Guid _currentRequestGuid = Guid.Empty;
/// <summary>
/// 询问结果,和上面的字段配合使用
/// </summary>
private TaskCompletionSource<DataRequestArgs> _requestTcs = new();
public async Task StartGame()
{
Running = true;
Dispatcher.StartServer();
AddDialog("", "探索者,欢迎入职【永恒方舟计划】,我是您的专属 AI协助您前往指定任务地点开展勘测工作。请问您的名字是");
string username;
do
{
username = await GetTextResultsAsync([]);
if (username == "")
{
WriteLine("请输入不为空的字符串。");
}
}
while (username == "");
DataRequestArgs response = await DataRequest(new("createuser")
{
Data = new()
{
{ "username", username }
}
});
if (response.Data.TryGetValue("user", out object? value) && value is User user)
{
User = user;
}
// 初始化菜单指令
Commands["quit"] = async (args) =>
{
Running = false;
WriteLine("游戏结束!");
};
AddCommandAlias("quit", "退出", "exit", "!q");
Commands["test"] = async (args) =>
{
WriteLine("打木桩测试");
Character enemy = new CustomCharacter(0, "木桩")
{
Level = User.Inventory.MainCharacter.Level,
CharacterState = CharacterState.NotActionable
};
enemy.Recovery();
List<string> strings = (await FunGameActionQueue.NewAndStartGame([User.Inventory.MainCharacter, enemy], showAllRound: true)).Result;
foreach (string str in strings)
{
WriteLine(str);
}
};
Commands["help"] = Help;
Commands["start"] = async (args) =>
{
if (InGame)
{
return;
}
InGame = true;
await Start();
InGame = false;
};
AddCommandAlias("help", "菜单", "h", "帮助");
AddCommandAlias("start", "开局");
AddDialog("柔哥", $"让我再次欢迎您,{username}。入职手续已办理完毕,接下来,您可以使用菜单了。请您记住我的名字:【柔哥】。");
await Help([]);
while (Running)
{
if (InGame)
{
await Task.Delay(1000);
continue;
}
string input = await GetTextResultsAsync([]);
string[] strings = input.Split(" ", StringSplitOptions.RemoveEmptyEntries);
if (strings.Length == 0)
{
continue;
}
string command = strings[0];
string[] args = [.. strings.Skip(1)];
if (Commands.TryGetValue(command.ToLower(), out Func<string[], Task>? func) && func != null)
{
await func.Invoke(args);
}
else if (CommandAlias.TryGetValue(command.ToLower(), out string? actualCommand) && actualCommand != null)
{
if (Commands.TryGetValue(actualCommand, out Func<string[], Task>? func2) && func2 != null)
{
await func2.Invoke(args);
}
}
else
{
WriteLine("未知指令");
}
}
}
public Action<string> WriteLineHandler
{
get => field ?? Console.WriteLine;
set => field = value;
}
public delegate Task ReadInput<T>(Dictionary<string, object> args, List<T> results);
public delegate Task<InquiryResponse> ReadInputInGameResponse(InquiryOptions options);
public event ReadInput<string>? ReadInputStringHandler;
public event ReadInput<double>? ReadInputNumberHandler;
public event ReadInputInGameResponse? ReadInputInGameResponseHandler;
public async Task<List<string>> GetChoiceResultsAsync(InquiryOptions options, Dictionary<string, object> args)
{
int index = 0;
Dictionary<int, string> indexToChoice = [];
foreach (string choice in options.Choices.Keys)
{
index++;
indexToChoice[index] = choice;
WriteLine($"{index}. {choice}{options.Choices[choice]}");
}
WriteLine($"--- {options.Topic} ---");
List<string> results = [];
if (ReadInputStringHandler != null)
{
await ReadInputStringHandler.Invoke(args, results);
}
return [.. indexToChoice.Where(kv => results.Contains(kv.Key.ToString())).Select(kv => kv.Value)];
}
public async Task<List<double>> GetNumberResultsAsync(Dictionary<string, object> args)
{
List<double> results = [];
if (ReadInputNumberHandler != null)
{
await ReadInputNumberHandler.Invoke(args, results);
}
return results;
}
public async Task<string> GetTextResultsAsync(Dictionary<string, object> args)
{
List<string> results = [];
if (ReadInputStringHandler != null)
{
await ReadInputStringHandler.Invoke(args, results);
}
return results.FirstOrDefault() ?? "";
}
public async Task<DataRequestArgs> DataRequest(DataRequestArgs args)
{
_requestTcs.TrySetCanceled();
_currentRequestGuid = Guid.NewGuid();
_requestTcs = new TaskCompletionSource<DataRequestArgs>(TaskCreationOptions.RunContinuationsAsynchronously);
await Dispatcher.DataRequest(_currentRequestGuid, args);
return await _requestTcs.Task;
}
public async Task DataRequestComplete(Guid guid, DataRequestArgs response)
{
if (guid == _currentRequestGuid)
{
_currentRequestGuid = Guid.Empty;
_requestTcs.TrySetResult(response);
}
}
public async Task<InquiryResponse> GetInGameResponse(InquiryOptions options)
{
if (ReadInputInGameResponseHandler is null) return new(options);
return await ReadInputInGameResponseHandler.Invoke(options);
}
public void WriteLine(string message = "")
{
WriteLineHandler(message);
}
public void AddDialog(string speaker, string message)
{
WriteLine(BuildSpeakerDialog(speaker, message));
}
public static string BuildSpeakerDialog(string speaker, string message)
{
return $"【{speaker}】{message}";
}
private void AddCommandAlias(string command, params string[] aliases)
{
foreach (string alias in aliases)
{
CommandAlias[alias] = command;
}
}
public string GetCommandAliases(string command)
{
string[] alias = [.. CommandAlias.Where(kv => kv.Value == command).Select(kv => kv.Key)];
return alias.Length > 0 ? $"(替代:{string.Join("", alias)}" : "";
}
private async Task Help(string[] args)
{
WriteLine($"可用指令:{string.Join("", Commands.Keys.Select(c => $"{c}{GetCommandAliases(c)}"))}");
}
private async Task Start()
{
await Dispatcher.CreateGameLoop(User.Username);
}
}
}

View File

@ -0,0 +1,387 @@
using Milimoe.FunGame.Core.Api.Utility;
using Milimoe.FunGame.Core.Entity;
using Milimoe.FunGame.Core.Library.Constant;
using Milimoe.FunGame.Core.Model;
using Milimoe.FunGame.Testing.Tests;
using Oshima.FunGame.OshimaModules.Characters;
using Oshima.FunGame.OshimaModules.Models;
using Oshima.FunGame.OshimaModules.Regions;
namespace Milimoe.FunGame.Testing.Solutions
{
public class RogueLikeServer(RogueLikeDispatcher dispatcher)
{
public bool Running => Dispatcher.Running;
public Task? Guard { get; set; } = null;
public RogueLikeDispatcher Dispatcher { get; set; } = dispatcher;
public ConcurrentQueue<(Guid Guid, DataRequestArgs Args)> Inquiries { get; } = [];
public Dictionary<string, User> Users { get; set; } = [];
public Dictionary<string, RogueLikeGameData> RogueLikeGameDatas { get; set; } = [];
public void ReceiveDataRequest(Guid guid, DataRequestArgs args)
{
Inquiries.Add((guid, args));
}
public void WriteLine(string message = "") => Dispatcher.WriteLine(message);
public async Task DataRequestGuard()
{
while (Running)
{
if (!Inquiries.IsEmpty && Inquiries.GetFirst(out var obj))
{
DataRequestArgs response = await HandleDataRequest(obj.Args);
await DataRequestCallback(obj.Guid, response);
}
await Task.Delay(50);
}
}
public async Task CreateGameLoop(string username)
{
Users.TryGetValue(username, out User? user);
user ??= General.UnknownUserInstance;
Character character = user.Inventory.MainCharacter;
RogueLikeGameData data = new(character);
RogueLikeGameDatas[username] = data;
await GameLoop(data);
}
private async Task GameLoop(RogueLikeGameData data)
{
while (data.RogueState != RogueState.Finish)
{
data.RogueState = await NextRogueState(data, data.RogueState);
if (data.RogueState == RogueState.Init)
{
data.RogueState = RogueState.Finish;
}
await Task.Delay(100);
}
}
private async Task<DataRequestArgs> HandleDataRequest(DataRequestArgs args)
{
DataRequestArgs response = new(args.RequestType);
switch (response.RequestType)
{
case "createuser":
{
string username = "";
if (args.Data.TryGetValue("username", out object? value) && value is string s)
{
username = s;
}
User user = Factory.GetUser(1, username);
Character character = new CustomCharacter(user.Id, username)
{
Level = 1
};
character.Recovery();
user.Inventory.Characters.Add(character);
user.Inventory.MainCharacter = character;
Users[username] = user;
response.Data["user"] = user;
break;
}
default:
break;
}
return response;
}
private async Task DataRequestCallback(Guid guid, DataRequestArgs response)
{
await Dispatcher.DataRequestComplete(guid, response);
}
private async Task<RogueState> NextRogueState(RogueLikeGameData data, RogueState state)
{
RogueState newState = RogueState.Init;
switch (state)
{
case RogueState.Init:
{
newState = RogueState.InArk;
}
break;
case RogueState.InArk:
{
OshimaRegion? region = await ChooseRegion(1);
if (region != null)
{
WriteLine("-- 【永恒方舟计划】进程·Ⅰ:启动 --");
data.CurrentRegion = region;
data.Chapter1Region = region;
newState = RogueState.Chapter1InArk;
}
else
{
WriteLine("未选择地区,永恒方舟计划终止。");
newState = RogueState.Finish;
}
}
break;
case RogueState.Chapter1InArk:
{
WriteLine("探索前的准备。");
await RestInArk();
newState = RogueState.ExploringChapter1Area1;
}
break;
case RogueState.Chapter2InArk:
{
WriteLine("回到方舟后,我们得到了新的任务。");
// TODO:提供一个菜单玩家可以选择第二章的地区随机从3-4★的地区里抽取3个来选一个
OshimaRegion? region = await ChooseRegion(2);
if (region != null)
{
WriteLine("-- 【永恒方舟计划】进程·Ⅱ:启动 --");
data.CurrentRegion = region;
data.Chapter2Region = region;
}
else
{
WriteLine("未选择地区,永恒方舟计划终止。");
newState = RogueState.Finish;
WriteLine("结算到目前为止的奖励。");
break;
}
await RestInArk();
newState = RogueState.ExploringChapter2Area1;
}
break;
case RogueState.Chapter3InArk:
{
OshimaRegion? region = await ChooseRegion(3);
if (region != null)
{
WriteLine("-- 【永恒方舟计划】进程·Ⅲ:启动 --");
data.CurrentRegion = region;
data.Chapter3Region = region;
}
else
{
WriteLine("未选择地区,永恒方舟计划终止。");
newState = RogueState.Finish;
WriteLine("结算到目前为止的奖励。");
break;
}
await RestInArk();
newState = RogueState.ExploringChapter3Area1;
}
break;
case RogueState.FinalInArk:
WriteLine("-- 【永恒方舟计划】紧急事件·夺还:启动 --");
newState = RogueState.ExploringArk;
break;
case RogueState.ExploringChapter1Area1:
newState = await ExploreRegion(data, 1, 1);
break;
case RogueState.ExploringChapter1Area2:
newState = await ExploreRegion(data, 1, 2);
break;
case RogueState.ExploringChapter2Area1:
newState = await ExploreRegion(data, 2, 1);
break;
case RogueState.ExploringChapter2Area2:
newState = await ExploreRegion(data, 2, 2);
break;
case RogueState.ExploringChapter3Area1:
newState = await ExploreRegion(data, 3, 1);
break;
case RogueState.ExploringChapter3Area2:
newState = await ExploreRegion(data, 3, 2);
break;
case RogueState.ExploringArk:
await ExploreArk();
newState = RogueState.FinalBossBattle;
break;
case RogueState.Chapter1BossBattle:
{
WriteLine("剧情过后,战斗一触即发。");
// TODO:调用FunGameActionQueue的战斗系统
WriteLine("战胜了BOSS返回方舟汇报工作并进行战后整备");
newState = RogueState.Chapter2InArk;
}
break;
case RogueState.Chapter2BossBattle:
{
WriteLine("剧情过后,战斗一触即发。");
// TODO:调用FunGameActionQueue的战斗系统
WriteLine("战胜了BOSS返回方舟汇报工作并进行战后整备");
newState = RogueState.Chapter3InArk;
}
break;
case RogueState.Chapter3BossBattle:
{
WriteLine("剧情过后,战斗一触即发。");
// TODO:调用FunGameActionQueue的战斗系统
WriteLine("战胜了BOSS返回方舟汇报工作并进行战后整备");
newState = RogueState.FinalInArk;
}
break;
case RogueState.FinalBossBattle:
{
WriteLine("剧情过后,战斗一触即发。");
// TODO:调用FunGameActionQueue的战斗系统
WriteLine("战胜了BOSS永恒方舟成功夺还");
WriteLine("本次【永恒方舟计划】成功!");
newState = RogueState.Finish;
}
break;
case RogueState.Finish:
break;
default:
break;
}
return newState;
}
private async Task<OshimaRegion?> ChooseRegion(int chapter)
{
string topic = chapter switch
{
1 => "选择初始探索地区",
_ => "选择下一个探索地区",
};
Func<OshimaRegion, bool> predicate = chapter switch
{
2 => r => (int)r.Difficulty >= (int)RarityType.ThreeStar && (int)r.Difficulty <= (int)RarityType.FourStar,
3 => r => (int)r.Difficulty == (int)RarityType.FiveStar,
_ => r => (int)r.Difficulty <= (int)RarityType.TwoStar
};
InquiryOptions options = new(InquiryType.Choice, topic)
{
Choices = FunGameConstant.Regions.Where(predicate).ToDictionary(r => r.Name, r => r.ToString())
};
InquiryResponse response = await Dispatcher.GetInGameResponse(options);
if (!response.Cancel)
{
if (response.Choices.FirstOrDefault() is string regionName)
{
OshimaRegion? region = FunGameConstant.Regions.FirstOrDefault(r => r.Name == regionName);
if (region != null && region.Areas.Count > 0)
{
return region;
}
}
}
return null;
}
private async Task<RogueState> ExploreRegion(RogueLikeGameData data, int chapter, int areaSeq)
{
RogueState newState = RogueState.Finish;
if (data.CurrentRegion is null)
{
WriteLine("突发!小队和方舟失联了!永恒方舟计划终止。");
return newState;
}
if (areaSeq == 1)
{
WriteLine("正在降落...");
data.CurrentArea = data.CurrentRegion.Areas.OrderByDescending(o => Random.Shared.Next()).First();
WriteLine($"在【{data.CurrentRegion.Name}】的【{data.CurrentArea}】区域完成降落!");
}
else if (areaSeq == 2)
{
WriteLine("该地区的第一个探索区域任务已完成!正在前往第二个区域……");
data.CurrentArea = data.CurrentRegion.Areas.OrderByDescending(o => Random.Shared.Next()).First(a => a != data.CurrentArea);
WriteLine($"在【{data.CurrentRegion.Name}】的【{data.CurrentArea}】区域完成降落!");
}
else
{
WriteLine("你误入了神秘地带,与方舟失联,游戏结束。");
return newState;
}
bool fin = false;
while (!fin)
{
// TODO:开始探索区域,主要抉择
fin = true;
}
if (areaSeq == 1)
{
newState = chapter switch
{
1 => RogueState.ExploringChapter1Area2,
2 => RogueState.ExploringChapter2Area2,
3 => RogueState.ExploringChapter3Area2,
_ => RogueState.Finish,
};
}
else if (areaSeq == 2)
{
WriteLine("BOSS房间出现了做好准备再继续出发吧。");
fin = false;
while (!fin)
{
// TODO:BOSS房前的准备提供菜单
fin = true;
}
newState = chapter switch
{
1 => RogueState.Chapter1BossBattle,
2 => RogueState.Chapter2BossBattle,
3 => RogueState.Chapter3BossBattle,
_ => RogueState.Finish,
};
}
else
{
WriteLine("你误入了神秘地带,与方舟失联,游戏结束。");
}
return newState;
}
private async Task RestInArk()
{
bool fin = false;
while (!fin)
{
// TODO:战后整备,提供菜单回复和提升等
fin = true;
}
WriteLine("出发!");
}
private async Task ExploreArk()
{
bool fin = false;
while (!fin)
{
// TODO:方舟事变需进行方舟探索和收复功能房间并找到最终BOSS房间
fin = true;
}
WriteLine("出发!");
}
}
public enum RogueState
{
Init,
InArk,
Chapter1InArk,
Chapter2InArk,
Chapter3InArk,
FinalInArk,
ExploringChapter1Area1,
ExploringChapter1Area2,
ExploringChapter2Area1,
ExploringChapter2Area2,
ExploringChapter3Area1,
ExploringChapter3Area2,
ExploringArk,
Chapter1BossBattle,
Chapter2BossBattle,
Chapter3BossBattle,
FinalBossBattle,
Finish
}
}

View File

@ -100,6 +100,24 @@ namespace Milimoe.FunGame.Testing.Tests
Console.WriteLine($"{userTeams[u2]} 禁用了 {x}");
}
}
else if (i == 6 || i == 7)
{
if (ban.Count < 4)
{
Character? x = xx.FirstOrDefault(c => !ban.Contains(c));
if (x is null) continue;
if (i % 2 != 0)
{
ban.Add(x);
Console.WriteLine($"{userTeams[u1]} 禁用了 {x}");
}
else if (i % 2 == 0)
{
ban.Add(x);
Console.WriteLine($"{userTeams[u2]} 禁用了 {x}");
}
}
}
else
{
// 选秀
@ -109,7 +127,7 @@ namespace Milimoe.FunGame.Testing.Tests
c.Level = 60;
c.NormalAttack.Level = 8;
FunGameService.AddCharacterSkills(c, 1, 6, 6);
if (i % 2 == 0 && t1.Count < 5)
if ((i < 7 ? i % 2 == 0 : i % 2 != 0) && t1.Count < 5)
{
User? uu = u1.FirstOrDefault(u => !t1.ContainsKey(u));
if (uu is null) continue;
@ -119,7 +137,7 @@ namespace Milimoe.FunGame.Testing.Tests
Console.WriteLine($"{userTeams[u1]}.{uu} 选择了 {c}");
c.User = uu;
}
if (i % 2 != 0 && t2.Count < 5)
if ((i < 7 ? i % 2 != 0 : i % 2 == 0) && t2.Count < 5)
{
User? uu = u2.FirstOrDefault(u => !t2.ContainsKey(u));
if (uu is null) continue;
@ -148,8 +166,8 @@ namespace Milimoe.FunGame.Testing.Tests
DropItems(team2.Members);
team1.Members.ForEach(c => c.Recovery());
team2.Members.ForEach(c => c.Recovery());
FunGameActionQueue queue = new();
List<string> msgs = await queue.StartTeamGame(teams, -1, 30);
FunGameActionQueue queue = await FunGameActionQueue.NewAndStartTeamGame(teams, -1, 30);
List<string> msgs = queue.Result;
foreach (Character character in queue.GamingQueue.CharacterStatistics.Keys)
{
FunGameTesting.UpdateStatistics(stats[character.User], queue.GamingQueue.CharacterStatistics[character]);

View File

@ -0,0 +1,264 @@
using Milimoe.FunGame.Core.Entity;
using Milimoe.FunGame.Core.Model;
using Milimoe.FunGame.Testing.Solutions;
using Oshima.FunGame.OshimaModules.Regions;
namespace Milimoe.FunGame.Testing.Tests
{
/// <summary>
/// 肉鸽客户端原型测试
/// </summary>
public class RogueLikeTest
{
public RogueLike RogueLike { get; set; }
public RogueLikeServer RogueLikeServer { get; set; }
public RogueLikeTest()
{
RogueLikeDispatcher dispatcher = new();
RogueLike = new(dispatcher);
RogueLikeServer = new(dispatcher);
dispatcher.RogueLikeInstance = RogueLike;
dispatcher.RogueLikeServer = RogueLikeServer;
RogueLike.ReadInputStringHandler += RogueLike_ReadInputStringHandler;
RogueLike.ReadInputNumberHandler += RogueLike_ReadInputNumberHandler;
RogueLike.ReadInputInGameResponseHandler += RogueLike_ReadInputInGameResponseHandler;
}
/// <summary>
/// 此方法完全自主处理options并输入
/// </summary>
/// <param name="options"></param>
/// <returns></returns>
private async Task<InquiryResponse> RogueLike_ReadInputInGameResponseHandler(InquiryOptions options)
{
InquiryResponse response = new(options);
switch (options.InquiryType)
{
case Core.Library.Constant.InquiryType.Choice:
case Core.Library.Constant.InquiryType.BinaryChoice:
{
int index = 0;
Dictionary<int, string> indexToChoice = [];
foreach (string choice in options.Choices.Keys)
{
index++;
indexToChoice[index] = choice;
RogueLike.WriteLine($"{index}. {choice}{options.Choices[choice]}");
}
RogueLike.WriteLine($"--- {options.Topic} ---");
bool resolve = false;
while (!resolve)
{
RogueLike.WriteLine($"选择一个选项(输入序号,输入 !c 表示取消):");
string result = (await Console.In.ReadLineAsync())?.Trim() ?? "";
if (result == "!c")
{
response.Cancel = true;
resolve = true;
}
else if (int.TryParse(result, out int inputIndex) && indexToChoice.TryGetValue(inputIndex, out string? value))
{
response.Choices = [value];
resolve = true;
}
}
break;
}
case Core.Library.Constant.InquiryType.MultipleChoice:
{
int index = 0;
Dictionary<int, string> indexToChoice = [];
foreach (string choice in options.Choices.Keys)
{
index++;
indexToChoice[index] = choice;
RogueLike.WriteLine($"{index}. {choice}{options.Choices[choice]}");
}
RogueLike.WriteLine($"--- {options.Topic} ---");
bool resolve = false;
while (!resolve)
{
RogueLike.WriteLine($"选择多个选项(输入序号,空格分隔,包含 !c 时表示取消):");
string result = (await Console.In.ReadLineAsync())?.Trim() ?? "";
if (result.Contains("!c"))
{
response.Cancel = true;
break;
}
string[] strings = result.Split(' ', StringSplitOptions.RemoveEmptyEntries);
foreach (string str in strings)
{
if (int.TryParse(str, out int inputIndex) && indexToChoice.TryGetValue(inputIndex, out string? value))
{
response.Choices.Add(value);
}
}
if (response.Choices.Count > 0)
{
resolve = true;
}
}
break;
}
case Core.Library.Constant.InquiryType.TextInput:
{
RogueLike.WriteLine($"--- {options.Topic} ---");
bool resolve = false;
while (!resolve)
{
RogueLike.WriteLine("输入回应(输入 !c 表示取消):");
string result = (await Console.In.ReadLineAsync())?.Trim() ?? "";
if (result == "!c")
{
response.Cancel = true;
resolve = true;
}
else if (result != "")
{
response.TextResult = result;
resolve = true;
}
}
break;
}
case Core.Library.Constant.InquiryType.NumberInput:
{
RogueLike.WriteLine($"--- {options.Topic} ---");
bool resolve = false;
while (!resolve)
{
RogueLike.WriteLine("输入结果(输入 !c 表示取消):");
string result = await Console.In.ReadLineAsync() ?? "";
if (result.Trim() == "!c")
{
response.Cancel = true;
resolve = true;
}
else if (double.TryParse(result, out double doubleResult))
{
response.NumberResult = doubleResult;
resolve = true;
}
}
break;
}
case Core.Library.Constant.InquiryType.Custom:
break;
default:
break;
}
return response;
}
private async Task RogueLike_ReadInputStringHandler(Dictionary<string, object> args, List<string> results)
{
string input = await Console.In.ReadLineAsync() ?? "";
if (input.Trim().Equals("!c", StringComparison.CurrentCultureIgnoreCase))
{
args["cancel"] = true;
}
results.Add(input.Trim());
}
private async Task RogueLike_ReadInputNumberHandler(Dictionary<string, object> args, List<double> results)
{
string input = await Console.In.ReadLineAsync() ?? "";
if (input.Trim().Equals("!c", StringComparison.CurrentCultureIgnoreCase))
{
args["cancel"] = true;
}
if (double.TryParse(input.Trim(), out double result))
{
results.Add(result);
}
}
}
/// <summary>
/// 仅本地测试原型用,实际使用时需替换为网络层
/// </summary>
public class RogueLikeDispatcher
{
public RogueLike? RogueLikeInstance { get; set; } = null;
public RogueLikeServer? RogueLikeServer { get; set; } = null;
public bool Running => RogueLikeInstance?.Running ?? false;
public void WriteLine(string str)
{
if (RogueLikeInstance is null) return;
RogueLikeInstance.WriteLine(str);
}
public async Task<List<string>> GetChoiceResultsAsync(InquiryOptions options, Dictionary<string, object> args)
{
if (RogueLikeInstance is null) return [];
return await RogueLikeInstance.GetChoiceResultsAsync(options, args);
}
public async Task<List<double>> GetNumberResultsAsync(Dictionary<string, object> args)
{
if (RogueLikeInstance is null) return [];
return await RogueLikeInstance.GetNumberResultsAsync(args);
}
public async Task<string> GetTextResultsAsync(Dictionary<string, object> args)
{
if (RogueLikeInstance is null) return "";
return await RogueLikeInstance.GetTextResultsAsync(args);
}
public async Task<InquiryResponse> GetInGameResponse(InquiryOptions options)
{
if (RogueLikeInstance is null) return new(options);
return await RogueLikeInstance.GetInGameResponse(options);
}
public async Task DataRequestComplete(Guid guid, DataRequestArgs response)
{
if (RogueLikeInstance is null) return;
await RogueLikeInstance.DataRequestComplete(guid, response);
}
public async Task DataRequest(Guid guid, DataRequestArgs args)
{
if (RogueLikeServer is null) return;
RogueLikeServer.ReceiveDataRequest(guid, args);
await Task.CompletedTask;
}
public void StartServer()
{
RogueLikeServer?.Guard ??= Task.Run(RogueLikeServer.DataRequestGuard);
}
public async Task CreateGameLoop(string username)
{
if (RogueLikeServer is null) return;
await RogueLikeServer.CreateGameLoop(username);
}
}
public class RogueLikeGameData(Character character)
{
public RogueState RogueState { get; set; } = RogueState.Init;
public Character Character { get; set; } = character;
public int Chapter { get; set; } = 1;
public OshimaRegion? CurrentRegion { get; set; } = null;
public string CurrentArea { get; set; } = "";
public int RoomId { get; set; } = -1;
public OshimaRegion? Chapter1Region { get; set; } = null;
public OshimaRegion? Chapter2Region { get; set; } = null;
public OshimaRegion? Chapter3Region { get; set; } = null;
}
public class DataRequestArgs(string type)
{
public string RequestType { get; } = type;
public bool Success { get; set; } = true;
public Dictionary<string, object> Data { get; set; } = [];
}
}