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

FREE KillaDome : 1.0.0 1.0.0

Нет прав для скачивания
C#:
/*
 * KillaDome.cs - Full COD-Style Rust Server Experience Plugin
 * 
 * Features:
 * - Lobby system with comprehensive UI (Play/Loadouts/Store/Stats tabs)
 * - Drag-and-drop loadout editor with image library
 * - Persistent weapon progression and attachment upgrades
 * - Custom VFX/SFX for bullets and attachments
 * - Store integration (Tebex-compatible)
 * - High performance, GC-friendly architecture
 * 
 * Version: 1.0.0
 * Author: KillaDome Dev Team
 */
using Oxide.Core;
using Oxide.Core.Libraries.Covalence;
using Oxide.Core.Plugins;
using Oxide.Game.Rust.Cui;
using System;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using Newtonsoft.Json;
using System.IO;
namespace Oxide.Plugins {
    [Info("KillaDome", "KillaDome", "1.0.0")]
    [Description("Full COD-style server experience with lobby, loadouts, and progression")]
    public class KillaDome : RustPlugin {
#region Fields
        [PluginReference]
        private Plugin ImageLibrary;
        private DomeManager _domeManager;
        private LobbyUI _lobbyUI;
        private LoadoutEditor _loadoutEditor;
        private AttachmentSystem _attachmentSystem;
        private VFXManager _vfxManager;
        private SFXManager _sfxManager;
        private ForgeStationSystem _forgeStation;
        private BloodTokenEconomy _tokenEconomy;
        private SaveManager _saveManager;
        private AntiExploit _antiExploit;
        private TelemetrySystem _telemetry;
        private PluginConfig _config;
        private GunConfig _gunConfig;
        private OutfitConfig _outfitConfig;
        private Dictionary<ulong, PlayerSession> _activeSessions = new Dictionary<ulong, PlayerSession>();
        private const string PERMISSION_ADMIN = "killadome.admin";
        private const string PERMISSION_VIP = "killadome.vip";
#endregion
#region Gun & Image Configuration
        /// <summary>
        /// SIMPLIFIED GUN AND SKIN CONFIGURATION
        /// Now loaded from external JSON file for easy management!
        ///
        /// File:
        /// - oxide/data/KillaDome/Guns.json - All weapon definitions with skin lists
        ///
        /// SIMPLIFIED FEATURES:
        /// - Each gun has a Cost and list of AvailableSkins (just skin IDs)
        /// - Base guns must be purchased (not free)
        /// - Skins are just IDs: "0" (default), or workshop IDs like "3611341751"
        /// - Skin pricing based on rarity tiers (Common/Rare/Epic/Legendary)
        ///
        /// To reload changes: Use 'oxide.reload KillaDome' command
        /// </summary>
        public class GunConfig {
            [JsonProperty("Guns")]
            public Dictionary<string, GunDefinition> Guns {
                get;
                set;
            }
            = new Dictionary<string, GunDefinition>();
            [JsonProperty("SkinPricing")]
            public SkinPricing SkinPricing {
                get;
                set;
            }
            = new SkinPricing();
            // ===== HELPER METHODS =====
            public string GetGunImageUrl(string gunId) {
                return Guns.ContainsKey(gunId) ? Guns[gunId].ImageUrl : "";
            }
            public string[] GetAllGunIds() {
                return Guns.Keys.ToArray();
            }
            public List<string> GetSkinsForWeapon(string weaponId) {
                if (Guns.ContainsKey(weaponId)) {
                    return Guns[weaponId].GetAllSkinIds();
                }
                return new List<string>();
            }
            public SkinDefinition GetSkinDefinition(string weaponId, string skinId) {
                if (Guns.ContainsKey(weaponId)) {
                    return Guns[weaponId].GetSkin(skinId);
                }
                return null;
            }
            public List<SkinDefinition> GetAllSkinDefinitions(string weaponId) {
                if (Guns.ContainsKey(weaponId) && Guns[weaponId].Skins != null) {
                    return Guns[weaponId].Skins;
                }
                return new List<SkinDefinition>();
            }
            // ===== DEFAULT CONFIGURATION =====
            public static GunConfig CreateDefault() {
                var config = new GunConfig();
                config.Guns = new Dictionary<string, GunDefinition> {
                    ["ak47"]
                    = new GunDefinition {
                        Id = "ak47", DisplayName = "AK-47", RustItemShortname = "rifle.ak", ImageUrl = "https://i.imgur.com/YourAK47Image.png", Cost = 500, DefaultSkinId = "0", Skins = new List<SkinDefinition> {
                            new SkinDefinition {
                                SkinId = "0", DisplayName = "Default", ImageUrl = "https://i.imgur.com/DefaultAK47.png", Cost = 0, Rarity = "Common", Tag = ""
                            }
                            , new SkinDefinition {
                                SkinId = "3602286295", DisplayName = "Tempered AK47", ImageUrl = "https://i.imgur.com/TemperedAK.png", Cost = 600, Rarity = "Epic", Tag = "HOT"
                            }
                            , new SkinDefinition {
                                SkinId = "3102802323", DisplayName = "Glory AK47", ImageUrl = "https://i.imgur.com/GloryAK.png", Cost = 800, Rarity = "Legendary", Tag = "NEW"
                            }
                            , new SkinDefinition {
                                SkinId = "2854463727", DisplayName = "Alien Red", ImageUrl = "https://i.imgur.com/AlienRedAK.png", Cost = 400, Rarity = "Rare", Tag = ""
                            }
                        }
                    }
                    , ["lr300"] = new GunDefinition {
                        Id = "lr300", DisplayName = "LR-300", RustItemShortname = "rifle.lr300", ImageUrl = "https://i.imgur.com/YourLR300Image.png", Cost = 500, DefaultSkinId = "0", Skins = new List<SkinDefinition> {
                            new SkinDefinition {
                                SkinId = "0", DisplayName = "Default", ImageUrl = "https://i.imgur.com/DefaultLR.png", Cost = 0, Rarity = "Common", Tag = ""
                            }
                            , new SkinDefinition {
                                SkinId = "2561668054", DisplayName = "Gold LR300", ImageUrl = "https://i.imgur.com/GoldLR.png", Cost = 800, Rarity = "Legendary", Tag = "POPULAR"
                            }
                        }
                    }
                    , ["m249"] = new GunDefinition {
                        Id = "m249", DisplayName = "M249", RustItemShortname = "lmg.m249", ImageUrl = "https://i.imgur.com/YourM249Image.png", Cost = 600, DefaultSkinId = "0", Skins = new List<SkinDefinition> {
                            new SkinDefinition {
                                SkinId = "0", DisplayName = "Default", ImageUrl = "https://i.imgur.com/DefaultM249.png", Cost = 0, Rarity = "Common", Tag = ""
                            }
                            , new SkinDefinition {
                                SkinId = "2854146553", DisplayName = "Chrome M249", ImageUrl = "https://i.imgur.com/ChromeM249.png", Cost = 600, Rarity = "Epic", Tag = ""
                            }
                        }
                    }
                    , ["mp5"] = new GunDefinition {
                        Id = "mp5", DisplayName = "MP5A4", RustItemShortname = "smg.mp5", ImageUrl = "https://i.imgur.com/YourMP5Image.png", Cost = 400, DefaultSkinId = "0", Skins = new List<SkinDefinition> {
                            new SkinDefinition {
                                SkinId = "0", DisplayName = "Default", ImageUrl = "https://i.imgur.com/DefaultMP5.png", Cost = 0, Rarity = "Common", Tag = ""
                            }
                            , new SkinDefinition {
                                SkinId = "2561668055", DisplayName = "Tactical MP5", ImageUrl = "https://i.imgur.com/TacticalMP5.png", Cost = 400, Rarity = "Rare", Tag = ""
                            }
                        }
                    }
                    , ["thompson"] = new GunDefinition {
                        Id = "thompson", DisplayName = "Thompson", RustItemShortname = "smg.thompson", ImageUrl = "https://i.imgur.com/YourThompsonImage.png", Cost = 400, DefaultSkinId = "0", Skins = new List<SkinDefinition> {
                            new SkinDefinition {
                                SkinId = "0", DisplayName = "Default", ImageUrl = "https://i.imgur.com/DefaultThompson.png", Cost = 0, Rarity = "Common", Tag = ""
                            }
                            , new SkinDefinition {
                                SkinId = "2561668056", DisplayName = "Dragon Thompson", ImageUrl = "https://i.imgur.com/DragonThompson.png", Cost = 600, Rarity = "Epic", Tag = "NEW"
                            }
                        }
                    }
                    , ["python"] = new GunDefinition {
                        Id = "python", DisplayName = "Python Revolver", RustItemShortname = "pistol.python", ImageUrl = "https://i.imgur.com/YourPythonImage.png", Cost = 300, DefaultSkinId = "0", Skins = new List<SkinDefinition> {
                            new SkinDefinition {
                                SkinId = "0", DisplayName = "Default", ImageUrl = "https://i.imgur.com/DefaultPython.png", Cost = 0, Rarity = "Common", Tag = ""
                            }
                            , new SkinDefinition {
                                SkinId = "2561668057", DisplayName = "Black Python", ImageUrl = "https://i.imgur.com/BlackPython.png", Cost = 400, Rarity = "Rare", Tag = ""
                            }
                        }
                    }
                    , ["bolt"] = new GunDefinition {
                        Id = "bolt", DisplayName = "Bolt Action Rifle", RustItemShortname = "rifle.bolt", ImageUrl = "https://i.imgur.com/YourBoltImage.png", Cost = 550, DefaultSkinId = "0", Skins = new List<SkinDefinition> {
                            new SkinDefinition {
                                SkinId = "0", DisplayName = "Default", ImageUrl = "https://i.imgur.com/DefaultBolt.png", Cost = 0, Rarity = "Common", Tag = ""
                            }
                        }
                    }
                    , ["sarpistol"] = new GunDefinition {
                        Id = "sarpistol", DisplayName = "Semi-Auto Pistol", RustItemShortname = "pistol.semiauto", ImageUrl = "https://i.imgur.com/YourSARImage.png", Cost = 250, DefaultSkinId = "0", Skins = new List<SkinDefinition> {
                            new SkinDefinition {
                                SkinId = "0", DisplayName = "Default", ImageUrl = "https://i.imgur.com/DefaultSAR.png", Cost = 0, Rarity = "Common", Tag = ""
                            }
                        }
                    }
                    , ["custom"] = new GunDefinition {
                        Id = "custom", DisplayName = "Custom SMG", RustItemShortname = "smg.2", ImageUrl = "https://i.imgur.com/YourCustomImage.png", Cost = 350, DefaultSkinId = "0", Skins = new List<SkinDefinition> {
                            new SkinDefinition {
                                SkinId = "0", DisplayName = "Default", ImageUrl = "https://i.imgur.com/DefaultCustom.png", Cost = 0, Rarity = "Common", Tag = ""
                            }
                        }
                    }
                    , ["m39"] = new GunDefinition {
                        Id = "m39", DisplayName = "M39 Rifle", RustItemShortname = "rifle.m39", ImageUrl = "https://i.imgur.com/YourM39Image.png", Cost = 450, DefaultSkinId = "0", Skins = new List<SkinDefinition> {
                            new SkinDefinition {
                                SkinId = "0", DisplayName = "Default", ImageUrl = "https://i.imgur.com/DefaultM39.png", Cost = 0, Rarity = "Common", Tag = ""
                            }
                        }
                    }
                }
                ;
                config.SkinPricing = new SkinPricing {
                    CommonCost = 250, RareCost = 400, EpicCost = 600, LegendaryCost = 800
                }
                ;
                return config;
            }
        }
        public class GunDefinition {
            public string Id {
                get;
                set;
            }
            public string DisplayName {
                get;
                set;
            }
            public string RustItemShortname {
                get;
                set;
            }
            public string ImageUrl {
                get;
                set;
            }
            public int Cost {
                get;
                set;
            }
            = 500;
            // Cost to purchase the base gun
            public string DefaultSkinId {
                get;
                set;
            }
            = "0";
            // Default Rust skin ID (0 = no skin)
            // Full skin definitions with individual image, price, rarity per skin
            [JsonProperty("Skins")]
            public List<SkinDefinition> Skins {
                get;
                set;
            }
            = new List<SkinDefinition>();
            // Legacy support - kept for backward compatibility with old config files
            // Legacy skin IDs are included in GetAllSkinIds() but won't have full metadata
            [JsonProperty("AvailableSkins")]
            public List<string> AvailableSkins {
                get;
                set;
            }
            = new List<string>();
            // Helper to get all skin IDs (combines Skins and legacy AvailableSkins)
            public List<string> GetAllSkinIds() {
                var ids = new List<string>();
                if (Skins != null) {
                    foreach (var skin in Skins) {
                        ids.Add(skin.SkinId);
                    }
                }
                // Also include legacy AvailableSkins that aren't in Skins list
                if (AvailableSkins != null) {
                    foreach (var skinId in AvailableSkins) {
                        if (!ids.Contains(skinId)) ids.Add(skinId);
                    }
                }
                return ids;
            }
            // Get skin definition by ID (returns null for legacy skins without definitions)
            public SkinDefinition GetSkin(string skinId) {
                if (Skins != null) {
                    return Skins.FirstOrDefault(s => s.SkinId == skinId);
                }
                return null;
            }
        }
        /// <summary>
        /// Individual skin definition with image, price, and rarity
        /// </summary>
        public class SkinDefinition {
            [JsonProperty("SkinId")]
            public string SkinId {
                get;
                set;
            }
            = "0";
            [JsonProperty("DisplayName")]
            public string DisplayName {
                get;
                set;
            }
            = "Default";
            [JsonProperty("ImageUrl")]
            public string ImageUrl {
                get;
                set;
            }
            = "";
            [JsonProperty("Cost")]
            public int Cost {
                get;
                set;
            }
            = 0;
            [JsonProperty("Rarity")]
            public string Rarity {
                get;
                set;
            }
            = "Common";
            // Common, Rare, Epic, Legendary
            [JsonProperty("Tag")]
            public string Tag {
                get;
                set;
            }
            = "";
            // NEW, HOT, POPULAR, etc.
        }
        public class SkinPricing {
            [JsonProperty("CommonCost")]
            public int CommonCost {
                get;
                set;
            }
            = 250;
            [JsonProperty("RareCost")]
            public int RareCost {
                get;
                set;
            }
            = 400;
            [JsonProperty("EpicCost")]
            public int EpicCost {
                get;
                set;
            }
            = 600;
            [JsonProperty("LegendaryCost")]
            public int LegendaryCost {
                get;
                set;
            }
            = 800;
            /// <summary>
            /// Get default cost for a rarity tier. Used as fallback when skin doesn't have explicit cost.
            /// </summary>
            public int GetCostForRarity(string rarity) {
                switch (rarity?.ToLower()) {
                    case "legendary" : return LegendaryCost;
                    case "epic" : return EpicCost;
                    case "rare" : return RareCost;
                    case "common" : default : return CommonCost;
                }
            }
            /// <summary>
            /// Get cost for a skin ID using heuristics. Used when skin definition is not available.
            /// </summary>
            public int GetCostForSkinId(string skinId) {
                // Simple heuristic: if skin ID is "0", it's free (default)
                if (skinId == "0") return 0;
                // Workshop IDs starting with 3 are typically higher quality
                if (skinId.StartsWith("3") && skinId.Length > 8) return LegendaryCost;
                // You can customize this logic or add a mapping
                return RareCost;
            }
        }
        // ===== OUTFIT/ARMOR CONFIGURATION =====
        /// <summary>
        /// Armor/Outfit configuration now loaded from external JSON file
        /// File: oxide/data/KillaDome/Armor.json
        /// Edit the JSON file to add/remove armor pieces without touching code!
        /// </summary>
        public class OutfitConfig {
            [JsonProperty("Armors")]
            public List<ArmorItem> Armors {
                get;
                set;
            }
            = new List<ArmorItem>();
            public ArmorItem[] GetArmorsBySlot(string slot) {
                return Armors.Where(a => a.Slot == slot).ToArray();
            }
            // ===== DEFAULT CONFIGURATION =====
            public static OutfitConfig CreateDefault() {
                var config = new OutfitConfig();
                config.Armors = new List<ArmorItem> {
                    // Head Armor
                    new ArmorItem {
                        Name = "Metal Facemask", ItemShortname = "metal.facemask", Slot = "head", SkinId = "0", ImageUrl = "https://i.imgur.com/mVY2Uav.png", Cost = 300, Rarity = "Common"
                    }
                    , new ArmorItem {
                        Name = "Coffee Can Helmet", ItemShortname = "coffeecan.helmet", Slot = "head", SkinId = "0", ImageUrl = "https://i.imgur.com/YourCoffeeCanImage.png", Cost = 250, Rarity = "Common"
                    }
                    ,  // Chest Armor
                    new ArmorItem {
                        Name = "Metal Chest Plate", ItemShortname = "metal.plate.torso", Slot = "chest", SkinId = "0", ImageUrl = "https://i.imgur.com/YourMetalChestImage.png", Cost = 400, Rarity = "Rare"
                    }
                    , new ArmorItem {
                        Name = "Road Sign Jacket", ItemShortname = "roadsign.jacket", Slot = "chest", SkinId = "0", ImageUrl = "https://i.imgur.com/YourRoadSignImage.png", Cost = 300, Rarity = "Common"
                    }
                    ,  // Legs Armor
                    new ArmorItem {
                        Name = "Heavy Plate Pants", ItemShortname = "heavy.plate.pants", Slot = "legs", SkinId = "0", ImageUrl = "https://i.imgur.com/YourHeavyPantsImage.png", Cost = 400, Rarity = "Rare"
                    }
                    , new ArmorItem {
                        Name = "Road Sign Kilt", ItemShortname = "roadsign.kilt", Slot = "legs", SkinId = "0", ImageUrl = "https://i.imgur.com/YourRoadSignKiltImage.png", Cost = 300, Rarity = "Common"
                    }
                    ,  // Hands/Gloves
                    new ArmorItem {
                        Name = "Tactical Gloves", ItemShortname = "tactical.gloves", Slot = "hands", SkinId = "0", ImageUrl = "https://i.imgur.com/YourTacticalGlovesImage.png", Cost = 200, Rarity = "Common"
                    }
                    ,  // Feet/Boots
                    new ArmorItem {
                        Name = "Heavy Plate Boots", ItemShortname = "shoes.boots", Slot = "feet", SkinId = "0", ImageUrl = "https://i.imgur.com/YourBootsImage.png", Cost = 250, Rarity = "Common"
                    }
                }
                ;
                return config;
            }
        }
        public class ArmorItem {
            public string Name {
                get;
                set;
            }
            public string DisplayName => Name;
            // Alias for consistency
            public string ItemShortname {
                get;
                set;
            }
            public string Slot {
                get;
                set;
            }
            // "head", "chest", "legs", "hands", "feet"
            public string SkinId {
                get;
                set;
            }
            = "0";
            public string ImageUrl {
                get;
                set;
            }
            public int Cost {
                get;
                set;
            }
            = 300;
            public string Rarity {
                get;
                set;
            }
            = "Common";
            public string Tag {
                get;
                set;
            }
            = "";
        }
#endregion
#region Configuration
        internal class PluginConfig {
            [JsonProperty("Lobby Spawn Position")]
            public Vector3 LobbySpawnPosition {
                get;
                set;
            }
            = new Vector3(0, 100, 0);
            [JsonProperty("Arena Spawn Positions")]
            public List<Vector3> ArenaSpawnPositions {
                get;
                set;
            }
            = new List<Vector3> {
                new Vector3(0, 100, 500)
            }
            ;
            [JsonProperty("Starting Blood Tokens")]
            public int StartingTokens {
                get;
                set;
            }
            = 500;
            [JsonProperty("Tokens Per Kill")]
            public int TokensPerKill {
                get;
                set;
            }
            = 10;
            [JsonProperty("Admin Daily Tokens")]
            public int AdminDailyTokens {
                get;
                set;
            }
            = 10000;
            [JsonProperty("Daily Token Refill Enabled")]
            public bool DailyRefillEnabled {
                get;
                set;
            }
            = true;
            [JsonProperty("UI Update Throttle MS")]
            public int UIUpdateThrottleMS {
                get;
                set;
            }
            = 100;
            [JsonProperty("Auto Save Interval Seconds")]
            public float AutoSaveInterval {
                get;
                set;
            }
            = 300f;
            [JsonProperty("Enable Debug Logging")]
            public bool EnableDebugLogging {
                get;
                set;
            }
            = false;
        }
        protected override void LoadDefaultConfig() {
            _config = new PluginConfig();
            SaveConfig();
        }
        protected override void LoadConfig() {
            base.LoadConfig();
            try {
                _config = Config.ReadObject<PluginConfig>();
                if (_config == null) {
                    LoadDefaultConfig();
                }
            }
            catch {
                PrintError("Configuration file is corrupt. Loading defaults...");
                LoadDefaultConfig();
            }
            SaveConfig();
        }
        protected override void SaveConfig() => Config.WriteObject(_config, true);
        // ===== LOAD EXTERNAL DATA CONFIGURATIONS =====
        private GunConfig LoadGunConfig() {
            string dataDirectory = Path.Combine(Interface.Oxide.DataDirectory, "KillaDome");
            string filePath = Path.Combine(dataDirectory, "Guns.json");
            if (!Directory.Exists(dataDirectory)) {
                Directory.CreateDirectory(dataDirectory);
            }
            if (!File.Exists(filePath)) {
                Puts("Guns.json not found. Creating default configuration...");
                var defaultConfig = GunConfig.CreateDefault();
                File.WriteAllText(filePath, JsonConvert.SerializeObject(defaultConfig, Formatting.Indented));
                return defaultConfig;
            }
            try {
                string json = File.ReadAllText(filePath);
                var config = JsonConvert.DeserializeObject<GunConfig>(json);
                if (config == null || config.Guns == null || config.SkinPricing == null) {
                    PrintWarning("Guns.json is invalid. Using defaults...");
                    return GunConfig.CreateDefault();
                }
                // Count total skins across all guns
                int totalSkins = 0;
                foreach (var gun in config.Guns.Values) {
                    if (gun.AvailableSkins != null) totalSkins += gun.AvailableSkins.Count;
                }
                Puts($"Loaded {config.Guns.Count} guns with {totalSkins} total skins from Guns.json");
                return config;
            }
            catch (Exception ex) {
                PrintError($"Failed to load Guns.json: {ex.Message}. Using defaults...");
                return GunConfig.CreateDefault();
            }
        }
        private OutfitConfig LoadOutfitConfig() {
            string dataDirectory = Path.Combine(Interface.Oxide.DataDirectory, "KillaDome");
            string filePath = Path.Combine(dataDirectory, "Armor.json");
            if (!Directory.Exists(dataDirectory)) {
                Directory.CreateDirectory(dataDirectory);
            }
            if (!File.Exists(filePath)) {
                Puts("Armor.json not found. Creating default configuration...");
                var defaultConfig = OutfitConfig.CreateDefault();
                File.WriteAllText(filePath, JsonConvert.SerializeObject(defaultConfig, Formatting.Indented));
                return defaultConfig;
            }
            try {
                string json = File.ReadAllText(filePath);
                var config = JsonConvert.DeserializeObject<OutfitConfig>(json);
                if (config == null || config.Armors == null) {
                    PrintWarning("Armor.json is invalid. Using defaults...");
                    return OutfitConfig.CreateDefault();
                }
                Puts($"Loaded {config.Armors.Count} armor pieces from Armor.json");
                return config;
            }
            catch (Exception ex) {
                PrintError($"Failed to load Armor.json: {ex.Message}. Using defaults...");
                return OutfitConfig.CreateDefault();
            }
        }
#endregion
#region Oxide Hooks
        private void Init() {
            permission.RegisterPermission(PERMISSION_ADMIN, this);
            permission.RegisterPermission(PERMISSION_VIP, this);
            // Load configurations from JSON files
            _gunConfig = LoadGunConfig();
            _outfitConfig = LoadOutfitConfig();
            // Initialize all systems
            _saveManager = new SaveManager(this, _config);
            _antiExploit = new AntiExploit(this);
            _tokenEconomy = new BloodTokenEconomy(this, _config);
            _attachmentSystem = new AttachmentSystem(this, _config);
            _vfxManager = new VFXManager(this);
            _sfxManager = new SFXManager(this);
            _forgeStation = new ForgeStationSystem(this, _config, _tokenEconomy, _attachmentSystem);
            _loadoutEditor = new LoadoutEditor(this, _attachmentSystem);
            _lobbyUI = new LobbyUI(this, _loadoutEditor, _forgeStation, _tokenEconomy);
            _domeManager = new DomeManager(this, _config);
            _telemetry = new TelemetrySystem(this);
            LogDebug("KillaDome initialized successfully");
        }
        private void OnServerInitialized() {
            timer.Every(_config.AutoSaveInterval, () => AutoSaveAllPlayers());
            LogDebug("Auto-save timer started");
            // Load images after server is ready
            timer.Once(5f, () => LoadImages());
        }
        private void LoadImages() {
            if (ImageLibrary == null || !ImageLibrary.IsLoaded) {
                PrintWarning("ImageLibrary not loaded. Images will not display. Please install ImageLibrary plugin.");
                return;
            }
            int skinCount = 0;
            // Load gun images and individual skin images
            foreach (var gun in _gunConfig.Guns.Values) {
                if (!string.IsNullOrEmpty(gun.ImageUrl)) {
                    ImageLibrary.Call("AddImage", gun.ImageUrl, gun.ImageUrl);
                }
                // Load individual skin images
                if (gun.Skins != null) {
                    foreach (var skin in gun.Skins) {
                        if (!string.IsNullOrEmpty(skin.ImageUrl)) {
                            // Use a unique key combining gun ID and skin ID
                            string imageKey = $"{gun.Id}_skin_{skin.SkinId}";
                            ImageLibrary.Call("AddImage", skin.ImageUrl, imageKey);
                            skinCount++;
                        }
                    }
                }
            }
            // Load armor images
            foreach (var armor in _outfitConfig.Armors) {
                if (!string.IsNullOrEmpty(armor.ImageUrl)) {
                    ImageLibrary.Call("AddImage", armor.ImageUrl, armor.ImageUrl);
                }
            }
            Puts($"Loaded {_gunConfig.Guns.Count} gun images, {skinCount} skin images, and {_outfitConfig.Armors.Count} armor images into ImageLibrary");
        }
        private void Unload() {
            // Clean up all UI
            foreach (var player in BasePlayer.activePlayerList) {
                _lobbyUI?.DestroyUI(player);
            }
            // Save all player data
            foreach (var session in _activeSessions.Values) {
                _saveManager?.SavePlayerProfile(session.Profile);
            }
            _activeSessions.Clear();
            LogDebug("KillaDome unloaded and cleaned up");
        }
        private void OnPlayerConnected(BasePlayer player) {
            if (player == null || _saveManager == null || _lobbyUI == null) return;
            NextTick(() => {
                if (player == null || !player.IsConnected) return;
                var profile = _saveManager.LoadPlayerProfile(player.userID);
                // Check for admin daily token refill
                if (_config.DailyRefillEnabled && permission.UserHasPermission(player.UserIDString, PERMISSION_ADMIN)) {
                    // Check if 24 hours have passed since last refill
                    TimeSpan timeSinceRefill = DateTime.UtcNow - profile.LastDailyRefill;
                    if (timeSinceRefill.TotalHours >= 24) {
                        profile.Tokens = _config.AdminDailyTokens;
                        profile.LastDailyRefill = DateTime.UtcNow;
                        _saveManager.SavePlayerProfile(profile);
                        SendReply(player, $"<color=#00ff00>✓ Admin Daily Tokens:</color> You received {_config.AdminDailyTokens} Blood Tokens!");
                        LogDebug($"Admin {player.displayName} received daily refill of {_config.AdminDailyTokens} tokens");
                    }
                }
                var session = new PlayerSession(player, profile);
                _activeSessions[player.userID] = session;
                // Teleport to lobby
                TeleportToLobby(player);
                // Show lobby UI
                timer.Once(1f, () => {
                    if (player != null && player.IsConnected) {
                        _lobbyUI.ShowLobbyUI(player);
                    }
                }
                );
                LogDebug($"Player {player.displayName} ({player.userID}) connected");
            }
            );
        }
        private void OnPlayerDisconnected(BasePlayer player, string reason) {
            if (player == null) return;
            _lobbyUI?.DestroyUI(player);
            if (_activeSessions.TryGetValue(player.userID, out var session)) {
                _saveManager?.SavePlayerProfile(session.Profile);
                _activeSessions.Remove(player.userID);
            }
            LogDebug($"Player {player.displayName} disconnected: {reason}");
        }
        private void OnEntityDeath(BasePlayer victim, HitInfo info) {
            if (victim == null || _tokenEconomy == null || _telemetry == null) return;
            var attacker = info?.InitiatorPlayer;
            if (attacker != null && attacker != victim && attacker.IsConnected) {
                // Award tokens for kill
                _tokenEconomy.AwardTokens(attacker.userID, _config.TokensPerKill);
                // Track telemetry
                _telemetry.RecordKill(attacker.userID, victim.userID);
                LogDebug($"{attacker.displayName} killed {victim.displayName}");
            }
            // Respawn victim in lobby after delay
            timer.Once(3f, () => {
                if (victim != null && victim.IsConnected) {
                    TeleportToLobby(victim);
                    victim.Respawn();
                }
            }
            );
        }
#endregion
#region Helper Methods
        private void TeleportToLobby(BasePlayer player) {
            if (player == null || !player.IsConnected) return;
            player.Teleport(_config.LobbySpawnPosition);
        }
        private void TeleportToArena(BasePlayer player) {
            if (player == null || !player.IsConnected) return;
            // Select random spawn point from configured arena spawns
            Vector3 spawnPos;
            if (_config.ArenaSpawnPositions != null && _config.ArenaSpawnPositions.Count > 0) {
                spawnPos = _config.ArenaSpawnPositions[UnityEngine.Random.Range(0, _config.ArenaSpawnPositions.Count)];
            }
            else {
                // Fallback to default if no spawns configured
                spawnPos = new Vector3(0, 100, 500);
            }
            player.Teleport(spawnPos);
            // Apply loadout when entering arena
            ApplyLoadout(player);
        }
        private void ApplyLoadout(BasePlayer player) {
            var session = GetSession(player.userID);
            if (session == null || session.Profile.Loadouts.Count == 0) return;
            var loadout = session.Profile.Loadouts[0];
            // Strip existing items
            player.inventory.Strip();
            // Give primary weapon
            GiveWeapon(player, loadout.Primary, loadout.PrimaryAttachments, loadout.Skins);
            // Give secondary weapon
            GiveWeapon(player, loadout.Secondary, loadout.SecondaryAttachments, loadout.Skins);
            // Give equipped armor/outfit
            GiveArmor(player, loadout);
            LogDebug($"Applied loadout to {player.displayName}");
        }
        private void GiveWeapon(BasePlayer player, string weaponName, Dictionary<string, string> attachments, Dictionary<string, string> skins) {
            if (string.IsNullOrEmpty(weaponName)) return;
            // Get weapon info from centralized config
            string itemName = "rifle.ak";
            // Default fallback
            if (_gunConfig?.Guns != null && _gunConfig.Guns.TryGetValue(weaponName, out var gunDef)) {
                itemName = gunDef.RustItemShortname;
            }
            var item = ItemManager.CreateByName(itemName, 1);
            if (item == null) {
                LogDebug($"Failed to create weapon: {itemName}");
                return;
            }
            // Apply skin if exists
            if (skins != null && skins.TryGetValue(weaponName, out string skinId)) {
                if (ulong.TryParse(skinId, out ulong skin)) {
                    item.skin = skin;
                    item.MarkDirty();
                    // Mark for network update
                }
            }
            // Apply attachments if exists
            if (attachments != null && attachments.Count > 0) {
                var heldEntity = item.GetHeldEntity() as BaseProjectile;
                if (heldEntity != null && item.contents != null) {
                    foreach (var attachmentEntry in attachments) {
                        string attachmentId = attachmentEntry.Value;
                        if (!string.IsNullOrEmpty(attachmentId)) {
                            var attachmentItem = ItemManager.CreateByName(attachmentId, 1);
                            if (attachmentItem != null) {
                                // Add attachment to weapon's content container
                                if (!attachmentItem.MoveToContainer(item.contents)) {
                                    attachmentItem.Remove();
                                    // Clean up if can't add
                                }
                            }
                        }
                    }
                }
            }
            // Give item to player
            if (!player.inventory.GiveItem(item)) {
                LogDebug($"Failed to give weapon {itemName} to {player.displayName} - inventory full?");
                item.Remove();
                // Clean up item if can't give
                return;
            }
            // If item was given to belt, ensure visual update
            var heldItem = item.GetHeldEntity();
            if (heldItem != null) {
                heldItem.skinID = item.skin;
                heldItem.SendNetworkUpdate();
            }
            // Give ammo
            string ammoType = weaponName == "pistol" ? "ammo.pistol" : "ammo.rifle";
            var ammo = ItemManager.CreateByName(ammoType, 250);
            if (ammo != null) {
                player.inventory.GiveItem(ammo);
            }
        }
        private void GiveArmor(BasePlayer player, Loadout loadout) {
            if (player == null || loadout == null) return;
            // Give head armor
            if (!string.IsNullOrEmpty(loadout.ArmorHead)) {
                GiveArmorPiece(player, loadout.ArmorHead);
            }
            // Give chest armor
            if (!string.IsNullOrEmpty(loadout.ArmorChest)) {
                GiveArmorPiece(player, loadout.ArmorChest);
            }
            // Give legs armor
            if (!string.IsNullOrEmpty(loadout.ArmorLegs)) {
                GiveArmorPiece(player, loadout.ArmorLegs);
            }
            // Give hands armor
            if (!string.IsNullOrEmpty(loadout.ArmorHands)) {
                GiveArmorPiece(player, loadout.ArmorHands);
            }
            // Give feet armor
            if (!string.IsNullOrEmpty(loadout.ArmorFeet)) {
                GiveArmorPiece(player, loadout.ArmorFeet);
            }
            LogDebug($"Applied armor to {player.displayName}");
        }
        private void GiveArmorPiece(BasePlayer player, string armorShortname) {
            if (string.IsNullOrEmpty(armorShortname)) return;
            var item = ItemManager.CreateByName(armorShortname, 1);
            if (item == null) {
                LogDebug($"Failed to create armor item: {armorShortname}");
                return;
            }
            // Try to move to wear container (for clothing/armor)
            if (!item.MoveToContainer(player.inventory.containerWear)) {
                // If wear container is full or item can't be worn, give to main inventory
                player.inventory.GiveItem(item);
            }
            LogDebug($"Gave armor piece {armorShortname} to {player.displayName}");
        }
        private void AutoSaveAllPlayers() {
            if (_saveManager == null) return;
            int saved = 0;
            foreach (var session in _activeSessions.Values) {
                if (session?.Profile != null) {
                    _saveManager.SavePlayerProfile(session.Profile);
                    saved++;
                }
            }
            LogDebug($"Auto-saved {saved} player profiles");
        }
        private void LogDebug(string message) {
            if (_config?.EnableDebugLogging == true) {
                Puts($"[DEBUG] {message}");
            }
        }
        internal PlayerSession GetSession(ulong steamId) {
            _activeSessions.TryGetValue(steamId, out var session);
            return session;
        }
#endregion
#region Console Commands
        [ConsoleCommand("kd.open")]
        private void CmdOpen(ConsoleSystem.Arg arg) {
            var player = arg.Player();
            if (player == null) return;
            if (!permission.UserHasPermission(player.UserIDString, PERMISSION_ADMIN)) {
                SendReply(arg, "You don't have permission to use this command");
                return;
            }
            _lobbyUI.ShowLobbyUI(player);
            SendReply(arg, "Lobby UI opened");
        }
        [ConsoleCommand("kd.start")]
        private void CmdStart(ConsoleSystem.Arg arg) {
            var player = arg.Player();
            if (player == null) return;
            if (!permission.UserHasPermission(player.UserIDString, PERMISSION_ADMIN)) {
                SendReply(arg, "You don't have permission to use this command");
                return;
            }
            _domeManager.StartMatch();
            SendReply(arg, "Match started");
        }
        [ConsoleCommand("kd.giveskin")]
        private void CmdGiveSkin(ConsoleSystem.Arg arg) {
            var player = arg.Player();
            if (player == null || !arg.HasArgs(2)) {
                SendReply(arg, "Usage: kd.giveskin <steamid> <skinid>");
                return;
            }
            if (!permission.UserHasPermission(player.UserIDString, PERMISSION_ADMIN)) {
                SendReply(arg, "You don't have permission to use this command");
                return;
            }
            if (!ulong.TryParse(arg.Args[0], out ulong targetId)) {
                SendReply(arg, "Invalid Steam ID");
                return;
            }
            string skinId = arg.Args[1];
            if (_activeSessions.TryGetValue(targetId, out var session)) {
                session.Profile.OwnedSkins.Add(skinId);
                _saveManager.SavePlayerProfile(session.Profile);
                SendReply(arg, $"Granted skin {skinId} to player {targetId}");
            }
            else {
                SendReply(arg, "Player not found or not online");
            }
        }
        [ConsoleCommand("kd.resetprogress")]
        private void CmdResetProgress(ConsoleSystem.Arg arg) {
            var player = arg.Player();
            if (player == null || !arg.HasArgs(1)) {
                SendReply(arg, "Usage: kd.resetprogress <steamid>");
                return;
            }
            if (!permission.UserHasPermission(player.UserIDString, PERMISSION_ADMIN)) {
                SendReply(arg, "You don't have permission to use this command");
                return;
            }
            if (!ulong.TryParse(arg.Args[0], out ulong targetId)) {
                SendReply(arg, "Invalid Steam ID");
                return;
            }
            var newProfile = new PlayerProfile(targetId, _config.StartingTokens);
            _saveManager.SavePlayerProfile(newProfile);
            if (_activeSessions.TryGetValue(targetId, out var session)) {
                session.Profile = newProfile;
            }
            SendReply(arg, $"Reset progress for player {targetId}");
        }
#endregion
#region Chat Commands
        [ChatCommand("kd")]
        private void CmdKD(BasePlayer player, string command, string[] args) {
            if (args.Length == 0) {
                SendReply(player, "KillaDome Commands:\n" + "/kd open - Open lobby UI\n" + "/kd stats - View your stats\n" + "/kd help - Show this help");
                return;
            }
            switch (args[0].ToLower()) {
                case "open" : _lobbyUI.ShowLobbyUI(player);
                SendReply(player, "Lobby UI opened");
                break;
                case "stats" : if (_activeSessions.TryGetValue(player.userID, out var session)) {
                    SendReply(player, $"Blood Tokens: {session.Profile.Tokens}\n" + $"VIP Status: {(session.Profile.IsVIP ? "Active" : "Inactive")}");
                }
                break;
                case "help" : SendReply(player, "KillaDome - Full COD Experience\n" + "Use /kd open to access the lobby");
                break;
                default : SendReply(player, "Unknown command. Use /kd help");
                break;
            }
        }
#endregion
#region UI Console Commands
        [ConsoleCommand("killadome.close")]
        private void CmdUIClose(ConsoleSystem.Arg arg) {
            var player = arg.Player();
            if (player == null) return;
            _lobbyUI.DestroyUI(player);
        }
        [ConsoleCommand("killadome.tab")]
        private void CmdUITab(ConsoleSystem.Arg arg) {
            var player = arg.Player();
            if (player == null || !arg.HasArgs(1)) return;
            string tab = arg.Args[0].ToLower();
            _lobbyUI.ShowLobbyUIWithTab(player, tab);
            LogDebug($"Player {player.displayName} opened tab: {tab}");
        }
        [ConsoleCommand("killadome.joinqueue")]
        private void CmdUIJoinQueue(ConsoleSystem.Arg arg) {
            var player = arg.Player();
            if (player == null) return;
            _domeManager.AddToQueue(player.userID);
            SendReply(player, "You have joined the queue!");
        }
        [ConsoleCommand("killadome.weapon.prev")]
        private void CmdWeaponPrev(ConsoleSystem.Arg arg) {
            var player = arg.Player();
            if (player == null || !arg.HasArgs(1)) return;
            if (!_antiExploit.CheckRateLimit(player.userID)) {
                SendReply(player, "Please slow down!");
                return;
            }
            string slot = arg.Args[0];
            // "primary" or "secondary"
            CycleWeapon(player, slot, -1);
            _lobbyUI.ShowLobbyUIWithTab(player, "loadouts");
        }
        [ConsoleCommand("killadome.weapon.next")]
        private void CmdWeaponNext(ConsoleSystem.Arg arg) {
            var player = arg.Player();
            if (player == null || !arg.HasArgs(1)) return;
            if (!
// ... [Code preview truncated due to length. Full source in attached file] ...

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