diff --git a/EXILED/Exiled.API/Features/Npc.cs b/EXILED/Exiled.API/Features/Npc.cs
index 82a6c73745..e79038065c 100644
--- a/EXILED/Exiled.API/Features/Npc.cs
+++ b/EXILED/Exiled.API/Features/Npc.cs
@@ -16,7 +16,6 @@ namespace Exiled.API.Features
using CentralAuth;
using CommandSystem;
using Exiled.API.Enums;
- using Exiled.API.Extensions;
using Exiled.API.Features.Components;
using Exiled.API.Features.Roles;
using Footprinting;
@@ -142,6 +141,7 @@ public override Vector3 Position
/// The userID of the NPC.
/// The position to spawn the NPC.
/// The spawned.
+ [Obsolete("This metod is marked as obsolet due to a bug that make player have the same id. Use Npc.Spawn(string) instead")]
public static Npc Spawn(string name, RoleTypeId role, int id = 0, string userId = PlayerAuthenticationManager.DedicatedId, Vector3? position = null)
{
GameObject newObject = UnityEngine.Object.Instantiate(Mirror.NetworkManager.singleton.playerPrefab);
@@ -208,18 +208,118 @@ public static Npc Spawn(string name, RoleTypeId role, int id = 0, string userId
return npc;
}
+ ///
+ /// Spawns an NPC based on the given parameters.
+ ///
+ /// The name of the NPC.
+ /// The RoleTypeId of the NPC, defaulting to None.
+ /// Whether the NPC should be ignored by round ending checks.
+ /// The userID of the NPC for authentication. Defaults to the Dedicated ID.
+ /// The position where the NPC should spawn. If null, the default spawn location is used.
+ /// The spawned.
+ public static Npc Spawn(string name, RoleTypeId role = RoleTypeId.None, bool ignored = false, string userId = PlayerAuthenticationManager.DedicatedId, Vector3? position = null)
+ {
+ GameObject newObject = UnityEngine.Object.Instantiate(Mirror.NetworkManager.singleton.playerPrefab);
+
+ Npc npc = new(newObject)
+ {
+ IsNPC = true,
+ };
+
+ FakeConnection fakeConnection = new(npc.Id);
+
+ try
+ {
+ if (userId == PlayerAuthenticationManager.DedicatedId)
+ {
+ npc.ReferenceHub.authManager.SyncedUserId = userId;
+ try
+ {
+ npc.ReferenceHub.authManager.InstanceMode = ClientInstanceMode.DedicatedServer;
+ }
+ catch (Exception e)
+ {
+ Log.Debug($"Ignore: {e.Message}");
+ }
+ }
+ else
+ {
+ npc.ReferenceHub.authManager.InstanceMode = ClientInstanceMode.Unverified;
+ npc.ReferenceHub.authManager._privUserId = userId == string.Empty ? $"Dummy-{npc.Id}@localhost" : userId;
+ }
+ }
+ catch (Exception e)
+ {
+ Log.Debug($"Ignore: {e.Message}");
+ }
+
+ try
+ {
+ npc.ReferenceHub.roleManager.InitializeNewRole(RoleTypeId.None, RoleChangeReason.None);
+ }
+ catch (Exception e)
+ {
+ Log.Debug($"Ignore: {e.Message}");
+ }
+
+ NetworkServer.AddPlayerForConnection(fakeConnection, newObject);
+
+ npc.ReferenceHub.nicknameSync.Network_myNickSync = name;
+ Dictionary.Add(newObject, npc);
+
+ Timing.CallDelayed(0.5f, () =>
+ {
+ npc.Role.Set(role, SpawnReason.RoundStart, position is null ? RoleSpawnFlags.All : RoleSpawnFlags.AssignInventory);
+
+ if (position is not null)
+ npc.Position = position.Value;
+ });
+
+ if (ignored)
+ Round.IgnoredPlayers.Add(npc.ReferenceHub);
+
+ return npc;
+ }
+
+ ///
+ /// Destroys all NPCs currently spawned.
+ ///
+ public static void DestroyAll()
+ {
+ foreach (Npc npc in List)
+ npc.Destroy();
+ }
+
///
/// Destroys the NPC.
///
public void Destroy()
{
- NetworkConnectionToClient conn = ReferenceHub.connectionToClient;
- if (ReferenceHub._playerId.Value <= RecyclablePlayerId._autoIncrement)
- ReferenceHub._playerId.Destroy();
- ReferenceHub.OnDestroy();
- CustomNetworkManager.TypedSingleton.OnServerDisconnect(conn);
- Dictionary.Remove(GameObject);
- Object.Destroy(GameObject);
+ try
+ {
+ Round.IgnoredPlayers.Remove(ReferenceHub);
+ NetworkConnectionToClient conn = ReferenceHub.connectionToClient;
+ ReferenceHub.OnDestroy();
+ CustomNetworkManager.TypedSingleton.OnServerDisconnect(conn);
+ Dictionary.Remove(GameObject);
+ Object.Destroy(GameObject);
+ }
+ catch (Exception e)
+ {
+ Log.Error($"Error while destroying a NPC: {e.Message}");
+ }
+ }
+
+ ///
+ /// Schedules the destruction of the NPC after a delay.
+ ///
+ /// The delay in seconds before the NPC is destroyed.
+ public void LateDestroy(float time)
+ {
+ Timing.CallDelayed(time, () =>
+ {
+ this?.Destroy();
+ });
}
}
}