using System; using System.Collections.Generic; using Oxide.Core; using Oxide.Core.Libraries.Covalence; using UnityEngine; namespace Oxide.Plugins { [Info("SetSpawn", "Kalvi", "1.0.0")] [Description("Plugin for setting and teleporting to a custom spawn point with delay and first-time spawn override")] public class SetSpawn : CovalencePlugin { private Vector3? spawnPoint = null; private const string permAdmin = "setspawn.admin"; private const string permUse = "setspawn.use"; private float teleportDelay; private Dictionary teleportTimers = new Dictionary(); private Dictionary initialPositions = new Dictionary(); private HashSet playersWhoSpawned = new HashSet(); private void LoadSpawnPoint() { var storedData = Interface.Oxide.DataFileSystem.ReadObject("SetSpawn"); if (storedData != null && storedData.SpawnPoint != null) { spawnPoint = new Vector3(storedData.SpawnPoint[0], storedData.SpawnPoint[1], storedData.SpawnPoint[2]); } } private void SaveSpawnPoint() { var storedData = new StoredData { SpawnPoint = spawnPoint.HasValue ? new float[] { spawnPoint.Value.x, spawnPoint.Value.y, spawnPoint.Value.z } : null }; Interface.Oxide.DataFileSystem.WriteObject("SetSpawn", storedData); } protected override void LoadDefaultConfig() { Config["TeleportDelaySeconds"] = 10f; // Default value SaveConfig(); } private void OnServerInitialized() { teleportDelay = GetConfig("TeleportDelaySeconds", 10f); LoadSpawnPoint(); } private T GetConfig(string key, T defaultValue) { if (Config[key] == null) { Config[key] = defaultValue; } return (T)Convert.ChangeType(Config[key], typeof(T)); } void Init() { permission.RegisterPermission(permAdmin, this); permission.RegisterPermission(permUse, this); LoadDefaultConfig(); OnServerInitialized(); // Load config and data lang.RegisterMessages(new Dictionary { ["NoPermission"] = "You don't have permission to use this command.", ["SpawnSet"] = "Spawn point set to {0}.", ["Teleporting"] = "Teleporting to spawn point in {0} seconds...", ["Teleported"] = "You have been teleported to the spawn point.", ["NoSpawnSet"] = "Spawn point has not been set.", ["TeleportCancelled"] = "Teleport cancelled due to movement.", ["PluginReloaded"] = "Plugin has been reloaded successfully.", ["ReloadFailed"] = "Failed to reload the plugin.", ["DelaySet"] = "Teleport delay has been set to {0} seconds.", ["InvalidDelay"] = "Invalid delay value. Please enter a positive number." }, this); // Register commands explicitly with unique names AddCommand("setspawn", "SetSpawnCommand"); AddCommand("spawn", "SpawnCommand"); AddCommand("setspawn-reload", "ReloadCommand"); AddCommand("setspawn-delay", "SetDelayCommand"); } private void AddCommand(string command, string methodName) { AddCovalenceCommand(command, methodName); } private void SetSpawnCommand(IPlayer player, string command, string[] args) { if (!player.HasPermission(permAdmin)) { player.Reply(Lang("NoPermission", player.Id)); return; } BasePlayer basePlayer = player.Object as BasePlayer; if (basePlayer != null) { spawnPoint = basePlayer.transform.position; SaveSpawnPoint(); player.Reply(string.Format(Lang("SpawnSet", player.Id), spawnPoint.Value)); Puts($"Spawn point set to {spawnPoint.Value} by {player.Name}"); } } private void SpawnCommand(IPlayer player, string command, string[] args) { if (!player.HasPermission(permUse)) { player.Reply(Lang("NoPermission", player.Id)); return; } BasePlayer basePlayer = player.Object as BasePlayer; if (basePlayer != null) { if (spawnPoint.HasValue) { // Clear any existing teleport timers for this player if (teleportTimers.TryGetValue(basePlayer, out var existingTimer)) { existingTimer.Destroy(); teleportTimers.Remove(basePlayer); initialPositions.Remove(basePlayer); player.Reply(Lang("TeleportCancelled", player.Id)); Puts($"Previous teleportation of {player.Name} was cancelled."); } player.Reply(string.Format(Lang("Teleporting", player.Id), teleportDelay)); Vector3 initialPosition = basePlayer.transform.position; initialPositions[basePlayer] = initialPosition; bool hasMoved = false; Timer movementTimer = null; movementTimer = timer.Every(0.1f, () => { if (initialPositions.ContainsKey(basePlayer)) { Vector3 currentPosition = basePlayer.transform.position; if (Vector3.Distance(initialPositions[basePlayer], currentPosition) > 0.1f) { hasMoved = true; movementTimer.Destroy(); // Destroy movement timer if moved if (teleportTimers.TryGetValue(basePlayer, out var teleportTimer)) { teleportTimer.Destroy(); teleportTimers.Remove(basePlayer); initialPositions.Remove(basePlayer); player.Reply(Lang("TeleportCancelled", player.Id)); Puts($"Teleportation of {player.Name} to spawn was cancelled due to movement."); } } } }); Timer teleportTimer = timer.Once(teleportDelay, () => { if (!hasMoved) { basePlayer.Teleport(spawnPoint.Value); player.Reply(Lang("Teleported", player.Id)); Puts($"Player {player.Name} teleported to spawn at {spawnPoint.Value}"); teleportTimers.Remove(basePlayer); movementTimer.Destroy(); // Ensure movement timer is destroyed after teleport initialPositions.Remove(basePlayer); } }); teleportTimers[basePlayer] = teleportTimer; } else { player.Reply(Lang("NoSpawnSet", player.Id)); } } } private void ReloadCommand(IPlayer player, string command, string[] args) { if (!player.HasPermission(permAdmin)) { player.Reply(Lang("NoPermission", player.Id)); return; } try { Unload(); OnServerInitialized(); // Reload configuration player.Reply(Lang("PluginReloaded", player.Id)); Puts($"Plugin reloaded by {player.Name}"); } catch (Exception ex) { player.Reply(Lang("ReloadFailed", player.Id)); Puts($"Failed to reload plugin: {ex.Message}"); } } private void SetDelayCommand(IPlayer player, string command, string[] args) { if (!player.HasPermission(permAdmin)) { player.Reply(Lang("NoPermission", player.Id)); return; } if (args.Length < 1 || !float.TryParse(args[0], out float newDelay) || newDelay <= 0) { player.Reply(Lang("InvalidDelay", player.Id)); return; } teleportDelay = newDelay; Config["TeleportDelaySeconds"] = teleportDelay; SaveConfig(); player.Reply(string.Format(Lang("DelaySet", player.Id), teleportDelay)); Puts($"Teleport delay set to {teleportDelay} seconds by {player.Name}"); } void OnPlayerRespawn(BasePlayer player) { if (spawnPoint.HasValue && !player.IsSleeping() && player.net?.connection != null) { NextTick(() => { if (!playersWhoSpawned.Contains(player.userID)) { player.Teleport(spawnPoint.Value); Puts($"Player {player.displayName} teleported to spawn at {spawnPoint.Value} on first connection."); playersWhoSpawned.Add(player.userID); } else { player.Teleport(spawnPoint.Value); Puts($"Player {player.displayName} teleported to spawn at {spawnPoint.Value} after respawn."); } }); } } void Unload() { spawnPoint = null; foreach (var timer in teleportTimers.Values) { timer.Destroy(); } teleportTimers.Clear(); } private string Lang(string key, string id = null) => lang.GetMessage(key, this, id); private class StoredData { public float[] SpawnPoint { get; set; } } } }