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

FREE GrandmasHouse : 2.9.1 2.9.1

Нет прав для скачивания
C#:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using Oxide.Core;
using Oxide.Core.Plugins;
using UnityEngine;
using Newtonsoft.Json;

namespace Oxide.Plugins
{
    [Info("GrandmasHouse", "Gemini", "2.9.1")]
    [Description("Independent Matriarch System. Advanced clone mimicry including jumping and looking direction.")]
    public class GrandmasHouse : RustPlugin
    {
        [PluginReference]
        private Plugin HoodWars, TurfGraffiti;

        private Dictionary<string, MatriarchSet> _activeMatriarchs = new Dictionary<string, MatriarchSet>();
        private List<BaseEntity> _manualMatriarchs = new List<BaseEntity>();
        private Dictionary<BaseEntity, ulong> _captors = new Dictionary<BaseEntity, ulong>();
        private HashSet<BaseEntity> _surrenderedMatriarchs = new HashSet<BaseEntity>();
        
        private Dictionary<ulong, Queue<CaptorFrame>> _captorTrails = new Dictionary<ulong, Queue<CaptorFrame>>();
        private Dictionary<BaseEntity, float> _aimTimers = new Dictionary<BaseEntity, float>();
        
        private const string PrefabNPC = "assets/prefabs/player/player.prefab";
        private const string PermAdmin = "grandmashouse.admin";
        private const string ItemC4 = "explosive.timed";

        private class MatriarchSet
        {
            public BaseEntity Grandma;
            public BaseEntity Mom;
        }

        private struct CaptorFrame
        {
            public Vector3 Position;
            public Quaternion Rotation;
            public float Pitch;
            public float Yaw;
            public bool IsSprinting;
            public bool IsCrouching;
            public bool IsOnGround;
        }

        #region Configuration

        private ConfigData _config;

        private class MatriarchSettings
        {
            public float Health;
            public float InfluencePenalty;
            public string Name;
            public float Radius;
        }

        private class ConfigData
        {
            [JsonProperty("Grandma Settings")]
            public MatriarchSettings Grandma { get; set; } = new MatriarchSettings 
            { 
                Health = 1000f, 
                InfluencePenalty = 25.0f, 
                Name = "Grandma",
                Radius = 15f
            };

            [JsonProperty("Mom Settings")]
            public MatriarchSettings Mom { get; set; } = new MatriarchSettings 
            { 
                Health = 800f, 
                InfluencePenalty = 35.0f, 
                Name = "Mom",
                Radius = 12f
            };

            public int FoodGiftIntervalMinutes = 30;
            public float RepairIntervalSeconds = 10f;
            public int RespawnTimeSeconds = 3600;
            public float StickUpDistance = 5f;
            public float FollowDistance = 1.2f;
            public float RequiredAimTime = 14.0f;
            public bool InvulnerableWhileSurrendered = false; 
        }

        protected override void LoadDefaultConfig() => _config = new ConfigData();
        protected override void LoadConfig()
        {
            base.LoadConfig();
            _config = Config.ReadObject<ConfigData>() ?? new ConfigData();
            SaveConfig();
        }
        protected override void SaveConfig() => Config.WriteObject(_config);

        #endregion

        #region Core Logic

        private void Init()
        {
            permission.RegisterPermission(PermAdmin, this);
        }

        private void OnServerInitialized()
        {
            if (HoodWars != null)
            {
                SpawnAllMatriarchs();
            }

            timer.Every(5f, UpdateMatriarchAuras);
            timer.Every(0.03f, UpdateHostageLogic); 
            timer.Every(_config.FoodGiftIntervalMinutes * 60, DistributeGrandmaMeals);
        }

        private void Unload()
        {
            foreach (var set in _activeMatriarchs.Values)
            {
                if (set.Grandma != null && !set.Grandma.IsDestroyed) set.Grandma.Kill();
                if (set.Mom != null && !set.Mom.IsDestroyed) set.Mom.Kill();
            }

            foreach (var ent in _manualMatriarchs)
                if (ent != null && !ent.IsDestroyed) ent.Kill();
        }

        private void SpawnAllMatriarchs()
        {
            string[] gangs = { "Westside Pirus", "Northside Vagos", "Southside Sureños", "Eastside Disciples" };
            foreach (var gang in gangs)
            {
                if (!_activeMatriarchs.ContainsKey(gang))
                    _activeMatriarchs[gang] = new MatriarchSet();
                
                SpawnMatriarchAtHQ(gang, true); 
                SpawnMatriarchAtHQ(gang, false);
            }
        }

        private void SpawnMatriarchAtHQ(string gangName, bool isGrandma)
        {
            if (HoodWars == null) return;
            object hqPosObj = HoodWars.Call("GetHQLocation", gangName);
            if (hqPosObj == null) return;
            
            Vector3 hqPos = (Vector3)hqPosObj;
            Vector3 spawnPos = hqPos + new Vector3(8, 0, 8);

            BaseEntity ent = InternalSpawn(spawnPos, gangName, isGrandma);
            if (isGrandma) _activeMatriarchs[gangName].Grandma = ent;
            else _activeMatriarchs[gangName].Mom = ent;
        }

        private BaseEntity InternalSpawn(Vector3 pos, string gangName, bool isGrandma)
        {
            BaseEntity npcEnt = GameManager.server.CreateEntity(PrefabNPC, pos, Quaternion.identity);
            if (npcEnt == null) return null;

            BasePlayer npc = npcEnt as BasePlayer;
            if (npc != null)
            {
                npc.userID = (ulong)UnityEngine.Random.Range(1000000, 9999999);
                npc.UserIDString = npc.userID.ToString();
                npc.displayName = !string.IsNullOrEmpty(gangName) ? $"{gangName}'s {(isGrandma ? _config.Grandma.Name : _config.Mom.Name)}" : (isGrandma ? _config.Grandma.Name : _config.Mom.Name);
                
                npc.SetPlayerFlag((BasePlayer.PlayerFlags)16384, true); 
                npc.Spawn();

                npc.InitializeHealth(isGrandma ? _config.Grandma.Health : _config.Mom.Health, isGrandma ? _config.Grandma.Health : _config.Mom.Health);
                npc.inventory.Strip();

                npc.metabolism.calories.value = 500;
                npc.metabolism.hydration.value = 500;
                
                if (isGrandma)
                {
                    // FIXED: Applied new Grandma Kit with provided Skin IDs
                    npc.inventory.GiveItem(ItemManager.CreateByName("tshirt.long", 1, 3642580871), npc.inventory.containerWear);
                    npc.inventory.GiveItem(ItemManager.CreateByName("pants", 1, 3642597643), npc.inventory.containerWear);
                    npc.inventory.GiveItem(ItemManager.CreateByName("mask.balaclava", 1, 3642598176), npc.inventory.containerWear);
                }
                else
                {
                    npc.inventory.GiveItem(ItemManager.CreateByName("shirt.collared", 1, 0), npc.inventory.containerWear);
                    npc.inventory.GiveItem(ItemManager.CreateByName("pants", 1, 3637162360), npc.inventory.containerWear);
                }
            }
            return npcEnt;
        }

        #endregion

        #region Hostage System (Live Echo Mimicry)

        private void UpdateHostageLogic()
        {
            foreach (var kvp in _activeMatriarchs)
            {
                HandleMatriarchInteraction(kvp.Value.Grandma, kvp.Key);
                HandleMatriarchInteraction(kvp.Value.Mom, kvp.Key);
            }

            for (int i = _manualMatriarchs.Count - 1; i >= 0; i--)
            {
                var ent = _manualMatriarchs[i];
                if (ent == null || ent.IsDestroyed) { _manualMatriarchs.RemoveAt(i); continue; }
                HandleMatriarchInteraction(ent, "Manual_Test");
            }
        }

        private void HandleMatriarchInteraction(BaseEntity entity, string homeGang)
        {
            if (entity == null || entity.IsDestroyed) return;

            if (!_captors.ContainsKey(entity))
            {
                BasePlayer activeAimer = null;
                List<BasePlayer> nearby = new List<BasePlayer>();
                Vis.Entities(entity.transform.position, _config.StickUpDistance, nearby);

                foreach (var p in nearby)
                {
                    if (IsLookingAt(p, entity) && IsHoldingWeapon(p))
                    {
                        activeAimer = p;
                        break;
                    }
                }

                if (activeAimer != null) ProcessAimTimer(entity, activeAimer);
                else if (_aimTimers.ContainsKey(entity)) _aimTimers.Remove(entity);
            }
            else
            {
                BasePlayer captor = BasePlayer.FindByID(_captors[entity]);
                if (captor == null || !captor.IsAlive() || Vector3.Distance(entity.transform.position, captor.transform.position) > 25f)
                {
                    ReleaseHostage(entity);
                    return;
                }

                BasePlayer npc = entity as BasePlayer;
                if (npc != null && !npc.isMounted)
                {
                    UpdateCaptorTrail(captor);
                    MimicCaptorFrames(npc, captor);
                }
            }
        }

        private void ProcessAimTimer(BaseEntity entity, BasePlayer aimer)
        {
            if (!_aimTimers.ContainsKey(entity)) _aimTimers[entity] = 0f;

            _aimTimers[entity] += 0.03f; 
            float progress = _aimTimers[entity];

            string color = progress > (_config.RequiredAimTime * 0.75f) ? "<color=red>" : "<color=yellow>";
            string text = $"{color}STICK-UP: {(_config.RequiredAimTime - progress):F1}s</color>";
            
            aimer.SendConsoleCommand("ddraw.text", 0.04f, Color.white, entity.CenterPoint() + new Vector3(0, 0.45f, 0), text);

            if (progress >= _config.RequiredAimTime)
            {
                _aimTimers.Remove(entity);
                EnterSurrenderState(entity, aimer);
            }
        }

        private void UpdateCaptorTrail(BasePlayer captor)
        {
            if (!_captorTrails.ContainsKey(captor.userID))
                _captorTrails[captor.userID] = new Queue<CaptorFrame>();

            Queue<CaptorFrame> trail = _captorTrails[captor.userID];
            
            // Record frames with higher precision for movement/jumping/looking
            if (trail.Count == 0 || Vector3.Distance(trail.Last().Position, captor.transform.position) > 0.02f || Quaternion.Angle(trail.Last().Rotation, captor.transform.rotation) > 1.0f)
            {
                trail.Enqueue(new CaptorFrame
                {
                    Position = captor.transform.position,
                    Rotation = captor.transform.rotation,
                    Pitch = captor.eyes.rotation.eulerAngles.x,
                    Yaw = captor.eyes.rotation.eulerAngles.y,
                    IsSprinting = captor.IsRunning(),
                    IsCrouching = captor.IsDucked(),
                    IsOnGround = captor.IsOnGround()
                });
            }

            while (trail.Count > 60) trail.Dequeue();
        }

        private void MimicCaptorFrames(BasePlayer npc, BasePlayer captor)
        {
            if (!_captorTrails.ContainsKey(captor.userID) || _captorTrails[captor.userID].Count == 0) return;

            float distToCaptor = Vector3.Distance(npc.transform.position, captor.transform.position);
            
            if (distToCaptor > _config.FollowDistance || _captorTrails[captor.userID].Count > 20)
            {
                CaptorFrame targetFrame = _captorTrails[captor.userID].Peek();
                
                if (Vector3.Distance(npc.transform.position, targetFrame.Position) < 0.15f && _captorTrails[captor.userID].Count > 1)
                {
                    _captorTrails[captor.userID].Dequeue();
                    targetFrame = _captorTrails[captor.userID].Peek();
                }

                float baseSpeed = targetFrame.IsSprinting ? 6.5f : 3.5f;
                float catchUpMultiplier = Mathf.Clamp(distToCaptor / _config.FollowDistance, 1.0f, 2.5f);
                float finalSpeed = baseSpeed * catchUpMultiplier;

                // Position Sync (Handles jumping via Y-coord mimicry)
                Vector3 newPos = Vector3.MoveTowards(npc.transform.position, targetFrame.Position, Time.deltaTime * finalSpeed);
                npc.ServerPosition = newPos;
                
                // Body Rotation Sync
                npc.transform.rotation = Quaternion.Lerp(npc.transform.rotation, targetFrame.Rotation, Time.deltaTime * 20f);

                // Animation & State Sync
                npc.modelState.SetFlag(ModelState.Flag.OnGround, targetFrame.IsOnGround);
                npc.modelState.SetFlag(ModelState.Flag.Sprinting, targetFrame.IsSprinting || catchUpMultiplier > 1.5f);
                npc.modelState.SetFlag(ModelState.Flag.Ducked, targetFrame.IsCrouching);

                // Mirror head/eye angles (Look around exactly like player)
                TrySetViewAngles(npc, targetFrame.Pitch, targetFrame.Yaw);

                npc.TransformChanged();
                npc.SendNetworkUpdateImmediate();
            }
            else
            {
                // Idle state mimicry (Still look where player is looking)
                npc.modelState.SetFlag(ModelState.Flag.Sprinting, false);
                npc.modelState.SetFlag(ModelState.Flag.Ducked, captor.IsDucked());
                npc.modelState.SetFlag(ModelState.Flag.OnGround, captor.IsOnGround());
                TrySetViewAngles(npc, captor.eyes.rotation.eulerAngles.x, captor.eyes.rotation.eulerAngles.y);
                npc.SendNetworkUpdate();
            }
        }

        #endregion

        #region ViewAngle Reflection

        private static MethodInfo miSetViewAngles;

        internal void TrySetViewAngles(BasePlayer player, float pitch, float yaw)
        {
            if (player == null) return;
            var angles = new Vector3(pitch, yaw, 0f);
            try
            {
                var t = typeof(BasePlayer);
                if (miSetViewAngles == null)
                    miSetViewAngles = t.GetMethod("SetViewAngles", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, new[] { typeof(Vector3) }, null);
                
                if (miSetViewAngles != null) miSetViewAngles.Invoke(player, new object[] { angles });
                
                if (player.eyes != null)
                    player.eyes.rotation = Quaternion.Euler(pitch, yaw, 0f);
            }
            catch { }
        }

        #endregion

        #region Surrender & Release

        private void EnterSurrenderState(BaseEntity entity, BasePlayer captor)
        {
            if (entity == null) return;
            _surrenderedMatriarchs.Add(entity);
            
            BasePlayer npc = entity as BasePlayer;
            if (npc != null)
            {
                npc.SetPlayerFlag(BasePlayer.PlayerFlags.Wounded, true);
                npc.SetFlag(BaseEntity.Flags.Reserved1, true); 
                npc.SendNetworkUpdate();
            }
            
            PrintToChat($"<color=#ff4444>[STREET NEWS]</color> {npc?.displayName} has surrendered to {captor.displayName}!");
            
            timer.Once(3.5f, () => {
                if (entity == null || entity.IsDestroyed || _captors.ContainsKey(entity)) return;
                StartHostageFollowing(entity, captor);
            });
        }

        private void StartHostageFollowing(BaseEntity entity, BasePlayer captor)
        {
            if (entity == null || captor == null) return;
            _captors[entity] = captor.userID;
            _surrenderedMatriarchs.Remove(entity);
            
            BasePlayer npc = entity as BasePlayer;
            if (npc != null)
            {
                npc.SetPlayerFlag(BasePlayer.PlayerFlags.Wounded, false);
                npc.SetFlag(BaseEntity.Flags.Reserved1, false);
                npc.SendNetworkUpdate();
            }
            
            captor.ChatMessage($"<color=#55ff55>RESTRAINED:</color> {npc?.displayName} is now mimicking your steps.");
        }

        private void ReleaseHostage(BaseEntity entity)
        {
            if (entity == null) return;
            ulong captorId;
            if (_captors.TryGetValue(entity, out captorId)) _captorTrails.Remove(captorId);

            _captors.Remove(entity);
            _surrenderedMatriarchs.Remove(entity);
            
            BasePlayer npc = entity as BasePlayer;
            if (npc != null)
            {
                npc.SetPlayerFlag(BasePlayer.PlayerFlags.Wounded, false);
                npc.SetFlag(BaseEntity.Flags.Reserved1, false);
                npc.modelState.SetFlag(ModelState.Flag.Sprinting, false);
                npc.modelState.SetFlag(ModelState.Flag.Ducked, false);
                npc.modelState.SetFlag(ModelState.Flag.OnGround, true);
                if (npc.isMounted) npc.DismountObject();
                npc.SendNetworkUpdate();
            }
            
            PrintToChat($"<color=#55ff55>[STREET NEWS]</color> {npc?.displayName} has been released.");
        }

        private bool IsLookingAt(BasePlayer player, BaseEntity target)
        {
            Vector3 directionToTarget = (target.transform.position - player.eyes.position).normalized;
            float dot = Vector3.Dot(player.eyes.HeadForward(), directionToTarget);
            return dot > 0.91f; 
        }

        private bool IsHoldingWeapon(BasePlayer player)
        {
            Item item = player.GetActiveItem();
            if (item == null) return false;
            return item.info.category == ItemCategory.Weapon;
        }

        #endregion

        #region Retaliation & Rewards

        private void TriggerRetaliation(string gangName, string matriarchName)
        {
            if (string.IsNullOrEmpty(gangName)) return;

            PrintToChat($"<color=#ffd700>★★★ ACHIEVEMENT UNLOCKED ★★★</color>");
            PrintToChat($"<color=#ff4444>STREET JUSTICE:</color> <color=#ffffff>{gangName}</color> is seeking revenge for the death of <color=#ffffff>{matriarchName}</color>!");
            PrintToChat($"<color=#ffd700>RETALIATION LOADOUT GRANTED TO ALL ONLINE MEMBERS.</color>");

            foreach (var player in BasePlayer.activePlayerList)
            {
                string pGang = HoodWars?.Call<string>("GetPlayerGangName", player.userID) ?? "Neutral";
                if (pGang == gangName)
                {
                    Item c4 = ItemManager.CreateByName(ItemC4, 2);
                    if (c4 != null)
                    {
                        player.GiveItem(c4);
                        player.ChatMessage("<color=#ff4444>[GANG LOADOUT]</color> You received 2 C4. Go get your revenge!");
                        Effect.server.Run("assets/prefabs/tools/timed.explosive.charge/effects/impact.prefab", player.transform.position);
                    }
                }
            }
        }

        #endregion

        #region Buffs & Admin

        private void UpdateMatriarchAuras()
        {
            foreach (var kvp in _activeMatriarchs)
            {
                HandleAuraProcessing(kvp.Value.Grandma, kvp.Key, true);
                HandleAuraProcessing(kvp.Value.Mom, kvp.Key, false);
            }

            foreach (var ent in _manualMatriarchs)
            {
                if (ent == null) continue;
                BasePlayer npc = ent as BasePlayer;
                if (npc == null) continue;
                bool isG = npc.displayName.Contains("Grandma");
                HandleAuraProcessing(ent, "Admin_Test", isG);
            }
        }

        private void HandleAuraProcessing(BaseEntity ent, string gang, bool isGrandma)
        {
            if (ent == null || ent.IsDestroyed || _captors.ContainsKey(ent) || _surrenderedMatriarchs.Contains(ent)) return;

            float radius = isGrandma ? _config.Grandma.Radius : _config.Mom.Radius;
            ApplyAura(ent, gang, radius, p => {
                if (isGrandma) {
                    p.metabolism.comfort.Add(0.5f);
                    if (p.health < 100) p.Heal(0.5f);
                } else {
                    foreach (var item in p.inventory.containerWear.itemList) {
                        if (item.hasCondition && item.condition < item.maxCondition) { item.condition += 1.0f; item.MarkDirty(); }
                    }
                }
            });
        }

        private void ApplyAura(BaseEntity source, string gangName, float radius, Action<BasePlayer> effect)
        {
            List<BasePlayer> nearby = new List<BasePlayer>();
            Vis.Entities(source.transform.position, radius, nearby);
            foreach (var player in nearby)
            {
                string pGang = HoodWars != null ? HoodWars.Call<string>("GetPlayerGangName", player.userID) : "Admin_Test";
                if (pGang == gangName || gangName == "Admin_Test") effect(player);
            }
        }

        private void DistributeGrandmaMeals()
        {
            foreach (var kvp in _activeMatriarchs)
            {
                BaseEntity grandma = kvp.Value.Grandma;
                if (grandma == null || grandma.IsDestroyed || _captors.ContainsKey(grandma)) continue;

                List<BasePlayer> nearby = new List<BasePlayer>();
                Vis.Entities(grandma.transform.position, _config.Grandma.Radius, nearby);
                
                BasePlayer luckyMember = nearby.FirstOrDefault(p => (HoodWars?.Call<string>("GetPlayerGangName", p.userID) ?? "Neutral") == kvp.Key);
                if (luckyMember != null)
                {
                    Item meal = ItemManager.CreateByName("porkbeans", 1);
                    if (meal != null)
                    {
                        luckyMember.GiveItem(meal);
                        luckyMember.ChatMessage($"<color=#55ff55>{_config.Grandma.Name}:</color> Here you go, baby. Eat something.");
                    }
                }
            }
        }

        [ChatCommand("gspawn")]
        private void CmdGSpawn(BasePlayer player, string command, string[] args)
        {
            if (!player.IsAdmin && !permission.UserHasPermission(player.UserIDString, PermAdmin)) return;
            if (args.Length < 1) { player.ChatMessage("Usage: /gspawn <grandma|mom>"); return; }

            bool isGrandma = args[0].ToLower() == "grandma";
            BaseEntity ent = InternalSpawn(player.transform.position, "TestHood", isGrandma);
            if (ent != null)
            {
                _manualMatriarchs.Add(ent);
                player.ChatMessage($"Spawned {args[0]}. Aim for 14s. Look-around mimicry active.");
            }
        }

        [ChatCommand("gclear")]
        private void CmdGClear(BasePlayer player)
        {
            if (!player.IsAdmin && !permission.UserHasPermission(player.UserIDString, PermAdmin)) return;
            foreach (var ent in _manualMatriarchs) if (ent != null) ent.Kill();
            _manualMatriarchs.Clear();
            player.ChatMessage("Cleared manual NPCs.");
        }

        #endregion

        #region Hooks

        private object OnEntityTakeDamage(BaseCombatEntity entity, HitInfo info)
        {
            if (entity == null || info == null) return null;

            bool isMatriarch = false;
            foreach (var set in _activeMatriarchs.Values)
                if (set.Grandma == entity || set.Mom == entity) { isMatriarch = true; break; }
            if (!isMatriarch) isMatriarch = _manualMatriarchs.Contains(entity);

            if (isMatriarch)
            {
                if (info.damageTypes.Has(Rust.DamageType.Collision) || 
                    info.damageTypes.Has(Rust.DamageType.Fall) || 
                    info.damageTypes.Has(Rust.DamageType.Drowned)) 
                    return true;
                
                if (_config.InvulnerableWhileSurrendered && _surrenderedMatriarchs.Contains(entity)) 
                    return true;
            }
            return null;
        }

        private void OnEntityDeath(BaseCombatEntity entity, HitInfo info)
        {
            if (entity == null) return;
            string gangOwner = "";
            bool wasG = false;

            foreach(var kvp in _activeMatriarchs)
            {
                if (kvp.Value.Grandma == entity) { gangOwner = kvp.Key; wasG = true; break; }
                if (kvp.Value.Mom == entity) { gangOwner = kvp.Key; wasG = false; break; }
            }

            if (string.IsNullOrEmpty(gangOwner)) return;

            TriggerRetaliation(gangOwner, wasG ? "Grandma" : "Mom");

            _captors.Remove(entity);
            _surrenderedMatriarchs.Remove(entity);
            
            float penalty = wasG ? _config.Grandma.InfluencePenalty : _config.Mom.InfluencePenalty;
            if (TurfGraffiti != null) TurfGraffiti.Call("ReduceInfluence", gangOwner, penalty);

            timer.Once(_config.RespawnTimeSeconds, () => SpawnMatriarchAtHQ(gangOwner, wasG));
        }

        #endregion
    }
}

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