diff --git a/.github/workflows/dev.yml b/.github/workflows/dev.yml
index 4d075e0..21f12f6 100644
--- a/.github/workflows/dev.yml
+++ b/.github/workflows/dev.yml
@@ -19,22 +19,17 @@ jobs:
- name: Setup Dotnet
uses: actions/setup-dotnet@v4
- - name: Setup NuGet
- uses: NuGet/setup-nuget@v2
-
- - name: Restore NuGet packages
- run: nuget restore YongAnFrame.sln
-
- name: Restore dependencies
run: dotnet restore
- name: Build
- run: dotnet build --no-restore
+ run: dotnet build --configuration Release
- name: Generate NuGet packages
- run: nuget pack
+ run: dotnet pack
- name: Upload NuGet package
uses: actions/upload-artifact@v4
with:
+ name: nupkg
path: YongAnFrame.*.nupkg
diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml
index 36f6bdd..915d168 100644
--- a/.github/workflows/docs.yml
+++ b/.github/workflows/docs.yml
@@ -1,5 +1,5 @@
# Simple workflow for deploying static content to GitHub Pages
-name: Build and Publish Docfx Pages
+name: Build and Publish Master Docfx Pages
on:
# Runs on pushes targeting the default branch
@@ -34,12 +34,6 @@ jobs:
- name: Dotnet Setup
uses: actions/setup-dotnet@v4
- - name: Setup NuGet
- uses: NuGet/setup-nuget@v2
-
- - name: Restore NuGet packages
- run: nuget restore YongAnFrame.sln
-
- run: dotnet tool update -g docfx
- run: docfx docs/docfx.json
diff --git a/.github/workflows/master.yml b/.github/workflows/master.yml
index b911f62..ee5ae43 100644
--- a/.github/workflows/master.yml
+++ b/.github/workflows/master.yml
@@ -18,24 +18,19 @@ jobs:
- name: Setup Dotnet
uses: actions/setup-dotnet@v4
-
- - name: Setup NuGet
- uses: NuGet/setup-nuget@v2
-
- - name: Restore NuGet packages
- run: nuget restore YongAnFrame.sln
- name: Restore dependencies
run: dotnet restore
- name: Build
- run: dotnet build --no-restore
+ run: dotnet build --configuration Release
- name: Generate NuGet packages
- run: nuget pack
+ run: dotnet pack
- name: Upload NuGet package
uses: actions/upload-artifact@v4
with:
+ name: nupkg
path: YongAnFrame.*.nupkg
diff --git a/.gitignore b/.gitignore
index 9c2122d..9491a2f 100644
--- a/.gitignore
+++ b/.gitignore
@@ -360,5 +360,4 @@ MigrationBackup/
.ionide/
# Fody - auto-generated XML schema
-FodyWeavers.xsd
-/nuget.exe
+FodyWeavers.xsd
\ No newline at end of file
diff --git a/Commands/ExpCommand.cs b/Commands/ExpCommand.cs
index d6329f4..419ec5a 100644
--- a/Commands/ExpCommand.cs
+++ b/Commands/ExpCommand.cs
@@ -2,19 +2,36 @@
using Exiled.API.Features;
using Exiled.Permissions.Extensions;
using System;
-using System.Linq;
+using YongAnFrame.Extensions;
namespace YongAnFrame.Commands
{
+ ///
+ /// 玩家经验指令
+ ///
[CommandHandler(typeof(RemoteAdminCommandHandler))]
public class ExpCommand : ICommand
{
- public string Command => "pexperience";
-
- public string[] Aliases => ["pexp"];
-
+ ///
+ /// 主要指令名
+ ///
+ public string Command => "PlayerExp";
+ ///
+ /// 次要指令名
+ ///
+ public string[] Aliases => ["pexp","pe"];
+ ///
+ /// 指令描述
+ ///
public string Description => "用于经验的设置";
+ ///
+ /// 指令逻辑
+ ///
+ /// 指令集
+ /// 发送者
+ /// 原因
+ /// 是否运行成功
public bool Execute(ArraySegment arguments, ICommandSender sender, out string response)
{
response = "NO";
diff --git a/Commands/MessageCommand.cs b/Commands/MessageCommand.cs
index 5b1ec40..1d50525 100644
--- a/Commands/MessageCommand.cs
+++ b/Commands/MessageCommand.cs
@@ -3,7 +3,11 @@
using Exiled.Permissions.Extensions;
using System;
using System.Collections.Generic;
-using YongAnFrame.Players;
+using System.Runtime.ConstrainedExecution;
+using YongAnFrame.Extensions;
+using YongAnFrame.Features.Players;
+using YongAnFrame.Features.UI.Enums;
+using YongAnFrame.Features.UI.Texts;
namespace YongAnFrame.Commands
{
@@ -13,12 +17,13 @@ namespace YongAnFrame.Commands
[CommandHandler(typeof(RemoteAdminCommandHandler))]
public sealed class MessageCommand : ICommand
{
+ ///
public string Command => "message";
-
- public string[] Aliases => ["mes", "msg"];
-
+ ///
+ public string[] Aliases => ["m", "msg"];
+ ///
public string Description => "用于发送消息";
-
+ ///
public bool Execute(ArraySegment arguments, ICommandSender sender, out string response)
{
List choicePlayer = [];
@@ -52,11 +57,7 @@ public bool Execute(ArraySegment arguments, ICommandSender sender, out s
if (int.TryParse(idString, out int id))
{
- FramePlayer yPlayer = FramePlayer.Get(id);
- if (yPlayer != null)
- {
- choicePlayer.Add(yPlayer);
- }
+ choicePlayer.Add(FramePlayer.Get(id));
}
break;
}
@@ -79,7 +80,7 @@ public bool Execute(ArraySegment arguments, ICommandSender sender, out s
foreach (FramePlayer yPlayer in choicePlayer)
{
- yPlayer.HintManager.MessageTexts.Add(new HintManager.Text($"[管理员发送]{arguments.Array[2]}", duration));
+ yPlayer.UI.MessageList.Add(new MessageText($"{arguments.Array[2]}", duration, MessageType.Admin));
}
response = "已成功运行";
return true;
diff --git a/Commands/PlayerCommand.cs b/Commands/PlayerCommand.cs
index 1823024..ff86f0b 100644
--- a/Commands/PlayerCommand.cs
+++ b/Commands/PlayerCommand.cs
@@ -1,33 +1,39 @@
using CommandSystem;
using Exiled.API.Features;
using System;
-using YongAnFrame.Players;
+using YongAnFrame.Extensions;
+using YongAnFrame.Features.Players;
namespace YongAnFrame.Commands
{
+ ///
+ /// 框架玩家指令
+ ///
[CommandHandler(typeof(ClientCommandHandler))]
public class PlayerCommand : ICommand
{
- public string Command => "hPlayer";
-
- public string[] Aliases => ["hPlay", "hp", "h"];
-
+ ///
+ public string Command => "FramePlayer";
+ ///
+ public string[] Aliases => ["player", "fp" ,"p"];
+ ///
public string Description => "用于管理自己的YongAnFrame用户";
-
+ ///
public bool Execute(ArraySegment arguments, ICommandSender sender, out string response)
{
response = "NULL";
if (arguments.Count >= 1 && Player.TryGet(sender, out Player player))
{
- FramePlayer fPlayer = FramePlayer.Get(player);
+ //FramePlayer fPlayer = player.ToFPlayer();
switch (arguments.Array[1])
{
case "BDNT":
- fPlayer.HintManager.Clean();
- fPlayer.ExPlayer.ShowHint($"{YongAnFramePlugin.Instance.Translation.BypassDoNotTrack.Split('\n')}", 10000f);
+ // 等待重置
+ //fPlayer.HintManager.Clean();
+ //fPlayer.ExPlayer.ShowHint($"{YongAnFramePlugin.Instance.Translation.BypassDoNotTrack.Split('\n')}", 10000f);
return true;
case "INFO":
-
+
return true;
}
}
diff --git a/Commands/SkillCommand.cs b/Commands/SkillCommand.cs
index c7d95a1..a45f45e 100644
--- a/Commands/SkillCommand.cs
+++ b/Commands/SkillCommand.cs
@@ -1,37 +1,57 @@
using CommandSystem;
using Exiled.API.Features;
using System;
-using YongAnFrame.Players;
-using YongAnFrame.Roles;
-using YongAnFrame.Roles.Properties;
+using YongAnFrame.Extensions;
+using YongAnFrame.Features.Players;
+using YongAnFrame.Features.Roles;
+using YongAnFrame.Features.UI.Enums;
+using YongAnFrame.Features.UI.Texts;
namespace YongAnFrame.Commands
{
///
- /// 未完成请勿乱用
+ /// 技能指令
///
[CommandHandler(typeof(ClientCommandHandler))]
public sealed class SkillsCommand : ICommand
{
+ ///
public string Command => "skills";
-
+ ///
public string[] Aliases => ["sk"];
-
+ ///
public string Description => "skills";
-
+ ///
public bool Execute(ArraySegment arguments, ICommandSender sender, out string response)
{
response = "NO";
if (arguments.Count >= 1 && int.TryParse(arguments.Array[1], out int num) && Player.TryGet(sender, out Player player))
{
- FramePlayer fPlayer = FramePlayer.Get(player);
-
- if (fPlayer.CustomRolePlus != null && fPlayer.CustomRolePlus.Check(fPlayer, out CustomRolePlusProperties data))
+ FramePlayer fPlayer = player.ToFPlayer();
+
+ if (fPlayer.CustomRolePlus is not null && fPlayer.CustomRolePlus.Check(fPlayer, out CustomRolePlusData data))
{
- SkillManager skillManager = data.SkillManagers[num];
- skillManager.Run();
- fPlayer.HintManager.MessageTexts.Add(new HintManager.Text($"技能[{skillManager.SkillProperties.Name}:{fPlayer.CustomRolePlus.GetType().GUID.ToString() + 10000}]已经发动,持续时间:{skillManager.SkillProperties.ActiveMaxTime}", skillManager.SkillProperties.ActiveMaxTime));
+ if (data.Skills == null)
+ {
+ response = "角色没有技能";
+ return false;
+ }
+
+ Skill skill = data.Skills[num];
+ if (skill.IsActive)
+ {
+ fPlayer.UI.MessageList.Add(new MessageText("技能正在持续", 5, MessageType.System));
+ }
+ else if (skill.IsBurial)
+ {
+ fPlayer.UI.MessageList.Add(new MessageText($"技能正在冷却(CD:{skill.BurialRemainingTime})", 5, MessageType.System));
+ }
+ else
+ {
+ skill.Run();
+ }
+
response = "OK";
return true;
}
diff --git a/Components/CapacityList.cs b/Components/CapacityList.cs
index ef1dff3..af58cc3 100644
--- a/Components/CapacityList.cs
+++ b/Components/CapacityList.cs
@@ -4,25 +4,32 @@
namespace YongAnFrame.Components
{
- public class CapacityList(int capacity) : ICollection, IEnumerable, IEnumerable
+ ///
+ /// 容量列表
+ ///
+ /// 存储
+ /// 容量
+ /// 修改委托
+ public class CapacityList(int capacity, Action? modify = null) : IList, ICollection, IEnumerable, IEnumerable
{
private readonly List list = new(capacity);
+ private readonly Action? modify = modify;
- public int Capacity { get; set; } = capacity;
-
+ ///
+ /// 获取容量
+ ///
+ public int Capacity { get; } = capacity;
+ ///
public int Count => list.Count;
-
+ ///
public bool IsReadOnly => false;
+ ///
public T this[int index]
{
get
{
- if (Count > index)
- {
- return list[index];
- }
- return default;
+ return list[index];
}
set
{
@@ -30,6 +37,7 @@ public T this[int index]
}
}
+ ///
public void Add(T item)
{
if (Capacity > list.Count)
@@ -41,46 +49,51 @@ public void Add(T item)
list.RemoveAt(0);
list.Add(item);
}
+ modify?.Invoke();
}
+ ///
public bool Remove(T item)
{
- return list.Remove(item);
+ bool v = list.Remove(item);
+ modify?.Invoke();
+ return v;
}
- public IEnumerator GetEnumerator()
- {
- return list.GetEnumerator();
- }
+ ///
+ IEnumerator IEnumerable.GetEnumerator() => list.GetEnumerator();
- IEnumerator IEnumerable.GetEnumerator()
- {
- return list.GetEnumerator();
- }
+ ///
+ public IEnumerator GetEnumerator() => list.GetEnumerator();
+ ///
public void Clear()
{
list.Clear();
+ modify?.Invoke();
}
- public bool Contains(T item)
- {
- return list.Contains(item);
- }
+ ///
+ public bool Contains(T item) => list.Contains(item);
- public void CopyTo(T[] array, int arrayIndex)
- {
- list.CopyTo(array, arrayIndex);
- }
+ ///
+ public void CopyTo(T[] array, int arrayIndex) => list.CopyTo(array, arrayIndex);
- public int IndexOf(T item)
- {
- return list.IndexOf(item);
- }
+ ///
+ public int IndexOf(T item) => list.IndexOf(item);
+ ///
public void RemoveAt(int index)
{
list.RemoveAt(index);
+ modify?.Invoke();
+ }
+
+ ///
+ public void Insert(int index, T item)
+ {
+ list.Insert(index, item);
+ modify?.Invoke();
}
}
}
diff --git a/Config.cs b/Config.cs
index bee8590..295a27b 100644
--- a/Config.cs
+++ b/Config.cs
@@ -1,10 +1,12 @@
using Exiled.API.Interfaces;
+using System.Collections.Generic;
using System.ComponentModel;
+using YongAnFrame.Features.Roles;
namespace YongAnFrame
{
///
- /// 插件的配置
+ /// 的配置
///
public sealed class Config : IConfig
{
@@ -13,10 +15,18 @@ public sealed class Config : IConfig
///
public bool Debug { get; set; }
///
- /// 全局的经验加成
+ /// 获取或设置全局的经验加成
///
[Description("全局的经验加成")]
public float GlobalExpMultiplier { get; set; } = 1;
-
+
+ ///
+ /// 获取或设置禁用自定义角色生成
+ ///
+ ///
+ /// 只能用于继承的类
+ ///
+ [Description("禁用自定义角色生成(只能用于继承CustomRolePlus的类)")]
+ public List DisableCustomRolePlus { get; set; } = ["114514", "1"];
}
}
diff --git a/Events/EventArgs/FramePlayer/FramePlayerCreatedEventArgs.cs b/Events/EventArgs/FramePlayer/FramePlayerCreatedEventArgs.cs
index 1132d5f..1f9275f 100644
--- a/Events/EventArgs/FramePlayer/FramePlayerCreatedEventArgs.cs
+++ b/Events/EventArgs/FramePlayer/FramePlayerCreatedEventArgs.cs
@@ -5,8 +5,11 @@ namespace YongAnFrame.Events.EventArgs.FramePlayer
///
/// FramePlayer被创建时的事件数据
///
- public sealed class FramePlayerCreatedEventArgs(Players.FramePlayer fPlayer) : IExiledEvent
+ public sealed class FramePlayerCreatedEventArgs(Features.Players.FramePlayer fPlayer) : IExiledEvent
{
- public Players.FramePlayer FPlayer { get; } = fPlayer;
+ ///
+ /// 获取框架玩家
+ ///
+ public Features.Players.FramePlayer FPlayer { get; } = fPlayer;
}
}
diff --git a/Events/EventArgs/FramePlayer/FramePlayerInvalidingEventArgs.cs b/Events/EventArgs/FramePlayer/FramePlayerInvalidingEventArgs.cs
index bff6f88..c626bdf 100644
--- a/Events/EventArgs/FramePlayer/FramePlayerInvalidingEventArgs.cs
+++ b/Events/EventArgs/FramePlayer/FramePlayerInvalidingEventArgs.cs
@@ -5,8 +5,11 @@ namespace YongAnFrame.Events.EventArgs.FramePlayer
///
/// FramePlayer被无效时的事件数据
///
- public sealed class FramePlayerInvalidatingEventArgs(Players.FramePlayer fPlayer) : IExiledEvent
+ public sealed class FramePlayerInvalidatingEventArgs(Features.Players.FramePlayer fPlayer) : IExiledEvent
{
- public Players.FramePlayer FPlayer { get; } = fPlayer;
+ ///
+ /// 获取框架玩家
+ ///
+ public Features.Players.FramePlayer FPlayer { get; } = fPlayer;
}
}
diff --git a/Events/Handlers/FramePlayer.cs b/Events/Handlers/FramePlayer.cs
index 80845a7..3a721d0 100644
--- a/Events/Handlers/FramePlayer.cs
+++ b/Events/Handlers/FramePlayer.cs
@@ -3,32 +3,36 @@
namespace YongAnFrame.Events.Handlers
{
+ ///
+ /// 框架玩家事件处理器
+ ///
public sealed class FramePlayer
{
///
- /// FramePlayer被创建时的事件
+ /// 被创建时事件
///
public static Event FramePlayerCreated { get; set; } = new Event();
///
- /// FramePlayer被无效时的事件
+ /// 被无效时事件
///
public static Event FramePlayerInvalidating { get; set; } = new Event();
///
- /// FramePlayer提示刷新前的事件
+ /// 提示刷新前事件
///
public static Event FramePlayerHintUpdate { get; set; } = new Event();
-
- public static void OnFramePlayerCreated(FramePlayerCreatedEventArgs args)
- {
- FramePlayerCreated.InvokeSafely(args);
- }
- public static void OnFramerHintUpdate()
- {
- FramePlayerHintUpdate.InvokeSafely();
- }
- public static void OnFramePlayerInvalidating(FramePlayerInvalidatingEventArgs args)
- {
- FramePlayerInvalidating.InvokeSafely(args);
- }
+ ///
+ /// 触发被创建时事件
+ ///
+ /// 事件数据
+ public static void OnFramePlayerCreated(FramePlayerCreatedEventArgs args) => FramePlayerCreated.InvokeSafely(args);
+ ///
+ /// 提示刷新前事件
+ ///
+ public static void OnFramerHintUpdate() => FramePlayerHintUpdate.InvokeSafely();
+ ///
+ /// 提示刷新前事件
+ ///
+ /// 事件数据
+ public static void OnFramePlayerInvalidating(FramePlayerInvalidatingEventArgs args) => FramePlayerInvalidating.InvokeSafely(args);
}
}
diff --git a/YongAnTool.cs b/Extensions/YongAnExtension.cs
similarity index 57%
rename from YongAnTool.cs
rename to Extensions/YongAnExtension.cs
index afcbd38..d50b238 100644
--- a/YongAnTool.cs
+++ b/Extensions/YongAnExtension.cs
@@ -1,17 +1,13 @@
using Exiled.API.Features;
-using Respawning;
using System;
-using System.Collections.Generic;
-using System.Reflection;
-using YongAnFrame.Players;
-using YongAnFrame.Roles.Enums;
+using YongAnFrame.Features.Players;
-namespace YongAnFrame
+namespace YongAnFrame.Extensions
{
///
- /// 扩展方法工具类
+ /// 扩展方法通用工具类
///
- public static class YongAnTool
+ public static class YongAnExtension
{
///
/// 作为种子取随机数
@@ -20,19 +16,13 @@ public static class YongAnTool
/// 最小值
/// 最大值
///
- public static int StrictNext(this Random r, int min, int max)
- {
- return new Random(BitConverter.ToInt32(Guid.NewGuid().ToByteArray(), 0)).Next(min, max);
- }
+ public static int StrictNext(this Random r, int min, int max) => new Random(BitConverter.ToInt32(Guid.NewGuid().ToByteArray(), 0)).Next(min, max);
///
/// 转换为
///
///
///
- public static FramePlayer ToFPlayer(this Player p)
- {
- return FramePlayer.Get(p);
- }
+ public static FramePlayer ToFPlayer(this Player? p) => FramePlayer.Get(p);
}
}
diff --git a/Features/MusicManager.cs b/Features/MusicManager.cs
new file mode 100644
index 0000000..92ae08e
--- /dev/null
+++ b/Features/MusicManager.cs
@@ -0,0 +1,210 @@
+
+using AudioApi;
+using AudioApi.EventArgs.Voice;
+using Exiled.API.Features;
+using Exiled.API.Features.Components;
+using Mirror;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using UnityEngine;
+using YongAnFrame.Features.Players;
+using static YongAnFrame.Features.TrackEvent;
+
+namespace YongAnFrame.Features
+{
+ ///
+ /// 音乐管理器
+ ///
+ public static class MusicManager
+ {
+ private static uint num = 0;
+ ///
+ /// 获取放音频的玩家(NPC)
+ ///
+ public static Dictionary MusicNpc { get; } = [];
+ private static readonly Dictionary trackEventDic = [];
+
+ static MusicManager()
+ {
+ VoicePlayerBase.OnTrackLoaded += TrackLoaded;
+ VoicePlayerBase.OnFinishedTrack += FinishedTrack;
+ }
+
+ private static void TrackLoaded(TrackLoadedEventArgs args)
+ {
+ if (trackEventDic.TryGetValue(args.VoicePlayerBase, out TrackEvent trackEvent))
+ {
+ trackEvent.PlayMusicAction?.Invoke(args);
+ }
+ }
+
+ private static void FinishedTrack(TrackFinishedEventArgs args)
+ {
+ if (trackEventDic.TryGetValue(args.VoicePlayerBase, out TrackEvent trackEvent))
+ {
+ trackEvent.StopMusicAction?.Invoke(args);
+ }
+
+ }
+
+ private static ReferenceHub CreateMusicNpc(string name)
+ {
+ var newNpc = UnityEngine.Object.Instantiate(NetworkManager.singleton.playerPrefab);
+ ReferenceHub hubNpc = newNpc.GetComponent();
+ NetworkServer.AddPlayerForConnection(new FakeConnection(0), newNpc);
+ hubNpc.nicknameSync.Network_myNickSync = name;
+ hubNpc.authManager.NetworkSyncedUserId = null;
+ MusicNpc.Add($"{num++}:{name}", hubNpc);
+ return hubNpc;
+ }
+
+ private static void KillMusicNpc(VoicePlayerBase playerBase)
+ {
+ if (playerBase is null) return;
+ ReferenceHub npc = playerBase.Owner;
+ MusicNpc.Remove(npc.nicknameSync.Network_myNickSync);
+ CustomNetworkManager.TypedSingleton.OnServerDisconnect(npc.connectionToClient);
+ Player.Dictionary.Remove(npc.gameObject);
+ UnityEngine.Object.Destroy(npc.gameObject);
+ }
+
+ ///
+ /// 立刻停止播放音频
+ ///
+ /// voicePlayerBase
+ public static void Stop(VoicePlayerBase playerBase)
+ {
+ if (playerBase is null) return;
+ playerBase.Stoptrack(true);
+ KillMusicNpc(playerBase);
+ }
+ ///
+ /// 向所有玩家播放音频
+ ///
+ /// 音频文件
+ /// NPC名称
+ ///
+ public static VoicePlayerBase Play(string musicName, string npcName) => Play(musicName, npcName, -1);
+ ///
+ /// 向玩家()播放音频
+ ///
+ /// 音频文件/Url
+ /// NPC名称
+ /// 传播距离检测源头玩家(可null,null时是NPC)
+ ///
+ public static VoicePlayerBase Play(string musicName, string npcName, FramePlayer source) => Play(musicName, npcName, source, 0);
+ ///
+ /// NPC在米内向玩家播放音频
+ ///
+ /// 音频文件/Url
+ /// NPC名称
+ /// 传播距离(-1时是全部玩家,0时是源头玩家)
+ ///
+ public static VoicePlayerBase Play(string musicName, string npcName, float distance) => Play(musicName, npcName, null, distance);
+ ///
+ /// 在米内向玩家播放音频
+ ///
+ /// 音频文件/Url
+ /// NPC名称
+ /// 传播距离检测源头玩家(可null,null时是NPC)
+ /// 传播距离(-1时是全部玩家,0时是源头玩家)
+ ///
+ public static VoicePlayerBase Play(string musicName, string npcName, FramePlayer? source, float distance) => Play(musicName, npcName, null, source, distance, null, 80, false);
+ ///
+ /// 播放音频
+ ///
+ /// 音频文件/Url
+ /// NPC名称
+ /// 播放事件(可null)
+ /// 传播距离检测源头玩家(可null,null时是NPC)
+ /// 传播距离(-1时是全部玩家,0时是源头玩家)
+ /// 额外可接收音频的玩家(可null)
+ /// 音量大小
+ /// 是否循环
+ ///
+ public static VoicePlayerBase Play(string musicName, string npcName, TrackEvent? trackEvent, FramePlayer? source, float distance, FramePlayer[]? extraPlay, float volume = 80, bool isLoop = false)
+ {
+ VoicePlayerBase? voicePlayerBase = null;
+ ReferenceHub npc = CreateMusicNpc(npcName);
+ voicePlayerBase = VoicePlayerBase.Get(npc);
+
+ try
+ {
+ if (trackEvent is not null)
+ {
+ if (trackEventDic.ContainsKey(voicePlayerBase))
+ {
+ trackEventDic[voicePlayerBase] = (TrackEvent)trackEvent;
+ }
+ else
+ {
+ trackEventDic.Add(voicePlayerBase, (TrackEvent)trackEvent);
+ }
+ }
+
+ if (distance != -1)
+ {
+ if (source is not null)
+ {
+ if (distance == 0)
+ {
+ voicePlayerBase.BroadcastTo.Add(npc.PlayerId);
+ }
+ else
+ {
+ voicePlayerBase.BroadcastTo = [.. FramePlayer.List.Where(p => Vector3.Distance(p.ExPlayer!.Position, source.ExPlayer!.Position) <= distance).Select((s) => s.ExPlayer!.Id)];
+ }
+ }
+
+ if (extraPlay is not null)
+ {
+ foreach (var player in extraPlay)
+ {
+ if (!voicePlayerBase.BroadcastTo.Contains(player.ExPlayer.Id))
+ {
+ voicePlayerBase.BroadcastTo.Add(player.ExPlayer.Id);
+ }
+ }
+ }
+ }
+
+ voicePlayerBase.CurrentPlay = $"{PathManager.Music}/{musicName}.ogg";
+ voicePlayerBase.Volume = volume;
+ voicePlayerBase.Loop = isLoop;
+ voicePlayerBase.Play(-1);
+ }
+ catch (Exception)
+ {
+ Stop(voicePlayerBase);
+ }
+ return voicePlayerBase;
+ }
+ }
+ ///
+ /// 音轨事件
+ ///
+ /// 播放音频委托
+ /// 停止音频委托
+ public readonly struct TrackEvent(PlayMusic? playMusic, StopMusic? stopMusic)
+ {
+ ///
+ /// 播放音频
+ ///
+ ///
+ public delegate void PlayMusic(TrackLoadedEventArgs args);
+ ///
+ /// 停止音频
+ ///
+ ///
+ public delegate void StopMusic(TrackFinishedEventArgs args);
+ ///
+ /// 获取播放音频委托
+ ///
+ public PlayMusic? PlayMusicAction { get; } = playMusic;
+ ///
+ /// 获取停止音频委托
+ ///
+ public StopMusic? StopMusicAction { get; } = stopMusic;
+ }
+}
\ No newline at end of file
diff --git a/Features/PathManager.cs b/Features/PathManager.cs
new file mode 100644
index 0000000..ed0d828
--- /dev/null
+++ b/Features/PathManager.cs
@@ -0,0 +1,35 @@
+using Exiled.API.Features;
+using System.IO;
+
+namespace YongAnFrame.Features
+{
+ ///
+ /// IO路径管理器
+ ///
+ public static class PathManager
+ {
+ ///
+ /// 获取音频路径
+ ///
+ public static string Music => $"{Paths.Exiled}/YongAnFrame/{Server.Port}/Music";
+ ///
+ /// 获取日志路径
+ ///
+ public static string Log => $"{Paths.Exiled}/YongAnFrame/{Server.Port}/Log";
+
+ ///
+ /// 检查路径是否存在
+ ///
+ public static void CheckPath()
+ {
+ if (!Directory.Exists(Music))
+ {
+ Directory.CreateDirectory(Music);
+ }
+ if (!Directory.Exists(Log))
+ {
+ Directory.CreateDirectory(Log);
+ }
+ }
+ }
+}
diff --git a/Features/PlayerUI.cs b/Features/PlayerUI.cs
new file mode 100644
index 0000000..0ae1bfb
--- /dev/null
+++ b/Features/PlayerUI.cs
@@ -0,0 +1,180 @@
+using HintServiceMeow.Core.Enum;
+using HintServiceMeow.Core.Models.Hints;
+using HintServiceMeow.Core.Utilities;
+using MEC;
+using System.Collections.Generic;
+using System.Text;
+using YongAnFrame.Components;
+using YongAnFrame.Features.Players;
+using YongAnFrame.Features.Roles;
+using YongAnFrame.Features.UI.Texts;
+
+namespace YongAnFrame.Features
+{
+ ///
+ /// 的UI
+ ///
+ public class PlayerUI
+ {
+ ///
+ /// 获取拥有该实例的
+ ///
+ public FramePlayer FPlayer { get; }
+ ///
+ /// 获取的HintServiceMeow核心
+ ///
+ public PlayerDisplay PlayerDisplay { get; private set; }
+ ///
+ /// 获取消息数据列表
+ ///
+ public CapacityList MessageList { get; }
+ ///
+ /// 获取聊天数据列表
+ ///
+ public CapacityList ChatList { get; }
+
+ private readonly CoroutineHandle coroutine;
+ #region Hint
+ private readonly Hint versionHint = new()
+ {
+ Text = "YongAnFrame 1.0.0-beta6+002",
+ FontSize = 20,
+ Alignment = HintAlignment.Center,
+ YCoordinateAlign = HintVerticalAlign.Top,
+ YCoordinate = 0
+ };
+ private readonly Hint customRoleHint = new()
+ {
+ FontSize = 20,
+ Alignment = HintAlignment.Center,
+ YCoordinateAlign = HintVerticalAlign.Bottom,
+ YCoordinate = 1080
+ };
+ private readonly Hint chatHint = new()
+ {
+ FontSize = 20,
+ Alignment = HintAlignment.Right,
+ YCoordinate = 400
+ };
+ private readonly Hint messageHint = new()
+ {
+ FontSize = 20,
+ Alignment = HintAlignment.Left,
+ YCoordinate = 400
+ };
+ #endregion
+
+ private IEnumerator Timer()
+ {
+ while (true)
+ {
+
+
+ for (int i = 0; i < MessageList.Count; i++)
+ {
+ MessageText message = MessageList[i];
+ if (message.Duration-- <= 0)
+ {
+ MessageList.Remove(message);
+ i--;
+ }
+ UpdateMessageUI();
+ }
+
+ bool isUpdate = false;
+
+ for (int i = 0; i < ChatList.Count; i++)
+ {
+ ChatText chat = ChatList[i];
+ if (chat.Duration-- <= 0)
+ {
+ ChatList.Remove(chat);
+ i--;
+ isUpdate = true;
+ }
+ }
+ if (isUpdate) UpdateChatUI();
+
+ yield return Timing.WaitForSeconds(1f);
+ }
+ }
+
+ ///
+ /// 更新全部UI
+ ///
+ public void UpdateUI()
+ {
+ UpdateCustomRoleUI();
+ UpdateMessageUI();
+ UpdateChatUI();
+ }
+
+ ///
+ /// 更新自定义角色UI
+ ///
+ public void UpdateCustomRoleUI()
+ {
+ if (FPlayer.CustomRolePlus is null)
+ {
+ customRoleHint.Text = null;
+ return;
+ }
+ StringBuilder builder = new($"{FPlayer.CustomRolePlus.Name}\n\r{FPlayer.CustomRolePlus.Description}");
+ Skill[]? Skills = FPlayer.CustomRolePlus.BaseData[FPlayer].Skills;
+ if (Skills != null)
+ {
+ foreach (var skill in Skills)
+ {
+ builder.AppendLine($"{skill.Name}({skill.UseItem}):{skill.Description}(激活:{skill.ActiveMaxTime}|冷却:{skill.BurialMaxTime})");
+ }
+ }
+ customRoleHint.Text = builder.ToString();
+ }
+
+ ///
+ /// 更新消息UI
+ ///
+ public void UpdateMessageUI()
+ {
+ messageHint.Text = string.Join("\n\r", MessageList);
+ }
+ ///
+ /// 更新聊天UI
+ ///
+ public void UpdateChatUI()
+ {
+ chatHint.Text = string.Join("\n\r", ChatList);
+ }
+ ///
+ /// 清除全部UI
+ ///
+ public void Clean()
+ {
+ Timing.KillCoroutines(coroutine);
+ PlayerDisplay.ClearHint();
+ }
+ ///
+ /// 构造方法
+ ///
+ ///
+ public PlayerUI(FramePlayer fPlayer)
+ {
+ FPlayer = fPlayer;
+ MessageList = new(7, UpdateMessageUI);
+ ChatList = new(7, UpdateChatUI);
+ coroutine = Timing.RunCoroutine(Timer());
+ PlayerDisplay = PlayerDisplay.Get(fPlayer);
+ PlayerDisplay.AddHint(customRoleHint);
+ PlayerDisplay.AddHint(chatHint);
+ PlayerDisplay.AddHint(messageHint);
+ PlayerDisplay.AddHint(versionHint);
+ }
+ ///
+ /// 解构方法
+ ///
+ ~PlayerUI()
+ {
+ Clean();
+ }
+ }
+}
diff --git a/Features/Players/CustomPlayer.cs b/Features/Players/CustomPlayer.cs
new file mode 100644
index 0000000..627ba95
--- /dev/null
+++ b/Features/Players/CustomPlayer.cs
@@ -0,0 +1,59 @@
+using Exiled.API.Features;
+using YongAnFrame.Features.Players.Interfaces;
+using YongAnFrame.Features.Roles;
+
+namespace YongAnFrame.Features.Players
+{
+ ///
+ /// 自定义玩家
+ ///
+ ///
+ /// 如果有自定义玩家需要挂载到的需求,请继承从而自动处理挂载逻辑
+ ///
+ /// 但是目前暂无实质性功能
+ ///
+ /// 框架玩家
+ public abstract class CustomPlayer(FramePlayer player)
+ {
+ ///
+ /// 获取拥有该实例的
+ ///
+ public FramePlayer FramePlayer { get; private set; } = player;
+ ///
+ public Player ExPlayer => FramePlayer.ExPlayer;
+ ///
+ /// 获取是否无效
+ ///
+ public bool IsInvalid => FramePlayer is null;
+ ///
+ public CustomRolePlus? CustomRolePlus => FramePlayer.CustomRolePlus;
+ ///
+ public PlayerUI UI => FramePlayer.UI;
+ ///
+ public ICustomAlgorithm CustomAlgorithm { get => FramePlayer.CustomAlgorithm; set => FramePlayer.CustomAlgorithm = value; }
+ ///
+ public ulong Level { get => FramePlayer.Level; set => FramePlayer.Level = value; }
+ ///
+ public ulong Exp { get => FramePlayer.Exp; set => FramePlayer.Exp = value; }
+ ///
+ public float ExpMultiplier { get => FramePlayer.ExpMultiplier; set => FramePlayer.ExpMultiplier = value; }
+ ///
+ public bool IsBDNT { get => FramePlayer.IsBDNT; set => FramePlayer.IsBDNT = value; }
+ ///
+ public PlayerTitle? UsingTitles { get => FramePlayer.UsingTitles; set => FramePlayer.UsingTitles = value; }
+ ///
+ public PlayerTitle? UsingRankTitles { get => FramePlayer.UsingRankTitles; set => FramePlayer.UsingRankTitles = value; }
+ ///
+ public void AddExp(ulong exp, string name = "未知原因") => FramePlayer.AddExp(exp, name);
+ ///
+ public void UpdateShowInfo() => FramePlayer.UpdateShowInfo();
+ ///
+ public abstract void Invalid();
+
+ ///
+ /// 隐性转换
+ ///
+ /// 自定义玩家
+ public static implicit operator FramePlayer(CustomPlayer yPlayer) => yPlayer.FramePlayer;
+ }
+}
diff --git a/Players/FramePlayer.cs b/Features/Players/FramePlayer.cs
similarity index 68%
rename from Players/FramePlayer.cs
rename to Features/Players/FramePlayer.cs
index d07f347..c3d800f 100644
--- a/Players/FramePlayer.cs
+++ b/Features/Players/FramePlayer.cs
@@ -1,53 +1,73 @@
using Exiled.API.Features;
+using Exiled.CustomRoles;
using Exiled.CustomRoles.API;
+using Exiled.CustomRoles.API.Features;
using Exiled.Events.EventArgs.Player;
using Exiled.Events.Features;
using MEC;
using System;
using System.Collections.Generic;
+using System.Collections.ObjectModel;
using YongAnFrame.Events.EventArgs.FramePlayer;
-using YongAnFrame.Roles;
-using static YongAnFrame.Players.HintManager;
+using YongAnFrame.Extensions;
+using YongAnFrame.Features.Players.Interfaces;
+using YongAnFrame.Features.Roles;
+using YongAnFrame.Features.UI.Enums;
+using YongAnFrame.Features.UI.Texts;
-namespace YongAnFrame.Players
+namespace YongAnFrame.Features.Players
{
+ ///
+ /// 永安框架的玩家类
+ ///
public sealed class FramePlayer : ICustomAlgorithm
{
- private PlayerTitle usingTitles = null;
- private PlayerTitle usingRankTitles = null;
+ private PlayerTitle? usingTitles = null;
+ private PlayerTitle? usingRankTitles = null;
private static readonly Dictionary dictionary = [];
+ private Player? exPlayer;
///
- /// 获取拥有该实例的
+ /// 获取该实例拥有的
///
- public Player ExPlayer { get; private set; }
+ ///
+ /// 在运行事件后实例无效,再调用可能会引发异常
+ /// 玩家退出后必须不再引用,否则会造成数字ID重复的问题
+ ///
+ public Player ExPlayer
+ {
+ get
+ {
+ if (exPlayer is null)
+ {
+ throw new InvalidCastException("FramePlayer实例已无效");
+ }
+ return exPlayer;
+ }
+ }
///
/// 获取有效的框架玩家列表
///
public static IReadOnlyCollection List => [.. dictionary.Values];
///
- /// 获取的玩家是否有效
- ///
- public bool IsInvalid { get => ExPlayer == null; }
- ///
/// 获取玩家拥有的自定义角色
///
- public CustomRolePlus CustomRolePlus
+ public CustomRolePlus? CustomRolePlus
{
get
{
- if (ExPlayer.GetCustomRoles().Count != 0)
+ ReadOnlyCollection customRoleList = ExPlayer.GetCustomRoles();
+ if (customRoleList.Count != 0 && customRoleList[0] is CustomRolePlus custom)
{
- return (CustomRolePlus)ExPlayer.GetCustomRoles()[0];
+ return custom;
}
return null;
}
}
///
- /// 获取或设置玩家的提示系统管理器
+ /// 获取玩家的UI
///
- public HintManager HintManager { get; private set; }
-
+ public PlayerUI UI { get; private set; }
///
/// 获取或设置玩家正在使用的主要自定义算法
///
@@ -62,6 +82,10 @@ public CustomRolePlus CustomRolePlus
///
public ulong Exp { get; set; }
///
+ /// 获取全局的经验加成
+ ///
+ public float GlobalExpMultiplier => YongAnFramePlugin.Instance.Config.GlobalExpMultiplier;
+ ///
/// 获取或设置玩家的经验倍率
///
public float ExpMultiplier { get; set; }
@@ -72,17 +96,27 @@ public CustomRolePlus CustomRolePlus
///
/// 获取或设置玩家正在使用的名称称号
///
- public PlayerTitle UsingTitles { get => usingTitles; set { if (value != null && !value.IsRank) { usingTitles = value; } } }
+ public PlayerTitle? UsingTitles
+ {
+ get => usingTitles;
+ set
+ {
+ if (value is not null && !value.IsRank)
+ {
+ usingTitles = value;
+ }
+ }
+ }
///
/// 获取或设置玩家正在使用的地位称号
///
- public PlayerTitle UsingRankTitles
+ public PlayerTitle? UsingRankTitles
{
get => usingRankTitles;
set
{
- if (value != null && value.IsRank)
+ if (value is not null && value.IsRank)
{
usingRankTitles = value;
}
@@ -93,7 +127,7 @@ public PlayerTitle UsingRankTitles
///
/// 获取或设置玩家的地位名称。
///
- public string RankName
+ public string? RankName
{
get => ExPlayer.RankName;
set
@@ -107,9 +141,9 @@ public string RankName
///
/// 获取或设置玩家的地位颜色。
///
- public string RankColor
+ public string? RankColor
{
- get => ExPlayer.RankColor;
+ get => ExPlayer.RankColor;
set
{
if (RankColor != value)
@@ -135,23 +169,25 @@ public string CustomName
#endregion
#region Static
+ ///
+ /// 注册全局事件
+ ///
public static void SubscribeStaticEvents()
{
Exiled.Events.Handlers.Player.Verified += new CustomEventHandler(OnStaticVerified);
- //Exiled.Events.Handlers.Server.WaitingForPlayers += new CustomEventHandler(OnStaticWaitingForPlayers);
Exiled.Events.Handlers.Player.Destroying += new CustomEventHandler(OnStaticDestroying);
}
-
+ ///
+ /// 注销全局事件
+ ///
public static void UnsubscribeStaticEvents()
{
Exiled.Events.Handlers.Player.Verified += new CustomEventHandler(OnStaticVerified);
- //Exiled.Events.Handlers.Server.WaitingForPlayers += new CustomEventHandler(OnStaticWaitingForPlayers);
Exiled.Events.Handlers.Player.Destroying += new CustomEventHandler(OnStaticDestroying);
}
private static void OnStaticVerified(VerifiedEventArgs args)
{
- if (args.Player.IsNPC) return;
new FramePlayer(args.Player);
}
private static void OnStaticDestroying(DestroyingEventArgs args)
@@ -159,10 +195,6 @@ private static void OnStaticDestroying(DestroyingEventArgs args)
FramePlayer fPlayer = args.Player.ToFPlayer();
fPlayer.Invalid();
}
- //private static void OnStaticWaitingForPlayers()
- //{
- // dictionary.Clear();
- //}
#endregion
@@ -172,9 +204,9 @@ private static void OnStaticDestroying(DestroyingEventArgs args)
/// Exiled玩家
internal FramePlayer(Player player)
{
- ExPlayer = player;
+ exPlayer = player;
dictionary.Add(ExPlayer.Id, this);
- HintManager = new HintManager(this);
+ UI = new(this);
CustomAlgorithm = this;
Events.Handlers.FramePlayer.OnFramePlayerCreated(new FramePlayerCreatedEventArgs(this));
UpdateShowInfo();
@@ -187,12 +219,11 @@ internal FramePlayer(Player player)
/// 原因
public void AddExp(ulong exp, string name = "未知原因")
{
- float globalExpMultiplier = YongAnFramePlugin.Instance.Config.GlobalExpMultiplier;
- float expMultiplier = ExpMultiplier * globalExpMultiplier;
+ float expMultiplier = ExpMultiplier * GlobalExpMultiplier;
ulong addExp = (ulong)(exp * expMultiplier);
Exp += addExp;
- HintManager.MessageTexts.Add(new Text($"{name},获得{exp}+{addExp - exp}经验({expMultiplier}倍经验)", 5));
+ UI.MessageList.Add(new MessageText($"{name},获得{exp}+{addExp - exp}经验({expMultiplier}倍经验)", 5, MessageType.System));
ulong needExp = CustomAlgorithm.GetNeedUpLevel(Level);
ulong oldLevel = Level;
@@ -206,7 +237,7 @@ public void AddExp(ulong exp, string name = "未知原因")
if (oldLevel < Level)
{
UpdateShowInfo();
- HintManager.MessageTexts.Add(new Text($"恭喜你从{oldLevel}级到达{Level}级,距离下一级需要{Exp}/{needExp}经验", 8));
+ UI.MessageList.Add(new MessageText($"恭喜你从{oldLevel}级到达{Level}级,距离下一级需要{Exp}/{needExp}经验", 8, MessageType.System));
}
}
@@ -220,12 +251,10 @@ public void AddExp(ulong exp, string name = "未知原因")
///
public void UpdateShowInfo()
{
- if (ExPlayer.IsNPC) return;
-
- if (ExPlayer.GlobalBadge != null)
+ if (ExPlayer.GlobalBadge is not null)
{
CustomName = $"[LV:{Level}][全球徽章]{ExPlayer.Nickname}";
- if (CustomRolePlus != null)
+ if (CustomRolePlus is not null)
{
RankName = $"*{ExPlayer.GlobalBadge.Value.Text}* {CustomRolePlus.Name}";
}
@@ -237,18 +266,18 @@ public void UpdateShowInfo()
return;
}
- string rankColor = null;
- string rankName = null;
+ string? rankColor = null;
+ string? rankName = null;
- if (CustomRolePlus != null)
+ if (CustomRolePlus is not null)
{
rankName = CustomRolePlus.Name;
rankColor = CustomRolePlus.NameColor;
}
- if (usingTitles != null)
+ if (usingTitles is not null)
{
- if (usingTitles.DynamicCommand != null)
+ if (usingTitles.DynamicCommand is not null)
{
Timing.KillCoroutines(coroutines[1]);
coroutines[1] = Timing.RunCoroutine(DynamicTitlesShow());
@@ -267,16 +296,16 @@ public void UpdateShowInfo()
ExPlayer.CustomName = $"[LV:{Level}]{ExPlayer.Nickname}";
}
- if (usingRankTitles != null)
+ if (usingRankTitles is not null)
{
- if (usingRankTitles.DynamicCommand != null)
+ if (usingRankTitles.DynamicCommand is not null)
{
Timing.KillCoroutines(coroutines[0]);
coroutines[0] = Timing.RunCoroutine(DynamicRankTitlesShow());
}
else
{
- if (CustomRolePlus != null)
+ if (CustomRolePlus is not null)
{
rankName = $"{CustomRolePlus.Name} *{usingRankTitles.Name}*";
}
@@ -300,9 +329,13 @@ private IEnumerator DynamicRankTitlesShow()
{
while (true)
{
+ if (usingRankTitles is null || usingRankTitles.DynamicCommand is null)
+ {
+ yield break;
+ }
foreach (var command in usingRankTitles.DynamicCommand)
{
- if (CustomRolePlus != null)
+ if (CustomRolePlus is not null)
{
RankName = $"{CustomRolePlus.Name} *{command[0]}*";
}
@@ -310,7 +343,7 @@ private IEnumerator DynamicRankTitlesShow()
{
RankName = $"{command[0]}";
}
- if (usingRankTitles == null)
+ if (usingRankTitles is null)
{
RankColor = command[1];
}
@@ -322,10 +355,14 @@ private IEnumerator DynamicTitlesShow()
{
while (true)
{
+ if (usingTitles is null || usingTitles.DynamicCommand is null)
+ {
+ yield break;
+ }
foreach (var command in usingTitles.DynamicCommand)
{
CustomName = $"[LV:{Level}][{command[0]}]{ExPlayer.Nickname}";
- if (usingRankTitles == null)
+ if (usingRankTitles is null)
{
RankColor = command[1];
}
@@ -336,33 +373,32 @@ private IEnumerator DynamicTitlesShow()
#endregion
///
- public ulong GetNeedUpLevel(ulong level)
- {
- return (ulong)(100 + Math.Floor(level / 10f) * 100);
- }
+ public ulong GetNeedUpLevel(ulong level) => (ulong)(100 + Math.Floor(level / 10f) * 100);
///
/// 获取框架玩家
///
/// Exiled玩家
/// 框架玩家
- public static FramePlayer Get(Player player)
+ public static FramePlayer Get(Player? player)
{
- if (dictionary.TryGetValue(player.Id, out FramePlayer yPlayer))
+ if (player is null)
{
- return yPlayer;
+ throw new InvalidCastException("Player实例无效");
}
- return null;
+ if (!dictionary.TryGetValue(player.Id, out FramePlayer yPlayer))
+ {
+ throw new InvalidCastException("FramePlayer实例无效");
+ }
+ return yPlayer;
}
+
///
/// 获取框架玩家
///
/// 玩家数字ID
/// 框架玩家
- public static FramePlayer Get(int numId)
- {
- return Get(Player.Get(numId));
- }
+ public static FramePlayer Get(int numId) => Get(Player.Get(numId));
///
/// 调用后该实例会立刻无效
@@ -374,13 +410,14 @@ public void Invalid()
Events.Handlers.FramePlayer.OnFramePlayerInvalidating(new FramePlayerInvalidatingEventArgs(this));
CustomRolePlus?.RemoveRole(this);
dictionary.Remove(ExPlayer.Id);
- HintManager?.Clean();
- ExPlayer = null;
+ UI.Clean();
+ exPlayer = null;
}
- public static implicit operator Player(FramePlayer yPlayer)
- {
- return yPlayer.ExPlayer;
- }
+ ///
+ /// 隐性转换
+ ///
+ /// 框架玩家
+ public static implicit operator Player(FramePlayer yPlayer) => yPlayer.ExPlayer;
}
}
diff --git a/Players/ICustomAlgorithm.cs b/Features/Players/Interfaces/ICustomAlgorithm.cs
similarity index 86%
rename from Players/ICustomAlgorithm.cs
rename to Features/Players/Interfaces/ICustomAlgorithm.cs
index 9e7190b..38ab96f 100644
--- a/Players/ICustomAlgorithm.cs
+++ b/Features/Players/Interfaces/ICustomAlgorithm.cs
@@ -1,4 +1,4 @@
-namespace YongAnFrame.Players
+namespace YongAnFrame.Features.Players.Interfaces
{
///
/// 自定义算法接口
diff --git a/Features/Players/PlayerTitle.cs b/Features/Players/PlayerTitle.cs
new file mode 100644
index 0000000..652cee5
--- /dev/null
+++ b/Features/Players/PlayerTitle.cs
@@ -0,0 +1,99 @@
+using Exiled.API.Features;
+using System;
+using System.Collections.Generic;
+
+namespace YongAnFrame.Features.Players
+{
+ ///
+ /// 的称号
+ ///
+ public sealed class PlayerTitle
+ {
+ private static readonly Dictionary dictionary = [];
+ ///
+ /// 获取有效的玩家称号列表
+ ///
+ public static IReadOnlyCollection List => [.. dictionary.Values];
+ ///
+ /// 获取或设置加载称号委托
+ ///
+ public static Func? LoadFunc { get; set; }
+ ///
+ /// 获取或设置称号的ID
+ ///
+ public uint Id { get; set; }
+ ///
+ /// 获取或设置称号的名称
+ ///
+ public string Name { get; set; }
+ ///
+ /// 获取或设置称号的颜色
+ ///
+ public string Color { get; set; }
+ ///
+ /// 获取或设置称号是否为Rank
+ ///
+ public bool IsRank { get; set; }
+ ///
+ /// 获取称号的动态指令集
+ ///
+ public List? DynamicCommand { get; private set; }
+
+ ///
+ /// 构造方法
+ ///
+ /// ID
+ /// 名称
+ /// 颜色
+ /// 是否为Rank
+ /// 动态指令集
+ public PlayerTitle(uint id, string name, string color, bool isRank, string? dynamicCommandString = null)
+ {
+ Id = id;
+ Name = name;
+ Color = color;
+ IsRank = isRank;
+ SetDynamicCommand(dynamicCommandString);
+ }
+
+ ///
+ /// 设置称号的动态指令集
+ ///
+ ///
+ public void SetDynamicCommand(string? dynamicCommandString)
+ {
+ List? dynamicCommands = null;
+ if (!string.IsNullOrEmpty(dynamicCommandString))
+ {
+ dynamicCommands = [];
+ foreach (string dCommand in dynamicCommandString!.Split(';'))
+ {
+ dynamicCommands.Add(dCommand.Split(','));
+ }
+ }
+ DynamicCommand = dynamicCommands;
+ }
+
+ ///
+ /// 获取称号
+ ///
+ /// 称号ID
+ /// 获取的称号
+ public static PlayerTitle? Get(uint id)
+ {
+ if (LoadFunc is null)
+ {
+ Log.Error("称号功能无法在框架内获取,请设置PlayerTitle.LoadFunc属性或写个缓存");
+ return null;
+ }
+
+ if (dictionary.TryGetValue(id, out PlayerTitle? title))
+ {
+ return title;
+ }
+ title = LoadFunc.Invoke(id);
+ if (title != null) dictionary.Add(id, title);
+ return title;
+ }
+ }
+}
diff --git a/Roles/CustomRolePlus.cs b/Features/Roles/CustomRolePlus.cs
similarity index 66%
rename from Roles/CustomRolePlus.cs
rename to Features/Roles/CustomRolePlus.cs
index a1b809b..8001a56 100644
--- a/Roles/CustomRolePlus.cs
+++ b/Features/Roles/CustomRolePlus.cs
@@ -10,16 +10,20 @@
using PlayerRoles;
using System;
using System.Collections.Generic;
-using System.Reflection;
-using System.Runtime.InteropServices;
-using YongAnFrame.Players;
-using YongAnFrame.Roles.Enums;
-using YongAnFrame.Roles.Interfaces;
-using YongAnFrame.Roles.Properties;
+using System.Diagnostics.CodeAnalysis;
+using YongAnFrame.Extensions;
+using YongAnFrame.Features.Players;
+using YongAnFrame.Features.Roles.Enums;
+using YongAnFrame.Features.Roles.Interfaces;
+using YongAnFrame.Features.Roles.Properties;
+using YongAnFrame.Features.UI.Enums;
+using YongAnFrame.Features.UI.Texts;
-namespace YongAnFrame.Roles
+namespace YongAnFrame.Features.Roles
{
- [Guid("913613e0-c6e7-4511-a079-bacc7bc0089c")]
+ ///
+ /// 高级自定义角色
+ ///
public abstract class CustomRolePlus : CustomRole
{
///
@@ -27,6 +31,13 @@ public abstract class CustomRolePlus : CustomRole
///
public override bool IgnoreSpawnSystem { get; set; } = false;
///
+ /// 获取禁用的自定义角色生成
+ ///
+ ///
+ /// 只能用于继承的类
+ ///
+ public List DisableCustomRolePlusList => YongAnFramePlugin.Instance.Config.DisableCustomRolePlus;
+ ///
/// 获取或设置自定义角色的生成属性
///
public new virtual Properties.SpawnProperties SpawnProperties { get; set; } = new Properties.SpawnProperties();
@@ -34,11 +45,11 @@ public abstract class CustomRolePlus : CustomRole
/// 获取或设置自定义角色是否开启生成
///
public bool IsStartSpawn { get; set; } = true;
- internal Dictionary BaseData { get; } = [];
+ internal Dictionary BaseData { get; } = [];
///
- /// 获取或设置自定义角色的更多属性
+ /// 获取或设置自定义角色的基础属性
///
- public virtual MoreProperties MoreProperties { get; set; } = new MoreProperties();
+ public virtual BaseProperties BaseProperties { get; set; } = new BaseProperties();
///
/// 获取或设置自定义角色的名字颜色
///
@@ -54,36 +65,37 @@ public abstract class CustomRolePlus : CustomRole
#region Static
+ ///
+ /// 获取或设置全局刷新次数
+ ///
public static int RespawnWave { get; private set; } = 0;
+ ///
+ /// 注册全局事件
+ ///
public static void SubscribeStaticEvents()
{
Exiled.Events.Handlers.Server.RoundStarted += new CustomEventHandler(OnStaticRoundStarted);
Exiled.Events.Handlers.Server.RespawningTeam += new CustomEventHandler(OnStaticRespawningTeam);
}
+ ///
+ /// 注销全局事件
+ ///
public static void UnsubscribeStaticEvents()
{
Exiled.Events.Handlers.Server.RoundStarted -= new CustomEventHandler(OnStaticRoundStarted);
Exiled.Events.Handlers.Server.RespawningTeam -= new CustomEventHandler(OnStaticRespawningTeam);
}
- private static void OnStaticRoundStarted()
- {
- RespawnWave = 0;
- }
- private static void OnStaticRespawningTeam(RespawningTeamEventArgs args)
- {
- RespawnWave++;
- }
+ private static void OnStaticRoundStarted() => RespawnWave = 0;
+ private static void OnStaticRespawningTeam(RespawningTeamEventArgs args) => RespawnWave++;
+
#endregion
///
- /// 获取这个角色所有自定义角色的属性
+ /// 获取这个角色的所有数据
///
/// 获取的值
- public virtual CustomRolePlusProperties[] GetAllProperties()
- {
- return [.. BaseData.Values];
- }
+ public virtual CustomRolePlusData[] GetAllData() => [.. BaseData.Values];
///
/// 检查玩家是否拥有该角色
@@ -91,26 +103,17 @@ public virtual CustomRolePlusProperties[] GetAllProperties()
/// 框架玩家
/// 返回的数据
///
- public virtual bool Check(FramePlayer player, out CustomRolePlusProperties data)
- {
- return BaseData.TryGetValue(player, out data);
- }
+ public virtual bool Check(FramePlayer player, out CustomRolePlusData data) => BaseData.TryGetValue(player, out data);
///
/// 检查玩家是否拥有该角色
///
/// 框架玩家
- public virtual bool Check(FramePlayer player)
- {
- return player.CustomRolePlus == this;
- }
+ public virtual bool Check(FramePlayer player) => player.CustomRolePlus == this;
///
/// 给玩家添加这个角色
///
/// EX玩家
- public override void AddRole(Player player)
- {
- AddRole(player.ToFPlayer());
- }
+ public override void AddRole(Player player) => AddRole(player.ToFPlayer());
///
/// 给玩家添加这个角色
///
@@ -119,40 +122,46 @@ public virtual void AddRole(FramePlayer fPlayer)
{
if (Check(fPlayer)) return;
- Log.Debug($"已添加{fPlayer.ExPlayer.Nickname}的{Name}({Id})角色");
-
base.AddRole(fPlayer.ExPlayer);
AddRoleData(fPlayer);
- if (MoreProperties.BaseMovementSpeedMultiplier < 1f)
+ if (BaseProperties.BaseMovementSpeedMultiplier < 1f)
{
- fPlayer.ExPlayer.EnableEffect(Exiled.API.Enums.EffectType.Disabled);
- fPlayer.ExPlayer.ChangeEffectIntensity(Exiled.API.Enums.EffectType.Disabled, 1);
+ fPlayer.ExPlayer.EnableEffect(Exiled.API.Enums.EffectType.Slowness);
+ fPlayer.ExPlayer.ChangeEffectIntensity(Exiled.API.Enums.EffectType.Slowness, (byte)((1f - BaseProperties.BaseMovementSpeedMultiplier) * 100));
}
- if (MoreProperties.BaseMovementSpeedMultiplier > 1f)
+ if (BaseProperties.BaseMovementSpeedMultiplier > 1f)
{
fPlayer.ExPlayer.EnableEffect(Exiled.API.Enums.EffectType.MovementBoost);
- fPlayer.ExPlayer.ChangeEffectIntensity(Exiled.API.Enums.EffectType.MovementBoost, (byte)((MoreProperties.BaseMovementSpeedMultiplier - 1f) * 100));
+ fPlayer.ExPlayer.ChangeEffectIntensity(Exiled.API.Enums.EffectType.MovementBoost, (byte)((BaseProperties.BaseMovementSpeedMultiplier - 1f) * 100));
}
if (!string.IsNullOrEmpty(SpawnProperties.Info)) Cassie.MessageTranslated($""/*ADMINISTER TEAM DESIGNATED {CASSIEDeathName} HASENTERED*/, SpawnProperties.Info, true, true, true);
- if (!string.IsNullOrEmpty(SpawnProperties.MusicFileName))
+ if (!string.IsNullOrEmpty(SpawnProperties.MusicNameName))
{
- MusicManager.Instance.Play(SpawnProperties.MusicFileName, $"{Name}");
+ MusicManager.Play(SpawnProperties.MusicNameName!, $"{Name}");
}
+
fPlayer.UpdateShowInfo();
+ fPlayer.UI.UpdateCustomRoleUI();
+
+ Log.Info($"已为{fPlayer.ExPlayer.Nickname}添加{Name}({Id})角色");
}
+ ///
+ /// 给玩家添加这个角色的数据
+ ///
+ /// 框架玩家
protected virtual void AddRoleData(FramePlayer fPlayer)
{
- CustomRolePlusProperties properties = new();
- BaseData.Add(fPlayer, properties);
+ CustomRolePlusData data = new();
+ BaseData.Add(fPlayer, data);
if (this is ISkill skill)
{
- properties.SkillManagers = new SkillManager[skill.SkillProperties.Length];
+ data.Skills = new Skill[skill.SkillProperties.Length];
for (int i = 0; i < skill.SkillProperties.Length; i++)
{
- properties.SkillManagers[i] = new(fPlayer, skill, (byte)i);
+ data.Skills[i] = new(fPlayer, skill.SkillProperties[i]);
}
}
}
@@ -162,11 +171,11 @@ protected virtual void AddRoleData(FramePlayer fPlayer)
/// EX玩家
public override void RemoveRole(Player player)
{
- FramePlayer fPlayer = player.ToFPlayer();
- if (fPlayer != null)
+ try
{
RemoveRole(player.ToFPlayer());
}
+ catch (InvalidCastException){ }
}
///
/// 给玩家移除这个角色
@@ -174,19 +183,21 @@ public override void RemoveRole(Player player)
/// 框架玩家
public virtual void RemoveRole(FramePlayer fPlayer)
{
- if (!Check(fPlayer)) return;
- Log.Debug($"已删除{fPlayer.ExPlayer.Nickname}的{Name}({Id})角色");
- if (Check(fPlayer, out CustomRolePlusProperties data) && !data.IsDeathHandling)
+ if (Check(fPlayer, out CustomRolePlusData data))
{
- Cassie.MessageTranslated($"Died", $"{Name}游玩二游被榨干而死(非常正常死亡)");
+ if (!data.IsDeathHandling)
+ {
+ Cassie.MessageTranslated($"Died", $"{Name}游玩二游被榨干而死(非常正常死亡)");
+ }
+ base.RemoveRole(fPlayer.ExPlayer);
+ BaseData.Remove(fPlayer);
+ fPlayer.UpdateShowInfo();
+ Log.Info($"已为{fPlayer.ExPlayer.Nickname}删除{Name}({Id})角色");
}
- base.RemoveRole(fPlayer.ExPlayer);
- BaseData.Remove(fPlayer);
- fPlayer.ExPlayer.ShowHint($"", 0.1f);
- fPlayer.UpdateShowInfo();
+
}
- #region TrySpawn
+ #region Spawn
private uint limitCount = 0;
private uint spawnCount = 0;
@@ -194,15 +205,10 @@ public virtual void RemoveRole(FramePlayer fPlayer)
/// 尝试给这个玩家生成这个角色
///
/// 框架玩家
- /// 是否重置limitCount
- ///
- public virtual bool TrySpawn(FramePlayer fPlayer, bool chanceRef = false)
+ /// 是否成功
+ public virtual bool TrySpawn(FramePlayer fPlayer)
{
- if (chanceRef)
- {
- limitCount = 0;
- }
- if (fPlayer.CustomRolePlus == null && spawnCount < SpawnProperties.MaxCount && Server.PlayerCount >= SpawnProperties.MinPlayer && SpawnChanceNum <= SpawnProperties.Chance && SpawnProperties.Limit > limitCount)
+ if (fPlayer.CustomRolePlus is null && ((OldRole != RoleTypeId.None && fPlayer.ExPlayer.Role.Type == OldRole) || (OldRole == RoleTypeId.None && fPlayer.ExPlayer.Role.Type == Role)) && spawnCount < SpawnProperties.MaxCount && Server.PlayerCount >= SpawnProperties.MinPlayer && spawnChanceNum <= SpawnProperties.Chance && SpawnProperties.Limit > limitCount)
{
limitCount++;
spawnCount++;
@@ -211,13 +217,6 @@ public virtual bool TrySpawn(FramePlayer fPlayer, bool chanceRef = false)
}
return false;
}
-
- [Obsolete("旧算法遗留方法,不再进行兼容性维护")]
- public virtual bool TrySpawn(List noCustomRole, bool chanceRef = false)
- {
- if (noCustomRole == null || noCustomRole.Count == 0) { return false; }
- return TrySpawn(noCustomRole[Loader.Random.StrictNext(0, noCustomRole.Count)]);
- }
#endregion
#region Events
@@ -234,18 +233,22 @@ private void OnRestartingRound()
// TrySpawn(NoCustomRole.FindAll((p) => OldRole == RoleTypeId.None && Role == p.ExPlayer.Role.Type || p.ExPlayer.Role.Type == OldRole));
// }
//}
- public int SpawnChanceNum { get; private set; } = Loader.Random.StrictNext(1, 101);
- private void OnStaticRestartingRound()
- {
- SpawnChanceNum = Loader.Random.StrictNext(1, 101);
- }
+ private int spawnChanceNum = Loader.Random.StrictNext(1, 101);
+
+ private void OnStaticRestartingRound() => spawnChanceNum = Loader.Random.StrictNext(1, 101);
- private void OnSpawning(SpawningEventArgs args)
+ private void OnSpawned(SpawnedEventArgs args)
{
FramePlayer fPlayer = args.Player.ToFPlayer();
- if (IsStartSpawn && (OldRole != RoleTypeId.None && args.Player.Role.Type == OldRole) || (OldRole == RoleTypeId.None && args.Player.Role.Type == Role))
+
+ if (fPlayer.ExPlayer.GetCustomRoles().Count > 0)
+ {
+ return;
+ }
+
+ if (IsStartSpawn && !DisableCustomRolePlusList.Contains(Id.ToString()))
{
switch (SpawnProperties.RefreshTeam)
{
@@ -265,26 +268,25 @@ private void OnSpawning(SpawningEventArgs args)
private void OnDroppingItem(DroppingItemEventArgs args)
{
FramePlayer fPlayer = args.Player.ToFPlayer();
- if (Check(fPlayer, out CustomRolePlusProperties data))
+ if (Check(fPlayer, out CustomRolePlusData data))
{
- if (data.SkillManagers != null)
+ if (data.Skills is not null)
{
- foreach (var skillsManager in data.SkillManagers)
+ foreach (var skill in data.Skills)
{
- if (args.Item.Type == skillsManager.SkillProperties.UseItem)
+ if (args.Item.Type == skill.UseItem)
{
- if (skillsManager.IsActive)
+ if (skill.IsActive)
{
- fPlayer.HintManager.MessageTexts.Add(new HintManager.Text("技能正在持续", 5));
+ fPlayer.UI.MessageList.Add(new MessageText("技能正在持续", 5, MessageType.System));
}
- else if (skillsManager.IsBurial)
+ else if (skill.IsBurial)
{
- fPlayer.HintManager.MessageTexts.Add(new HintManager.Text($"技能正在冷却(CD:{skillsManager.BurialRemainingTime})", 5));
+ fPlayer.UI.MessageList.Add(new MessageText($"技能正在冷却(CD:{skill.BurialRemainingTime})", 5, MessageType.System));
}
else
{
- skillsManager.Run();
- fPlayer.HintManager.MessageTexts.Add(new HintManager.Text($"技能[{skillsManager.SkillProperties.Name}]已经发动,持续时间:{skillsManager.SkillProperties.ActiveMaxTime}", skillsManager.SkillProperties.ActiveMaxTime));
+ skill.Run();
}
args.IsAllowed = false;
}
@@ -294,24 +296,24 @@ private void OnDroppingItem(DroppingItemEventArgs args)
}
private void OnHurting(HurtingEventArgs args)
{
- if (args.Attacker != null && args.Player != null)
+ if (args.Attacker is not null && args.Player is not null)
{
if (Check(args.Player))
{
- args.Amount *= MoreProperties.DamageResistanceMultiplier;
+ args.Amount *= BaseProperties.DamageResistanceMultiplier;
}
else if (Check(args.Attacker))
{
DamageHandler damageHandler = args.DamageHandler;
- float damage = damageHandler.Damage * MoreProperties.AttackDamageMultiplier;
- if (MoreProperties.IsAttackIgnoresArmor)
+ float damage = damageHandler.Damage * BaseProperties.AttackDamageMultiplier;
+ if (BaseProperties.IsAttackIgnoresArmor)
{
if (damageHandler is FirearmDamageHandler firearmDamageHandler)
{
damage += ((Exiled.API.Features.Roles.HumanRole)damageHandler.Target.Role).GetArmorEfficacy(firearmDamageHandler.Hitbox);
}
}
- if (MoreProperties.IsAttackIgnoresAhp)
+ if (BaseProperties.IsAttackIgnoresAhp)
{
damage += damageHandler.AbsorbedAhpDamage;
}
@@ -331,13 +333,12 @@ private void OnHurting(HurtingEventArgs args)
}
}
}
-
private void OnDying(DyingEventArgs args)
{
FramePlayer fPlayer = args.Player.ToFPlayer();
- if (Check(fPlayer, out CustomRolePlusProperties data))
+ if (Check(fPlayer, out CustomRolePlusData data))
{
- if (args.Attacker == null)
+ if (args.Attacker is null)
{
Cassie.MessageTranslated($"Died", $"{Name}被充满恶意的游戏环境草飞了");
data.IsDeathHandling = true;
@@ -364,11 +365,13 @@ private void OnDying(DyingEventArgs args)
data.IsDeathHandling = true;
}
}
-
+ ///
+ /// 注册事件
+ ///
protected override void SubscribeEvents()
{
//Exiled.Events.Handlers.Server.RoundStarted += new CustomEventHandler(OnRoundStarted);
- Exiled.Events.Handlers.Player.Spawning += new CustomEventHandler(OnSpawning);
+ Exiled.Events.Handlers.Player.Spawned += new CustomEventHandler(OnSpawned);
Exiled.Events.Handlers.Player.Hurting += new CustomEventHandler(OnHurting);
Exiled.Events.Handlers.Server.RestartingRound += new CustomEventHandler(OnRestartingRound);
Exiled.Events.Handlers.Player.DroppingItem += new CustomEventHandler(OnDroppingItem);
@@ -380,15 +383,17 @@ protected override void SubscribeEvents()
{
Inventory.Add(ItemType.Coin.ToString());
}
-
}
+ ///
+ /// 注销事件
+ ///
protected override void UnsubscribeEvents()
{
//Exiled.Events.Handlers.Server.RoundStarted -= new CustomEventHandler(OnRoundStarted);
Exiled.Events.Handlers.Player.Hurting -= new CustomEventHandler(OnHurting);
Exiled.Events.Handlers.Server.RestartingRound -= new CustomEventHandler(OnRestartingRound);
Exiled.Events.Handlers.Player.DroppingItem -= new CustomEventHandler(OnDroppingItem);
- Exiled.Events.Handlers.Player.Spawning -= new CustomEventHandler(OnSpawning);
+ Exiled.Events.Handlers.Player.Spawned -= new CustomEventHandler(OnSpawned);
Exiled.Events.Handlers.Player.Dying -= new CustomEventHandler(OnDying);
Exiled.Events.Handlers.Server.RestartingRound -= new CustomEventHandler(OnStaticRestartingRound);
base.UnsubscribeEvents();
@@ -400,24 +405,31 @@ protected override void UnsubscribeEvents()
}
#endregion
+ ///
+ /// 不要覆写
+ ///
+ /// EX玩家
protected override void ShowMessage(Player player)
{
}
}
- [Guid("913613e0-c6e7-4511-a079-bacc7bc9000c")]
- public abstract class CustomRolePlus : CustomRolePlus where T : CustomRolePlusProperties, new()
+ ///
+ /// 带有自定义数据的高级自定义角色
+ ///
+ /// 自定义数据
+ public abstract class CustomRolePlus : CustomRolePlus where T : CustomRolePlusData, new()
{
///
/// 检查玩家是否拥有该角色
///
/// 框架玩家
/// 返回的数据
- ///
- public virtual bool Check(FramePlayer player, out T data)
+ /// 是否拥有该角色
+ public virtual bool Check(FramePlayer player, out T? data)
{
- if (BaseData.TryGetValue(player, out CustomRolePlusProperties baseData))
+ if (BaseData.TryGetValue(player, out CustomRolePlusData baseData))
{
data = (T)baseData;
return true;
@@ -426,16 +438,17 @@ public virtual bool Check(FramePlayer player, out T data)
return false;
}
+ ///
protected override void AddRoleData(FramePlayer fPlayer)
{
T properties = new();
BaseData.Add(fPlayer, properties);
if (this is ISkill skill)
{
- properties.SkillManagers = new SkillManager[skill.SkillProperties.Length];
+ properties.Skills = new Skill[skill.SkillProperties.Length];
for (int i = 0; i < skill.SkillProperties.Length; i++)
{
- properties.SkillManagers[i] = new(fPlayer, skill, (byte)i);
+ properties.Skills[i] = new(fPlayer, skill.SkillProperties[i]);
}
}
}
diff --git a/Features/Roles/CustomRolePlusData.cs b/Features/Roles/CustomRolePlusData.cs
new file mode 100644
index 0000000..9e5fbf6
--- /dev/null
+++ b/Features/Roles/CustomRolePlusData.cs
@@ -0,0 +1,17 @@
+namespace YongAnFrame.Features.Roles
+{
+ ///
+ /// 的数据
+ ///
+ public class CustomRolePlusData
+ {
+ ///
+ /// 获取或设置技能
+ ///
+ public Skill[]? Skills { get; internal set; }
+ ///
+ /// 获取或设置是否正常死亡
+ ///
+ public bool IsDeathHandling { get; set; }
+ }
+}
diff --git a/Features/Roles/Enums/RefreshTeamType.cs b/Features/Roles/Enums/RefreshTeamType.cs
new file mode 100644
index 0000000..88e34c9
--- /dev/null
+++ b/Features/Roles/Enums/RefreshTeamType.cs
@@ -0,0 +1,21 @@
+namespace YongAnFrame.Features.Roles.Enums
+{
+ ///
+ /// 的刷新队伍类型
+ ///
+ public enum RefreshTeamType
+ {
+ ///
+ /// 初始生成
+ ///
+ Start = 0,
+ ///
+ /// 九尾狐
+ ///
+ MTF = 1,
+ ///
+ /// 混沌分裂者
+ ///
+ CI = 2,
+ }
+}
diff --git a/Features/Roles/Interfaces/ISkill.cs b/Features/Roles/Interfaces/ISkill.cs
new file mode 100644
index 0000000..71bc6e6
--- /dev/null
+++ b/Features/Roles/Interfaces/ISkill.cs
@@ -0,0 +1,18 @@
+using YongAnFrame.Features.Roles.Properties;
+
+namespace YongAnFrame.Features.Roles.Interfaces
+{
+ ///
+ /// 技能接口
+ ///
+ ///
+ /// 所有有技能的自定义角色必须实现此接口
+ ///
+ public interface ISkill
+ {
+ ///
+ /// 获取技能属性
+ ///
+ SkillProperties[] SkillProperties { get; }
+ }
+}
diff --git a/Roles/Properties/MoreProperties.cs b/Features/Roles/Properties/BaseProperties.cs
similarity index 81%
rename from Roles/Properties/MoreProperties.cs
rename to Features/Roles/Properties/BaseProperties.cs
index ed3ff56..c7fc4dc 100644
--- a/Roles/Properties/MoreProperties.cs
+++ b/Features/Roles/Properties/BaseProperties.cs
@@ -1,6 +1,9 @@
-namespace YongAnFrame.Roles.Properties
+namespace YongAnFrame.Features.Roles.Properties
{
- public struct MoreProperties
+ ///
+ /// 的基础属性
+ ///
+ public struct BaseProperties()
{
///
/// 获取或设置伤害加成倍数
@@ -22,9 +25,5 @@ public struct MoreProperties
/// 获取或设置基础移动速度倍数
///
public float BaseMovementSpeedMultiplier { get; set; } = 1;
-
- public MoreProperties()
- {
- }
}
}
diff --git a/Features/Roles/Properties/SkillProperties.cs b/Features/Roles/Properties/SkillProperties.cs
new file mode 100644
index 0000000..25e5eaa
--- /dev/null
+++ b/Features/Roles/Properties/SkillProperties.cs
@@ -0,0 +1,60 @@
+using static YongAnFrame.Features.Roles.Skill;
+
+namespace YongAnFrame.Features.Roles.Properties
+{
+ ///
+ /// 的原始技能属性
+ ///
+ ///
+ /// 你无法修改结构体里的任何对象,如果想修改对象请从对象修改,因为要保留技能的原始属性,从而保证可以恢复到原始属性
+ ///
+ /// 名称
+ /// 发动描述
+ /// 介绍
+ /// 最大作用时间
+ /// 最大冷却时间
+ /// 激活开始委托
+ /// 激活结束委托
+ /// 冷却结束委托
+ /// 绑定物品
+ public readonly struct SkillProperties(string name, string statement, string description, float activeMaxTime, float burialMaxTime,
+ ActiveStart? activeStart = null, ActiveEnd? activeEnd = null, BurialEnd? burialEnd = null, ItemType useItem = ItemType.Coin)
+ {
+ ///
+ /// 获取名称
+ ///
+ public string Name { get; } = name;
+ ///
+ /// 获取绑定物品
+ ///
+ public ItemType UseItem { get; } = useItem;
+ ///
+ /// 获取发动描述
+ ///
+ public string Statement { get; } = statement;
+ ///
+ /// 获取介绍
+ ///
+ public string Description { get; } = description;
+ ///
+ /// 获取最大作用时间
+ ///
+ public float ActiveMaxTime { get; } = activeMaxTime;
+ ///
+ /// 获取最大冷却时间
+ ///
+ public float BurialMaxTime { get; } = burialMaxTime;
+ ///
+ /// 获取激活开始委托
+ ///
+ public ActiveStart? ActiveStartAction { get; } = activeStart;
+ ///
+ /// 获取激活结束委托
+ ///
+ public ActiveEnd? ActiveEndAction { get; } = activeEnd;
+ ///
+ /// 获取冷却结束委托
+ ///
+ public BurialEnd? BurialEndAction { get; } = burialEnd;
+ }
+}
diff --git a/Roles/Properties/SpawnProperties.cs b/Features/Roles/Properties/SpawnProperties.cs
similarity index 72%
rename from Roles/Properties/SpawnProperties.cs
rename to Features/Roles/Properties/SpawnProperties.cs
index 2467f94..632ea3a 100644
--- a/Roles/Properties/SpawnProperties.cs
+++ b/Features/Roles/Properties/SpawnProperties.cs
@@ -1,13 +1,12 @@
-using YongAnFrame.Roles.Enums;
+using YongAnFrame.Features.Roles.Enums;
-namespace YongAnFrame.Roles.Properties
+namespace YongAnFrame.Features.Roles.Properties
{
- public struct SpawnProperties
+ ///
+ /// 的生成属性
+ ///
+ public struct SpawnProperties()
{
- public SpawnProperties()
- {
- }
-
///
/// 获取或设置每次生成的最多数量
///
@@ -23,7 +22,7 @@ public SpawnProperties()
///
/// 获取或设置生成时播放音频文件
///
- public string MusicFileName { get; set; } = null;
+ public string? MusicNameName { get; set; } = null;
///
/// 获取或设置生成时跟随的队伍
///
@@ -31,17 +30,17 @@ public SpawnProperties()
///
/// 暂时弃用
///
- public string Info { get; set; } = null;
+ public string? Info { get; set; } = null;
///
- /// 获取或设置生成的数量限制
+ /// 获取或设置生成数量限制
///
public uint Limit { get; set; } = 1;
///
- /// 获取或设置每次生成的概率
+ /// 获取或设置生成概率
///
public float Chance { get; set; } = 100;
///
- /// 获取或设置的刷新波次
+ /// 获取或设置刷新波次
///
///
/// 只适用于除 以外的所有内容
diff --git a/Features/Roles/Skill.cs b/Features/Roles/Skill.cs
new file mode 100644
index 0000000..7edc26f
--- /dev/null
+++ b/Features/Roles/Skill.cs
@@ -0,0 +1,152 @@
+using MEC;
+using System.Collections.Generic;
+using YongAnFrame.Features.Players;
+using YongAnFrame.Features.Roles.Properties;
+using YongAnFrame.Features.UI.Enums;
+using YongAnFrame.Features.UI.Texts;
+
+namespace YongAnFrame.Features.Roles
+{
+ ///
+ /// 的技能
+ ///
+ /// 框架玩家
+ /// 技能原始属性
+ public class Skill(FramePlayer fPlayer, SkillProperties properties)
+ {
+ ///
+ /// 激活开始
+ ///
+ /// 播放音乐文件名称
+ public delegate string? ActiveStart(FramePlayer fPlayer);
+ ///
+ /// 激活结束
+ ///
+ /// 播放音乐文件名称
+ public delegate string? ActiveEnd(FramePlayer fPlayer);
+ ///
+ /// 冷却结束
+ ///
+ /// 播放音乐文件名称
+ public delegate string? BurialEnd(FramePlayer fPlayer);
+ ///
+ /// 获取原始属性
+ ///
+ public SkillProperties Properties { get; } = properties;
+ ///
+ /// 获取名称
+ ///
+ public string Name { get; set; } = properties.Name;
+ ///
+ /// 获取绑定物品
+ ///
+ public ItemType UseItem => Properties.UseItem;
+ ///
+ /// 获取发动描述
+ ///
+ public string? Statement { get; set; } = properties.Statement;
+ ///
+ /// 获取介绍
+ ///
+ public string Description { get; set; } = properties.Description;
+ ///
+ /// 获取最大作用时间
+ ///
+ public float ActiveMaxTime { get; set; } = properties.ActiveMaxTime;
+ ///
+ /// 获取最大冷却时间
+ ///
+ public float BurialMaxTime { get; set; } = properties.BurialMaxTime;
+ ///
+ /// 获取激活开始委托
+ ///
+ public ActiveStart? ActiveStartAction { get; set; } = properties.ActiveStartAction;
+ ///
+ /// 获取激活结束委托
+ ///
+ public ActiveEnd? ActiveEndAction { get; set; } = properties.ActiveEndAction;
+ ///
+ /// 获取冷却结束委托
+ ///
+ public BurialEnd? BurialEndAction { get; set; } = properties.BurialEndAction;
+ ///
+ /// 获取是否激活
+ ///
+ public bool IsActive { get => ActiveRemainingTime > 0; }
+ ///
+ /// 获取是否冷却
+ ///
+ public bool IsBurial { get => BurialRemainingTime > 0; }
+ ///
+ /// 获取行动时间
+ ///
+ public float ActiveRemainingTime { get; private set; }
+ ///
+ /// 获取冷却时间
+ ///
+ public float BurialRemainingTime { get; private set; }
+
+ private CoroutineHandle coroutineHandle;
+
+
+ ///
+ /// 使用技能
+ ///
+ ///
+ /// 有计时任务会直接覆盖
+ ///
+ public void Run()
+ {
+ if (coroutineHandle.IsValid)
+ {
+ Timing.KillCoroutines(coroutineHandle);
+ }
+
+ ActiveRemainingTime = ActiveMaxTime;
+ BurialRemainingTime = BurialMaxTime;
+
+ coroutineHandle = Timing.RunCoroutine(Timer());
+ fPlayer.UI.MessageList.Add(new MessageText($"{(string.IsNullOrEmpty(Statement) ? $"技能[{Name}]已经发动" : $"{Name}:{Statement}")}(持续时间:{ActiveMaxTime})", ActiveMaxTime, MessageType.System));
+ }
+
+ ///
+ /// 还原技能
+ ///
+ public void Restore()
+ {
+ Name = Properties.Name;
+ Statement = Properties.Statement;
+ Description = Properties.Description;
+ ActiveMaxTime = Properties.ActiveMaxTime;
+ BurialMaxTime = Properties.BurialMaxTime;
+ ActiveStartAction = Properties.ActiveStartAction;
+ ActiveEndAction = Properties.ActiveEndAction;
+ BurialEndAction = Properties.BurialEndAction;
+ if (coroutineHandle.IsValid)
+ {
+ Timing.KillCoroutines(coroutineHandle);
+ }
+ fPlayer.UI.MessageList.Add(new MessageText($"技能[{Name}]被其他人影响,技能信息全部重置", 10, MessageType.System));
+ }
+
+ private IEnumerator Timer()
+ {
+ string? musicName = ActiveStartAction?.Invoke(fPlayer);
+ if (musicName is not null) MusicManager.Play(musicName, $"技能发动语音", fPlayer, 10);
+ while (IsActive)
+ {
+ ActiveRemainingTime--;
+ yield return Timing.WaitForSeconds(1f);
+ }
+ musicName = ActiveEndAction?.Invoke(fPlayer);
+ if (musicName is not null) MusicManager.Play(musicName, $"技能结束语音", fPlayer, 10);
+ while (IsBurial)
+ {
+ BurialRemainingTime--;
+ yield return Timing.WaitForSeconds(1f);
+ }
+ musicName = BurialEndAction?.Invoke(fPlayer);
+ if (musicName is not null) MusicManager.Play(musicName, $"技能准备好语音", fPlayer, 10);
+ }
+ }
+}
diff --git a/Features/UI/Enums/ChatType.cs b/Features/UI/Enums/ChatType.cs
new file mode 100644
index 0000000..9078c46
--- /dev/null
+++ b/Features/UI/Enums/ChatType.cs
@@ -0,0 +1,27 @@
+using YongAnFrame.Features.UI.Texts;
+
+namespace YongAnFrame.Features.UI.Enums
+{
+ ///
+ /// 的类型
+ ///
+ public enum ChatType : byte
+ {
+ ///
+ /// 未知
+ ///
+ Unknown = 0,
+ ///
+ /// 全部
+ ///
+ All = 1,
+ ///
+ /// 队伍
+ ///
+ Team = 2,
+ ///
+ /// 私聊
+ ///
+ Private = 3,
+ }
+}
diff --git a/Features/UI/Enums/MessageType.cs b/Features/UI/Enums/MessageType.cs
new file mode 100644
index 0000000..73c03ad
--- /dev/null
+++ b/Features/UI/Enums/MessageType.cs
@@ -0,0 +1,35 @@
+using YongAnFrame.Features.UI.Texts;
+
+namespace YongAnFrame.Features.UI.Enums
+{
+ ///
+ /// 的类型
+ ///
+ public enum MessageType : byte
+ {
+ ///
+ /// 未知
+ ///
+ Unknown = 0,
+ ///
+ /// 管理员
+ ///
+ Admin = 1,
+ ///
+ /// 反馈
+ ///
+ Feedback = 2,
+ ///
+ /// 系统
+ ///
+ System = 3,
+ ///
+ /// 安全
+ ///
+ Safety = 4,
+ ///
+ /// 异常
+ ///
+ Abnormal = 5
+ }
+}
diff --git a/Features/UI/Texts/ChatText.cs b/Features/UI/Texts/ChatText.cs
new file mode 100644
index 0000000..055ec92
--- /dev/null
+++ b/Features/UI/Texts/ChatText.cs
@@ -0,0 +1,52 @@
+using YongAnFrame.Features.Players;
+using YongAnFrame.Features.UI.Enums;
+
+namespace YongAnFrame.Features.UI.Texts
+{
+ ///
+ /// 的消息文本
+ ///
+ /// 内容
+ /// 时效
+ /// 聊天类型
+ /// 发送者(null时是匿名)
+ public class ChatText(string text, float duration, ChatType type = ChatType.Unknown, FramePlayer? player = null) : Text(text, duration)
+ {
+ ///
+ /// 获取聊天类型
+ ///
+ public ChatType Type { get; } = type;
+ ///
+ public override string ToString()
+ {
+ string text = "Error";
+ switch (Type)
+ {
+ case ChatType.Unknown:
+ text = $"[未知]|[{(player is null ? $"匿名" : $"{player.ExPlayer.Nickname}({player.ExPlayer.Role.Team})")}]:{Content}";
+ break;
+ case ChatType.All:
+ text = $"[全部]|[{(player is null ? $"匿名" : $"{player.ExPlayer.Nickname}({player.ExPlayer.Role.Team})")}]:{Content}";
+ break;
+ case ChatType.Team:
+ text = $"[队伍]|[{(player is null ? $"匿名" : $"{player.ExPlayer.Nickname}({player.ExPlayer.Role.Team})")}]:{Content}";
+ break;
+ case ChatType.Private:
+ text = $"[私聊]|[{(player is null ? $"匿名" : $"{player.ExPlayer.Nickname}({player.ExPlayer.Role.Team})")}]:{Content}";
+ break;
+ }
+ return text;
+ }
+
+ ///
+ /// 隐性转换
+ ///
+ /// 准备转换的对象
+ public static implicit operator string(ChatText text) => text.ToString();
+ ///
+ /// 隐性转换
+ ///
+ /// 准备转换的对象
+ public static implicit operator ChatText(string text) => new(text, 60);
+ }
+}
diff --git a/Features/UI/Texts/MessageText.cs b/Features/UI/Texts/MessageText.cs
new file mode 100644
index 0000000..fab6a79
--- /dev/null
+++ b/Features/UI/Texts/MessageText.cs
@@ -0,0 +1,56 @@
+using YongAnFrame.Features.UI.Enums;
+
+namespace YongAnFrame.Features.UI.Texts
+{
+ ///
+ /// 的消息文本
+ ///
+ /// 内容
+ /// 时效
+ /// 信息类型
+ public class MessageText(string text, float duration, MessageType type = MessageType.Unknown) : Text(text, duration)
+ {
+ ///
+ /// 获取信息类型
+ ///
+ public MessageType Type { get; } = type;
+
+ ///
+ public override string ToString()
+ {
+ string text = "Error";
+ switch (Type)
+ {
+ case MessageType.Unknown:
+ text = $"[{Duration}][未知] {text}";
+ break;
+ case MessageType.Admin:
+ text = $"[{Duration}][管理员] {Content}";
+ break;
+ case MessageType.Feedback:
+ text = $"[{Duration}][玩家反馈] {Content}";
+ break;
+ case MessageType.System:
+ text = $"[{Duration}][系统] {Content}";
+ break;
+ case MessageType.Safety:
+ text = $"[{Duration}][安全] {Content}";
+ break;
+ case MessageType.Abnormal:
+ text = $"[{Duration}][异常] {Content}";
+ break;
+ }
+ return text;
+ }
+ ///
+ /// 隐性转换
+ ///
+ /// 准备转换的对象
+ public static implicit operator string(MessageText text) => text.ToString();
+ ///
+ /// 隐性转换
+ ///
+ /// 准备转换的对象
+ public static implicit operator MessageText(string text) => new(text, -1);
+ }
+}
diff --git a/Features/UI/Texts/Text.cs b/Features/UI/Texts/Text.cs
new file mode 100644
index 0000000..ad18f9f
--- /dev/null
+++ b/Features/UI/Texts/Text.cs
@@ -0,0 +1,31 @@
+namespace YongAnFrame.Features.UI.Texts
+{
+ ///
+ /// 的基础文本
+ ///
+ /// 内容
+ /// 时效
+ public class Text(string text, float duration)
+ {
+ ///
+ /// 获取内容
+ ///
+ public string Content { get; } = text;
+ ///
+ /// 获取时效
+ ///
+ public float Duration { get; internal set; } = duration;
+ ///
+ public override string ToString() => Content;
+ ///
+ /// 隐性转换
+ ///
+ /// 准备转换的对象
+ public static implicit operator string(Text text) => text.ToString();
+ ///
+ /// 隐性转换
+ ///
+ /// 准备转换的对象
+ public static implicit operator Text(string text) => new(text, 60);
+ }
+}
diff --git a/Patch/AddLogPatch.cs b/Patch/AddLogPatch.cs
new file mode 100644
index 0000000..6231603
--- /dev/null
+++ b/Patch/AddLogPatch.cs
@@ -0,0 +1,65 @@
+using HarmonyLib;
+using System;
+using System.Collections.Concurrent;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.IO;
+using System.Text;
+using System.Threading.Tasks;
+using YongAnFrame.Features;
+
+namespace YongAnFrame.Patch
+{
+ ///
+ /// 在添加的补丁
+ ///
+ [HarmonyPatch(typeof(ServerConsole), nameof(ServerConsole.AddLog))]
+ public static class AddLogPatch
+ {
+#pragma warning disable IDE0060 // 删除未使用的参数
+ private static void Prefix(string q, ConsoleColor color = ConsoleColor.Gray, bool hideFromOutputs = false)
+#pragma warning restore IDE0060 // 删除未使用的参数
+ {
+ SaveLog(q, new StackTrace());
+ }
+
+ private static readonly Queue logQueue = new();
+ private static readonly Task logTask = new(async () =>
+ {
+ while (true)
+ {
+ while (logQueue.Count != 0)
+ {
+ InfoData infoData = logQueue.Dequeue();
+ using StreamWriter writer = new($"{PathManager.Log}/{DateTime.Now:yyyy-MM-dd}.log", true, Encoding.UTF8);
+ writer.WriteLine(infoData);
+ }
+ await Task.Delay(1000);
+ }
+ });
+
+ ///
+ /// 启动日志任务
+ ///
+ public static void StartTask()
+ {
+ if (logTask.Status == TaskStatus.Created)
+ {
+ logTask.Start();
+ }
+ }
+
+ private static void SaveLog(string log, StackTrace trace)
+ {
+ logQueue.Enqueue(new InfoData(log, trace));
+ }
+
+ private readonly struct InfoData(string content, StackTrace trace)
+ {
+ public string Content { get; } = content;
+ public StackTrace StackTrace { get; } = trace;
+ public override readonly string ToString() => $"{Content}::{StackTrace}";
+ public static implicit operator string(InfoData data) => data.ToString();
+ }
+ }
+}
diff --git a/Players/CustomPlayer.cs b/Players/CustomPlayer.cs
deleted file mode 100644
index 9b60650..0000000
--- a/Players/CustomPlayer.cs
+++ /dev/null
@@ -1,43 +0,0 @@
-using YongAnFrame.Roles;
-
-namespace YongAnFrame.Players
-{
- public abstract class CustomPlayer(FramePlayer player)
- {
- ///
- /// 获取拥有该实例的
- ///
- public FramePlayer FramePlayer { get; private set; } = player;
- ///
- public bool IsInvalid => FramePlayer == null;
- ///
- public CustomRolePlus CustomRolePlus => FramePlayer.CustomRolePlus;
- ///
- public HintManager HintManager => FramePlayer.HintManager;
- ///
- public ICustomAlgorithm CustomAlgorithm { get => FramePlayer.CustomAlgorithm; set => FramePlayer.CustomAlgorithm = value; }
- ///
- public ulong Level { get => FramePlayer.Level; set => FramePlayer.Level = value; }
- ///
- public ulong Exp { get => FramePlayer.Exp; set => FramePlayer.Exp = value; }
- ///
- public float ExpMultiplier { get => FramePlayer.ExpMultiplier; set => FramePlayer.ExpMultiplier = value; }
- ///
- public bool IsBDNT { get => FramePlayer.IsBDNT; set => FramePlayer.IsBDNT = value; }
- ///
- public PlayerTitle UsingTitles { get => FramePlayer.UsingTitles; set => FramePlayer.UsingTitles = value; }
- ///
- public PlayerTitle UsingRankTitles { get => FramePlayer.UsingRankTitles; set => FramePlayer.UsingRankTitles = value; }
-
- ///
- public void AddExp(ulong exp, string name = "未知原因") => FramePlayer.AddExp(exp, name);
-
- public void UpdateShowInfoList() => FramePlayer.UpdateShowInfo();
-
- ///
- public virtual void Invalid()
- {
- FramePlayer = null;
- }
- }
-}
diff --git a/Players/HintManager.cs b/Players/HintManager.cs
deleted file mode 100644
index a7eb021..0000000
--- a/Players/HintManager.cs
+++ /dev/null
@@ -1,132 +0,0 @@
-using MEC;
-using System.Collections.Generic;
-using YongAnFrame.Components;
-
-namespace YongAnFrame.Players
-{
- ///
- /// 提示系统管理器
- ///
- public sealed class HintManager
- {
- private readonly FramePlayer fPlayer;
- private readonly CoroutineHandle coroutine;
-
- public Text[] CustomText = new Text[20];
- public CapacityList MessageTexts { get; } = new(7);
- public CapacityList ChatTexts { get; } = new(6);
- public HintManager(FramePlayer player)
- {
- fPlayer = player;
- coroutine = Timing.RunCoroutine(Update());
- }
-
- private IEnumerator Update()
- {
- while (true)
- {
- CustomText = new Text[20];
- Events.Handlers.FramePlayer.OnFramerHintUpdate();
- string[] text = new string[36];
-
- int used = 0;
- text[used] = $"YongAnFrame 1.0.0-Beta5";
-
- if (fPlayer.ExPlayer.DoNotTrack && !fPlayer.IsBDNT)
- {
- text[used] = "[注意]已开启DoNotTrack(DNT),游戏数据不会被保存,想保存数据请控制台输入pl BDNT查看详情";
- }
-
- used = 1;
- text[used] = "";
-
- for (int i = 0; i < ChatTexts.Capacity; i++)
- {
- Text chatText = ChatTexts[i];
- if (chatText != null)
- {
- text[used] += chatText;
- chatText.Duration--;
-
- if (chatText.Duration <= 0)
- {
- ChatTexts.Remove(chatText);
- i--;
- }
- }
- else
- {
- text[used] += Text.Empty;
- }
- used++;
- }
- text[used] = "";
-
- foreach (Text data in CustomText)
- {
- text[used] += data ?? Text.Empty;
- used++;
- }
-
- for (int i = 0; i < MessageTexts.Capacity; i++)
- {
- Text messageText = MessageTexts[i];
- if (messageText != null)
- {
- text[used] = $"[{messageText.Duration}]{messageText}";
-
- messageText.Duration--;
- if (messageText.Duration <= 0)
- {
- MessageTexts.Remove(messageText);
- i--;
- }
- }
- else
- {
- text[used] += Text.Empty;
- }
- used++;
- }
- text[34] = "";
-
- if (fPlayer.CustomRolePlus != null)
- {
- text[34] += $"{fPlayer.CustomRolePlus.Name}";
- text[35] = fPlayer.CustomRolePlus.Description;
- }
- fPlayer.ExPlayer.ShowHint($"{string.Join("\n", text)}\n\n\n\n\n\n\n\n\n\n\n\n\n\n", 2f);
- yield return Timing.WaitForSeconds(1f);
- }
- }
-
- ///
- /// 立刻停用这个提示系统管理器
- ///
- public void Clean()
- {
- Timing.KillCoroutines(coroutine);
- }
-
- public class Text(string text, float duration)
- {
- public string Content { get; private set; } = text;
- public float Duration { get; internal set; } = duration;
-
- public static string Empty => "占";
-
- public override string ToString()
- {
- return Content;
- }
- public static implicit operator string(Text text)
- {
- return text.ToString();
- }
- public static implicit operator Text(string text)
- {
- return new Text(text, -1);
- }
- }
- }
-}
diff --git a/Players/PlayerTitle.cs b/Players/PlayerTitle.cs
deleted file mode 100644
index 46f0e42..0000000
--- a/Players/PlayerTitle.cs
+++ /dev/null
@@ -1,58 +0,0 @@
-using System.Collections.Generic;
-
-namespace YongAnFrame.Players
-{
- ///
- /// 玩家称号
- ///
- public sealed class PlayerTitle
- {
- ///
- /// 获取或设置称号的ID
- ///
- public uint Id { get; set; }
- ///
- /// 获取或设置称号的名称
- ///
- public string Name { get; set; }
- ///
- /// 获取或设置称号的颜色
- ///
- public string Color { get; set; }
- ///
- /// 获取或设置称号是否为Rank
- ///
- public bool IsRank { get; set; }
- ///
- /// 获取称号的动态指令集
- ///
- public List DynamicCommand { get; private set; }
-
- public PlayerTitle(uint id, string name, string color, bool isRank, string dynamicCommandString)
- {
- Id = id;
- Name = name;
- Color = color;
- IsRank = isRank;
- SetDynamicCommand(dynamicCommandString);
- }
-
- ///
- /// 设置称号的动态指令集
- ///
- ///
- public void SetDynamicCommand(string dynamicCommandString)
- {
- List dynamicCommands = null;
- if (!string.IsNullOrEmpty(dynamicCommandString))
- {
- dynamicCommands = [];
- foreach (string dCommand in dynamicCommandString.Split(';'))
- {
- dynamicCommands.Add(dCommand.Split(','));
- }
- }
- DynamicCommand = dynamicCommands;
- }
- }
-}
diff --git a/Properties/AssemblyInfo.cs b/Properties/AssemblyInfo.cs
deleted file mode 100644
index ca90715..0000000
--- a/Properties/AssemblyInfo.cs
+++ /dev/null
@@ -1,35 +0,0 @@
-using System.Reflection;
-using System.Runtime.InteropServices;
-
-// 有关程序集的一般信息由以下
-// 控制。更改这些特性值可修改
-// 与程序集关联的信息。
-[assembly: AssemblyTitle("YongAnFrame")]
-[assembly: AssemblyDescription("基于EXILED写的框架")]
-[assembly: AssemblyConfiguration("")]
-[assembly: AssemblyCompany("YongAn404")]
-[assembly: AssemblyProduct("YongAnFrame")]
-[assembly: AssemblyCopyright("Copyright © YongAn404")]
-[assembly: AssemblyTrademark("")]
-[assembly: AssemblyCulture("zh-cn")]
-
-// 将 ComVisible 设置为 false 会使此程序集中的类型
-//对 COM 组件不可见。如果需要从 COM 访问此程序集中的类型
-//请将此类型的 ComVisible 特性设置为 true。
-[assembly: ComVisible(false)]
-
-// 如果此项目向 COM 公开,则下列 GUID 用于类型库的 ID
-[assembly: Guid("913613e0-c6e7-4511-a079-bacc7bc9089c")]
-
-// 程序集的版本信息由下列四个值组成:
-//
-// 主版本
-// 次版本
-// 生成号
-// 修订号
-//
-//可以指定所有这些值,也可以使用“生成号”和“修订号”的默认值
-//通过使用 "*",如下所示:
-// [assembly: AssemblyVersion("1.0.*")]
-[assembly: AssemblyVersion("1.0.0.13")]
-[assembly: AssemblyFileVersion("1.0.0.13")]
diff --git a/README.md b/README.md
index b3e98b3..051e850 100644
--- a/README.md
+++ b/README.md
@@ -1,20 +1,20 @@
# YongAnFrame
[](https://app.fossa.com/projects/git%2Bgithub.com%2FSCP-SL-Plugin-YongAnTeam%2FYongAnFrame?ref=badge_shield)
-基于[EXILED](https://github.com/ExMod-Team/EXILED)写的框架,为国内服务器常见的插件需求简化开发
+基于[EXILED](https://github.com/ExSLMod-Team/EXILED)写的框架,为国内服务器常见的插件需求简化开发
## YongAnFrame是什么类型的插件/框架?
-**YongAnFrame** 是一个集成了命令系统、整合了自定义角色及技能系统、称号系统、提示系统的免费框架,使用 **[EXILED](https://github.com/ExMod-Team/EXILED)** 开发。\
+**YongAnFrame** 是一个集成了命令系统、整合了自定义角色及技能系统、称号系统、UI系统等的免费框架,使用 **[EXILED](https://github.com/ExMod-Team/EXILED)** 开发。\
您可以通过安装该插件并使用该框架开发插件,简化您的开发流程。
## 功能
- 称号系统:提供动态指令运行集
- DNT检测和BDNT(Bypass DNT)请求:使信息获取和保存符合VSR规则
-- 提示系统:提供可方便调用显示提示,拒绝冲突
+- UI系统:使用[HSM](https://github.com/MeowServer/HintServiceMeow)预制一些比较常用的UI
- 自定义角色:提供对[EXILED](https://github.com/ExMod-Team/EXILED)的自定义角色升级,部分兼容[EXILED](https://github.com/ExMod-Team/EXILED)自定义角色
- 等级系统:提供整合统一的等级API,支持自定义算法
- 自定义算法:提供一个可替换主要算法的属性(该功能不支持多元化,只能采用一个主要算法)
-- 音频API:提供方便更好调用SCPSLAudioApi
+- 音频API:提供更方便调用SCPSLAudioApi
## 为服务端安装框架
如图,下载 **Release下的最新压缩包** 全部解压并合并到**EXILED目录**,如C:\Users\Administrator\Appdata\Roaming\ ,安装后请启动一次服务端,并在EXILED\Config内根据提示调整配置文件.
diff --git a/Roles/Enums/RefreshTeamType.cs b/Roles/Enums/RefreshTeamType.cs
deleted file mode 100644
index c7b307f..0000000
--- a/Roles/Enums/RefreshTeamType.cs
+++ /dev/null
@@ -1,9 +0,0 @@
-namespace YongAnFrame.Roles.Enums
-{
- public enum RefreshTeamType
- {
- Start = 0,
- MTF = 1,
- CI = 2,
- }
-}
diff --git a/Roles/Interfaces/ISkill.cs b/Roles/Interfaces/ISkill.cs
deleted file mode 100644
index c700fb8..0000000
--- a/Roles/Interfaces/ISkill.cs
+++ /dev/null
@@ -1,12 +0,0 @@
-using YongAnFrame.Roles.Properties;
-
-namespace YongAnFrame.Roles.Interfaces
-{
- public interface ISkill
- {
- ///
- /// 技能属性
- ///
- SkillProperties[] SkillProperties { get; }
- }
-}
diff --git a/Roles/Interfaces/ISkillActiveEnd.cs b/Roles/Interfaces/ISkillActiveEnd.cs
deleted file mode 100644
index b7870e4..0000000
--- a/Roles/Interfaces/ISkillActiveEnd.cs
+++ /dev/null
@@ -1,14 +0,0 @@
-using YongAnFrame.Players;
-
-namespace YongAnFrame.Roles.Interfaces
-{
- public interface ISkillActiveEnd : ISkill
- {
- ///
- /// 行动结束
- ///
- ///
- /// 方法的音乐文件名称
- string ActiveEnd(FramePlayer yPlayer, byte id);
- }
-}
diff --git a/Roles/Interfaces/ISkillActiveStart.cs b/Roles/Interfaces/ISkillActiveStart.cs
deleted file mode 100644
index c009897..0000000
--- a/Roles/Interfaces/ISkillActiveStart.cs
+++ /dev/null
@@ -1,14 +0,0 @@
-using YongAnFrame.Players;
-
-namespace YongAnFrame.Roles.Interfaces
-{
- public interface ISkillActiveStart : ISkill
- {
- ///
- /// 行动开始
- ///
- ///
- /// 方法的音乐文件名称
- string ActiveStart(FramePlayer yPlayer, byte id);
- }
-}
diff --git a/Roles/Interfaces/ISkillBurialEnd.cs b/Roles/Interfaces/ISkillBurialEnd.cs
deleted file mode 100644
index 96af3da..0000000
--- a/Roles/Interfaces/ISkillBurialEnd.cs
+++ /dev/null
@@ -1,14 +0,0 @@
-using YongAnFrame.Players;
-
-namespace YongAnFrame.Roles.Interfaces
-{
- public interface ISkillBurialEnd : ISkill
- {
- ///
- /// 冷却结束
- ///
- ///
- /// 方法的音乐文件名称
- string BurialEnd(FramePlayer yPlayer, byte id);
- }
-}
diff --git a/Roles/MusicManager.cs b/Roles/MusicManager.cs
deleted file mode 100644
index f1fd2cd..0000000
--- a/Roles/MusicManager.cs
+++ /dev/null
@@ -1,248 +0,0 @@
-using Exiled.API.Features;
-using Exiled.API.Features.Components;
-using Mirror;
-using SCPSLAudioApi.AudioCore;
-using System;
-using System.Collections.Generic;
-using System.IO;
-using System.Linq;
-using System.Net.Http;
-using System.Threading.Tasks;
-using UnityEngine;
-using YongAnFrame.Players;
-using static SCPSLAudioApi.AudioCore.AudioPlayerBase;
-
-namespace YongAnFrame.Roles
-{
- ///
- /// 一个通用的音频控制器
- ///
- public sealed class MusicManager
- {
- private static readonly MusicManager instance = new();
-
- private uint num = 0;
- ///
- /// 获取单例
- ///
- public static MusicManager Instance => instance;
- ///
- /// 获取或设置放音频的玩家(NPC)
- ///
- public Dictionary MusicNpc { get; } = [];
- private MusicManager() { }
-
- internal void Init()
- {
- OnFinishedTrack += TrackFinished;
- Log.Info("MusicManager----------OK");
- }
-
- private void TrackFinished(AudioPlayerBase playerBase, string track, bool directPlay, ref int nextQueuePos)
- {
- Stop(playerBase);
- }
-
- private ReferenceHub CreateMusicNpc(string name)
- {
- var newNpc = UnityEngine.Object.Instantiate(NetworkManager.singleton.playerPrefab);
- ReferenceHub hubNpc = newNpc.GetComponent();
- NetworkServer.AddPlayerForConnection(new FakeConnection(0), newNpc);
- hubNpc.nicknameSync.Network_myNickSync = name;
- hubNpc.authManager.NetworkSyncedUserId = null;
- MusicNpc.Add($"{num++}:{name}", hubNpc);
- return hubNpc;
- }
-
- ///
- /// 立刻停止播放音频
- ///
- /// AudioPlayerBase
- public void Stop(AudioPlayerBase playerBase)
- {
- if (playerBase == null) return;
- ReferenceHub npc = playerBase.Owner;
- playerBase.Stoptrack(true);
- MusicNpc.Remove(npc.nicknameSync.Network_myNickSync);
- CustomNetworkManager.TypedSingleton.OnServerDisconnect(npc.connectionToClient);
- Player.Dictionary.Remove(npc.gameObject);
- UnityEngine.Object.Destroy(npc.gameObject);
- }
- ///
- /// 向所有玩家播放音频
- ///
- /// 音频文件
- /// NPC名称
- ///
- public AudioPlayerBase Play(string musicFile, string npcName)
- {
- return Play(musicFile, npcName, -1);
- }
- ///
- /// 向一名玩家播放音频
- ///
- /// 音频文件
- /// NPC名称
- /// 传播距离检测源头玩家(可null,null时是NPC)
- ///
- public AudioPlayerBase Play(string musicFile, string npcName, FramePlayer source)
- {
- return Play(musicFile, npcName, source, 0);
- }
- ///
- /// NPC向玩家播放音频
- ///
- /// 音频文件
- /// NPC名称
- /// 传播距离(-1时是全部玩家,0时是源头玩家)
- ///
- public AudioPlayerBase Play(string musicFile, string npcName,float distance)
- {
- return Play(musicFile, npcName, null, distance);
- }
- ///
- /// 在多少米内向玩家播放音频
- ///
- /// 音频文件
- /// NPC名称
- /// 传播距离检测源头玩家(可null,null时是NPC)
- /// 传播距离(-1时是全部玩家,0时是源头玩家)
- ///
- public AudioPlayerBase Play(string musicFile, string npcName, FramePlayer source, float distance)
- {
- return Play(musicFile, npcName, null, source, distance, null, false, 80, false);
- }
- ///
- /// 播放音频
- ///
- /// 音频文件
- /// NPC名称
- /// 播放事件(可null)
- /// 传播距离检测源头玩家(可null,null时是NPC)
- /// 传播距离(-1时是全部玩家,0时是源头玩家)
- /// 额外可接收音频的玩家(可null)
- /// [弃用]是否覆盖播放
- /// 音量大小
- /// 是否循环
- ///
- public AudioPlayerBase Play(string musicFile, string npcName, TrackEvent? trackEvent, FramePlayer source, float distance, FramePlayer[] extraPlay, bool isSole = false, float volume = 80, bool isLoop = false)
- {
- AudioPlayerBase audioPlayerBase = null;
- try
- {
- if (trackEvent.HasValue)
- {
- OnTrackLoaded += trackEvent.Value.TrackLoaded;
- }
-
- ReferenceHub npc = CreateMusicNpc(npcName);
- audioPlayerBase = Get(npc);
-
- if (distance != -1)
- {
- if (source != null)
- {
- if (distance == 0)
- {
- audioPlayerBase.BroadcastTo.Add(npc.PlayerId);
- }
- else
- {
- audioPlayerBase.BroadcastTo = FramePlayer.List.Where(p => Vector3.Distance(p.ExPlayer.Position, source.ExPlayer.Position) <= distance).Select((s) => s.ExPlayer.Id).ToList();
- }
- }
-
- if (extraPlay != null)
- {
- foreach (var player in extraPlay)
- {
- if (!audioPlayerBase.BroadcastTo.Contains(player.ExPlayer.Id))
- {
- audioPlayerBase.BroadcastTo.Add(player.ExPlayer.Id);
- }
- }
- }
- }
-
- audioPlayerBase.CurrentPlay = $"{Paths.Plugins}/{Server.Port}/YongAnPluginData/{musicFile}.ogg";
- audioPlayerBase.Volume = volume;
- audioPlayerBase.Loop = isLoop;
- audioPlayerBase.Play(-1);
- }
- catch (Exception)
- {
- Stop(audioPlayerBase);
- }
- return audioPlayerBase;
- }
-
- ///
- /// 播放音频(Url)
- ///
- /// 音频文件
- /// NPC名称
- /// 播放事件(可null)
- /// 传播距离检测源头玩家(可null,null时是NPC)
- /// 传播距离(-1时是全部玩家,0时是源头玩家)
- /// 额外可接收音频的玩家(可null)
- /// [弃用]是否覆盖播放
- /// 音量大小
- /// 是否循环
- ///
- public AudioPlayerBase PlayUrl(string musicUrl, string npcName, TrackEvent? trackEvent, FramePlayer source, float distance, FramePlayer[] extraPlay, bool isSole = false, float volume = 80, bool isLoop = false)
- {
- AudioPlayerBase audioPlayerBase = null;
- try
- {
- if (trackEvent.HasValue)
- {
- OnTrackLoaded += trackEvent.Value.TrackLoaded;
- }
-
- ReferenceHub npc = CreateMusicNpc(npcName);
- audioPlayerBase = Get(npc);
-
- if (distance != -1)
- {
- if (source != null)
- {
- if (distance == 0)
- {
- audioPlayerBase.BroadcastTo.Add(npc.PlayerId);
- }
- else
- {
- audioPlayerBase.BroadcastTo = FramePlayer.List.Where(p => Vector3.Distance(p.ExPlayer.Position, source.ExPlayer.Position) <= distance).Select((s) => s.ExPlayer.Id).ToList();
- }
- }
-
- if (extraPlay != null)
- {
- foreach (var player in extraPlay)
- {
- if (!audioPlayerBase.BroadcastTo.Contains(player.ExPlayer.Id))
- {
- audioPlayerBase.BroadcastTo.Add(player.ExPlayer.Id);
- }
- }
- }
- }
-
- audioPlayerBase.CurrentPlay = musicUrl;
- audioPlayerBase.Volume = volume;
- audioPlayerBase.Loop = isLoop;
- audioPlayerBase.AllowUrl = true;
- audioPlayerBase.Play(-1);
- }
- catch (Exception)
- {
- Stop(audioPlayerBase);
- }
- return audioPlayerBase;
- }
- }
- public readonly struct TrackEvent(TrackLoaded trackLoaded)
- {
- public TrackLoaded TrackLoaded { get; } = trackLoaded;
- }
-}
\ No newline at end of file
diff --git a/Roles/Properties/CustomRolePlusProperties.cs b/Roles/Properties/CustomRolePlusProperties.cs
deleted file mode 100644
index d616143..0000000
--- a/Roles/Properties/CustomRolePlusProperties.cs
+++ /dev/null
@@ -1,17 +0,0 @@
-namespace YongAnFrame.Roles.Properties
-{
- ///
- /// 自定义角色玩家数据
- ///
- public class CustomRolePlusProperties
- {
- ///
- /// 获取或设置自定义角色的技能管理器
- ///
- public SkillManager[] SkillManagers { get; set; }
- ///
- /// 获取或设置自定义角色是否正常死亡
- ///
- public bool IsDeathHandling { get; set; }
- }
-}
diff --git a/Roles/Properties/SkillProperties.cs b/Roles/Properties/SkillProperties.cs
deleted file mode 100644
index 0d51d2d..0000000
--- a/Roles/Properties/SkillProperties.cs
+++ /dev/null
@@ -1,30 +0,0 @@
-namespace YongAnFrame.Roles.Properties
-{
- public readonly struct SkillProperties(string name, string statement, string description, float activeMaxTime, float burialMaxTime, ItemType useItem = ItemType.Coin)
- {
- ///
- /// 获取技能的名称
- ///
- public string Name { get; } = name;
- ///
- /// 获取技能的绑定物品
- ///
- public ItemType UseItem { get; } = useItem;
- ///
- /// 获取技能的发动描述
- ///
- public string Statement { get; } = statement;
- ///
- /// 获取技能的介绍
- ///
- public string Description { get; } = description;
- ///
- /// 获取技能的最大作用时间
- ///
- public float ActiveMaxTime { get; } = activeMaxTime;
- ///
- /// 获取技能的最大冷却时间
- ///
- public float BurialMaxTime { get; } = burialMaxTime;
- }
-}
diff --git a/Roles/SkillManager.cs b/Roles/SkillManager.cs
deleted file mode 100644
index 48049f3..0000000
--- a/Roles/SkillManager.cs
+++ /dev/null
@@ -1,119 +0,0 @@
-using MEC;
-using System.Collections.Generic;
-using YongAnFrame.Players;
-using YongAnFrame.Roles.Interfaces;
-using YongAnFrame.Roles.Properties;
-using static YongAnFrame.Roles.MusicManager;
-
-namespace YongAnFrame.Roles
-{
- ///
- /// 技能控制器
- ///
- ///
- ///
- ///
- public sealed class SkillManager(FramePlayer fPlayer, ISkill skill, byte id)
- {
- ///
- /// 获取或设置技能的ID
- ///
- public byte Id { get; } = id;
- private ISkillActiveStart SkillActiveStart
- {
- get
- {
- if (skill is ISkillActiveStart skillActiveStart)
- {
- return skillActiveStart;
- }
- return null;
- }
- }
- private ISkillActiveEnd SkillActiveEnd
- {
- get
- {
- if (skill is ISkillActiveEnd skillActiveEnd)
- {
- return skillActiveEnd;
- }
- return null;
- }
- }
- private ISkillBurialEnd SkillBurialEnd
- {
- get
- {
- if (skill is ISkillBurialEnd skillBurialEnd)
- {
- return skillBurialEnd;
- }
- return null;
- }
- }
- ///
- /// 获取技能的属性
- ///
- public SkillProperties SkillProperties { get => skill.SkillProperties[Id]; }
-
- ///
- /// 获取或设置技能是否行动
- ///
- public bool IsActive { get => ActiveRemainingTime > 0; }
- ///
- /// 获取或设置技能是否冷却
- ///
- public bool IsBurial { get => BurialRemainingTime > 0; }
- ///
- /// 获取或设置技能的行动时间
- ///
- public float ActiveRemainingTime { get; private set; }
- ///
- /// 获取或设置技能的冷却时间
- ///
- public float BurialRemainingTime { get; private set; }
-
- private CoroutineHandle coroutineHandle;
-
-
- ///
- /// 使用技能
- ///
- ///
- /// 有计时任务会直接覆盖
- ///
- public void Run()
- {
- if (coroutineHandle != null)
- {
- Timing.KillCoroutines(coroutineHandle);
- }
-
- ActiveRemainingTime = SkillProperties.ActiveMaxTime;
- BurialRemainingTime = SkillProperties.BurialMaxTime;
-
- coroutineHandle = Timing.RunCoroutine(Timer());
- }
-
- private IEnumerator Timer()
- {
- string musicFileName = SkillActiveStart?.ActiveStart(fPlayer, Id);
- if (musicFileName != null) Instance.Play(musicFileName, $"技能发动语音", fPlayer, 10);
- while (IsActive)
- {
- ActiveRemainingTime--;
- yield return Timing.WaitForSeconds(1f);
- }
- musicFileName = SkillActiveEnd?.ActiveEnd(fPlayer, Id);
- if (musicFileName != null) Instance.Play(musicFileName, $"技能结束语音", fPlayer, 10);
- while (IsBurial)
- {
- BurialRemainingTime--;
- yield return Timing.WaitForSeconds(1f);
- }
- musicFileName = SkillBurialEnd?.BurialEnd(fPlayer, Id);
- if (musicFileName != null) Instance.Play(musicFileName, $"技能准备好语音", fPlayer, 10);
- }
- }
-}
diff --git a/Translation.cs b/Translation.cs
index 6eb1ac0..762aa4d 100644
--- a/Translation.cs
+++ b/Translation.cs
@@ -4,14 +4,14 @@
namespace YongAnFrame
{
///
- /// 插件的翻译
+ /// 的翻译
///
public sealed class Translation : ITranslation
{
///
- /// BDNT(Bypass Do Not Track)协议文本
+ /// 获取或设置BDNT(Bypass Do Not Track)协议文本
///
[Description("BDNT(Bypass Do Not Track)协议文本")]
- public string BypassDoNotTrack { get; set; } = "BDNT(Bypass Do Not Track)协议\n根据VSR(Verified Server Rules) 8.11,所以开启DNT(Do Not Track)的玩家不会进行非服务器安全性的游戏数据收集或保存。\n根据VSR 8.11.5,所以只有签署BDNT的玩家才会对DNT相关的规则不适用。\n根据VSR 8.11.5.3,所以欲签署BDNT的玩家有知晓收集或保存数据内容的权利。\n|||如果你看不懂BDNT协议的条例请不要签署|||\n1.你将会被收集SteamID用来保存等级和称号数据,这个条例收集的数据是公开展示的,任何人都可以访问!\n2.签署玩家依然有请求删除收集或保存数据的权利,请求之后你依然有24小时可以撤销请求(注意!删除数据是不可逆的)!";
+ public string BypassDoNotTrack { get; set; } = "BDNT(Bypass Do Not Track)协议\n根据VSR(Verified Server Rules) 8.11,所以开启DNT(Do Not Track)的玩家不会进行非服务器安全性的游戏数据收集或保存。\n根据VSR 8.11.5,所以只有签署BDNT的玩家才会对DNT相关的规则不适用。\n根据VSR 8.11.5.3,所以需要签署BDNT的玩家有知晓收集或保存数据内容的权利。\n|||如果你看不懂BDNT协议的条例请不要签署|||\n1.你将会被收集SteamID用来保存等级和称号数据,这个条例收集的数据是公开展示的,任何人都可以访问!\n2.签署玩家依然有请求删除收集或保存数据的权利,请求之后你依然有24小时可以撤销请求(注意!删除数据是不可逆的)!";
}
}
diff --git a/YongAnFrame.csproj b/YongAnFrame.csproj
index ef6366c..beeb5cf 100644
--- a/YongAnFrame.csproj
+++ b/YongAnFrame.csproj
@@ -1,145 +1,80 @@
-
-
-
+
- Debug
- AnyCPU
- {B036C1E0-4D95-487C-BCA3-32AF84D820EA}
- Library
- Properties
- YongAnFrame
- YongAnFrame
+ enable
latest
- v4.8
- 512
- true
-
-
- true
- full
- false
- bin\Debug\
- DEBUG;TRACE
- prompt
- 4
- bin\Debug\YongAnFrame.xml
-
-
- pdbonly
- true
- bin\Release\
- TRACE
- prompt
- 4
-
-
- embedded
+ net48
+ x64
+ True
+ True
+ zh-Hans
+ False
+ 1.0.0-beta6
+ YongAn404
+ $(Authors)
+ Copyright © YongAn404
+ $(AssemblyName)
+ 1.0.0.0
+ 1.0.0.0
+ disable
+ True
+ LGPL-3.0-only
+ README.md
+ git
+ EXILED;SCPSL;SCP:SL
+ https://github.com/YongAn404/YongAnFrame
+ https://github.com/YongAn404/YongAnFrame
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ True
+ \
+
+
+ True
+ lib\$(TargetFramework)\HintServiceMeow-Exiled.dll
+
+
+
+
+
+
+
+
- lib\net48\Assembly-CSharp-firstpass.dll
-
-
- packages\ExMod.Exiled.9.0.0-beta.1\lib\net48\Assembly-CSharp-Publicized.dll
- False
+ lib\$(TargetFramework)\Assembly-CSharp-firstpass.dll
False
-
- packages\ExMod.Exiled.9.0.0-beta.1\lib\net48\CommandSystem.Core.dll
-
-
- packages\ExMod.Exiled.9.0.0-beta.1\lib\net48\Exiled.API.dll
-
-
- packages\ExMod.Exiled.9.0.0-beta.1\lib\net48\Exiled.CreditTags.dll
-
-
- packages\ExMod.Exiled.9.0.0-beta.1\lib\net48\Exiled.CustomItems.dll
-
-
- packages\ExMod.Exiled.9.0.0-beta.1\lib\net48\Exiled.CustomRoles.dll
-
-
- packages\ExMod.Exiled.9.0.0-beta.1\lib\net48\Exiled.Events.dll
-
-
- packages\ExMod.Exiled.9.0.0-beta.1\lib\net48\Exiled.Loader.dll
-
-
- packages\ExMod.Exiled.9.0.0-beta.1\lib\net48\Exiled.Permissions.dll
+
+ lib\$(TargetFramework)\HintServiceMeow-Exiled.dll
- lib\net48\Mirror.dll
-
-
- packages\ExMod.Exiled.9.0.0-beta.1\lib\net48\NorthwoodLib.dll
-
-
- packages\ExMod.Exiled.9.0.0-beta.1\lib\net48\PluginAPI.dll
-
-
- False
- lib\net48\SCPSLAudioApi.dll
+ lib\$(TargetFramework)\Mirror.dll
+ False
-
-
-
-
-
-
-
-
- lib\net48\UnityEngine.CoreModule.dll
+ lib\$(TargetFramework)\UnityEngine.CoreModule.dll
+ False
- lib\net48\UnityEngine.PhysicsModule.dll
-
-
- packages\ExMod.Exiled.9.0.0-beta.1\lib\net48\YamlDotNet.dll
+ lib\$(TargetFramework)\UnityEngine.PhysicsModule.dll
+ False
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/YongAnFrame.nuspec b/YongAnFrame.nuspec
deleted file mode 100644
index 5586d46..0000000
--- a/YongAnFrame.nuspec
+++ /dev/null
@@ -1,22 +0,0 @@
-
-
-
- $id$
- 1.0.0-beta5
- $title$
- $author$
- true
- LGPL-3.0-only
-
- docs\README.md
- https://github.com/SCP-SL-Plugin-YongAnTeam/YongAnFrame
- $description$
- $version$
- $copyright$
- EXILED SCPSL SCP:SL
-
-
-
-
-
-
\ No newline at end of file
diff --git a/YongAnFramePlugin.cs b/YongAnFramePlugin.cs
index 4201c13..0fb00fb 100644
--- a/YongAnFramePlugin.cs
+++ b/YongAnFramePlugin.cs
@@ -1,10 +1,11 @@
using Exiled.API.Enums;
using Exiled.API.Features;
-using Exiled.API.Interfaces;
-using Exiled.Events.Commands.Reload;
-using SCPSLAudioApi;
-using YongAnFrame.Players;
-using YongAnFrame.Roles;
+using HarmonyLib;
+using System;
+using YongAnFrame.Features;
+using YongAnFrame.Features.Players;
+using YongAnFrame.Features.Roles;
+using YongAnFrame.Patch;
namespace YongAnFrame
{
@@ -13,26 +14,39 @@ namespace YongAnFrame
///
public sealed class YongAnFramePlugin : Plugin
{
- private static YongAnFramePlugin instance;
+ private static YongAnFramePlugin? instance;
///
/// 获取单例
///
- public static YongAnFramePlugin Instance => instance;
- ///
- public override PluginPriority Priority => PluginPriority.First;
+ public static YongAnFramePlugin Instance
+ {
+ get
+ {
+ if (instance is null)
+ {
+ throw new InvalidCastException("YongAnFramePlugin实例无效");
+ }
+ return instance;
+ }
+ }
+ ///
+ /// 获取实例
+ ///
+ public Harmony Harmony { get; } = new Harmony("YongAnFrame.Harmony");
+
///
- public override bool IgnoreRequiredVersionCheck => true;
+ public override PluginPriority Priority => PluginPriority.Higher;
///
public override void OnEnabled()
{
instance = this;
Log.Info("\r\n __ __ ______ __ __ ______ ______ __ __ \r\n/\\ \\_\\ \\ /\\ __ \\ /\\ \"-.\\ \\ /\\ ___\\ /\\ __ \\ /\\ \"-.\\ \\ \r\n\\ \\____ \\ \\ \\ \\/\\ \\ \\ \\ \\-. \\ \\ \\ \\__ \\ \\ \\ __ \\ \\ \\ \\-. \\ \r\n \\/\\_____\\ \\ \\_____\\ \\ \\_\\\\\"\\_\\ \\ \\_____\\ \\ \\_\\ \\_\\ \\ \\_\\\\\"\\_\\ \r\n \\/_____/ \\/_____/ \\/_/ \\/_/ \\/_____/ \\/_/\\/_/ \\/_/ \\/_/ \r\n \r\n ______ ______ ______ __ __ ______ \r\n/\\ ___\\ /\\ == \\ /\\ __ \\ /\\ \"-./ \\ /\\ ___\\ \r\n\\ \\ __\\ \\ \\ __< \\ \\ __ \\ \\ \\ \\-./\\ \\ \\ \\ __\\ \r\n \\ \\_\\ \\ \\_\\ \\_\\ \\ \\_\\ \\_\\ \\ \\_\\ \\ \\_\\ \\ \\_____\\ \r\n \\/_/ \\/_/ /_/ \\/_/\\/_/ \\/_/ \\/_/ \\/_____/ \r\n \r\n ");
- Log.Info("============System============");
+ PathManager.CheckPath();
FramePlayer.SubscribeStaticEvents();
- MusicManager.Instance.Init();
CustomRolePlus.SubscribeStaticEvents();
- Startup.SetupDependencies();
+ AddLogPatch.StartTask();
+ Harmony.PatchAll();
base.OnEnabled();
}
@@ -42,6 +56,7 @@ public override void OnDisabled()
instance = null;
FramePlayer.UnsubscribeStaticEvents();
CustomRolePlus.UnsubscribeStaticEvents();
+ Harmony.UnpatchAll();
base.OnDisabled();
}
}
diff --git a/docs/docfx.json b/docs/docfx.json
index a4a8c88..3a4b1f4 100644
--- a/docs/docfx.json
+++ b/docs/docfx.json
@@ -34,6 +34,7 @@
"files": [
"api/**.yml",
"api/index.md",
+ "articles/*.md",
"toc.yml",
"*.md"
]
diff --git a/docs/toc.yml b/docs/toc.yml
index 3ff9579..5104f91 100644
--- a/docs/toc.yml
+++ b/docs/toc.yml
@@ -1,5 +1,7 @@
+- name: Home
+ href: index.html
- name: Articles
- href: https://github.com/SCP-SL-Plugin-YongAnTeam/YongAnFrame/wiki/%5BCN%5D%E5%BC%80%E5%8F%91%E6%95%99%E7%A8%8B
+ href: articles/index.html
- name: API Documentation
href: api/YongAnFrame.html
- name: GitHub
diff --git a/lib/net48/Assembly-CSharp-firstpass.dll b/lib/net48/Assembly-CSharp-firstpass.dll
index 9383cfa..040e8b6 100644
Binary files a/lib/net48/Assembly-CSharp-firstpass.dll and b/lib/net48/Assembly-CSharp-firstpass.dll differ
diff --git a/lib/net48/HintServiceMeow-Exiled.dll b/lib/net48/HintServiceMeow-Exiled.dll
new file mode 100644
index 0000000..cb7623d
Binary files /dev/null and b/lib/net48/HintServiceMeow-Exiled.dll differ
diff --git a/lib/net48/Mirror.dll b/lib/net48/Mirror.dll
index bf8dc56..d469c7a 100644
Binary files a/lib/net48/Mirror.dll and b/lib/net48/Mirror.dll differ
diff --git a/lib/net48/SCPSLAudioApi.dll b/lib/net48/SCPSLAudioApi.dll
deleted file mode 100644
index ce14bf2..0000000
Binary files a/lib/net48/SCPSLAudioApi.dll and /dev/null differ
diff --git a/lib/net48/UnityEngine.CoreModule.dll b/lib/net48/UnityEngine.CoreModule.dll
index 4c01b22..c2f8e2a 100644
Binary files a/lib/net48/UnityEngine.CoreModule.dll and b/lib/net48/UnityEngine.CoreModule.dll differ
diff --git a/lib/net48/UnityEngine.PhysicsModule.dll b/lib/net48/UnityEngine.PhysicsModule.dll
index 14c31ba..4c918bd 100644
Binary files a/lib/net48/UnityEngine.PhysicsModule.dll and b/lib/net48/UnityEngine.PhysicsModule.dll differ
diff --git a/packages.config b/packages.config
deleted file mode 100644
index e06816f..0000000
--- a/packages.config
+++ /dev/null
@@ -1,4 +0,0 @@
-
-
-
-
\ No newline at end of file