Иконка ресурса

FREE SettlementManager : 0.1.0 0.1.0

Нет прав для скачивания
C#:
// Все права принадлежат дискорд сообществу https://discord.gg/VgNHPpNrz6
using System;
using System.Collections.Generic;
using System.Linq;
using Newtonsoft.Json;
using Oxide.Core;
using Oxide.Core.Configuration;
using Oxide.Core.Libraries.Covalence;
using Oxide.Core.Plugins;
using UnityEngine;

namespace Oxide.Plugins
{
    /// <summary>
    /// SettlementManager provides a framework for players to build AI‑driven settlements
    /// on their Rust server.A settlement consists of a defined zone (via ZoneManager)
    /// and a roster of NPC workers.Workers can be assigned specialised roles such
    /// as builder, farmer, miner, guard or vendor.This plugin integrates with
    /// existing AI plugins (PersonalBuilder, PersonalFarmer, PersonalNPC, etc.) to
    /// delegate the heavy lifting of pathfinding and task execution.The goal of
    /// this plugin is to orchestrate these workers, track settlement resources and
    /// provide a simple command interface for players.///
    /// NOTE: This file is a **skeleton** to demonstrate the concept.Many of the
    /// functions here contain TODOs where detailed logic should be implemented./// Use this as a starting point to build a fully‑fledged plugin./// </summary>
    [Info("Settlement Manager", "AI Companion", "0.1.0")]
    [Description("Allows players to create AI managed settlements with workers.")]
    public class SettlementManager : CovalencePlugin
    {
        #region Plugin References

        // Existing plugins we depend on.These will be assigned automatically
        // when the server loads the plugin.You should ensure that the
        // referenced plugins are installed and loaded or these variables will be null.[PluginReference] private Plugin PersonalNPC;
        [PluginReference] private Plugin PersonalBuilder;
        [PluginReference] private Plugin PersonalFarmer;
        [PluginReference] private Plugin ZoneManager;
        [PluginReference] private Plugin Economics;

        #endregion

        #region Data Models

        /// <summary>
        /// Represents a player‑owned settlement.A settlement has a unique ID,
        /// an owner (steamID) and a collection of workers.Additional state
        /// variables such as resource stores, settlement level or defences can be
        /// added as needed./// </summary>
        public class Settlement
        {
            /// <summary>
            /// Unique identifier for the settlement.This could be a GUID or
            /// simply the owner's ID combined with a counter.It's used to
            /// reference the settlement in ZoneManager./// </summary>
            public string Id;

            /// <summary>
            /// The owner of the settlement (steam ID in string form)./// </summary>
            public string OwnerId;

            /// <summary>
            /// Name of the settlement, chosen by the owner when creating it./// </summary>
            public string Name;

            /// <summary>
            /// List of workers currently assigned to this settlement./// </summary>
            public readonly List<NpcWorker> Workers = new List<NpcWorker>();

            /// <summary>
            /// Coordinates of the settlement centre.This is used when
            /// creating the zone and for workers to navigate relative to the
            /// settlement.It is stored as a string because the data file
            /// format requires serializable types; Vector3 is not serializable
            /// by default./// </summary>
            public string Centre;

            /// <summary>
            /// Radius of the settlement zone.Workers should not wander
            /// outside of this radius when performing tasks./// </summary>
            public float Radius;
        }

        /// <summary>
        /// Enum describing the roles available to workers.Each role corresponds
        /// to an integration with another plugin.New roles can be added here
        /// along with corresponding logic in the spawner./// </summary>
        public enum WorkerRole
        {
            Builder,
            Farmer,
            Guard,
            Vendor,
            Miner,
            Lumberjack
        }

        /// <summary>
        /// Represents an NPC worker in a settlement.It holds the role and
        /// references to any objects needed to control the worker.In a full
        /// implementation you would also persist the worker's last position,
        /// inventory and task queue./// </summary>
        public class NpcWorker
        {
            /// <summary>
            /// Unique ID of the worker (steamID for the NPC).This is
            /// assigned by the underlying plugin that spawns the bot./// </summary>
            public ulong Id;

            /// <summary>
            /// The role of the worker./// </summary>
            public WorkerRole Role;

            /// <summary>
            /// Human‑readable name of the worker.Useful for UI./// </summary>
            public string Name;

            /// <summary>
            /// Reference to the controlling object returned by the plugin./// For example, PersonalNPC returns a PlayerBotController when
            /// spawning a bot.We store this as an object because the type
            /// depends on the plugin used./// </summary>
            public object Controller;
        }

        #endregion

        #region Storage

        // Configuration file for plugin options
        private PluginConfig config;
        private DynamicConfigFile dataFile;

        // All settlements keyed by their unique identifier
        private Dictionary<string, Settlement> settlements = new Dictionary<string, Settlement>();

        #endregion

        #region Configuration

        /// <summary>
        /// Holds all configurable options for the plugin.Exposed values
        /// include settlement radius, max workers per settlement and cost to
        /// create workers.Users can adjust these via the config file./// </summary>
        public class PluginConfig
        {
            [JsonProperty("Settlement radius")]
            public float DefaultSettlementRadius = 50f;

            [JsonProperty("Maximum workers per settlement")]
            public int MaxWorkersPerSettlement = 5;

            [JsonProperty("Worker costs")]
            public Dictionary<WorkerRole, double> WorkerCosts = new Dictionary<WorkerRole, double>
            {
                { WorkerRole.Builder, 1000 },
                { WorkerRole.Farmer, 800 },
                { WorkerRole.Guard, 500 },
                { WorkerRole.Vendor, 1500 },
                { WorkerRole.Miner, 700 },
                { WorkerRole.Lumberjack, 600 }
            };
        }

        protected override void LoadDefaultConfig()
        {
            config = new PluginConfig();
            SaveConfig();
        }

        protected override void LoadConfig()
        {
            base.LoadConfig();
            try
            {
                config = Config.ReadObject<PluginConfig>();
                if (config == null)
                    throw new Exception("Config file is empty");
            }
            catch (Exception)
            {
                PrintWarning("Using default configuration");
                LoadDefaultConfig();
            }
            SaveConfig();
        }

        protected override void SaveConfig()
        {
            Config.WriteObject(config, true);
        }

        #endregion

        #region Initialization

        private void Init()
        {
            // Register chat commands.These provide the core functionality of
            // the plugin.Additional commands can be added as needed.AddCovalenceCommand("settlement.create", nameof(CmdCreateSettlement));
            AddCovalenceCommand("settlement.addworker", nameof(CmdAddWorker));
            AddCovalenceCommand("settlement.status", nameof(CmdSettlementStatus));
            AddCovalenceCommand("settlement.delete", nameof(CmdDeleteSettlement));

            // Load persistent data (settlements)
            dataFile = Interface.Oxide.DataFileSystem.GetFile(Name);
            LoadData();
        }

        /// <summary>
        /// Loads previously stored settlements from the data file.If no
        /// settlements exist, initialises an empty dictionary.In a more
        /// advanced version you could migrate data between versions here./// </summary>
        private void LoadData()
        {
            try
            {
                settlements = dataFile.ReadObject<Dictionary<string, Settlement>>();
            }
            catch
            {
                settlements = new Dictionary<string, Settlement>();
            }
        }

        /// <summary>
        /// Saves settlement data to disk.Call this after making changes
        /// (creating or deleting settlements, adding workers, etc.)./// </summary>
        private void SaveData()
        {
            dataFile.WriteObject(settlements);
        }

        #endregion

        #region Commands

        /// <summary>
        /// Creates a settlement at the player's location.The player becomes the
        /// owner and can subsequently add workers.A zone is created via the
        /// ZoneManager plugin to bound the settlement.Usage: /settlement.create
        /// (creates with default name) or /settlement.create name radius
        /// </summary>
        private void CmdCreateSettlement(IPlayer player, string command, string[] args)
        {
            // Ensure the command was issued by a player (not console)
            if (player.IsServer)
            {
                player.Reply("Only players can create settlements.");
                return;
            }

            var basePlayer = player.Object as BasePlayer;
            if (basePlayer == null)
            {
                player.Reply("Only in‑game players can create settlements.");
                return;
            }

            // Determine settlement name and radius
            string name = args.Length > 0 ? args[0] : $"{player.Name}'s Settlement";
            float radius = config.DefaultSettlementRadius;
            if (args.Length > 1)
            {
                float.TryParse(args[1], out radius);
                if (radius <= 0) radius = config.DefaultSettlementRadius;
            }

            // Generate a unique ID (GUID) for the settlement.We use a guid to
            // avoid collisions across reloads.The owner ID is kept separately.string id = Guid.NewGuid().ToString("N");

            // Create the settlement model
            var settlement = new Settlement
            {
                Id = id,
                OwnerId = player.Id,
                Name = name,
                Centre = basePlayer.transform.position.ToString(),
                Radius = radius
            };
            settlements[id] = settlement;
            SaveData();

            // Create a zone via ZoneManager.We pass a list of rules to
            // disable PVP and building by non‑owners.Additional rules can be
            // configured via the config or by plugin commands.if (ZoneManager != null)
            {
                var rules = new List<string>
                {
                    "no_pvp",        // disable player versus player combat
                    "undestr",       // prevent building destruction
                    "noplayerloot"   // prevent loot boxes being looted by others
                };
                var msg = new List<string>();
                ZoneManager.Call("CreateOrUpdateZone", id, radius, basePlayer.transform.position, new Vector3(), rules, msg);
            }

            player.Reply($"Settlement '{name}' created with radius {radius} at your current location.Use /settlement.addworker role to add workers.");
        }

        /// <summary>
        /// Adds a worker of the specified role to the player's settlement.The
        /// plugin verifies that the settlement exists and that the player is
        /// the owner.It also checks resource costs using the Economics
        /// plugin (if available).Usage: /settlement.addworker role
        /// </summary>
        private void CmdAddWorker(IPlayer player, string command, string[] args)
        {
            if (args.Length == 0)
            {
                player.Reply("You must specify a worker role (builder, farmer, guard, vendor, miner, lumberjack)");
                return;
            }

            // Find the player's settlement.In this demo we assume a player owns only
            // one settlement.If multiple settlements per player are supported,
            // additional logic would be needed to select the correct one.var settlement = settlements.Values.FirstOrDefault(s => s.OwnerId == player.Id);
            if (settlement == null)
            {
                player.Reply("You don't have a settlement.Use /settlement.create first.");
                return;
            }
            if (settlement.Workers.Count >= config.MaxWorkersPerSettlement)
            {
                player.Reply($"Your settlement already has the maximum number of workers ({config.MaxWorkersPerSettlement}).");
                return;
            }

            // Parse the role
            if (!Enum.TryParse(args[0], true, out WorkerRole role))
            {
                player.Reply($"Unknown worker role '{args[0]}'.Valid roles: {string.Join(", ", Enum.GetNames(typeof(WorkerRole)))}");
                return;
            }

            // Check cost and deduct from player's balance (Economics).If the
            // Economics plugin isn't installed the worker is free.double cost = 0;
            config.WorkerCosts.TryGetValue(role, out cost);
            if (Economics != null && cost > 0)
            {
                var balance = (double)Economics.Call("GetPlayerMoney", player.Id);
                if (balance < cost)
                {
                    player.Reply($"You need {cost} coins to hire a {role}, but you only have {balance}.");
                    return;
                }
                Economics.Call("Withdraw", player.Id, cost);
            }

            // Spawn the worker via the appropriate plugin.The details of
            // spawning vary per role and plugin.We encapsulate this logic in
            // SpawnWorker() for clarity.If spawning fails the cost should be
            // refunded.var worker = SpawnWorker(role, player.Object as BasePlayer, settlement);
            if (worker == null)
            {
                player.Reply("Failed to spawn worker.Please check server logs for details.");
                if (Economics != null && cost > 0)
                {
                    // Refund
                    Economics.Call("Deposit", player.Id, cost);
                }
                return;
            }

            settlement.Workers.Add(worker);
            SaveData();

            player.Reply($"Hired {role} '{worker.Name}' for your settlement!");
        }

        /// <summary>
        /// Shows information about the player's settlement: workers, roles and
        /// zone details.Usage: /settlement.status
        /// </summary>
        private void CmdSettlementStatus(IPlayer player, string command, string[] args)
        {
            var settlement = settlements.Values.FirstOrDefault(s => s.OwnerId == player.Id);
            if (settlement == null)
            {
                player.Reply("You don't have a settlement.Use /settlement.create first.");
                return;
            }

            var msg = $"Settlement '{settlement.Name}' (ID: {settlement.Id})\n";
            msg += $"Centre: {settlement.Centre}, Radius: {settlement.Radius}\n";
            msg += $"Workers ({settlement.Workers.Count}/{config.MaxWorkersPerSettlement}):\n";
            if (settlement.Workers.Count == 0)
            {
                msg += "  None\n";
            }
            else
            {
                foreach (var w in settlement.Workers)
                {
                    msg += $"  • {w.Role} – {w.Name} (ID: {w.Id})\n";
                }
            }
            player.Reply(msg);
        }

        /// <summary>
        /// Deletes the player's settlement and despawns all workers.Usage:
        /// /settlement.delete
        /// </summary>
        private void CmdDeleteSettlement(IPlayer player, string command, string[] args)
        {
            var settlement = settlements.Values.FirstOrDefault(s => s.OwnerId == player.Id);
            if (settlement == null)
            {
                player.Reply("You don't have a settlement.");
                return;
            }

            // Despawn workers.We call the appropriate plugin to despawn
            // depending on the worker's role.After despawning we remove
            // their entry from the settlement.foreach (var worker in settlement.Workers)
            {
                try
                {
                    DespawnWorker(worker);
                }
                catch (Exception ex)
                {
                    PrintError($"Error despawning worker {worker.Id}: {ex.Message}");
                }
            }

            // Delete the zone via ZoneManager
            if (ZoneManager != null)
            {
                ZoneManager.Call("EraseZone", settlement.Id);
            }

            settlements.Remove(settlement.Id);
            SaveData();
            player.Reply($"Settlement '{settlement.Name}' deleted.");
        }

        #endregion

        #region Worker Management

        /// <summary>
        /// Spawns a worker of the given role for the specified settlement.This
        /// method uses the appropriate plugin to spawn the bot and returns a
        /// NpcWorker with all necessary info populated.If the spawn fails it
        /// returns null.In a full implementation you should capture
        /// exceptions and handle plugin API changes gracefully./// </summary>
        private NpcWorker SpawnWorker(WorkerRole role, BasePlayer owner, Settlement settlement)
        {
            try
            {
                switch (role)
                {
                    case WorkerRole.Builder:
                        // Spawn a PersonalBuilder bot.The PersonalBuilder plugin
                        // exposes a chat command /pbuilder; however we can call
                        // its API directly to create a bot if it provides one.For
                        // demonstration we call the chat command on behalf of
                        // the player.A production implementation should use
                        // PersonalBuilder.Call("SpawnBot",...) if available.owner.ChatMessage("Spawning builder bot...");
                        owner.SendConsoleCommand("pbuilder");
                        return new NpcWorker
                        {
                            Id = 0, // Unknown because we aren't capturing the bot's ID
                            Role = role,
                            Name = $"BuilderBot_{settlement.Workers.Count + 1}",
                            Controller = null
                        };

                    case WorkerRole.Farmer:
                        owner.ChatMessage("Spawning farmer bot...");
                        owner.SendConsoleCommand("pfarmer");
                        return new NpcWorker
                        {
                            Id = 0,
                            Role = role,
                            Name = $"FarmerBot_{settlement.Workers.Count + 1}",
                            Controller = null
                        };

                    case WorkerRole.Guard:
                        owner.ChatMessage("Spawning guard bot...");
                        owner.SendConsoleCommand("pnpc");
                        owner.SendConsoleCommand("pnpc combat");
                        return new NpcWorker
                        {
                            Id = 0,
                            Role = role,
                            Name = $"GuardBot_{settlement.Workers.Count + 1}",
                            Controller = null
                        };

                    case WorkerRole.Vendor:
                        owner.ChatMessage("Spawning vendor NPC...");
                        return new NpcWorker
                        {
                            Id = 0,
                            Role = role,
                            Name = $"Vendor_{settlement.Workers.Count + 1}",
                            Controller = null
                        };

                    case WorkerRole.Miner:
                        owner.ChatMessage("Spawning miner bot...");
                        owner.SendConsoleCommand("pnpc auto-farm stone");
                        owner.SendConsoleCommand("pnpc auto-farm metal");
                        owner.SendConsoleCommand("pnpc auto-farm enable");
                        return new NpcWorker
                        {
                            Id = 0,
                            Role = role,
                            Name = $"MinerBot_{settlement.Workers.Count + 1}",
                            Controller = null
                        };

                    case WorkerRole.Lumberjack:
                        owner.ChatMessage("Spawning lumberjack bot...");
                        owner.SendConsoleCommand("pnpc auto-farm wood");
                        owner.SendConsoleCommand("pnpc auto-farm enable");
                        return new NpcWorker
                        {
                            Id = 0,
                            Role = role,
                            Name = $"LumberjackBot_{settlement.Workers.Count + 1}",
                            Controller = null
                        };

                    default:
                        return null;
                }
            }
            catch (Exception ex)
            {
                PrintError($"Error spawning {role} worker: {ex.Message}");
                return null;
            }
        }

        /// <summary>
        /// Despawns a worker.This method should call into the relevant plugin
        /// to remove the bot from the world.In this skeleton we simply
        /// attempt to call the despawn commands.A full implementation would
        /// need to look up the bot by ID and call the correct API./// </summary>
        private void DespawnWorker(NpcWorker worker)
        {
            switch (worker.Role)
            {
                case WorkerRole.Builder:
                    // TODO: Use PersonalBuilder.Call("DespawnBot", worker.Id);
                    break;
                case WorkerRole.Farmer:
                    // TODO: Use PersonalFarmer.Call("DespawnFarmer", worker.Id);
                    break;
                case WorkerRole.Guard:
                case WorkerRole.Miner:
                case WorkerRole.Lumberjack:
                    // TODO: Use PersonalNPC.Call("DespawnBot", worker.Id);
                    break;
                case WorkerRole.Vendor:
                    // TODO: Use HumanNPC to despawn
                    break;
            }
        }

        #endregion
    }
}

Автор
15FPS
Скачиваний
0
Просмотры
1
Первый выпуск
Обновление
Оценка
0.00 звёзд 0 оценок
Сверху